Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bin/sugar-emulator9
-rw-r--r--bin/sugar-session18
-rw-r--r--bin/sugar.in6
-rw-r--r--configure.ac6
-rw-r--r--data/sugar.schemas.in3
-rw-r--r--extensions/cpsection/Makefile.am2
-rw-r--r--extensions/cpsection/aboutcomputer/model.py2
-rw-r--r--extensions/cpsection/language/view.py10
-rw-r--r--extensions/cpsection/network/view.py21
-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.py156
-rwxr-xr-xextensions/cpsection/updater/model.py292
-rw-r--r--extensions/cpsection/updater/view.py382
-rw-r--r--extensions/deviceicon/battery.py8
-rw-r--r--extensions/deviceicon/network.py50
-rw-r--r--extensions/deviceicon/volume.py2
-rw-r--r--po/POTFILES.in4
-rw-r--r--po/fr.po392
-rw-r--r--po/it.po131
-rw-r--r--po/ne.po212
-rw-r--r--po/nl.po133
-rw-r--r--src/jarabe/controlpanel/cmd.py3
-rw-r--r--src/jarabe/controlpanel/gui.py2
-rw-r--r--src/jarabe/desktop/Makefile.am1
-rw-r--r--src/jarabe/desktop/activitieslist.py132
-rw-r--r--src/jarabe/desktop/favoriteslayout.py6
-rw-r--r--src/jarabe/desktop/favoritesview.py53
-rw-r--r--src/jarabe/desktop/homebox.py49
-rw-r--r--src/jarabe/desktop/homewindow.py9
-rw-r--r--src/jarabe/desktop/keydialog.py6
-rw-r--r--src/jarabe/desktop/meshbox.py44
-rwxr-xr-xsrc/jarabe/desktop/proc_smaps.py107
-rw-r--r--src/jarabe/desktop/schoolserver.py6
-rw-r--r--src/jarabe/frame/activitiestray.py36
-rw-r--r--src/jarabe/frame/clipboard.py20
-rw-r--r--src/jarabe/frame/clipboardicon.py2
-rw-r--r--src/jarabe/frame/clipboardmenu.py16
-rw-r--r--src/jarabe/frame/clipboardobject.py20
-rw-r--r--src/jarabe/frame/clipboardpanelwindow.py6
-rw-r--r--src/jarabe/frame/clipboardtray.py18
-rw-r--r--src/jarabe/frame/frame.py4
-rw-r--r--src/jarabe/frame/framewindow.py3
-rw-r--r--src/jarabe/frame/zoomtoolbar.py16
-rw-r--r--src/jarabe/intro/window.py5
-rw-r--r--src/jarabe/journal/Makefile.am2
-rw-r--r--src/jarabe/journal/__init__.py15
-rw-r--r--src/jarabe/journal/collapsedentry.py397
-rw-r--r--src/jarabe/journal/expandedentry.py40
-rw-r--r--src/jarabe/journal/journalactivity.py21
-rw-r--r--src/jarabe/journal/journalentrybundle.py5
-rw-r--r--src/jarabe/journal/journaltoolbox.py49
-rw-r--r--src/jarabe/journal/listmodel.py204
-rw-r--r--src/jarabe/journal/listview.py777
-rw-r--r--src/jarabe/journal/misc.py15
-rw-r--r--src/jarabe/journal/model.py124
-rw-r--r--src/jarabe/journal/objectchooser.py69
-rw-r--r--src/jarabe/journal/palettes.py8
-rw-r--r--src/jarabe/journal/volumestoolbar.py6
-rw-r--r--src/jarabe/model/buddy.py11
-rw-r--r--src/jarabe/model/bundleregistry.py52
-rw-r--r--src/jarabe/model/filetransfer.py21
-rw-r--r--src/jarabe/model/friends.py8
-rw-r--r--src/jarabe/model/neighborhood.py10
-rw-r--r--src/jarabe/model/network.py39
-rw-r--r--src/jarabe/model/notifications.py4
-rw-r--r--src/jarabe/model/owner.py4
-rw-r--r--src/jarabe/model/screen.py21
-rw-r--r--src/jarabe/model/shell.py28
-rw-r--r--src/jarabe/view/buddymenu.py13
-rw-r--r--src/jarabe/view/keyhandler.py172
-rw-r--r--src/jarabe/view/launcher.py3
-rw-r--r--src/jarabe/view/palettes.py84
-rw-r--r--src/jarabe/view/service.py19
-rw-r--r--src/jarabe/view/tabbinghandler.py37
-rw-r--r--src/jarabe/view/viewsource.py12
78 files changed, 2775 insertions, 1949 deletions
diff --git a/bin/sugar-emulator b/bin/sugar-emulator
index acd5976..7f06609 100644
--- a/bin/sugar-emulator
+++ b/bin/sugar-emulator
@@ -88,11 +88,10 @@ def _start_xephyr(dpi, dimensions, fullscreen):
tries -= 1
time.sleep(0.1)
-def _start_matchbox():
- cmd = ['matchbox-window-manager']
+def _start_window_manager():
+ cmd = ['metacity']
- cmd.extend(['-use_titlebar', 'no'])
- cmd.extend(['-theme', 'sugar'])
+ cmd.extend(['--no-force-fullscreen'])
gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
@@ -157,7 +156,7 @@ def main():
if not args:
command.append('sugar')
else:
- _start_matchbox()
+ _start_window_manager()
if args[0].endswith('.py'):
command.append('python')
diff --git a/bin/sugar-session b/bin/sugar-session
index 6a5e9a4..c891bdb 100644
--- a/bin/sugar-session
+++ b/bin/sugar-session
@@ -45,7 +45,7 @@ gettext.bindtextdomain('sugar', config.locale_path)
gettext.bindtextdomain('sugar-toolkit', config.locale_path)
gettext.textdomain('sugar')
-from jarabe.desktop.homewindow import HomeWindow
+from jarabe.desktop import homewindow
from jarabe.model import sound
from jarabe.view import launcher
from jarabe import intro
@@ -117,9 +117,10 @@ def setup_journal_cb():
from jarabe.journal import journalactivity
journalactivity.start()
-def show_software_updates_cb(home_window):
+def show_software_updates_cb():
logging.debug('STARTUP: show_software_updates_cb')
if os.path.isfile(os.path.expanduser('~/.sugar-update')):
+ home_window = homewindow.get_instance()
home_window.get_home_box().show_software_updates_alert()
def setup_notification_service_cb():
@@ -175,14 +176,17 @@ def main():
cleanup_logs()
logger.start('shell')
- intro.check_profile()
-
client = gconf.client_get_default()
- timezone = client.get_string('/desktop/sugar/date/timezone')
+ client.set_string('/desktop/gnome/peripherals/mouse/cursor_theme', 'sugar')
+ client.set_string('/apps/metacity/general/mouse_button_modifier',
+ 'disabled')
+ timezone = client.get_string('/desktop/sugar/date/timezone')
if timezone is not None and timezone:
os.environ['TZ'] = timezone
+ intro.check_profile()
+
start_ui_service()
start_session_manager()
@@ -195,7 +199,7 @@ def main():
launcher.setup()
- home_window = HomeWindow()
+ home_window = homewindow.get_instance()
home_window.show()
gobject.idle_add(unfreeze_dcon_cb)
@@ -204,7 +208,7 @@ def main():
gobject.idle_add(setup_journal_cb)
gobject.idle_add(setup_notification_service_cb)
gobject.idle_add(setup_file_transfer_cb)
- gobject.idle_add(show_software_updates_cb, home_window)
+ gobject.idle_add(show_software_updates_cb)
try:
import xklavier
diff --git a/bin/sugar.in b/bin/sugar.in
index 3c9b9b6..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
@@ -16,7 +19,6 @@ if [ -f ~/.sugar/debug ]; then
. ~/.sugar/debug
fi
-matchbox-window-manager -use_titlebar no -theme sugar \
- -kbdconfig @prefix@/share/sugar/data/kbdconfig &
+metacity --no-force-fullscreen &
exec sugar-session
diff --git a/configure.ac b/configure.ac
index 1f95513..c781cc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT([Sugar],[0.84.1],[],[sugar])
+AC_INIT([Sugar],[0.85.3],[],[sugar])
AC_PREREQ([2.59])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([configure.ac])
-SUCROSE_VERSION="0.84.1"
+SUCROSE_VERSION="0.85.1"
AC_SUBST(SUCROSE_VERSION)
AM_INIT_AUTOMAKE([1.9 foreign dist-bzip2 no-dist-gzip])
@@ -62,6 +62,8 @@ extensions/cpsection/keyboard/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/data/sugar.schemas.in b/data/sugar.schemas.in
index d84e062..8c43930 100644
--- a/data/sugar.schemas.in
+++ b/data/sugar.schemas.in
@@ -175,9 +175,8 @@
<type>bool</type>
<default>true</default>
<locale name="C">
- <!-- FIXME: Enable these after string freeze is over
<short>Show Log out</short>
- <long>If TRUE, Sugar will show a "Log out" option.</long>-->
+ <long>If TRUE, Sugar will show a "Log out" option.</long>
</locale>
</schema>
diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am
index 4bee4ff..dd0a6b8 100644
--- a/extensions/cpsection/Makefile.am
+++ b/extensions/cpsection/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = aboutme aboutcomputer datetime frame keyboard language network power
+SUBDIRS = aboutme aboutcomputer datetime frame keyboard language network power updater
sugardir = $(pkgdatadir)/extensions/cpsection
sugar_PYTHON = __init__.py
diff --git a/extensions/cpsection/aboutcomputer/model.py b/extensions/cpsection/aboutcomputer/model.py
index 6cddb4c..898d79c 100644
--- a/extensions/cpsection/aboutcomputer/model.py
+++ b/extensions/cpsection/aboutcomputer/model.py
@@ -111,7 +111,7 @@ def _read_file(path):
value = value.strip('\n')
return value
else:
- _logger.debug('No information in file or directory: %s' % path)
+ _logger.debug('No information in file or directory: %s', path)
return None
def get_license():
diff --git a/extensions/cpsection/language/view.py b/extensions/cpsection/language/view.py
index 30dc05d..a896aa5 100644
--- a/extensions/cpsection/language/view.py
+++ b/extensions/cpsection/language/view.py
@@ -1,4 +1,5 @@
# Copyright (C) 2008, OLPC
+# Copyright (C) 2009, Simon Schampijer
#
# 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
@@ -52,6 +53,15 @@ class Language(SectionView):
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
+ explanation = gettext.gettext("Add languages in the order you prefer." \
+ " If a translation is not available,"\
+ " the next in the list will be used.")
+ self._text = gtk.Label(explanation)
+ self._text.set_line_wrap(True)
+ self._text.set_alignment(0, 0)
+ self.pack_start(self._text, False)
+ self._text.show()
+
self._table = gtk.Table(rows=1, columns=3, homogeneous=False)
self.pack_start(self._table, False)
self._table.show()
diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
index ef28f00..588daeb 100644
--- a/extensions/cpsection/network/view.py
+++ b/extensions/cpsection/network/view.py
@@ -49,13 +49,16 @@ class Network(SectionView):
self._radio_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
self._jabber_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ workspace = gtk.VBox()
+ workspace.show()
+
separator_wireless = gtk.HSeparator()
- self.pack_start(separator_wireless, expand=False)
+ workspace.pack_start(separator_wireless, expand=False)
separator_wireless.show()
label_wireless = gtk.Label(_('Wireless'))
label_wireless.set_alignment(0, 0)
- self.pack_start(label_wireless, expand=False)
+ workspace.pack_start(label_wireless, expand=False)
label_wireless.show()
box_wireless = gtk.VBox()
box_wireless.set_border_width(style.DEFAULT_SPACING * 2)
@@ -105,16 +108,16 @@ class Network(SectionView):
box_wireless.pack_start(box_clear_history, expand=False)
box_clear_history.show()
- self.pack_start(box_wireless, expand=False)
+ workspace.pack_start(box_wireless, expand=False)
box_wireless.show()
separator_mesh = gtk.HSeparator()
- self.pack_start(separator_mesh, False)
+ workspace.pack_start(separator_mesh, False)
separator_mesh.show()
label_mesh = gtk.Label(_('Collaboration'))
label_mesh.set_alignment(0, 0)
- self.pack_start(label_mesh, expand=False)
+ workspace.pack_start(label_mesh, expand=False)
label_mesh.show()
box_mesh = gtk.VBox()
box_mesh.set_border_width(style.DEFAULT_SPACING * 2)
@@ -161,9 +164,15 @@ class Network(SectionView):
self._jabber_alert.props.msg = self.restart_msg
self._jabber_alert.show()
- self.pack_start(box_mesh, expand=False)
+ workspace.pack_start(box_mesh, expand=False)
box_mesh.show()
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ scrolled.add_with_viewport(workspace)
+ scrolled.show()
+ self.add(scrolled)
+
self.setup()
def setup(self):
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..443fd1c
--- /dev/null
+++ b/extensions/cpsection/updater/backends/aslo.py
@@ -0,0 +1,156 @@
+#!/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..102edea
--- /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/extensions/deviceicon/battery.py b/extensions/deviceicon/battery.py
index 76b1565..edfcce4 100644
--- a/extensions/deviceicon/battery.py
+++ b/extensions/deviceicon/battery.py
@@ -189,28 +189,28 @@ class DeviceModel(gobject.GObject):
try:
return self._battery.GetProperty(_LEVEL_PROP)
except dbus.DBusException:
- logging.error('Cannot access %s' % _LEVEL_PROP)
+ logging.error('Cannot access %s', _LEVEL_PROP)
return 0
def _get_charging(self):
try:
return self._battery.GetProperty(_CHARGING_PROP)
except dbus.DBusException:
- logging.error('Cannot access %s' % _CHARGING_PROP)
+ logging.error('Cannot access %s', _CHARGING_PROP)
return False
def _get_discharging(self):
try:
return self._battery.GetProperty(_DISCHARGING_PROP)
except dbus.DBusException:
- logging.error('Cannot access %s' % _DISCHARGING_PROP)
+ logging.error('Cannot access %s', _DISCHARGING_PROP)
return False
def _get_present(self):
try:
return self._battery.GetProperty(_PRESENT_PROP)
except dbus.DBusException:
- logging.error('Cannot access %s' % _PRESENT_PROP)
+ logging.error('Cannot access %s', _PRESENT_PROP)
return False
def do_get_property(self, pspec):
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
index 0abf25e..0803936 100644
--- a/extensions/deviceicon/network.py
+++ b/extensions/deviceicon/network.py
@@ -1,5 +1,6 @@
#
# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
#
# 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
@@ -17,7 +18,7 @@
from gettext import gettext as _
import logging
-import sha
+import hashlib
import socket
import struct
@@ -124,6 +125,12 @@ class WirelessPalette(Palette):
self._set_channel(frequency)
self._set_ip_address(iaddress)
self._disconnect_item.show()
+
+ def set_disconnected(self):
+ self.props.primary_text = ''
+ self.props.secondary_text = ''
+ self._disconnect_item.hide()
+ self.set_content(None)
def __disconnect_activate_cb(self, menuitem):
self.emit('deactivate-connection')
@@ -208,6 +215,7 @@ class WirelessDeviceView(ToolButton):
self._device_props = None
self._flags = 0
self._name = ''
+ self._mode = network.NM_802_11_MODE_UNKNOWN
self._strength = 0
self._frequency = 0
self._device_state = None
@@ -301,8 +309,12 @@ class WirelessDeviceView(ToolButton):
self._update_properties(properties)
def _update_properties(self, properties):
+ if 'Mode' in properties:
+ self._mode = properties['Mode']
+ self._color = None
if 'Ssid' in properties:
self._name = properties['Ssid']
+ self._color = None
if 'Strength' in properties:
self._strength = properties['Strength']
if 'Flags' in properties:
@@ -310,14 +322,21 @@ class WirelessDeviceView(ToolButton):
if 'Frequency' in properties:
self._frequency = properties['Frequency']
- sh = sha.new()
- data = self._name + hex(self._flags)
- sh.update(data)
- h = hash(sh.digest())
- idx = h % len(xocolor.colors)
-
- self._color = xocolor.XoColor('%s,%s' % (xocolor.colors[idx][0],
- xocolor.colors[idx][1]))
+ if self._color == None:
+ if self._mode == network.NM_802_11_MODE_ADHOC:
+ encoded_color = self._name.split("#", 1)
+ if len(encoded_color) == 2:
+ self._color = xocolor.XoColor('#' + encoded_color[1])
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ sha_hash = hashlib.sha1()
+ data = self._name + hex(self._flags)
+ sha_hash.update(data)
+ digest = hash(sha_hash.digest())
+ index = digest % len(xocolor.colors)
+
+ self._color = xocolor.XoColor('%s,%s' %
+ (xocolor.colors[index][0],
+ xocolor.colors[index][1]))
self._update()
def __get_all_ap_props_reply_cb(self, properties):
@@ -362,6 +381,12 @@ class WirelessDeviceView(ToolButton):
address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
self._palette.set_connected(self._frequency, address)
self._icon.props.pulsing = False
+ else:
+ self._icon.props.badge_name = None
+ self._icon.props.pulsing = False
+ self._icon.props.pulse_color = self._inactive_color
+ self._icon.props.base_color = self._inactive_color
+ self._palette.set_disconnected()
def _update_color(self):
self._icon.props.base_color = self._color
@@ -386,7 +411,8 @@ class WirelessDeviceView(ToolButton):
def __create_connection_cb(self, palette, data=None):
client = gconf.client_get_default()
nick = client.get_string('/desktop/sugar/user/nick')
- connection_name = _('%s\'s network') % nick
+ color = client.get_string('/desktop/sugar/user/color')
+ connection_name = _('%s\'s network %s') % (nick, color)
connection = network.find_connection(connection_name)
if connection is None:
@@ -394,11 +420,9 @@ class WirelessDeviceView(ToolButton):
settings.connection.id = 'Auto ' + connection_name
settings.connection.uuid = unique_id()
settings.connection.type = '802-11-wireless'
- settings.connection.mode = 'adhoc'
settings.wireless.ssid = dbus.ByteArray(connection_name)
- settings.wireless.channel = 'bg'
+ settings.wireless.band = 'bg'
settings.wireless.mode = 'adhoc'
-
settings.ip4_config = IP4Config()
settings.ip4_config.method = 'shared'
diff --git a/extensions/deviceicon/volume.py b/extensions/deviceicon/volume.py
index 5c4c49e..4ccae7a 100644
--- a/extensions/deviceicon/volume.py
+++ b/extensions/deviceicon/volume.py
@@ -97,7 +97,7 @@ def _mount(volume, tray):
volume.mount(gtk.MountOperation(tray.get_toplevel()), _mount_cb)
def _mount_cb(volume, result):
- logging.debug('_mount_cb %r %r' % (volume, result))
+ logging.debug('_mount_cb %r %r', volume, result)
volume.mount_finish(result)
def _mount_added_cb(volume_monitor, mount, tray):
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2e46597..a93f555 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -20,6 +20,8 @@ extensions/cpsection/network/view.py
extensions/cpsection/power/__init__.py
extensions/cpsection/power/model.py
extensions/cpsection/power/view.py
+extensions/cpsection/updater/__init__.py
+extensions/cpsection/updater/view.py
extensions/deviceicon/battery.py
extensions/deviceicon/network.py
extensions/deviceicon/speaker.py
@@ -30,6 +32,7 @@ src/jarabe/controlpanel/cmd.py
src/jarabe/controlpanel/gui.py
src/jarabe/controlpanel/sectionview.py
src/jarabe/controlpanel/toolbar.py
+src/jarabe/desktop/activitieslist.py
src/jarabe/desktop/favoriteslayout.py
src/jarabe/desktop/favoritesview.py
src/jarabe/desktop/homebox.py
@@ -42,7 +45,6 @@ src/jarabe/frame/clipboardobject.py
src/jarabe/frame/devicestray.py
src/jarabe/frame/zoomtoolbar.py
src/jarabe/intro/window.py
-src/jarabe/journal/collapsedentry.py
src/jarabe/journal/detailview.py
src/jarabe/journal/expandedentry.py
src/jarabe/journal/journalactivity.py
diff --git a/po/fr.po b/po/fr.po
index 3e6a802..151c438 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-03-19 00:30-0400\n"
-"PO-Revision-Date: 2009-05-01 11:59-0400\n"
+"POT-Creation-Date: 2009-08-21 00:33-0400\n"
+"PO-Revision-Date: 2009-08-24 16:27-0400\n"
"Last-Translator: samy boutayeb <s.boutayeb@free.fr>\n"
"Language-Team: French <traduc@traduc.org>\n"
"Language: fr\n"
@@ -65,50 +65,43 @@ msgstr "Cliquer pour changer de couleur :"
msgid "About my Computer"
msgstr "Mon ordinateur"
-#: ../extensions/cpsection/aboutcomputer/model.py:26
+#: ../extensions/cpsection/aboutcomputer/model.py:28
msgid "Not available"
msgstr "Non disponible"
-#: ../extensions/cpsection/aboutcomputer/view.py:59
+#: ../extensions/cpsection/aboutcomputer/view.py:60
msgid "Identity"
msgstr "Identité"
-#: ../extensions/cpsection/aboutcomputer/view.py:68
+#: ../extensions/cpsection/aboutcomputer/view.py:69
msgid "Serial Number:"
msgstr "Numéro de série :"
-#: ../extensions/cpsection/aboutcomputer/view.py:90
+#: ../extensions/cpsection/aboutcomputer/view.py:91
msgid "Software"
msgstr "Logiciel"
-#: ../extensions/cpsection/aboutcomputer/view.py:99
+#: ../extensions/cpsection/aboutcomputer/view.py:100
msgid "Build:"
msgstr "Version :"
-#: ../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 "Micrologiciel :"
-#: ../extensions/cpsection/aboutcomputer/view.py:145
+#: ../extensions/cpsection/aboutcomputer/view.py:146
msgid "Wireless Firmware:"
msgstr "Micrologiciel sans fil :"
-#: ../extensions/cpsection/aboutcomputer/view.py:168
+#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
msgstr "Copyright et licence"
-#: ../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 ; et "
-"contributeurs."
-
-#: ../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 ""
"License). Vous êtes autorisé à le modifier et/ou à en distribuer des copies "
"aux conditions spécifiées."
-#: ../extensions/cpsection/aboutcomputer/view.py:195
+#: ../extensions/cpsection/aboutcomputer/view.py:196
msgid "Full license:"
msgstr "Licence complète :"
@@ -132,7 +125,7 @@ msgstr "Date & heure"
msgid "Error timezone does not exist."
msgstr "Erreur : le fuseau horaire n'existe pas."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:19
+#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:21
msgid "Timezone"
msgstr "Fuseau horaire"
@@ -171,7 +164,7 @@ msgid "Edge"
msgstr "Bord"
#: ../extensions/cpsection/language/__init__.py:21
-#: ../extensions/cpsection/language/view.py:32
+#: ../extensions/cpsection/language/view.py:33
msgid "Language"
msgstr "Langue"
@@ -189,6 +182,14 @@ msgstr "La langue associée au code = %s n'a pas pu être déterminée."
msgid "Sorry I do not speak '%s'."
msgstr "Désolé je ne parle pas '%s'."
+#: ../extensions/cpsection/language/view.py:56
+msgid ""
+"Add languages in the order you prefer. If a translation is not available, "
+"the next in the list will be used."
+msgstr ""
+"Ajoutez des langues dans l'ordre souhaité. Si la traduction n'est pas "
+"disponible, la suivante dans la liste sera utilisée."
+
#: ../extensions/cpsection/network/__init__.py:21
#: ../extensions/cpsection/network/view.py:28
msgid "Network"
@@ -206,33 +207,33 @@ msgstr "Argument 'radio' spécifié incorrect. Utiliser marche/arrêt."
msgid "Error in specified argument use 0/1."
msgstr "Argument spécifié incorrect. Utiliser 0/1."
-#: ../extensions/cpsection/network/view.py:56
+#: ../extensions/cpsection/network/view.py:59
msgid "Wireless"
msgstr "Réseau sans fil"
-#: ../extensions/cpsection/network/view.py:64
+#: ../extensions/cpsection/network/view.py:67
msgid "Turn off the wireless radio to save battery life"
msgstr "Désactiver la radio sans fil pour prolonger la batterie"
-#: ../extensions/cpsection/network/view.py:77
+#: ../extensions/cpsection/network/view.py:80
msgid "Radio"
msgstr "Radio"
-#: ../extensions/cpsection/network/view.py:93
+#: ../extensions/cpsection/network/view.py:96
msgid "Discard network history if you have trouble connecting to the network"
msgstr ""
"Ignorer l'historique du réseau si vous avez du mal à vous connecter au "
"réseau"
-#: ../extensions/cpsection/network/view.py:102
+#: ../extensions/cpsection/network/view.py:105
msgid "Discard network history"
msgstr "Ignorer l'historique du réseau"
-#: ../extensions/cpsection/network/view.py:115
+#: ../extensions/cpsection/network/view.py:118
msgid "Collaboration"
msgstr "Collaboration"
-#: ../extensions/cpsection/network/view.py:123
+#: ../extensions/cpsection/network/view.py:126
msgid ""
"The server is the equivalent of what room you are in; people on the same "
"server will be able to see each other, even when they aren't on the same "
@@ -242,7 +243,7 @@ msgstr ""
"personnes présentes sur le même serveur pourront se voir même si elles ne se "
"trouvent pas sur le même réseau."
-#: ../extensions/cpsection/network/view.py:133
+#: ../extensions/cpsection/network/view.py:136
msgid "Server:"
msgstr "Serveur :"
@@ -298,7 +299,7 @@ msgstr "%(hour)d:%(min).2d restantes"
msgid "Charged"
msgstr "Charge complète"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:44
#, python-format
msgid "IP address: %s"
msgstr "Adresse IP : %s"
@@ -307,50 +308,83 @@ msgstr "Adresse IP : %s"
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
-#: ../extensions/deviceicon/network.py:104
+#: ../extensions/deviceicon/network.py:110
msgid "Disconnect..."
msgstr "Déconnexion..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:248
+#: ../extensions/deviceicon/network.py:114
+msgid "Create new wireless network"
+msgstr "Créer un nouveau réseau sans fil"
+
+#: ../extensions/deviceicon/network.py:120
+#: ../src/jarabe/desktop/meshbox.py:261
msgid "Connecting..."
msgstr "Connexion..."
# 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:124
+#: ../extensions/deviceicon/network.py:186
+#: ../src/jarabe/desktop/meshbox.py:267
msgid "Connected"
msgstr "Connecté"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:146
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:161
msgid "Wired Network"
msgstr "Réseau filaire"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:189
msgid "Speed"
msgstr "Vitesse"
+#: ../extensions/deviceicon/network.py:415
+#, python-format
+msgid "%s's network %s"
+msgstr "Réseau %s %s"
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "Haut-parleurs"
-#: ../extensions/deviceicon/speaker.py:135
+#: ../extensions/deviceicon/speaker.py:133
msgid "Unmute"
msgstr "Activer le son"
-#: ../extensions/deviceicon/speaker.py:138
+#: ../extensions/deviceicon/speaker.py:136
msgid "Mute"
msgstr "Mettre en sourdine"
-#: ../extensions/globalkey/screenshot.py:51
+#: ../extensions/globalkey/screenshot.py:56
+msgid "Mesh"
+msgstr "Réseau maillé"
+
+#: ../extensions/globalkey/screenshot.py:58
+#: ../src/jarabe/frame/zoomtoolbar.py:39
+msgid "Group"
+msgstr "Groupe"
+
+#: ../extensions/globalkey/screenshot.py:60
+#: ../src/jarabe/frame/zoomtoolbar.py:41
+msgid "Home"
+msgstr "Accueil"
+
+#: ../extensions/globalkey/screenshot.py:66
+#: ../src/jarabe/frame/zoomtoolbar.py:43
+msgid "Activity"
+msgstr "Activité"
+
+#: ../extensions/globalkey/screenshot.py:69
msgid "Screenshot"
msgstr "Capture d'écran"
+#: ../extensions/globalkey/screenshot.py:71
+#, python-format
+msgid "Screenshot of \"%s\""
+msgstr "Capture d'écran de \"%s\""
+
#: ../data/sugar.schemas.in.h:1
msgid "Backup URL"
msgstr "Sauvegarde de l'URL"
@@ -394,78 +428,86 @@ msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
msgstr ""
-"Si VRAI, Sugar les autres utilisateurs du serveur Jabber pourront nous "
+"Si VRAI, Sugar permettra aux autres utilisateurs du serveur Jabber de nous "
"retrouver."
#: ../data/sugar.schemas.in.h:10
+msgid "If TRUE, Sugar will show a \"Log out\" option."
+msgstr "Si VRAI, Sugar affichera une option \"Déconnexion\"."
+
+#: ../data/sugar.schemas.in.h:11
msgid "Jabber Server"
msgstr "Serveur Jabber"
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:12
msgid "Layout of the favorites view."
msgstr "Disposition de la vue favorite."
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:13
msgid "Power Automatic"
msgstr "Alimentation automatique"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:14
msgid "Power Automatic."
msgstr "Alimentation automatique."
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:15
msgid "Power Extreme"
msgstr "Alimentation extrême"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:16
msgid "Power Extreme."
msgstr "Alimentation extrême."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:17
msgid "Publish to Gadget"
msgstr "Publication vers Gadget"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:18
msgid "Setting for muting the sound device."
msgstr "Configuration de la mise en sourdine du périphérique audio."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:19
+msgid "Show Log out"
+msgstr "Afficher Déconnexion"
+
+#: ../data/sugar.schemas.in.h:20
msgid "Sound Muted"
msgstr "Audio désactivé"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:22
msgid "Timezone setting for the system."
msgstr "Configuration du fuseau horaire du système."
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:23
msgid "Url of the jabber server to use."
msgstr "URL du serveur Jabber à utiliser."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:24
msgid "Url where the backup is saved to."
msgstr "URL d'enregistrement de la sauvegarde."
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:25
msgid "User Color"
msgstr "Couleurs de l'utilisateur"
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:26
msgid "User Name"
msgstr "Nom de l'utilisateur"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:27
msgid "User name that is used throughout the desktop."
msgstr "Nom identifiant l'utilisateur sur le bureau."
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:28
msgid "Volume Level"
msgstr "Niveau de volume"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:29
msgid "Volume level for the sound device."
msgstr "Niveau de volume du périphérique audio."
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:30
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -536,7 +578,7 @@ msgstr "Relancer pour valider"
msgid "Cancel changes"
msgstr "Abandonner"
-#: ../src/jarabe/controlpanel/gui.py:284 ../src/jarabe/desktop/homebox.py:113
+#: ../src/jarabe/controlpanel/gui.py:284 ../src/jarabe/desktop/homebox.py:70
msgid "Later"
msgstr "Plus tard"
@@ -549,18 +591,72 @@ msgid "Done"
msgstr "Accepter"
#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:111
+#: ../src/jarabe/desktop/homebox.py:68
#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:821
-#: ../src/jarabe/frame/activitiestray.py:849
+#: ../src/jarabe/frame/activitiestray.py:822
+#: ../src/jarabe/frame/activitiestray.py:850
msgid "Cancel"
msgstr "Annuler"
#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:339
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Ok"
msgstr "Ok"
+#: ../src/jarabe/desktop/activitieslist.py:80
+#: ../src/jarabe/journal/listview.py:147
+msgid "Title"
+msgstr "Titre"
+
+#: ../src/jarabe/desktop/activitieslist.py:91
+msgid "Version"
+msgstr "Version"
+
+#: ../src/jarabe/desktop/activitieslist.py:105
+#: ../src/jarabe/journal/listview.py:178
+msgid "Date"
+msgstr "Date"
+
+#: ../src/jarabe/desktop/activitieslist.py:234
+#, python-format
+msgid "Version %s"
+msgstr "Version %s"
+
+#: ../src/jarabe/desktop/activitieslist.py:355
+msgid "Confirm erase"
+msgstr "Confirmer la suppression"
+
+# Conformer la suppression : faut-il supprimer %s définitivement ?
+#: ../src/jarabe/desktop/activitieslist.py:357
+#, python-format
+msgid "Confirm erase: Do you want to permanently erase %s?"
+msgstr "Confirmer la suppression : faut-il supprimer %s définitivement ?"
+
+# self._stop_item = MenuItem(_('Stop download'), 'stock-close')
+# TODO: Implement stopping downloads
+# self._stop_item.connect('activate', self._stop_item_activate_cb)
+# self.append_menu_item(self._stop_item)
+#: ../src/jarabe/desktop/activitieslist.py:361
+#: ../src/jarabe/frame/clipboardmenu.py:62
+#: ../src/jarabe/view/viewsource.py:218
+msgid "Keep"
+msgstr "Conserver"
+
+#: ../src/jarabe/desktop/activitieslist.py:364
+#: ../src/jarabe/desktop/activitieslist.py:406
+#: ../src/jarabe/journal/journaltoolbox.py:360
+#: ../src/jarabe/journal/palettes.py:112
+msgid "Erase"
+msgstr "Supprimer"
+
+#: ../src/jarabe/desktop/activitieslist.py:427
+msgid "Remove favorite"
+msgstr "Retirer le favori"
+
+#: ../src/jarabe/desktop/activitieslist.py:431
+msgid "Make favorite"
+msgstr "Ajouter aux favoris"
+
# TRANS: label for the freeform layout in the favorites view
#. TRANS: label for the freeform layout in the favorites view
#: ../src/jarabe/desktop/favoriteslayout.py:116
@@ -591,77 +687,52 @@ msgstr "Boîte"
msgid "Triangle"
msgstr "Triangle"
-#: ../src/jarabe/desktop/favoritesview.py:330
+#: ../src/jarabe/desktop/favoritesview.py:321
msgid "Registration Failed"
msgstr "Echec de l'enregistrement"
-#: ../src/jarabe/desktop/favoritesview.py:331
+#: ../src/jarabe/desktop/favoritesview.py:322
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:333
+#: ../src/jarabe/desktop/favoritesview.py:324
msgid "Registration Successful"
msgstr "Enregistrement réussi"
-#: ../src/jarabe/desktop/favoritesview.py:334
+#: ../src/jarabe/desktop/favoritesview.py:325
msgid "You are now registered with your school server."
msgstr "Vous êtes maintenant enregistré sur le serveur de l'école"
-#: ../src/jarabe/desktop/favoritesview.py:674
+#: ../src/jarabe/desktop/favoritesview.py:658
msgid "Register"
msgstr "S'enregistrer"
-#: ../src/jarabe/desktop/homebox.py:67
-msgid "Confirm erase"
-msgstr "Confirmer la suppression"
-
-# Conformer la suppression : faut-il supprimer %s définitivement ?
-#: ../src/jarabe/desktop/homebox.py:69
-#, python-format
-msgid "Confirm erase: Do you want to permanently erase %s?"
-msgstr "Confirmer la suppression : faut-il supprimer %s définitivement ?"
-
-# self._stop_item = MenuItem(_('Stop download'), 'stock-close')
-# TODO: Implement stopping downloads
-# self._stop_item.connect('activate', self._stop_item_activate_cb)
-# self.append_menu_item(self._stop_item)
-#: ../src/jarabe/desktop/homebox.py:73 ../src/jarabe/frame/clipboardmenu.py:62
-#: ../src/jarabe/view/viewsource.py:218
-msgid "Keep"
-msgstr "Conserver"
-
-#: ../src/jarabe/desktop/homebox.py:76
-#: ../src/jarabe/journal/journaltoolbox.py:357
-#: ../src/jarabe/journal/palettes.py:112 ../src/jarabe/view/palettes.py:153
-msgid "Erase"
-msgstr "Supprimer"
-
-#: ../src/jarabe/desktop/homebox.py:106
+#: ../src/jarabe/desktop/homebox.py:63
msgid "Software Update"
msgstr "Mise à jour logicielle"
-#: ../src/jarabe/desktop/homebox.py:107
+#: ../src/jarabe/desktop/homebox.py:64
msgid "Update your activities to ensure compatibility with your new software"
msgstr "Actualiser les activités pour assurer la compatibilité logicielle"
-#: ../src/jarabe/desktop/homebox.py:116
+#: ../src/jarabe/desktop/homebox.py:73
msgid "Check now"
msgstr "Vérifier maintenant"
-#: ../src/jarabe/desktop/homebox.py:233
+#: ../src/jarabe/desktop/homebox.py:190
msgid "List view"
msgstr "Écran liste"
-#: ../src/jarabe/desktop/homebox.py:234
+#: ../src/jarabe/desktop/homebox.py:191
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:296
+#: ../src/jarabe/desktop/homebox.py:253
msgid "Favorites view"
msgstr "Écran favoris"
-#: ../src/jarabe/desktop/homebox.py:297
+#: ../src/jarabe/desktop/homebox.py:254
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
@@ -681,24 +752,24 @@ msgstr "WPA & WPA2 Personal"
msgid "Wireless Security:"
msgstr "Sécurité sans fil :"
-#: ../src/jarabe/desktop/meshbox.py:132
+#: ../src/jarabe/desktop/meshbox.py:136
msgid "Connect"
msgstr "Connecter"
-#: ../src/jarabe/desktop/meshbox.py:136
+#: ../src/jarabe/desktop/meshbox.py:140
msgid "Disconnect"
msgstr "Déconnecter"
# 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:463
#: ../src/jarabe/frame/activitiestray.py:761
-#: ../src/jarabe/journal/journaltoolbox.py:425
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:66
+#: ../src/jarabe/journal/journaltoolbox.py:428
+#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:64
msgid "Resume"
msgstr "Reprendre"
-#: ../src/jarabe/desktop/meshbox.py:447
+#: ../src/jarabe/desktop/meshbox.py:468
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Rejoindre"
@@ -750,13 +821,13 @@ msgid "Accept"
msgstr "Accepter"
#: ../src/jarabe/frame/activitiestray.py:716
-#: ../src/jarabe/frame/activitiestray.py:839
+#: ../src/jarabe/frame/activitiestray.py:840
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
#: ../src/jarabe/frame/activitiestray.py:750
-#: ../src/jarabe/frame/activitiestray.py:873
+#: ../src/jarabe/frame/activitiestray.py:875
msgid "Dismiss"
msgstr "Refuser"
@@ -783,21 +854,25 @@ msgstr "Ouvrir avec"
msgid "%s clipping"
msgstr "%s coupure"
-#: ../src/jarabe/frame/zoomtoolbar.py:36
+#: ../src/jarabe/frame/zoomtoolbar.py:37
msgid "Neighborhood"
msgstr "Voisinage"
-#: ../src/jarabe/frame/zoomtoolbar.py:38
-msgid "Group"
-msgstr "Groupe"
+#: ../src/jarabe/frame/zoomtoolbar.py:37
+msgid "F1"
+msgstr "F1"
-#: ../src/jarabe/frame/zoomtoolbar.py:40
-msgid "Home"
-msgstr "Accueil"
+#: ../src/jarabe/frame/zoomtoolbar.py:39
+msgid "F2"
+msgstr "F2"
-#: ../src/jarabe/frame/zoomtoolbar.py:42
-msgid "Activity"
-msgstr "Activité"
+#: ../src/jarabe/frame/zoomtoolbar.py:41
+msgid "F3"
+msgstr "F3"
+
+#: ../src/jarabe/frame/zoomtoolbar.py:43
+msgid "F4"
+msgstr "F4"
#: ../src/jarabe/intro/window.py:124
msgid "Click to change color:"
@@ -811,7 +886,6 @@ msgstr "Précédent"
msgid "Next"
msgstr "Suivant"
-#: ../src/jarabe/journal/collapsedentry.py:258
#: ../src/jarabe/journal/expandedentry.py:159
#: ../src/jarabe/journal/palettes.py:66
msgid "Untitled"
@@ -838,84 +912,83 @@ msgstr "Étiquettes :"
msgid "Journal"
msgstr "Journal"
-#: ../src/jarabe/journal/journaltoolbox.py:65
+#: ../src/jarabe/journal/journaltoolbox.py:67
msgid "Search"
msgstr "Rechercher"
-#: ../src/jarabe/journal/journaltoolbox.py:124
+#: ../src/jarabe/journal/journaltoolbox.py:126
msgid "Anytime"
msgstr "N'importe quand"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:128
msgid "Today"
msgstr "Aujourd'hui"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:130
msgid "Since yesterday"
msgstr "Depuis hier"
# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:132
msgid "Past week"
msgstr "Depuis une semaine"
# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
-#: ../src/jarabe/journal/journaltoolbox.py:132
+#: ../src/jarabe/journal/journaltoolbox.py:134
msgid "Past month"
msgstr "Depuis un mois"
# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
-#: ../src/jarabe/journal/journaltoolbox.py:134
+#: ../src/jarabe/journal/journaltoolbox.py:136
msgid "Past year"
msgstr "Depuis une année"
-#: ../src/jarabe/journal/journaltoolbox.py:141
+#: ../src/jarabe/journal/journaltoolbox.py:143
msgid "Anyone"
msgstr "Tout le monde"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:145
msgid "My friends"
msgstr "Mes amis"
-#: ../src/jarabe/journal/journaltoolbox.py:144
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "My class"
msgstr "Ma classe"
# TRANS: Item in a combo box that filters by entry type.
-#. TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:271
+#: ../src/jarabe/journal/journaltoolbox.py:274
msgid "Anything"
msgstr "Tout"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:347
+#: ../src/jarabe/journal/journaltoolbox.py:350
#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Copier"
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:75 ../src/jarabe/view/palettes.py:135
+#: ../src/jarabe/journal/journaltoolbox.py:431
+#: ../src/jarabe/journal/palettes.py:75
msgid "Start"
msgstr "Lancer"
-#: ../src/jarabe/journal/listview.py:40
+#: ../src/jarabe/journal/listview.py:361
msgid "Your Journal is empty"
msgstr "Le journal est vide"
-#: ../src/jarabe/journal/listview.py:41
-msgid "No matching entries "
+#: ../src/jarabe/journal/listview.py:363
+msgid "No matching entries"
msgstr "Aucune entrée correspondante"
-#: ../src/jarabe/journal/listview.py:369
+#: ../src/jarabe/journal/listview.py:374
msgid "Clear search"
msgstr "Effacer la recherche"
-#: ../src/jarabe/journal/misc.py:93
+#: ../src/jarabe/journal/misc.py:92
msgid "No date"
msgstr "Sans date"
@@ -933,11 +1006,11 @@ msgstr ""
msgid "Show Journal"
msgstr "Montre le Journal"
-#: ../src/jarabe/journal/objectchooser.py:147
+#: ../src/jarabe/journal/objectchooser.py:146
msgid "Choose an object"
msgstr "Choisir un objet"
-#: ../src/jarabe/journal/objectchooser.py:152
+#: ../src/jarabe/journal/objectchooser.py:151
#: ../src/jarabe/view/viewsource.py:308
msgid "Close"
msgstr "Fermer"
@@ -1003,37 +1076,33 @@ msgstr "Éteindre"
msgid "Invite to %s"
msgstr "Inviter à %s"
-#: ../src/jarabe/view/palettes.py:47
+#: ../src/jarabe/view/palettes.py:45
msgid "Starting..."
msgstr "Démarrage..."
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:73
+#: ../src/jarabe/view/palettes.py:71
msgid "View Source"
msgstr "Afficher la source"
-#: ../src/jarabe/view/palettes.py:84
+#: ../src/jarabe/view/palettes.py:82
msgid "Stop"
msgstr "Arrêter"
-#: ../src/jarabe/view/palettes.py:171
-msgid "Remove favorite"
-msgstr "Retirer le favori"
-
-#: ../src/jarabe/view/palettes.py:175
-msgid "Make favorite"
-msgstr "Ajouter aux favoris"
+#: ../src/jarabe/view/palettes.py:120
+msgid "Start new"
+msgstr "Commencer un nouveau"
-#: ../src/jarabe/view/palettes.py:238
+#: ../src/jarabe/view/palettes.py:169
msgid "Show contents"
msgstr "Afficher les contenus"
-#: ../src/jarabe/view/palettes.py:260 ../src/jarabe/view/palettes.py:309
+#: ../src/jarabe/view/palettes.py:191 ../src/jarabe/view/palettes.py:241
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d Mo de libre"
-#: ../src/jarabe/view/palettes.py:285
+#: ../src/jarabe/view/palettes.py:216
msgid "Unmount"
msgstr "Démonter"
@@ -1054,6 +1123,12 @@ msgstr "Source du paquet activité"
msgid "View source: %r"
msgstr "Afficher le code source : %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 ; et "
+#~ "contributeurs."
+
#~ msgid "Document"
#~ msgstr "Document"
@@ -1079,9 +1154,6 @@ msgstr "Afficher le code source : %r"
#~ msgid "About my XO"
#~ msgstr "Mon XO"
-#~ msgid "Mesh"
-#~ msgstr "Réseau maillé"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "Connecté au portail du réseau maillé d'école"
diff --git a/po/it.po b/po/it.po
index a9bb231..59af549 100644
--- a/po/it.po
+++ b/po/it.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-04-30 09:08-0400\n"
+"POT-Creation-Date: 2009-06-11 00:31-0400\n"
+"PO-Revision-Date: 2009-07-07 12:04-0400\n"
"Last-Translator: Carlo Falciola <cfalciola@yahoo.it>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: it\n"
@@ -65,49 +65,43 @@ msgstr "Seleziona per cambiare il tuo colore:"
msgid "About my Computer"
msgstr "Il mio Computer"
-#: ../extensions/cpsection/aboutcomputer/model.py:26
+#: ../extensions/cpsection/aboutcomputer/model.py:28
msgid "Not available"
msgstr "Non disponibile"
-#: ../extensions/cpsection/aboutcomputer/view.py:59
+#: ../extensions/cpsection/aboutcomputer/view.py:60
msgid "Identity"
msgstr "Identità"
-#: ../extensions/cpsection/aboutcomputer/view.py:68
+#: ../extensions/cpsection/aboutcomputer/view.py:69
msgid "Serial Number:"
msgstr "Numero di Serie:"
-#: ../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 "Build:"
-#: ../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 "Wireless Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:168
+#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
msgstr "Copyright e Licenza"
-#: ../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; e Contributori."
-
-#: ../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 "
@@ -119,7 +113,7 @@ msgstr ""
"chiunque è il benvenuto per apportare modifiche e migliorie e/o distribuirne "
"copie, alle condizioni descritte nella licenza medesima."
-#: ../extensions/cpsection/aboutcomputer/view.py:195
+#: ../extensions/cpsection/aboutcomputer/view.py:196
msgid "Full license:"
msgstr "Testo della Licenza:"
@@ -300,7 +294,7 @@ msgstr "%(hour)d:%(min).2d rimanenti"
msgid "Charged"
msgstr "Carica"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:43
#, python-format
msgid "IP address: %s"
msgstr "Indirizzo IP: %s"
@@ -309,50 +303,83 @@ msgstr "Indirizzo IP: %s"
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
-#: ../extensions/deviceicon/network.py:104
+#: ../extensions/deviceicon/network.py:109
msgid "Disconnect..."
msgstr "Disconnessione..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:248
+#: ../extensions/deviceicon/network.py:113
+msgid "Create new wireless network"
+msgstr "Crea una nuova rete wireless"
+
+#: ../extensions/deviceicon/network.py:119
+#: ../src/jarabe/desktop/meshbox.py:250
msgid "Connecting..."
msgstr "Connessione..."
# 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 "Connesso"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:139
msgid "Channel"
msgstr "Canale"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:154
msgid "Wired Network"
msgstr "Rete su cavo"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:182
msgid "Speed"
msgstr "Velocità"
+#: ../extensions/deviceicon/network.py:389
+#, python-format
+msgid "%s's network"
+msgstr "rete di %s"
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "I miei Altoparlanti"
-#: ../extensions/deviceicon/speaker.py:135
+#: ../extensions/deviceicon/speaker.py:133
msgid "Unmute"
msgstr "Attiva"
-#: ../extensions/deviceicon/speaker.py:138
+#: ../extensions/deviceicon/speaker.py:136
msgid "Mute"
msgstr "Silenzia"
-#: ../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 "Gruppo"
+
+#: ../extensions/globalkey/screenshot.py:60
+#: ../src/jarabe/frame/zoomtoolbar.py:40
+msgid "Home"
+msgstr "Casa"
+
+#: ../extensions/globalkey/screenshot.py:66
+#: ../src/jarabe/frame/zoomtoolbar.py:42
+msgid "Activity"
+msgstr "Attività"
+
+#: ../extensions/globalkey/screenshot.py:69
msgid "Screenshot"
msgstr "Schermata"
+#: ../extensions/globalkey/screenshot.py:71
+#, python-format
+msgid "Screenshot of \"%s\""
+msgstr "Schermata di \"%s\""
+
#: ../data/sugar.schemas.in.h:1
msgid "Backup URL"
msgstr "URL di salvataggio"
@@ -688,24 +715,24 @@ msgstr "WPA & WPA2 Personal"
msgid "Wireless Security:"
msgstr "Wireless Security:"
-#: ../src/jarabe/desktop/meshbox.py:132
+#: ../src/jarabe/desktop/meshbox.py:134
msgid "Connect"
msgstr "Connetti"
-#: ../src/jarabe/desktop/meshbox.py:136
+#: ../src/jarabe/desktop/meshbox.py:138
msgid "Disconnect"
msgstr "Disconnetti"
# 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 "Riprendi"
-#: ../src/jarabe/desktop/meshbox.py:447
+#: ../src/jarabe/desktop/meshbox.py:454
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Associa"
@@ -795,18 +822,6 @@ msgstr "ritaglio %s"
msgid "Neighborhood"
msgstr "I miei vicini"
-#: ../src/jarabe/frame/zoomtoolbar.py:38
-msgid "Group"
-msgstr "Gruppo"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:40
-msgid "Home"
-msgstr "Casa"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:42
-msgid "Activity"
-msgstr "Attività"
-
#: ../src/jarabe/intro/window.py:124
msgid "Click to change color:"
msgstr "Seleziona per cambiare colore:"
@@ -919,7 +934,7 @@ msgstr "Il tuo Diario è vuoto"
msgid "No matching entries "
msgstr "Non ci sono dati corrispondenti "
-#: ../src/jarabe/journal/listview.py:369
+#: ../src/jarabe/journal/listview.py:370
msgid "Clear search"
msgstr "Annulla ricerca"
@@ -1024,24 +1039,24 @@ msgstr "Visualizza Sorgente"
msgid "Stop"
msgstr "Chiudi"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:174
msgid "Remove favorite"
msgstr "Rimuovi preferito"
-#: ../src/jarabe/view/palettes.py:175
+#: ../src/jarabe/view/palettes.py:178
msgid "Make favorite"
msgstr "Definisci preferito"
-#: ../src/jarabe/view/palettes.py:238
+#: ../src/jarabe/view/palettes.py:241
msgid "Show contents"
msgstr "Mostra i contenuti"
-#: ../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 Liberi"
-#: ../src/jarabe/view/palettes.py:285
+#: ../src/jarabe/view/palettes.py:288
msgid "Unmount"
msgstr "Rimuovi"
@@ -1062,6 +1077,11 @@ msgstr "Sorgente della Attività"
msgid "View source: %r"
msgstr "Vedi codice sorgente: %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; e Contributori."
+
#~ msgid "Document"
#~ msgstr "Documento"
@@ -1088,9 +1108,6 @@ msgstr "Vedi codice sorgente: %r"
#~ msgid "About my XO"
#~ msgstr "Informazioni sul mio XO"
-#~ msgid "Mesh"
-#~ msgstr "Mesh"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "Connesso ad un Portale Mesh di scuola"
diff --git a/po/ne.po b/po/ne.po
index 51b85ea..dd2c9a7 100644
--- a/po/ne.po
+++ b/po/ne.po
@@ -6,14 +6,16 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-02-21 00:30-0500\n"
-"PO-Revision-Date: 2009-03-01 00:19-0500\n"
-"Last-Translator: Pradosh Kharel <pradosh@olenepal.org>\n"
+"POT-Creation-Date: 2009-06-11 00:31-0400\n"
+"PO-Revision-Date: 2009-07-27 00:08-0400\n"
+"Last-Translator: Daniel Drake <dsd@laptop.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: ne\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Pootle 1.1.0rc2\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Pootle 1.2.1\n"
#: ../extensions/cpsection/aboutme/__init__.py:24
msgid "About Me"
@@ -63,50 +65,43 @@ msgstr "रङà¥à¤— परिवरà¥à¤¤à¤¨ गरà¥à¤¨ कà¥à¤²à¤¿à¤• गà¤
msgid "About my Computer"
msgstr "मेरो कमà¥à¤ªà¤¯à¥‚टरको बारेमा"
-#: ../extensions/cpsection/aboutcomputer/model.py:26
+#: ../extensions/cpsection/aboutcomputer/model.py:28
msgid "Not available"
msgstr "उपलबà¥à¤§ छैन"
-#: ../extensions/cpsection/aboutcomputer/view.py:59
+#: ../extensions/cpsection/aboutcomputer/view.py:60
msgid "Identity"
msgstr "वà¥à¤¯à¤•à¥à¤¤à¤¿à¤¤à¥à¤¬"
-#: ../extensions/cpsection/aboutcomputer/view.py:68
+#: ../extensions/cpsection/aboutcomputer/view.py:69
msgid "Serial Number:"
msgstr "कà¥à¤°à¤®à¤¿à¤• अङà¥à¤•"
-#: ../extensions/cpsection/aboutcomputer/view.py:90
+#: ../extensions/cpsection/aboutcomputer/view.py:91
msgid "Software"
msgstr "सफà¥à¤Ÿà¤µà¥‡à¤°"
-#: ../extensions/cpsection/aboutcomputer/view.py:99
+#: ../extensions/cpsection/aboutcomputer/view.py:100
msgid "Build:"
msgstr "बनाइ:"
-#: ../extensions/cpsection/aboutcomputer/view.py:114
+#: ../extensions/cpsection/aboutcomputer/view.py:115
msgid "Sugar:"
msgstr "सà¥à¤—र:"
-#: ../extensions/cpsection/aboutcomputer/view.py:130
+#: ../extensions/cpsection/aboutcomputer/view.py:131
msgid "Firmware:"
msgstr "फरà¥à¤®à¤µà¥‡à¤°:"
-#: ../extensions/cpsection/aboutcomputer/view.py:145
+#: ../extensions/cpsection/aboutcomputer/view.py:146
msgid "Wireless Firmware:"
msgstr "तारबिनाको फरà¥à¤®à¤µà¥‡à¤°:"
-#: ../extensions/cpsection/aboutcomputer/view.py:168
+#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
msgstr "कपिराइट र अनà¥à¤®à¤¤à¤¿à¤ªà¤¤à¥à¤°"
-#: ../extensions/cpsection/aboutcomputer/view.py:176
-msgid ""
-"© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
-msgstr ""
-"© २००८ वान लà¥à¤¯à¤ªà¥ टप पर चाईलà¥à¤¡ असोसिà¤à¤¸à¤¨ आइ à¤à¤¨ सि; रेड हà¥à¤¯à¤Ÿ आइ à¤à¤¨ सि; र अरॠ"
-"निरà¥à¤®à¤¾à¤¤à¤¾à¤¹à¤°à¥à¥¤"
-
-#: ../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 "
@@ -117,7 +112,7 @@ msgstr ""
"हो, सà¥à¤—र ले जि à¤à¤¨ यॠजनरल पबà¥à¤²à¤¿à¤• अनà¥à¤®à¤¤à¤¿ पतà¥à¤° पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤› र तपाईंले यसलाई "
"परिवरà¥à¤¤à¤¨ गरेर नियम अनà¥à¤¸à¤¾à¤° अरà¥à¤²à¤¾à¤ˆ दिन सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›à¥¤"
-#: ../extensions/cpsection/aboutcomputer/view.py:195
+#: ../extensions/cpsection/aboutcomputer/view.py:196
msgid "Full license:"
msgstr "समà¥à¤ªà¥‚रà¥à¤£ अनà¥à¤®à¤¤à¤¿ पतà¥à¤°:"
@@ -267,7 +262,7 @@ msgstr ""
"अति किफायत शकà¥à¤¤à¤¿à¤•à¥‹ पà¥à¤°à¤¬à¤¨à¥à¤§à¤•à¤°à¥à¤¤à¤¾ (तार बिनाको रेडियो असमरà¥à¤¥ पारà¥à¤›, बà¥à¤¯à¤¾à¤Ÿà¤°à¤¿à¤•à¥‹ "
"जीवन बढाउà¤à¤›)"
-#: ../extensions/deviceicon/battery.py:56
+#: ../extensions/deviceicon/battery.py:58
msgid "My Battery"
msgstr "मेरो बेटरी"
@@ -286,13 +281,13 @@ msgstr "à¤à¤•à¤¦à¤®à¥ˆ थोरै शकà¥â€à¤¤à¤¿ बाà¤à¤•à¥€ छ"
#: ../extensions/deviceicon/battery.py:149
#, python-format
msgid "%(hour)d:%(min).2d remaining"
-msgstr "%(घणà¥à¤Ÿà¤¾)d:%(मिनेट).2d बाà¤à¤•à¥€"
+msgstr "%(hour)d:%(min).2d बाà¤à¤•à¥€"
#: ../extensions/deviceicon/battery.py:152
msgid "Charged"
msgstr "चारà¥à¤œ भयो"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:43
#, python-format
msgid "IP address: %s"
msgstr "आइपी अडà¥à¤°à¥‡à¤¸: %s"
@@ -301,54 +296,87 @@ msgstr "आइपी अडà¥à¤°à¥‡à¤¸: %s"
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
-#: ../extensions/deviceicon/network.py:104
+#: ../extensions/deviceicon/network.py:109
msgid "Disconnect..."
msgstr "जडान विचà¥à¤›à¥‡à¤¦..."
+#: ../extensions/deviceicon/network.py:113
+msgid "Create new wireless network"
+msgstr ""
+
# Only show disconnect when there's a mesh device, because mesh takes
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:247
+#: ../extensions/deviceicon/network.py:119
+#: ../src/jarabe/desktop/meshbox.py:250
msgid "Connecting..."
msgstr "जडान हà¥à¤¦à¥ˆà¤›..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:113
-#: ../extensions/deviceicon/network.py:166
-#: ../src/jarabe/desktop/meshbox.py:253
+#: ../extensions/deviceicon/network.py:123
+#: ../extensions/deviceicon/network.py:179
+#: ../src/jarabe/desktop/meshbox.py:256
msgid "Connected"
msgstr "जडान भयो"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:139
msgid "Channel"
msgstr "माधà¥à¤¯à¤®"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:154
msgid "Wired Network"
msgstr "तार यà¥à¤•à¥à¤¤ संजाल"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:182
msgid "Speed"
msgstr "गति"
-#: ../extensions/deviceicon/speaker.py:46
+#: ../extensions/deviceicon/network.py:389
+#, python-format
+msgid "%s's network"
+msgstr ""
+
+#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "मेरो सà¥à¤ªà¤¿à¤•à¤°à¤¹à¤°à¥"
-#: ../extensions/deviceicon/speaker.py:128
+#: ../extensions/deviceicon/speaker.py:133
msgid "Unmute"
msgstr "आवाज खोल"
-#: ../extensions/deviceicon/speaker.py:131
+#: ../extensions/deviceicon/speaker.py:136
msgid "Mute"
msgstr "आवाज बनà¥à¤¦"
-#: ../extensions/globalkey/screenshot.py:50
+#: ../extensions/globalkey/screenshot.py:56
+msgid "Mesh"
+msgstr "मेश"
+
+#: ../extensions/globalkey/screenshot.py:58
+#: ../src/jarabe/frame/zoomtoolbar.py:38
+msgid "Group"
+msgstr "समà¥à¤¹"
+
+#: ../extensions/globalkey/screenshot.py:60
+#: ../src/jarabe/frame/zoomtoolbar.py:40
+msgid "Home"
+msgstr "गृह"
+
+#: ../extensions/globalkey/screenshot.py:66
+#: ../src/jarabe/frame/zoomtoolbar.py:42
+msgid "Activity"
+msgstr "कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª"
+
+#: ../extensions/globalkey/screenshot.py:69
msgid "Screenshot"
msgstr "परà¥à¤¦à¤¾à¤›à¤µà¤¿"
+#: ../extensions/globalkey/screenshot.py:71
+#, python-format
+msgid "Screenshot of \"%s\""
+msgstr ""
+
#: ../data/sugar.schemas.in.h:1
msgid "Backup URL"
msgstr "बैकअप URL"
@@ -475,8 +503,8 @@ msgid ""
"sugar-control-panel: WARNING, found more than one option with the same name: "
"%s module: %r"
msgstr ""
-"सà¥à¤—र-कनà¥à¤Ÿà¥à¤°à¥‹à¤²-पà¥à¤¯à¤¾à¤¨à¤²: सावधान, तà¥à¤¯à¤¹à¥€ नाम भà¤à¤•à¥‹ à¤à¤‰à¤Ÿà¤¾ भनà¥à¤¦à¤¾ धेरै रोजाई भेटियो: "
-"%s मोडियà¥à¤²: %r"
+"सà¥à¤—र-कनà¥à¤Ÿà¥à¤°à¥‹à¤²-पà¥à¤¯à¤¾à¤¨à¤²: सावधान, तà¥à¤¯à¤¹à¥€ नाम भà¤à¤•à¥‹ à¤à¤‰à¤Ÿà¤¾ भनà¥à¤¦à¤¾ धेरै रोजाई भेटियो: %"
+"s मोडियà¥à¤²: %r"
#: ../src/jarabe/controlpanel/cmd.py:30
#, python-format
@@ -547,65 +575,65 @@ msgstr "भयो"
#: ../src/jarabe/controlpanel/toolbar.py:115
#: ../src/jarabe/desktop/homebox.py:111
-#: ../src/jarabe/frame/activitiestray.py:687
-#: ../src/jarabe/frame/activitiestray.py:766
-#: ../src/jarabe/frame/activitiestray.py:794
+#: ../src/jarabe/frame/activitiestray.py:726
+#: ../src/jarabe/frame/activitiestray.py:821
+#: ../src/jarabe/frame/activitiestray.py:849
msgid "Cancel"
msgstr "रदà¥à¤¦"
#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:338
+#: ../src/jarabe/desktop/favoritesview.py:339
msgid "Ok"
msgstr "हà¥à¤¨à¥à¤›"
# TRANS: label for the freeform layout in the favorites view
#. TRANS: label for the freeform layout in the favorites view
-#: ../src/jarabe/desktop/favoriteslayout.py:114
+#: ../src/jarabe/desktop/favoriteslayout.py:116
msgid "Freeform"
msgstr "सà¥à¤µà¤¤à¤¨à¥à¤¤à¥à¤° आकारको"
# TRANS: label for the ring layout in the favorites view
#. TRANS: label for the ring layout in the favorites view
-#: ../src/jarabe/desktop/favoriteslayout.py:196
+#: ../src/jarabe/desktop/favoriteslayout.py:198
msgid "Ring"
msgstr "औà¤à¤ à¥€"
# TRANS: label for the spiral layout in the favorites view
#. TRANS: label for the spiral layout in the favorites view
-#: ../src/jarabe/desktop/favoriteslayout.py:332
+#: ../src/jarabe/desktop/favoriteslayout.py:334
msgid "Spiral"
msgstr "पेंचदार"
# TRANS: label for the box layout in the favorites view
#. TRANS: label for the box layout in the favorites view
-#: ../src/jarabe/desktop/favoriteslayout.py:399
+#: ../src/jarabe/desktop/favoriteslayout.py:401
msgid "Box"
msgstr "बाकस"
# TRANS: label for the box layout in the favorites view
#. TRANS: label for the box layout in the favorites view
-#: ../src/jarabe/desktop/favoriteslayout.py:440
+#: ../src/jarabe/desktop/favoriteslayout.py:442
msgid "Triangle"
msgstr "तà¥à¤°à¤¿à¤•à¥‹à¤£"
-#: ../src/jarabe/desktop/favoritesview.py:329
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Failed"
msgstr "दरà¥à¤¤à¤¾ असफल भयो"
-#: ../src/jarabe/desktop/favoritesview.py:330
+#: ../src/jarabe/desktop/favoritesview.py:331
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:332
+#: ../src/jarabe/desktop/favoritesview.py:333
msgid "Registration Successful"
msgstr "दरà¥à¤¤à¤¾ सफल"
-#: ../src/jarabe/desktop/favoritesview.py:333
+#: ../src/jarabe/desktop/favoritesview.py:334
msgid "You are now registered with your school server."
msgstr "अब तपाईà¤à¤•à¥‹ नाम बिदà¥à¤¯à¤¾à¤²à¤¯à¤•à¥‹ सरà¥à¤­à¤°à¤®à¤¾ दरà¥à¤¤à¤¾ भइसकà¥à¤¯à¥‹à¥¤"
-#: ../src/jarabe/desktop/favoritesview.py:668
+#: ../src/jarabe/desktop/favoritesview.py:674
msgid "Register"
msgstr "दरà¥à¤¤à¤¾"
@@ -677,25 +705,25 @@ msgstr "WPA र WPA2 निजी"
msgid "Wireless Security:"
msgstr "तारबिनाको लागि सà¥à¤°à¤•à¥à¤·à¤¾"
-#: ../src/jarabe/desktop/meshbox.py:131
+#: ../src/jarabe/desktop/meshbox.py:134
msgid "Connect"
msgstr "जोड"
-#: ../src/jarabe/desktop/meshbox.py:135
+#: ../src/jarabe/desktop/meshbox.py:138
msgid "Disconnect"
msgstr "विचà¥à¤›à¥‡à¤¦ भयो"
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:441
-#: ../src/jarabe/frame/activitiestray.py:711
+#: ../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 "पà¥à¤¨à¤°à¤¾à¤°à¤®à¥à¤­"
-#: ../src/jarabe/desktop/meshbox.py:446
-#: ../src/jarabe/frame/activitiestray.py:227
+#: ../src/jarabe/desktop/meshbox.py:454
+#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "सहभागी होऊ"
@@ -711,47 +739,52 @@ msgstr "सरà¥à¤­à¤°à¤®à¤¾ जड़ान हà¥à¤¨ सकेन।"
msgid "The server could not complete the request."
msgstr "सरà¥à¤­à¤°à¤²à¥‡ अनà¥à¤°à¥‹à¤§ पà¥à¤°à¤¾ गरà¥à¤¨ सकेन।"
-#: ../src/jarabe/frame/activitiestray.py:232
-#: ../src/jarabe/frame/activitiestray.py:659
+#: ../src/jarabe/frame/activitiestray.py:240
+#: ../src/jarabe/frame/activitiestray.py:698
msgid "Decline"
msgstr "नाई"
-#: ../src/jarabe/frame/activitiestray.py:612
+#: ../src/jarabe/frame/activitiestray.py:650
#, python-format
msgid "%dB"
msgstr "%dB"
-#: ../src/jarabe/frame/activitiestray.py:614
+#: ../src/jarabe/frame/activitiestray.py:652
#, python-format
msgid "%dKB"
msgstr "%dKB"
-#: ../src/jarabe/frame/activitiestray.py:616
+#: ../src/jarabe/frame/activitiestray.py:654
#, python-format
msgid "%dMB"
msgstr "%dMB"
-#: ../src/jarabe/frame/activitiestray.py:633
+#: ../src/jarabe/frame/activitiestray.py:671
#, python-format
msgid "%s of %s"
msgstr "%s को %s"
-#: ../src/jarabe/frame/activitiestray.py:644
+#: ../src/jarabe/frame/activitiestray.py:683
#, python-format
msgid "Transfer from %r"
msgstr "%r बाट सारà¥à¤¨à¥à¤¹à¥‹à¤¸"
-#: ../src/jarabe/frame/activitiestray.py:654
+#: ../src/jarabe/frame/activitiestray.py:693
msgid "Accept"
msgstr "सà¥à¤µà¤¿à¤•à¤¾à¤°"
-#: ../src/jarabe/frame/activitiestray.py:677
-#: ../src/jarabe/frame/activitiestray.py:784
+#: ../src/jarabe/frame/activitiestray.py:716
+#: ../src/jarabe/frame/activitiestray.py:839
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:755
+#: ../src/jarabe/frame/activitiestray.py:750
+#: ../src/jarabe/frame/activitiestray.py:873
+msgid "Dismiss"
+msgstr ""
+
+#: ../src/jarabe/frame/activitiestray.py:810
#, python-format
msgid "Transfer to %r"
msgstr "%r मा सारà¥à¤¨à¥à¤¹à¥‹à¤¸"
@@ -778,18 +811,6 @@ msgstr "%s कà¥à¤²à¤¿à¤ªà¤¿à¤™"
msgid "Neighborhood"
msgstr "छिमेक"
-#: ../src/jarabe/frame/zoomtoolbar.py:38
-msgid "Group"
-msgstr "समà¥à¤¹"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:40
-msgid "Home"
-msgstr "गृह"
-
-#: ../src/jarabe/frame/zoomtoolbar.py:42
-msgid "Activity"
-msgstr "कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª"
-
#: ../src/jarabe/intro/window.py:124
msgid "Click to change color:"
msgstr "रङà¥à¤— परिवरà¥à¤¤à¤¨ गरà¥à¤¨ कà¥à¤²à¤¿à¤• गरः"
@@ -902,11 +923,11 @@ msgstr "तिमà¥à¤°à¥‹ पंजिका खाली छ"
msgid "No matching entries "
msgstr "मिलà¥à¤¨à¥‡ लेखा छैन "
-#: ../src/jarabe/journal/listview.py:369
+#: ../src/jarabe/journal/listview.py:370
msgid "Clear search"
msgstr "खोजलाई सफा गर"
-#: ../src/jarabe/journal/misc.py:91
+#: ../src/jarabe/journal/misc.py:93
msgid "No date"
msgstr "मिति छैन"
@@ -1005,24 +1026,24 @@ msgstr "सà¥à¤°à¥‹à¤¤ हेर"
msgid "Stop"
msgstr "बनà¥à¤¦ गर"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:174
msgid "Remove favorite"
msgstr "पà¥à¤°à¤¿à¤¯ हटाउ"
-#: ../src/jarabe/view/palettes.py:175
+#: ../src/jarabe/view/palettes.py:178
msgid "Make favorite"
msgstr "पà¥à¤°à¤¿à¤¯ बनाउ"
-#: ../src/jarabe/view/palettes.py:238
+#: ../src/jarabe/view/palettes.py:241
msgid "Show contents"
msgstr "वसà¥à¤¤à¥à¤¹à¤°à¥ देखाउ"
-#: ../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 "%(खालि॒_ठाउठ)d MB खालि"
+msgstr "%(free_space)d MB खालि"
-#: ../src/jarabe/view/palettes.py:285
+#: ../src/jarabe/view/palettes.py:288
msgid "Unmount"
msgstr "हटाऊ"
@@ -1043,6 +1064,12 @@ msgstr "कà¥à¤°à¤¿à¤¯à¤¾à¤•à¤²à¤¾à¤ª बनà¥à¤¡à¤²à¤•à¥‹ मà¥à¤²"
msgid "View source: %r"
msgstr "सà¥à¤°à¥‹à¤¤ हेर : %r"
+#~ msgid ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
+#~ msgstr ""
+#~ "© २००८ वान लà¥à¤¯à¤ªà¥ टप पर चाईलà¥à¤¡ असोसिà¤à¤¸à¤¨ आइ à¤à¤¨ सि; रेड हà¥à¤¯à¤Ÿ आइ à¤à¤¨ सि; र अरॠ"
+#~ "निरà¥à¤®à¤¾à¤¤à¤¾à¤¹à¤°à¥à¥¤"
+
#~ msgid "Document"
#~ msgstr "कागतपतà¥à¤°"
@@ -1069,9 +1096,6 @@ msgstr "सà¥à¤°à¥‹à¤¤ हेर : %r"
#~ msgid "About my XO"
#~ msgstr "मिरो XO बारे"
-#~ msgid "Mesh"
-#~ msgstr "मेश"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "विदà¥à¤¯à¤¾à¤²à¤¯ मेश पोरà¥à¤Ÿà¤²à¤®à¤¾ जडान भयो"
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/controlpanel/cmd.py b/src/jarabe/controlpanel/cmd.py
index 12de1e2..5f2de68 100644
--- a/src/jarabe/controlpanel/cmd.py
+++ b/src/jarabe/controlpanel/cmd.py
@@ -105,6 +105,9 @@ def main():
for method in methods:
if method.startswith('get_'):
print ' %s' % method[4:]
+ elif method.startswith('clear_'):
+ print " %s (use the -c argument with this option)" \
+ % method[6:]
if option in ("-g"):
for module in modules:
method = getattr(module, 'print_' + key, None)
diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
index 33d0aff..39f9c4a 100644
--- a/src/jarabe/controlpanel/gui.py
+++ b/src/jarabe/controlpanel/gui.py
@@ -357,7 +357,7 @@ class ModelWrapper(object):
try:
method(self._options[key])
except Exception, detail:
- _logger.debug('Error undo option: %s' % detail)
+ _logger.debug('Error undo option: %s', detail)
class _SectionIcon(gtk.EventBox):
__gtype_name__ = "SugarSectionIcon"
diff --git a/src/jarabe/desktop/Makefile.am b/src/jarabe/desktop/Makefile.am
index 94d8ab9..5b47455 100644
--- a/src/jarabe/desktop/Makefile.am
+++ b/src/jarabe/desktop/Makefile.am
@@ -12,7 +12,6 @@ sugar_PYTHON = \
keydialog.py \
meshbox.py \
myicon.py \
- proc_smaps.py \
schoolserver.py \
snowflakelayout.py \
spreadlayout.py \
diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
index 598774a..8b35560 100644
--- a/src/jarabe/desktop/activitieslist.py
+++ b/src/jarabe/desktop/activitieslist.py
@@ -15,6 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+import os
import logging
from gettext import gettext as _
@@ -22,13 +23,13 @@ import gobject
import pango
import gconf
import gtk
-import hippo
from sugar import util
from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon, CellRendererIcon
-from sugar.graphics.palette import Palette
+from sugar.graphics.icon import Icon, CellRendererIcon
from sugar.graphics.xocolor import XoColor
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.alert import Alert
from sugar.activity import activityfactory
from sugar.activity.activityhandle import ActivityHandle
@@ -56,7 +57,7 @@ class ActivitiesTreeView(gtk.TreeView):
self.set_model(model)
cell_favorite = CellRendererFavorite(self)
- cell_favorite.connect('activate', self.__favorite_activate_cb)
+ cell_favorite.connect('clicked', self.__favorite_clicked_cb)
column = gtk.TreeViewColumn('')
column.pack_start(cell_favorite)
@@ -65,7 +66,7 @@ class ActivitiesTreeView(gtk.TreeView):
cell_icon = CellRendererActivityIcon(self)
cell_icon.connect('erase-activated', self.__erase_activated_cb)
- cell_icon.connect('activate', self.__icon_activate_cb)
+ cell_icon.connect('clicked', self.__icon_clicked_cb)
column = gtk.TreeViewColumn('')
column.pack_start(cell_icon)
@@ -127,14 +128,14 @@ class ActivitiesTreeView(gtk.TreeView):
cell.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
cell.props.fill_color = style.COLOR_WHITE.get_svg()
- def __favorite_activate_cb(self, cell, path):
+ def __favorite_clicked_cb(self, cell, path):
row = self.get_model()[path]
registry = bundleregistry.get_registry()
registry.set_bundle_favorite(row[ListModel.COLUMN_BUNDLE_ID],
row[ListModel.COLUMN_VERSION],
not row[ListModel.COLUMN_FAVORITE])
- def __icon_activate_cb(self, cell, path):
+ def __icon_clicked_cb(self, cell, path):
row = self.get_model()[path]
registry = bundleregistry.get_registry()
@@ -190,7 +191,7 @@ class ListModel(gtk.TreeModelSort):
bundle_id = activity_info.get_bundle_id()
version = activity_info.get_activity_version()
favorite = activity_registry.is_bundle_favorite(bundle_id, version)
- for row in self:
+ for row in self._model:
if row[ListModel.COLUMN_BUNDLE_ID] == bundle_id and \
row[ListModel.COLUMN_VERSION] == version:
row[ListModel.COLUMN_FAVORITE] = favorite
@@ -199,11 +200,10 @@ class ListModel(gtk.TreeModelSort):
def __activity_removed_cb(self, activity_registry, activity_info):
bundle_id = activity_info.get_bundle_id()
version = activity_info.get_activity_version()
- favorite = activity_registry.is_bundle_favorite(bundle_id, version)
- for row in self:
+ 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):
@@ -286,7 +286,7 @@ class CellRendererActivityIcon(CellRendererIcon):
bundle_id = row[ListModel.COLUMN_BUNDLE_ID]
registry = bundleregistry.get_registry()
- palette = ActivityPalette(registry.get_bundle(bundle_id))
+ palette = ActivityListPalette(registry.get_bundle(bundle_id))
palette.connect('erase-activated', self.__erase_activated_cb)
return palette
@@ -296,11 +296,6 @@ class CellRendererActivityIcon(CellRendererIcon):
class ActivitiesList(gtk.VBox):
__gtype_name__ = 'SugarActivitiesList'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self):
logging.debug('STARTUP: Loading the activities list')
@@ -353,5 +348,106 @@ class ActivitiesList(gtk.VBox):
self._alert = None
def __erase_activated_cb(self, tree_view, bundle_id):
- self.emit('erase-activated', bundle_id)
+ registry = bundleregistry.get_registry()
+ activity_info = registry.get_bundle(bundle_id)
+
+ alert = Alert()
+ alert.props.title = _('Confirm erase')
+ alert.props.msg = \
+ _('Confirm erase: Do you want to permanently erase %s?') \
+ % activity_info.get_name()
+
+ cancel_icon = Icon(icon_name='dialog-cancel')
+ alert.add_button(gtk.RESPONSE_CANCEL, _('Keep'), cancel_icon)
+
+ erase_icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_OK, _('Erase'), erase_icon)
+
+ alert.connect('response', self.__erase_confirmation_dialog_response_cb,
+ bundle_id)
+
+ self.add_alert(alert)
+
+ def __erase_confirmation_dialog_response_cb(self, alert, response_id,
+ bundle_id):
+ self.remove_alert()
+ if response_id == gtk.RESPONSE_OK:
+ registry = bundleregistry.get_registry()
+ bundle = registry.get_bundle(bundle_id)
+ registry.uninstall(bundle)
+
+class ActivityListPalette(ActivityPalette):
+ __gtype_name__ = 'SugarActivityListPalette'
+
+ __gsignals__ = {
+ 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([str]))
+ }
+
+ def __init__(self, activity_info):
+ ActivityPalette.__init__(self, activity_info)
+
+ self._bundle_id = activity_info.get_bundle_id()
+ self._version = activity_info.get_activity_version()
+
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+
+ self._favorite_item = MenuItem('')
+ self._favorite_icon = Icon(icon_name='emblem-favorite',
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._favorite_item.set_image(self._favorite_icon)
+ self._favorite_item.connect('activate',
+ self.__change_favorite_activate_cb)
+ self.menu.append(self._favorite_item)
+ self._favorite_item.show()
+
+ menu_item = MenuItem(_('Erase'), 'list-remove')
+ menu_item.connect('activate', self.__erase_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
+
+ if not os.access(activity_info.get_path(), os.W_OK):
+ menu_item.props.sensitive = False
+
+ registry = bundleregistry.get_registry()
+ self._activity_changed_sid = registry.connect('bundle_changed',
+ self.__activity_changed_cb)
+ self._update_favorite_item()
+
+ self.connect('destroy', self.__destroy_cb)
+
+ def __destroy_cb(self, palette):
+ self.disconnect(self._activity_changed_sid)
+
+ def _update_favorite_item(self):
+ label = self._favorite_item.child
+ if self._favorite:
+ label.set_text(_('Remove favorite'))
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ else:
+ label.set_text(_('Make favorite'))
+ client = gconf.client_get_default()
+ xo_color = XoColor(client.get_string("/desktop/sugar/user/color"))
+
+ self._favorite_icon.props.xo_color = xo_color
+
+ def __change_favorite_activate_cb(self, menu_item):
+ registry = bundleregistry.get_registry()
+ registry.set_bundle_favorite(self._bundle_id,
+ self._version,
+ not self._favorite)
+
+ def __activity_changed_cb(self, activity_registry, activity_info):
+ if activity_info.get_bundle_id() == self._bundle_id and \
+ activity_info.get_activity_version() == self._version:
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+ self._update_favorite_item()
+
+ def __erase_activate_cb(self, menu_item):
+ self.emit('erase-activated', self._bundle_id)
diff --git a/src/jarabe/desktop/favoriteslayout.py b/src/jarabe/desktop/favoriteslayout.py
index 506446f..3ff0ba8 100644
--- a/src/jarabe/desktop/favoriteslayout.py
+++ b/src/jarabe/desktop/favoriteslayout.py
@@ -57,14 +57,14 @@ class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
def append(self, icon, locked=False):
if not hasattr(type(icon), 'fixed_position'):
- logging.debug('Icon without fixed_position: %r' % icon)
+ logging.debug('Icon without fixed_position: %r', icon)
return
icon.props.size = max(icon.props.size, style.STANDARD_ICON_SIZE)
relative_x, relative_y = icon.fixed_position
if relative_x < 0 or relative_y < 0:
- logging.debug('Icon out of bounds: %r' % icon)
+ logging.debug('Icon out of bounds: %r', icon)
return
min_width_, width = self.box.get_width_request()
@@ -82,7 +82,7 @@ class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
raise ValueError('Child not in box.')
if not(hasattr(icon, 'get_bundle_id') and hasattr(icon, 'get_version')):
- logging.debug('Not an activity icon %r' % icon)
+ logging.debug('Not an activity icon %r', icon)
return
min_width_, width = self.box.get_width_request()
diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py
index 411b579..e8bc7e7 100644
--- a/src/jarabe/desktop/favoritesview.py
+++ b/src/jarabe/desktop/favoritesview.py
@@ -65,11 +65,6 @@ about the layout can be accessed with fields of the class."""
class FavoritesView(hippo.Canvas):
__gtype_name__ = 'SugarFavoritesView'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self, **kwargs):
logging.debug('STARTUP: Loading the favorites view')
@@ -97,6 +92,7 @@ class FavoritesView(hippo.Canvas):
self._layout = None
self._alert = None
self._datastore_listener = DatastoreListener()
+ self._resume_mode = True
# More DND stuff
self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
@@ -134,14 +130,11 @@ class FavoritesView(hippo.Canvas):
if activity_info.get_bundle_id() == 'org.laptop.JournalActivity':
return
icon = ActivityIcon(activity_info, self._datastore_listener)
- icon.connect('erase-activated', self.__erase_activated_cb)
icon.props.size = style.STANDARD_ICON_SIZE
+ icon.set_resume_mode(self._resume_mode)
self._box.insert_sorted(icon, 0, self._layout.compare_activities)
self._layout.append(icon)
- def __erase_activated_cb(self, activity_icon, bundle_id):
- self.emit('erase-activated', bundle_id)
-
def __activity_added_cb(self, activity_registry, activity_info):
registry = bundleregistry.get_registry()
if registry.is_bundle_favorite(activity_info.get_bundle_id(),
@@ -344,6 +337,12 @@ class FavoritesView(hippo.Canvas):
def __register_alert_response_cb(self, alert, response_id):
self.remove_alert()
+ def set_resume_mode(self, resume_mode):
+ self._resume_mode = resume_mode
+ for icon in self._box.get_children():
+ if hasattr(icon, 'set_resume_mode'):
+ icon.set_resume_mode(self._resume_mode)
+
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
@@ -377,7 +376,7 @@ class DatastoreListener(object):
def get_last_activity_async(self, bundle_id, properties, callback_cb):
query = {'activity': bundle_id,
'limit': 5,
- 'order_by': ['-mtime']}
+ 'order_by': ['+timestamp']}
reply_handler = lambda entries, total_count: self.__reply_handler_cb(
entries, total_count, callback_cb)
@@ -386,8 +385,8 @@ class DatastoreListener(object):
error, callback_cb)
self._datastore.find(query, properties, byte_arrays=True,
- reply_handler=reply_handler,
- error_handler=error_handler)
+ reply_handler=reply_handler,
+ error_handler=error_handler)
def __reply_handler_cb(self, entries, total_count, callback_cb):
logging.debug('__reply_handler_cb')
@@ -402,11 +401,6 @@ class ActivityIcon(CanvasIcon):
_BORDER_WIDTH = style.zoom(3)
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self, activity_info, datastore_listener):
CanvasIcon.__init__(self, cache=True,
file_name=activity_info.get_icon())
@@ -414,6 +408,7 @@ class ActivityIcon(CanvasIcon):
self._activity_info = activity_info
self._journal_entries = []
self._hovering = False
+ self._resume_mode = True
self.connect('hovering-changed', self.__hovering_changed_event_cb)
self.connect('button-release-event', self.__button_release_event_cb)
@@ -445,7 +440,8 @@ class ActivityIcon(CanvasIcon):
def __get_last_activity_async_cb(self, entries, error=None):
if error is not None:
- logging.error('Error retrieving most recent activities: %r' % error)
+ logging.error('Error retrieving most recent activities: %r', error)
+ return
# If there's a problem with the DS index, we may get entries not related
# to this activity.
@@ -459,7 +455,7 @@ class ActivityIcon(CanvasIcon):
def _update(self):
self.palette = None
- if not self._journal_entries:
+ if not self._resume_mode or not self._journal_entries:
self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
else:
@@ -469,12 +465,8 @@ class ActivityIcon(CanvasIcon):
def create_palette(self):
palette = FavoritePalette(self._activity_info, self._journal_entries)
palette.connect('activate', self.__palette_activate_cb)
- palette.connect('erase-activated', self.__erase_activated_cb)
return palette
- def __erase_activated_cb(self, palette, bundle_id):
- self.emit('erase-activated', bundle_id)
-
def __palette_activate_cb(self, palette):
self._activate()
@@ -523,7 +515,7 @@ class ActivityIcon(CanvasIcon):
def _activate(self):
self.palette.popdown(immediate=True)
- if self._journal_entries:
+ if self._resume_mode and self._journal_entries:
entry = self._journal_entries[0]
shell_model = shell.get_model()
@@ -565,6 +557,10 @@ class ActivityIcon(CanvasIcon):
return registry.get_bundle_position(self.bundle_id, self.version)
fixed_position = property(_get_fixed_position, None)
+ def set_resume_mode(self, resume_mode):
+ self._resume_mode = resume_mode
+ self._update()
+
class FavoritePalette(ActivityPalette):
__gtype_name__ = 'SugarFavoritePalette'
@@ -606,7 +602,7 @@ class FavoritePalette(ActivityPalette):
def __resume_entry_cb(self, menu_item, entry):
if entry is not None:
- journal.misc.resume(entry, self._bundle_id)
+ journal.misc.resume(entry, entry['activity'])
class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
def __init__(self):
@@ -623,7 +619,8 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
self.connect('button-release-event', self.__button_release_event_cb)
def __button_release_event_cb(self, icon, event):
- self._home_model.get_active_activity().get_window().activate(1)
+ window = self._home_model.get_active_activity().get_window()
+ window.activate(gtk.get_current_event_time())
def _update(self):
self.props.file_name = self._home_activity.get_icon_path()
@@ -694,7 +691,7 @@ class FavoritesSetting(object):
def __init__(self):
client = gconf.client_get_default()
self._layout = client.get_string(self._FAVORITES_KEY)
- logging.debug('FavoritesSetting layout %r' % (self._layout))
+ logging.debug('FavoritesSetting layout %r', self._layout)
self._mode = None
@@ -704,7 +701,7 @@ class FavoritesSetting(object):
return self._layout
def set_layout(self, layout):
- logging.debug('set_layout %r %r' % (layout, self._layout))
+ logging.debug('set_layout %r %r', layout, self._layout)
if layout != self._layout:
self._layout = layout
diff --git a/src/jarabe/desktop/homebox.py b/src/jarabe/desktop/homebox.py
index 6fdc8f1..604001e 100644
--- a/src/jarabe/desktop/homebox.py
+++ b/src/jarabe/desktop/homebox.py
@@ -27,7 +27,6 @@ from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.alert import Alert
from sugar.graphics.icon import Icon
-from jarabe.model import bundleregistry
from jarabe.desktop import favoritesview
from jarabe.desktop.activitieslist import ActivitiesList
@@ -47,10 +46,6 @@ class HomeBox(gtk.VBox):
self._favorites_view = favoritesview.FavoritesView()
self._list_view = ActivitiesList()
- self._favorites_view.connect('erase-activated',
- self.__erase_activated_cb)
- self._list_view.connect('erase-activated', self.__erase_activated_cb)
-
self._toolbar = HomeToolbar()
self._toolbar.connect('query-changed', self.__toolbar_query_changed_cb)
self._toolbar.connect('view-changed', self.__toolbar_view_changed_cb)
@@ -58,44 +53,6 @@ class HomeBox(gtk.VBox):
self._toolbar.show()
self._set_view(_FAVORITES_VIEW)
-
- def __erase_activated_cb(self, view, bundle_id):
- registry = bundleregistry.get_registry()
- activity_info = registry.get_bundle(bundle_id)
-
- alert = Alert()
- alert.props.title = _('Confirm erase')
- alert.props.msg = \
- _('Confirm erase: Do you want to permanently erase %s?') \
- % activity_info.get_name()
-
- cancel_icon = Icon(icon_name='dialog-cancel')
- alert.add_button(gtk.RESPONSE_CANCEL, _('Keep'), cancel_icon)
-
- erase_icon = Icon(icon_name='dialog-ok')
- alert.add_button(gtk.RESPONSE_OK, _('Erase'), erase_icon)
-
- if self._list_view in self.get_children():
- self._list_view.add_alert(alert)
- else:
- self._favorites_view.add_alert(alert)
- # TODO: If the favorite layouts didn't hardcoded the box size, we could
- # just pack an alert between the toolbar and the canvas.
- #self.pack_start(alert, False)
- #self.reorder_child(alert, 1)
- alert.connect('response', self.__erase_confirmation_dialog_response_cb,
- bundle_id)
-
- def __erase_confirmation_dialog_response_cb(self, alert, response_id,
- bundle_id):
- if self._list_view in self.get_children():
- self._list_view.remove_alert()
- else:
- self._favorites_view.remove_alert()
- if response_id == gtk.RESPONSE_OK:
- registry = bundleregistry.get_registry()
- bundle = registry.get_bundle(bundle_id)
- registry.uninstall(bundle)
def show_software_updates_alert(self):
alert = Alert()
@@ -132,8 +89,8 @@ class HomeBox(gtk.VBox):
try:
os.unlink(update_trigger_file)
except OSError:
- logging.error('Software-update: Can not remove file %s' %
- update_trigger_file)
+ logging.error('Software-update: Can not remove file %s',
+ update_trigger_file)
if response_id == gtk.RESPONSE_OK:
from jarabe.controlpanel.gui import ControlPanel
@@ -184,6 +141,8 @@ class HomeBox(gtk.VBox):
def focus_search_entry(self):
self._toolbar.search_entry.grab_focus()
+ def set_resume_mode(self, resume_mode):
+ self._favorites_view.set_resume_mode(resume_mode)
class HomeToolbar(gtk.Toolbar):
__gtype_name__ = 'SugarHomeToolbar'
diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py
index bbb0db1..6212970 100644
--- a/src/jarabe/desktop/homewindow.py
+++ b/src/jarabe/desktop/homewindow.py
@@ -141,3 +141,12 @@ class HomeWindow(gtk.Window):
def get_home_box(self):
return self._home_box
+
+_instance = None
+
+def get_instance():
+ global _instance
+ if not _instance:
+ _instance = HomeWindow()
+ return _instance
+
diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py
index f5995e6..b9b229b 100644
--- a/src/jarabe/desktop/keydialog.py
+++ b/src/jarabe/desktop/keydialog.py
@@ -14,7 +14,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import md5
+import hashlib
from gettext import gettext as _
import gtk
@@ -53,7 +53,7 @@ def hash_passphrase(passphrase):
elif len(passphrase) < 64:
while len(passphrase) < 64:
passphrase += passphrase[:64 - len(passphrase)]
- passphrase = md5.new(passphrase).digest()
+ passphrase = hashlib.md5(passphrase).digest()
return string_to_hex(passphrase)[:26]
class CanceledKeyRequestError(dbus.DBusException):
@@ -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/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
index abbdaa6..ba9c103 100644
--- a/src/jarabe/desktop/meshbox.py
+++ b/src/jarabe/desktop/meshbox.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
#
# 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
@@ -16,7 +17,7 @@
from gettext import gettext as _
import logging
-import sha
+import hashlib
import dbus
import hippo
@@ -46,6 +47,7 @@ from jarabe.model import bundleregistry
from jarabe.model import network
from jarabe.model import shell
from jarabe.model.network import Settings
+from jarabe.model.network import IP4Config
from jarabe.model.network import WirelessSecurity
_NM_SERVICE = 'org.freedesktop.NetworkManager'
@@ -74,7 +76,7 @@ class AccessPointView(CanvasPulsingIcon):
self._flags = 0
self._wpa_flags = 0
self._rsn_flags = 0
- self._mode = 0
+ self._mode = network.NM_802_11_MODE_UNKNOWN
self._device_caps = 0
self._device_state = None
self._connection = None
@@ -156,8 +158,12 @@ class AccessPointView(CanvasPulsingIcon):
self._update_state()
def _update_properties(self, properties):
+ if 'Mode' in properties:
+ self._mode = properties['Mode']
+ self._color = None
if 'Ssid' in properties:
self._name = properties['Ssid']
+ self._color = None
if 'Strength' in properties:
self._strength = properties['Strength']
if 'Flags' in properties:
@@ -166,18 +172,22 @@ class AccessPointView(CanvasPulsingIcon):
self._wpa_flags = properties['WpaFlags']
if 'RsnFlags' in properties:
self._rsn_flags = properties['RsnFlags']
- if 'Mode' in properties:
- self._mode = properties['Mode']
-
- sh = sha.new()
- data = self._name + hex(self._flags)
- sh.update(data)
- h = hash(sh.digest())
- idx = h % len(xocolor.colors)
-
- self._color = XoColor('%s,%s' % (xocolor.colors[idx][0],
- xocolor.colors[idx][1]))
+ if self._color == None:
+ if self._mode == network.NM_802_11_MODE_ADHOC:
+ encoded_color = self._name.split("#", 1)
+ if len(encoded_color) == 2:
+ self._color = XoColor('#' + encoded_color[1])
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ sha_hash = hashlib.sha1()
+ data = self._name + hex(self._flags)
+ sha_hash.update(data)
+ digest = hash(sha_hash.digest())
+ index = digest % len(xocolor.colors)
+
+ self._color = XoColor('%s,%s' %
+ (xocolor.colors[index][0],
+ xocolor.colors[index][1]))
self._update()
def __get_active_ap_reply_cb(self, ap):
@@ -228,7 +238,8 @@ class AccessPointView(CanvasPulsingIcon):
if state == network.DEVICE_STATE_ACTIVATED:
connection = network.find_connection(self._name)
if connection:
- connection.set_connected()
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection.set_connected()
icon_name = '%s-connected' % _ICON_NAME
else:
@@ -357,6 +368,9 @@ class AccessPointView(CanvasPulsingIcon):
settings.wireless.mode = 'infrastructure'
elif self._mode == network.NM_802_11_MODE_ADHOC:
settings.wireless.mode = 'adhoc'
+ settings.wireless.band = 'bg'
+ settings.ip4_config = IP4Config()
+ settings.ip4_config.method = 'shared'
wireless_security = self._get_security()
settings.wireless_security = wireless_security
@@ -862,7 +876,7 @@ class MeshBox(gtk.VBox):
self._layout.remove(icon)
del self.access_points[ap_o]
else:
- logging.error('Can not remove access point %s' % ap_o)
+ logging.error('Can not remove access point %s', ap_o)
def suspend(self):
if not self._suspended:
diff --git a/src/jarabe/desktop/proc_smaps.py b/src/jarabe/desktop/proc_smaps.py
deleted file mode 100755
index 090a4cf..0000000
--- a/src/jarabe/desktop/proc_smaps.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (C) 2007 Red Hat, Inc.
-#
-# 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 os
-
-# /proc/PID/maps consists of a number of lines like this:
-# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
-# 006b1000-006bb000 rw-p 000b1000 fd:00 5767206 /bin/bash
-# 006bb000-006c0000 rw-p 006bb000 00:00 0
-# ...
-# The fields are: address, permissions, offset, device, inode, and
-# (for non-anonymous mappings) pathname.
-#
-# /proc/PID/smaps gives additional information for each mapping:
-# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
-# Size: 708 kB
-# Rss: 476 kB
-# Shared_Clean: 468 kB
-# Shared_Dirty: 0 kB
-# Private_Clean: 8 kB
-# Private_Dirty: 0 kB
-# Referenced: 0 kb
-#
-# The "Referenced" line only appears in kernel 2.6.22 and later.
-
-def get_shared_mapping_names(pid):
- """Returns a set of the files for which PID has a shared mapping"""
-
- mappings = set()
- infile = open("/proc/%s/maps" % pid, "r")
- for line in infile:
- # sharable mappings are non-anonymous and either read-only
- # (permissions "r-..") or writable but explicitly marked
- # shared ("rw.s")
- fields = line.split()
- if len(fields) < 6 or not fields[5].startswith('/'):
- continue
- if fields[1][0] != 'r' or (fields[1][1] == 'w' and fields[1][3] != 's'):
- continue
- mappings.add(fields[5])
- infile.close()
- return mappings
-
-_smaps_lines_per_entry = None
-
-def get_mappings(pid, ignored_shared_mappings):
- """Returns a list of (name, private, shared) tuples describing the
- memory mappings of PID. Shared mappings named in
- ignored_shared_mappings are ignored
- """
-
- global _smaps_lines_per_entry
- if _smaps_lines_per_entry is None:
- if os.path.isfile('/proc/%s/clear_refs' % os.getpid()):
- _smaps_lines_per_entry = 8
- else:
- _smaps_lines_per_entry = 7
-
- mappings = []
-
- smapfile = "/proc/%s/smaps" % pid
- infile = open(smapfile, "r")
- data = infile.read()
- infile.close()
- lines = data.splitlines()
-
- for line_idx in range(0, len(lines), _smaps_lines_per_entry):
- name_idx = lines[line_idx].find('/')
- if name_idx == -1:
- name = None
- else:
- name = lines[line_idx][name_idx:]
-
- private_clean = int(lines[line_idx + 5][14:-3])
- private_dirty = int(lines[line_idx + 6][14:-3])
- if name in ignored_shared_mappings:
- shared_clean = 0
- shared_dirty = 0
- else:
- shared_clean = int(lines[line_idx + 3][14:-3])
- shared_dirty = int(lines[line_idx + 4][14:-3])
-
- mapping = Mapping(name, private_clean + private_dirty,
- shared_clean + shared_dirty)
- mappings.append (mapping)
-
- return mappings
-
-class Mapping:
- def __init__ (self, name, private, shared):
- self.name = name
- self.private = private
- self.shared = shared
diff --git a/src/jarabe/desktop/schoolserver.py b/src/jarabe/desktop/schoolserver.py
index 1dd9edc..2df2a40 100644
--- a/src/jarabe/desktop/schoolserver.py
+++ b/src/jarabe/desktop/schoolserver.py
@@ -46,12 +46,12 @@ def register_laptop(url=REGISTER_URL):
server = ServerProxy(url)
try:
data = server.register(sn, nick, uuid, profile.pubkey)
- except (Error, socket.error), e:
- logging.error('Registration: cannot connect to server: %s' % e)
+ except (Error, socket.error):
+ logging.exception('Registration: cannot connect to server')
raise RegisterError(_('Cannot connect to the server.'))
if data['success'] != 'OK':
- logging.error('Registration: server could not complete request: %s' %
+ logging.error('Registration: server could not complete request: %s',
data['error'])
raise RegisterError(_('The server could not complete the request.'))
diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py
index 1e2b8e8..390cf88 100644
--- a/src/jarabe/frame/activitiestray.py
+++ b/src/jarabe/frame/activitiestray.py
@@ -335,7 +335,7 @@ class ActivitiesTray(HTray):
filetransfer.new_file_transfer.connect(self.__new_file_transfer_cb)
def __activity_added_cb(self, home_model, home_activity):
- logging.debug('__activity_added_cb: %r' % home_activity)
+ logging.debug('__activity_added_cb: %r', home_activity)
if self.get_children():
group = self.get_children()[0]
else:
@@ -348,7 +348,7 @@ class ActivitiesTray(HTray):
button.show()
def __activity_removed_cb(self, home_model, home_activity):
- logging.debug('__activity_removed_cb: %r' % home_activity)
+ logging.debug('__activity_removed_cb: %r', home_activity)
button = self._buttons[home_activity.get_activity_id()]
self.remove_item(button)
del self._buttons[home_activity.get_activity_id()]
@@ -366,14 +366,14 @@ class ActivitiesTray(HTray):
self.window.process_updates(True)
def __activity_changed_cb(self, home_model, home_activity):
- logging.debug('__activity_changed_cb: %r' % home_activity)
+ logging.debug('__activity_changed_cb: %r', home_activity)
# Only select the new activity, if there is no tabbing activity.
if home_model.get_tabbing_activity() is None:
self._activate_activity(home_activity)
def __tabbing_activity_changed_cb(self, home_model, home_activity):
- logging.debug('__tabbing_activity_changed_cb: %r' % home_activity)
+ logging.debug('__tabbing_activity_changed_cb: %r', home_activity)
# If the tabbing_activity is set to None just do nothing.
# The active activity will be updated a bit later (and it will
# be set to the activity that is currently selected).
@@ -424,7 +424,7 @@ class ActivitiesTray(HTray):
def __new_file_transfer_cb(self, **kwargs):
file_transfer = kwargs['file_transfer']
- logging.debug('__new_file_transfer_cb %r' % file_transfer)
+ logging.debug('__new_file_transfer_cb %r', file_transfer)
if isinstance(file_transfer, filetransfer.IncomingFileTransfer):
button = IncomingTransferButton(file_transfer)
@@ -463,8 +463,8 @@ class BaseTransferButton(ToolButton):
self.props.parent.remove(self)
def __notify_state_cb(self, file_transfer, pspec):
- logging.debug('_update state: %r %r' % (file_transfer.props.state,
- file_transfer.reason_last_change))
+ logging.debug('_update state: %r %r', file_transfer.props.state,
+ file_transfer.reason_last_change)
if file_transfer.props.state == filetransfer.FT_STATE_CANCELLED:
if file_transfer.reason_last_change == \
filetransfer.FT_REASON_LOCAL_STOPPED:
@@ -565,10 +565,10 @@ class IncomingTransferButton(BaseTransferButton):
return dbus.Interface(remote_object, DS_DBUS_INTERFACE)
def __reply_handler_cb(self):
- logging.debug('__reply_handler_cb %r' % self._object_id)
+ logging.debug('__reply_handler_cb %r', self._object_id)
def __error_handler_cb(self, error):
- logging.debug('__error_handler_cb %r %s' % (self._object_id, error))
+ logging.debug('__error_handler_cb %r %s', self._object_id, error)
def __dismiss_clicked_cb(self, palette):
self.remove()
@@ -654,7 +654,7 @@ class BaseTransferPalette(Palette):
return _('%dMB') % (size / 1048576)
def update_progress(self):
- logging.debug('update_progress: %r' %
+ logging.debug('update_progress: %r',
self.file_transfer.props.transferred_bytes)
if self.progress_bar is None:
@@ -663,7 +663,7 @@ class BaseTransferPalette(Palette):
self.progress_bar.props.fraction = \
self.file_transfer.props.transferred_bytes / \
float(self.file_transfer.file_size)
- logging.debug('update_progress: %r' % self.progress_bar.props.fraction)
+ logging.debug('update_progress: %r', self.progress_bar.props.fraction)
transferred = self._format_size(
self.file_transfer.props.transferred_bytes)
@@ -688,7 +688,7 @@ class IncomingTransferPalette(BaseTransferPalette):
self._update()
def _update(self):
- logging.debug('_update state: %r' % self.file_transfer.props.state)
+ logging.debug('_update state: %r', self.file_transfer.props.state)
if self.file_transfer.props.state == filetransfer.FT_STATE_PENDING:
menu_item = MenuItem(_('Accept'), icon_name='dialog-ok')
menu_item.connect('activate', self.__accept_activate_cb)
@@ -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/frame/clipboard.py b/src/jarabe/frame/clipboard.py
index 0e3e125..1cefcc1 100644
--- a/src/jarabe/frame/clipboard.py
+++ b/src/jarabe/frame/clipboard.py
@@ -59,9 +59,9 @@ class Clipboard(gobject.GObject):
cb_object = self._objects[object_id]
if format_type == 'XdndDirectSave0':
- format = Format('text/uri-list', data + '\r\n', on_disk)
- format.owns_disk_data = True
- cb_object.add_format(format)
+ format_ = Format('text/uri-list', data + '\r\n', on_disk)
+ format_.owns_disk_data = True
+ cb_object.add_format(format_)
elif on_disk and cb_object.get_percent() == 100:
new_uri = self._copy_file(data)
cb_object.add_format(Format(format_type, new_uri, on_disk))
@@ -77,7 +77,7 @@ class Clipboard(gobject.GObject):
cb_object = self._objects.pop(object_id)
cb_object.destroy()
self.emit('object-deleted', object_id)
- logging.debug('Deleted object with object_id %r' % object_id)
+ logging.debug('Deleted object with object_id %r', object_id)
def set_object_percent(self, object_id, percent):
cb_object = self._objects[object_id]
@@ -98,10 +98,10 @@ class Clipboard(gobject.GObject):
def _process_object(self, cb_object):
formats = cb_object.get_formats()
- for format_name, format in formats.iteritems():
- if format.is_on_disk() and not format.owns_disk_data:
- new_uri = self._copy_file(format.get_data())
- format.set_data(new_uri)
+ for format_name, format_ in formats.iteritems():
+ if format_.is_on_disk() and not format_.owns_disk_data:
+ new_uri = self._copy_file(format_.get_data())
+ format_.set_data(new_uri)
# Add a text/plain format to objects that are text but lack it
if 'text/plain' not in formats.keys():
@@ -121,8 +121,8 @@ class Clipboard(gobject.GObject):
def get_object_data(self, object_id, format_type):
logging.debug('Clipboard.get_object_data')
cb_object = self._objects[object_id]
- format = cb_object.get_formats()[format_type]
- return format
+ format_ = cb_object.get_formats()[format_type]
+ return format_
def _copy_file(self, original_uri):
uri = urlparse.urlparse(original_uri)
diff --git a/src/jarabe/frame/clipboardicon.py b/src/jarabe/frame/clipboardicon.py
index ff63ad9..93a177e 100644
--- a/src/jarabe/frame/clipboardicon.py
+++ b/src/jarabe/frame/clipboardicon.py
@@ -96,7 +96,7 @@ class ClipboardIcon(RadioToolButton):
def _clipboard_data_get_cb(self, x_clipboard, selection, info, targets):
if not selection.target in [target[0] for target in targets]:
logging.warning('ClipboardIcon._clipboard_data_get_cb: asked %s' \
- ' but only have %r.' % (selection.target, targets))
+ ' but only have %r.', selection.target, targets)
return
data = self._cb_object.get_formats()[selection.target].get_data()
selection.set(selection.target, 8, data)
diff --git a/src/jarabe/frame/clipboardmenu.py b/src/jarabe/frame/clipboardmenu.py
index d1d4105..e2eba68 100644
--- a/src/jarabe/frame/clipboardmenu.py
+++ b/src/jarabe/frame/clipboardmenu.py
@@ -74,7 +74,7 @@ class ClipboardMenu(Palette):
def _update_open_submenu(self):
activities = self._get_activities()
- logging.debug('_update_open_submenu: %r' % activities)
+ logging.debug('_update_open_submenu: %r', activities)
child = self._open_item.get_child()
if activities is None or len(activities) <= 1:
child.set_text(_('Open'))
@@ -97,7 +97,7 @@ class ClipboardMenu(Palette):
activity_info = registry.get_bundle(service_name)
if not activity_info:
- logging.warning('Activity %s is unknown.' % service_name)
+ logging.warning('Activity %s is unknown.', service_name)
item = gtk.MenuItem(activity_info.get_name())
item.connect('activate', self._open_submenu_item_activate_cb,
@@ -204,26 +204,26 @@ class ClipboardMenu(Palette):
def _copy_to_journal(self):
formats = self._cb_object.get_formats().keys()
most_significant_mime_type = mime.choose_most_significant(formats)
- format = self._cb_object.get_formats()[most_significant_mime_type]
+ format_ = self._cb_object.get_formats()[most_significant_mime_type]
transfer_ownership = False
if most_significant_mime_type == 'text/uri-list':
- uris = mime.split_uri_list(format.get_data())
+ uris = mime.split_uri_list(format_.get_data())
if len(uris) == 1 and uris[0].startswith('file://'):
file_path = urlparse.urlparse(uris[0]).path
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
- file_path = self._write_to_temp_file(format.get_data())
+ file_path = self._write_to_temp_file(format_.get_data())
transfer_ownership = True
mime_type = 'text/uri-list'
else:
- if format.is_on_disk():
- file_path = urlparse.urlparse(format.get_data()).path
+ if format_.is_on_disk():
+ file_path = urlparse.urlparse(format_.get_data()).path
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
- file_path = self._write_to_temp_file(format.get_data())
+ file_path = self._write_to_temp_file(format_.get_data())
transfer_ownership = True
sniffed_mime_type = mime.get_for_file(file_path)
if sniffed_mime_type == 'application/octet-stream':
diff --git a/src/jarabe/frame/clipboardobject.py b/src/jarabe/frame/clipboardobject.py
index 38da151..d6a55db 100644
--- a/src/jarabe/frame/clipboardobject.py
+++ b/src/jarabe/frame/clipboardobject.py
@@ -33,8 +33,8 @@ class ClipboardObject(object):
self._formats = {}
def destroy(self):
- for format in self._formats.itervalues():
- format.destroy()
+ for format_ in self._formats.itervalues():
+ format_.destroy()
def get_id(self):
return self._id
@@ -91,8 +91,8 @@ class ClipboardObject(object):
def set_percent(self, percent):
self._percent = percent
- def add_format(self, format):
- self._formats[format.get_type()] = format
+ def add_format(self, format_):
+ self._formats[format_.get_type()] = format_
def get_formats(self):
return self._formats
@@ -101,18 +101,18 @@ class ClipboardObject(object):
if not self._formats:
return ''
- format = mime.choose_most_significant(self._formats.keys())
- if format == 'text/uri-list':
+ format_ = mime.choose_most_significant(self._formats.keys())
+ if format_ == 'text/uri-list':
data = self._formats['text/uri-list'].get_data()
uri = urlparse.urlparse(mime.split_uri_list(data)[0], 'file')
if uri.scheme == 'file':
if os.path.exists(uri.path):
- format = mime.get_for_file(uri.path)
+ format_ = mime.get_for_file(uri.path)
else:
- format = mime.get_from_file_name(uri.path)
- logging.debug('Choosed %r!' % format)
+ format_ = mime.get_from_file_name(uri.path)
+ logging.debug('Chose %r!', format_)
- return format
+ return format_
class Format(object):
diff --git a/src/jarabe/frame/clipboardpanelwindow.py b/src/jarabe/frame/clipboardpanelwindow.py
index 7093199..7d08d32 100644
--- a/src/jarabe/frame/clipboardpanelwindow.py
+++ b/src/jarabe/frame/clipboardpanelwindow.py
@@ -63,10 +63,10 @@ class ClipboardPanelWindow(FrameWindow):
for target in targets:
if target not in ('TIMESTAMP', 'TARGETS',
'MULTIPLE', 'SAVE_TARGETS'):
- logging.debug('Asking for target %s.' % target)
+ logging.debug('Asking for target %s.', target)
selection = x_clipboard.wait_for_contents(target)
if not selection:
- logging.warning('no data for selection target %s.' % target)
+ logging.warning('no data for selection target %s.', target)
continue
self._add_selection(key, selection)
@@ -74,7 +74,7 @@ class ClipboardPanelWindow(FrameWindow):
def _add_selection(self, key, selection):
if not selection.data:
- logging.warning('no data for selection target %s.' % selection.type)
+ logging.warning('no data for selection target %s.', selection.type)
return
logging.debug('adding type ' + selection.type + '.')
diff --git a/src/jarabe/frame/clipboardtray.py b/src/jarabe/frame/clipboardtray.py
index 40f0a32..75c1ecf 100644
--- a/src/jarabe/frame/clipboardtray.py
+++ b/src/jarabe/frame/clipboardtray.py
@@ -79,7 +79,7 @@ class ClipboardTray(tray.VTray):
if not selection.data:
return
- logging.debug('ClipboardTray: adding type %r' % selection.type)
+ logging.debug('ClipboardTray: adding type %r', selection.type)
cb_service = clipboard.get_instance()
if selection.type == 'text/uri-list':
@@ -115,13 +115,13 @@ class ClipboardTray(tray.VTray):
cb_service = clipboard.get_instance()
cb_service.delete_object(icon.get_object_id())
- logging.debug('ClipboardTray: %r was added' % cb_object.get_id())
+ logging.debug('ClipboardTray: %r was added', cb_object.get_id())
def _object_deleted_cb(self, cb_service, object_id):
icon = self._icons[object_id]
self.remove_item(icon)
del self._icons[object_id]
- logging.debug('ClipboardTray: %r was deleted' % object_id)
+ logging.debug('ClipboardTray: %r was deleted', object_id)
def drag_motion_cb(self, widget, context, x, y, time):
logging.debug('ClipboardTray._drag_motion_cb')
@@ -153,7 +153,7 @@ class ClipboardTray(tray.VTray):
if 'XdndDirectSave0' in context.targets:
window = context.source_window
- prop_type, format, filename = \
+ prop_type, format_, filename = \
window.property_get('XdndDirectSave0','text/plain')
# FIXME query the clipboard service for a filename?
@@ -165,7 +165,7 @@ class ClipboardTray(tray.VTray):
dest_uri = 'file://' + os.path.join(base_dir, dest_filename)
- window.property_change('XdndDirectSave0', prop_type, format,
+ window.property_change('XdndDirectSave0', prop_type, format_,
gtk.gdk.PROP_MODE_REPLACE, dest_uri)
widget.drag_get_data(context, 'XdndDirectSave0', time)
@@ -180,14 +180,14 @@ class ClipboardTray(tray.VTray):
def drag_data_received_cb(self, widget, context, x, y, selection,
targetType, time):
- logging.debug('ClipboardTray: got data for target %r'
- % selection.target)
+ logging.debug('ClipboardTray: got data for target %r',
+ selection.target)
object_id = self._context_map.get_object_id(context)
try:
if selection is None:
- logging.warn('ClipboardTray: empty selection for target %s'
- % selection.target)
+ logging.warn('ClipboardTray: empty selection for target %s',
+ selection.target)
elif selection.target == 'XdndDirectSave0':
if selection.data == 'S':
window = context.source_window
diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py
index 0bb8d92..1e02fc8 100644
--- a/src/jarabe/frame/frame.py
+++ b/src/jarabe/frame/frame.py
@@ -318,7 +318,7 @@ class Frame(object):
def remove_notification(self, icon):
if icon not in self._notif_by_icon:
- logging.debug('icon %r not in list of notifications.' % icon)
+ logging.debug('icon %r not in list of notifications.', icon)
return
window = self._notif_by_icon[icon]
@@ -326,7 +326,7 @@ class Frame(object):
del self._notif_by_icon[icon]
def __notification_received_cb(self, **kwargs):
- logging.debug('__notification_received_cb %r' % kwargs)
+ logging.debug('__notification_received_cb %r', kwargs)
icon = NotificationIcon()
hints = kwargs['hints']
diff --git a/src/jarabe/frame/framewindow.py b/src/jarabe/frame/framewindow.py
index 02bb131..87eefb9 100644
--- a/src/jarabe/frame/framewindow.py
+++ b/src/jarabe/frame/framewindow.py
@@ -93,7 +93,8 @@ class FrameWindow(gtk.Window):
self.resize(self.size, gtk.gdk.screen_height())
def _realize_cb(self, widget):
- self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.window.set_override_redirect(True)
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
self.window.set_accept_focus(False)
def _enter_notify_cb(self, window, event):
diff --git a/src/jarabe/frame/zoomtoolbar.py b/src/jarabe/frame/zoomtoolbar.py
index 43cc358..4f44600 100644
--- a/src/jarabe/frame/zoomtoolbar.py
+++ b/src/jarabe/frame/zoomtoolbar.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2009 Simon Schampijer
#
# 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
@@ -33,25 +34,26 @@ class ZoomToolbar(gtk.Toolbar):
self.set_direction(gtk.TEXT_DIR_LTR)
self._mesh_button = self._add_button('zoom-neighborhood',
- _('Neighborhood'), shell.ShellModel.ZOOM_MESH)
+ _('Neighborhood'), _('F1'), shell.ShellModel.ZOOM_MESH)
self._groups_button = self._add_button('zoom-groups',
- _('Group'), shell.ShellModel.ZOOM_GROUP)
+ _('Group'), _('F2'), shell.ShellModel.ZOOM_GROUP)
self._home_button = self._add_button('zoom-home',
- _('Home'), shell.ShellModel.ZOOM_HOME)
+ _('Home'), _('F3'), shell.ShellModel.ZOOM_HOME)
self._activity_button = self._add_button('zoom-activity',
- _('Activity'), shell.ShellModel.ZOOM_ACTIVITY)
+ _('Activity'), _('F4'), shell.ShellModel.ZOOM_ACTIVITY)
shell_model = shell.get_model()
self._set_zoom_level(shell_model.zoom_level)
shell_model.zoom_level_changed.connect(self.__zoom_level_changed_cb)
- def _add_button(self, icon_name, label, zoom_level):
+ def _add_button(self, icon_name, label, accelerator, zoom_level):
if self.get_children():
group = self.get_children()[0]
else:
group = None
- button = RadioToolButton(named_icon=icon_name, group=group)
+ button = RadioToolButton(named_icon=icon_name, group=group,
+ accelerator=accelerator)
button.connect('clicked', self.__level_clicked_cb, zoom_level)
self.add(button)
button.show()
@@ -73,7 +75,7 @@ class ZoomToolbar(gtk.Toolbar):
self._set_zoom_level(kwargs['new_level'])
def _set_zoom_level(self, new_level):
- logging.debug('new zoom level: %r' % new_level)
+ logging.debug('new zoom level: %r', new_level)
if new_level == shell.ShellModel.ZOOM_MESH:
self._mesh_button.props.active = True
elif new_level == shell.ShellModel.ZOOM_GROUP:
diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py
index 3975900..94c6782 100644
--- a/src/jarabe/intro/window.py
+++ b/src/jarabe/intro/window.py
@@ -55,7 +55,7 @@ def create_profile(name, color=None, pixbuf=None):
cmd = "ssh-keygen -q -t dsa -f %s -C '' -N ''" % keypath
(s, o) = commands.getstatusoutput(cmd)
if s != 0:
- logging.error("Could not generate key pair: %d %s" % (s, o))
+ logging.error("Could not generate key pair: %d %s", s, o)
else:
logging.error("Keypair exists, skip generation.")
@@ -241,6 +241,9 @@ class IntroWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
+ self.props.decorated = False
+ self.maximize()
+
self._canvas = hippo.Canvas()
self._intro_box = _IntroBox()
self._intro_box.connect('done', self._done_cb)
diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am
index 5f66480..f4bf273 100644
--- a/src/jarabe/journal/Makefile.am
+++ b/src/jarabe/journal/Makefile.am
@@ -1,13 +1,13 @@
sugardir = $(pythondir)/jarabe/journal
sugar_PYTHON = \
__init__.py \
- collapsedentry.py \
detailview.py \
expandedentry.py \
journalactivity.py \
journalentrybundle.py \
journaltoolbox.py \
keepicon.py \
+ listmodel.py \
listview.py \
misc.py \
modalalert.py \
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/collapsedentry.py b/src/jarabe/journal/collapsedentry.py
deleted file mode 100644
index 3eeb087..0000000
--- a/src/jarabe/journal/collapsedentry.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# 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
-
-import logging
-from gettext import gettext as _
-
-import gobject
-import gtk
-import hippo
-import cjson
-
-from sugar.graphics.icon import CanvasIcon
-from sugar.graphics.xocolor import XoColor
-from sugar.graphics import style
-from sugar.graphics.entry import CanvasEntry
-
-from jarabe.journal.keepicon import KeepIcon
-from jarabe.journal.palettes import ObjectPalette, BuddyPalette
-from jarabe.journal import misc
-from jarabe.journal import model
-
-class BuddyIcon(CanvasIcon):
- def __init__(self, buddy, **kwargs):
- CanvasIcon.__init__(self, **kwargs)
- self._buddy = buddy
-
- def create_palette(self):
- return BuddyPalette(self._buddy)
-
-class BuddyList(hippo.CanvasBox):
- def __init__(self, buddies, width):
- hippo.CanvasBox.__init__(self,
- orientation=hippo.ORIENTATION_HORIZONTAL,
- box_width=width,
- xalign=hippo.ALIGNMENT_START)
- self.set_buddies(buddies)
-
- def set_buddies(self, buddies):
- for item in self.get_children():
- self.remove(item)
-
- for buddy in buddies[0:3]:
- nick_, color = buddy
- icon = BuddyIcon(buddy,
- icon_name='computer-xo',
- xo_color=XoColor(color),
- cache=True)
- self.append(icon)
-
-class EntryIcon(CanvasIcon):
-
- __gtype_name__ = 'EntryIcon'
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
-
- def __init__(self, **kwargs):
- CanvasIcon.__init__(self, **kwargs)
- self._metadata = None
-
- def set_metadata(self, metadata):
- self._metadata = metadata
- self.props.file_name = misc.get_icon_name(metadata)
- self.palette = None
-
- def create_palette(self):
- if self.show_palette:
- palette = ObjectPalette(self._metadata, detail=True)
- palette.connect('detail-clicked',
- self.__detail_clicked_cb)
- return palette
- else:
- return None
-
- def __detail_clicked_cb(self, event):
- self.emit('detail-clicked')
-
- show_palette = gobject.property(type=bool, default=False)
-
-class BaseCollapsedEntry(hippo.CanvasBox):
- __gtype_name__ = 'BaseCollapsedEntry'
-
- _DATE_COL_WIDTH = style.GRID_CELL_SIZE * 3
- _BUDDIES_COL_WIDTH = style.GRID_CELL_SIZE * 3
- _PROGRESS_COL_WIDTH = style.GRID_CELL_SIZE * 5
-
- def __init__(self):
- hippo.CanvasBox.__init__(self,
- spacing=style.DEFAULT_SPACING,
- padding_top=style.DEFAULT_PADDING,
- padding_bottom=style.DEFAULT_PADDING,
- padding_left=style.DEFAULT_PADDING * 2,
- padding_right=style.DEFAULT_PADDING * 2,
- box_height=style.GRID_CELL_SIZE,
- orientation=hippo.ORIENTATION_HORIZONTAL)
-
- self._metadata = None
- self._is_selected = False
-
- self.keep_icon = self._create_keep_icon()
- self.append(self.keep_icon)
-
- self.icon = self._create_icon()
- self.append(self.icon)
-
- self.title = self._create_title()
- self.append(self.title, hippo.PACK_EXPAND)
-
- self.buddies_list = self._create_buddies_list()
- self.append(self.buddies_list)
-
- self.date = self._create_date()
- self.append(self.date)
-
- # Progress controls
- self.progress_bar = self._create_progress_bar()
- self.append(self.progress_bar)
-
- self.cancel_button = self._create_cancel_button()
- self.append(self.cancel_button)
-
- def _create_keep_icon(self):
- keep_icon = KeepIcon(False)
- keep_icon.connect('button-release-event',
- self.__keep_icon_button_release_event_cb)
- return keep_icon
-
- def _create_date(self):
- date = hippo.CanvasText(text='',
- xalign=hippo.ALIGNMENT_START,
- font_desc=style.FONT_NORMAL.get_pango_desc(),
- box_width=self._DATE_COL_WIDTH)
- return date
-
- def _create_icon(self):
- icon = EntryIcon(size=style.STANDARD_ICON_SIZE, cache=True)
- return icon
-
- def _create_title(self):
- # TODO: We'd prefer to ellipsize in the middle
- title = hippo.CanvasText(text='',
- xalign=hippo.ALIGNMENT_START,
- font_desc=style.FONT_BOLD.get_pango_desc(),
- size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END)
- return title
-
- def _create_buddies_list(self):
- return BuddyList([], self._BUDDIES_COL_WIDTH)
-
- def _create_progress_bar(self):
- progress_bar = gtk.ProgressBar()
- return hippo.CanvasWidget(widget=progress_bar,
- yalign=hippo.ALIGNMENT_CENTER,
- box_width=self._PROGRESS_COL_WIDTH)
-
- def _create_cancel_button(self):
- button = CanvasIcon(icon_name='activity-stop',
- size=style.SMALL_ICON_SIZE,
- box_width=style.GRID_CELL_SIZE)
- button.connect('button-release-event',
- self._cancel_button_release_event_cb)
- return button
-
- def _decode_buddies(self):
- if self.metadata.has_key('buddies') and \
- self.metadata['buddies']:
- buddies = cjson.decode(self.metadata['buddies']).values()
- else:
- buddies = []
- return buddies
-
- def update_visibility(self):
- in_process = self.is_in_progress()
-
- self.buddies_list.set_visible(not in_process)
- self.date.set_visible(not in_process)
-
- self.progress_bar.set_visible(in_process)
- self.cancel_button.set_visible(in_process)
-
- # TODO: determine the appearance of in-progress entries
- def _update_color(self):
- if self.is_in_progress():
- self.props.background_color = style.COLOR_WHITE.get_int()
- else:
- self.props.background_color = style.COLOR_WHITE.get_int()
-
- def is_in_progress(self):
- return self.metadata.has_key('progress') and \
- int(self.metadata['progress']) < 100
-
- def get_keep(self):
- keep = int(self.metadata.get('keep', 0))
- return keep == 1
-
- def __keep_icon_button_release_event_cb(self, button, event):
- logging.debug('__keep_icon_button_release_event_cb')
- metadata = model.get(self._metadata['uid'])
- if self.get_keep():
- metadata['keep'] = 0
- else:
- metadata['keep'] = 1
- model.write(metadata, update_mtime=False)
-
- self.keep_icon.props.keep = self.get_keep()
- self._update_color()
-
- return True
-
- def _cancel_button_release_event_cb(self, button, event):
- logging.debug('_cancel_button_release_event_cb')
- model.delete(self._metadata['uid'])
- return True
-
- def set_selected(self, is_selected):
- self._is_selected = is_selected
- self._update_color()
-
- def set_metadata(self, metadata):
- self._metadata = metadata
- self._is_selected = False
-
- self.keep_icon.props.keep = self.get_keep()
-
- self.date.props.text = misc.get_date(metadata)
-
- self.icon.set_metadata(metadata)
- if misc.is_activity_bundle(metadata):
- self.icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
- self.icon.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
- else:
- if metadata.has_key('icon-color') and \
- metadata['icon-color']:
- self.icon.props.xo_color = XoColor( \
- metadata['icon-color'])
- else:
- self.icon.props.xo_color = None
-
- if metadata.get('title', ''):
- title_text = metadata['title']
- else:
- title_text = _('Untitled')
- self.title.props.text = title_text
-
- self.buddies_list.set_buddies(self._decode_buddies())
-
- if metadata.has_key('progress'):
- self.progress_bar.props.widget.props.fraction = \
- int(metadata['progress']) / 100.0
-
- self.update_visibility()
- self._update_color()
-
- def get_metadata(self):
- return self._metadata
-
- metadata = property(get_metadata, set_metadata)
-
- def update_date(self):
- self.date.props.text = misc.get_date(self._metadata)
-
-class CollapsedEntry(BaseCollapsedEntry):
- __gtype_name__ = 'CollapsedEntry'
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
-
- def __init__(self):
- BaseCollapsedEntry.__init__(self)
-
- self.icon.props.show_palette = True
- self.icon.connect('button-release-event',
- self.__icon_button_release_event_cb)
- self.icon.connect('detail-clicked',
- self.__detail_clicked_palette_cb)
-
- self.title.connect('button_release_event',
- self.__title_button_release_event_cb)
-
- self._title_entry = self._create_title_entry()
- self.insert_after(self._title_entry, self.title, hippo.PACK_EXPAND)
- self._title_entry.set_visible(False)
-
- self._detail_button = self._create_detail_button()
- self._detail_button.connect('motion-notify-event',
- self.__detail_button_motion_notify_event_cb)
- self.append(self._detail_button)
-
- if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
- self.reverse()
-
- def _create_title_entry(self):
- title_entry = CanvasEntry()
- title_entry.set_background(style.COLOR_WHITE.get_html())
- title_entry.props.widget.connect('focus-out-event',
- self.__title_entry_focus_out_event_cb)
- title_entry.props.widget.connect('activate',
- self.__title_entry_activate_cb)
- title_entry.connect('key-press-event',
- self.__title_entry_key_press_event_cb)
- return title_entry
-
- def _create_detail_button(self):
- button = CanvasIcon(icon_name='go-right',
- size=style.SMALL_ICON_SIZE,
- box_width=style.GRID_CELL_SIZE * 3 / 5,
- fill_color=style.COLOR_BUTTON_GREY.get_svg())
- button.connect('button-release-event',
- self.__detail_button_release_event_cb)
- return button
-
- def update_visibility(self):
- BaseCollapsedEntry.update_visibility(self)
- self._detail_button.set_visible(not self.is_in_progress())
-
- def set_metadata(self, metadata):
- BaseCollapsedEntry.set_metadata(self, metadata)
- self._title_entry.props.text = self.title.props.text
-
- metadata = property(BaseCollapsedEntry.get_metadata, set_metadata)
-
- def _detail_clicked(self):
- if not self.is_in_progress():
- self.emit('detail-clicked')
-
- def __detail_clicked_palette_cb(self, entry):
- self._detail_clicked()
-
- def __detail_button_release_event_cb(self, button, event):
- logging.debug('_detail_button_release_event_cb')
- self._detail_clicked()
- return True
-
- def __detail_button_motion_notify_event_cb(self, button, event):
- if event.detail == hippo.MOTION_DETAIL_ENTER:
- button.props.fill_color = style.COLOR_TOOLBAR_GREY.get_svg()
- elif event.detail == hippo.MOTION_DETAIL_LEAVE:
- button.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
-
- def __icon_button_release_event_cb(self, button, event):
- logging.debug('__icon_button_release_event_cb')
- misc.resume(self.metadata)
- return True
-
- def __title_button_release_event_cb(self, button, event):
- self.title.set_visible(False)
- self._title_entry.set_visible(True)
- self._title_entry.props.widget.grab_focus()
-
- def __title_entry_focus_out_event_cb(self, entry, event):
- self._apply_title_change(entry.props.text)
-
- def __title_entry_activate_cb(self, entry):
- self._apply_title_change(entry.props.text)
-
- def __title_entry_key_press_event_cb(self, entry, event):
- if event.key == hippo.KEY_ESCAPE:
- self._cancel_title_change()
-
- def _apply_title_change(self, title):
- self._title_entry.set_visible(False)
- self.title.set_visible(True)
-
- if title == '':
- self._cancel_title_change()
- elif self.title.props.text != title:
- self.title.props.text = title
-
- self._metadata = model.get(self._metadata['uid'])
- self._metadata['title'] = title
- self._metadata['title_set_by_user'] = '1'
- model.write(self._metadata, update_mtime=False)
-
- def _cancel_title_change(self):
- self._title_entry.props.text = self.title.props.text
- self._title_entry.set_visible(False)
- self.title.set_visible(True)
-
diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py
index 6ef531b..924f872 100644
--- a/src/jarabe/journal/expandedentry.py
+++ b/src/jarabe/journal/expandedentry.py
@@ -17,6 +17,7 @@
import logging
from gettext import gettext as _
import StringIO
+import time
import hippo
import cairo
@@ -29,6 +30,7 @@ from sugar.graphics.icon import CanvasIcon
from sugar.graphics.xocolor import XoColor
from sugar.graphics.entry import CanvasEntry
from sugar.graphics.canvastextview import CanvasTextView
+from sugar.util import format_size
from jarabe.journal.keepicon import KeepIcon
from jarabe.journal.palettes import ObjectPalette, BuddyPalette
@@ -118,6 +120,9 @@ class ExpandedEntry(hippo.CanvasBox):
self._preview = self._create_preview()
first_column.append(self._preview)
+ technical_box = self._create_technical()
+ first_column.append(technical_box)
+
# Second column
description_box, self._description = self._create_description()
@@ -187,8 +192,8 @@ class ExpandedEntry(hippo.CanvasBox):
try:
surface = cairo.ImageSurface.create_from_png(png_file)
has_preview = True
- except Exception, e:
- logging.error('Error while loading the preview: %r' % e)
+ except Exception:
+ logging.exception('Error while loading the preview')
has_preview = False
else:
has_preview = False
@@ -216,6 +221,37 @@ class ExpandedEntry(hippo.CanvasBox):
box.append(preview_box)
return box
+ def _create_technical(self):
+ vbox = hippo.CanvasBox()
+ vbox.props.spacing = style.DEFAULT_SPACING
+
+ lines = [
+ _('Kind: %s') % (self._metadata.get('mime_type') or _('Unknown'),),
+ _('Date: %s') % (self._format_date(),),
+ _('Size: %s') % (format_size(model.get_file_size(self._metadata['uid'])),),
+ ]
+
+ for line in lines:
+ text = hippo.CanvasText(text=line,
+ font_desc=style.FONT_NORMAL.get_pango_desc())
+ text.props.color = style.COLOR_BUTTON_GREY.get_int()
+
+ if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
+ text.props.xalign = hippo.ALIGNMENT_END
+ else:
+ text.props.xalign = hippo.ALIGNMENT_START
+
+ vbox.append(text)
+
+ return vbox
+
+ def _format_date(self):
+ if 'timestamp' in self._metadata:
+ timestamp = float(self._metadata['timestamp'])
+ return time.strftime('%x', time.localtime(timestamp))
+ else:
+ return _('No date')
+
def _create_buddy_list(self):
vbox = hippo.CanvasBox()
diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py
index 4ca751c..08a5a0f 100644
--- a/src/jarabe/journal/journalactivity.py
+++ b/src/jarabe/journal/journalactivity.py
@@ -185,8 +185,8 @@ class JournalActivity(Window):
if keyname == 'Escape':
self.show_main_view()
- def __detail_clicked_cb(self, list_view, entry):
- self._show_secondary_view(entry.metadata)
+ def __detail_clicked_cb(self, list_view, object_id):
+ self._show_secondary_view(object_id)
def __clear_clicked_cb(self, list_view):
self._main_toolbox.search_toolbar.clear_query()
@@ -207,9 +207,8 @@ class JournalActivity(Window):
self.set_canvas(self._main_view)
self._main_view.show()
- def _show_secondary_view(self, metadata):
- # Need to get the full set of properties
- metadata = model.get(metadata['uid'])
+ def _show_secondary_view(self, object_id):
+ metadata = model.get(object_id)
try:
self._detail_toolbox.entry_toolbar.set_metadata(metadata)
except Exception:
@@ -233,11 +232,11 @@ class JournalActivity(Window):
if metadata is None:
return False
else:
- self._show_secondary_view(metadata)
+ self._show_secondary_view(object_id)
return True
def __volume_changed_cb(self, volume_toolbar, mount_point):
- logging.debug('Selected volume: %r.' % mount_point)
+ logging.debug('Selected volume: %r.', mount_point)
self._main_toolbox.search_toolbar.set_mount_point(mount_point)
self._main_toolbox.set_current_toolbar(0)
@@ -282,12 +281,12 @@ class JournalActivity(Window):
try:
registry.install(bundle)
except (ZipExtractException, RegistrationException):
- logging.warning('Could not install bundle %s:\n%s' % \
- (bundle.get_path(), traceback.format_exc()))
+ logging.exception('Could not install bundle %s', bundle.get_path())
return
if metadata['mime_type'] == JournalEntryBundle.MIME_TYPE:
model.delete(object_id)
+ return
metadata['bundle_id'] = bundle.get_bundle_id()
model.write(metadata)
@@ -297,14 +296,14 @@ class JournalActivity(Window):
search_toolbar.give_entry_focus()
def __window_state_event_cb(self, window, event):
- logging.debug('window_state_event_cb %r' % self)
+ logging.debug('window_state_event_cb %r', self)
if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
state = event.new_window_state
visible = not state & gtk.gdk.WINDOW_STATE_ICONIFIED
self._list_view.set_is_visible(visible)
def __visibility_notify_event_cb(self, window, event):
- logging.debug('visibility_notify_event_cb %r' % self)
+ logging.debug('visibility_notify_event_cb %r', self)
visible = event.state != gtk.gdk.VISIBILITY_FULLY_OBSCURED
self._list_view.set_is_visible(visible)
diff --git a/src/jarabe/journal/journalentrybundle.py b/src/jarabe/journal/journalentrybundle.py
index 5d4086c..ebe7ec3 100644
--- a/src/jarabe/journal/journalentrybundle.py
+++ b/src/jarabe/journal/journalentrybundle.py
@@ -40,7 +40,7 @@ class JournalEntryBundle(Bundle):
def __init__(self, path):
Bundle.__init__(self, path)
- def install(self):
+ def install(self, install_path):
if os.environ.has_key('SUGAR_ACTIVITY_ROOT'):
install_dir = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],
'data')
@@ -62,6 +62,9 @@ class JournalEntryBundle(Bundle):
finally:
shutil.rmtree(bundle_dir, ignore_errors=True)
+ def get_bundle_id(self):
+ return None
+
def _read_metadata(self, bundle_dir):
metadata_path = os.path.join(bundle_dir, '_metadata.json')
if not os.path.exists(metadata_path):
diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py
index 17a65e6..201bf76 100644
--- a/src/jarabe/journal/journaltoolbox.py
+++ b/src/jarabe/journal/journaltoolbox.py
@@ -1,4 +1,5 @@
# Copyright (C) 2007, One Laptop Per Child
+# Copyright (C) 2009, Walter Bender
#
# 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
@@ -55,7 +56,8 @@ _ACTION_ANYTHING = 0
_ACTION_EVERYBODY = 0
_ACTION_MY_FRIENDS = 1
_ACTION_MY_CLASS = 2
-
+
+
class MainToolbox(Toolbox):
def __init__(self):
Toolbox.__init__(self)
@@ -72,8 +74,8 @@ class SearchToolbar(gtk.Toolbar):
'query-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([object]))
- }
-
+ }
+
def __init__(self):
gtk.Toolbar.__init__(self)
@@ -203,7 +205,7 @@ class SearchToolbar(gtk.Toolbar):
date_range = (today_start - timedelta(30), right_now)
elif self._when_search_combo.props.value == _ACTION_PAST_YEAR:
date_range = (today_start - timedelta(356), right_now)
-
+
return (time.mktime(date_range[0].timetuple()),
time.mktime(date_range[1].timetuple()))
@@ -256,7 +258,7 @@ class SearchToolbar(gtk.Toolbar):
break
if what_filter_index == -1:
- logging.warning('what_filter %r not known' % what_filter)
+ logging.warning('what_filter %r not known', what_filter)
else:
self._what_search_combo.set_active(what_filter_index)
@@ -268,17 +270,30 @@ class SearchToolbar(gtk.Toolbar):
try:
self._what_search_combo.remove_all()
# TRANS: Item in a combo box that filters by entry type.
- self._what_search_combo.append_item(_ACTION_ANYTHING, _('Anything'))
+ self._what_search_combo.append_item(_ACTION_ANYTHING,
+ _('Anything'))
registry = bundleregistry.get_registry()
appended_separator = False
+
+ types = mime.get_all_generic_types()
+ for generic_type in types:
+ if not appended_separator:
+ self._what_search_combo.append_separator()
+ appended_separator = True
+ self._what_search_combo.append_item(
+ generic_type.type_id, generic_type.name, generic_type.icon)
+ if generic_type.type_id == current_value:
+ current_value_index = \
+ len(self._what_search_combo.get_model()) - 1
+
+ self._what_search_combo.set_active(current_value_index)
+
+ self._what_search_combo.append_separator()
+
for service_name in model.get_unique_values('activity'):
activity_info = registry.get_bundle(service_name)
if not activity_info is None:
- if not appended_separator:
- self._what_search_combo.append_separator()
- appended_separator = True
-
if os.path.exists(activity_info.get_icon()):
self._what_search_combo.append_item(service_name,
activity_info.get_name(),
@@ -291,18 +306,6 @@ class SearchToolbar(gtk.Toolbar):
if service_name == current_value:
current_value_index = \
len(self._what_search_combo.get_model()) - 1
-
- self._what_search_combo.append_separator()
-
- types = mime.get_all_generic_types()
- for generic_type in types :
- self._what_search_combo.append_item(
- generic_type.type_id, generic_type.name, generic_type.icon)
- if generic_type.type_id == current_value:
- current_value_index = \
- len(self._what_search_combo.get_model()) - 1
-
- self._what_search_combo.set_active(current_value_index)
finally:
self._what_search_combo.handler_unblock(
self._what_combo_changed_sid)
@@ -398,7 +401,7 @@ class EntryToolbar(gtk.Toolbar):
def _refresh_copy_palette(self):
palette = self._copy.get_palette()
-
+
for menu_item in palette.menu.get_children():
palette.menu.remove(menu_item)
menu_item.destroy()
diff --git a/src/jarabe/journal/listmodel.py b/src/jarabe/journal/listmodel.py
new file mode 100644
index 0000000..917fbb1
--- /dev/null
+++ b/src/jarabe/journal/listmodel.py
@@ -0,0 +1,204 @@
+# 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
+
+import logging
+
+import cjson
+import gobject
+import gtk
+
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics import style
+from sugar import util
+
+from jarabe.journal import model
+from jarabe.journal import misc
+
+DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
+DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
+DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
+
+class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource):
+ __gtype_name__ = 'JournalListModel'
+
+ __gsignals__ = {
+ 'ready': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ 'progress': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ }
+
+ COLUMN_UID = 0
+ COLUMN_FAVORITE = 1
+ COLUMN_ICON = 2
+ COLUMN_ICON_COLOR = 3
+ COLUMN_TITLE = 4
+ COLUMN_DATE = 5
+ COLUMN_PROGRESS = 6
+ COLUMN_BUDDY_1 = 7
+ COLUMN_BUDDY_2 = 8
+ COLUMN_BUDDY_3 = 9
+
+ _COLUMN_TYPES = {COLUMN_UID: str,
+ COLUMN_FAVORITE: bool,
+ COLUMN_ICON: str,
+ COLUMN_ICON_COLOR: object,
+ COLUMN_TITLE: str,
+ COLUMN_DATE: str,
+ COLUMN_PROGRESS: int,
+ COLUMN_BUDDY_1: object,
+ COLUMN_BUDDY_3: object,
+ COLUMN_BUDDY_2: object}
+
+ _PAGE_SIZE = 10
+
+ def __init__(self, query):
+ gobject.GObject.__init__(self)
+
+ self._last_requested_index = None
+ self._cached_row = None
+ self._result_set = model.find(query, ListModel._PAGE_SIZE)
+ self._temp_drag_file_path = None
+
+ # HACK: The view will tell us that it is resizing so the model can
+ # avoid hitting D-Bus and disk.
+ self.view_is_resizing = False
+
+ self._result_set.ready.connect(self.__result_set_ready_cb)
+ self._result_set.progress.connect(self.__result_set_progress_cb)
+
+ def __result_set_ready_cb(self, **kwargs):
+ self.emit('ready')
+
+ def __result_set_progress_cb(self, **kwargs):
+ self.emit('progress')
+
+ def setup(self):
+ self._result_set.setup()
+
+ def stop(self):
+ self._result_set.stop()
+
+ def get_metadata(self, path):
+ return model.get(self[path][ListModel.COLUMN_UID])
+
+ def on_get_n_columns(self):
+ return len(ListModel._COLUMN_TYPES)
+
+ def on_get_column_type(self, index):
+ return ListModel._COLUMN_TYPES[index]
+
+ def on_iter_n_children(self, iter):
+ if iter == None:
+ return self._result_set.length
+ else:
+ return 0
+
+ def on_get_value(self, index, column):
+ if self.view_is_resizing:
+ return None
+
+ if index == self._last_requested_index:
+ return self._cached_row[column]
+
+ if index >= self._result_set.length:
+ return None
+
+ self._result_set.seek(index)
+ metadata = self._result_set.read()
+
+ self._last_requested_index = index
+ self._cached_row = []
+ self._cached_row.append(metadata['uid'])
+ self._cached_row.append(metadata.get('keep', '0') == '1')
+ self._cached_row.append(misc.get_icon_name(metadata))
+
+ if misc.is_activity_bundle(metadata):
+ xo_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ else:
+ if metadata.get('icon-color', ''):
+ xo_color = XoColor(metadata['icon-color'])
+ else:
+ xo_color = None
+ self._cached_row.append(xo_color)
+
+ title = gobject.markup_escape_text(metadata.get('title', None))
+ self._cached_row.append('<b>%s</b>' % title)
+
+ timestamp = int(metadata.get('timestamp', 0))
+ self._cached_row.append(util.timestamp_to_elapsed_string(timestamp))
+
+ self._cached_row.append(int(metadata.get('progress', 100)))
+
+ if metadata.get('buddies', ''):
+ buddies = cjson.decode(metadata['buddies']).values()
+ else:
+ buddies = []
+
+ for n in xrange(0, 3):
+ if buddies:
+ nick, color = buddies.pop(0)
+ self._cached_row.append((nick, XoColor(color)))
+ else:
+ self._cached_row.append(None)
+
+ return self._cached_row[column]
+
+ def on_iter_nth_child(self, iter, n):
+ return n
+
+ def on_get_path(self, iter):
+ return (iter)
+
+ def on_get_iter(self, path):
+ return path[0]
+
+ def on_iter_next(self, iter):
+ if iter != None:
+ if iter >= self._result_set.length - 1:
+ return None
+ return iter + 1
+ return None
+
+ def on_get_flags(self):
+ return gtk.TREE_MODEL_ITERS_PERSIST | gtk.TREE_MODEL_LIST_ONLY
+
+ def on_iter_children(self, iter):
+ return None
+
+ def on_iter_has_child(self, iter):
+ return False
+
+ def on_iter_parent(self, iter):
+ return None
+
+ def do_drag_data_get(self, path, selection):
+ uid = self[path][ListModel.COLUMN_UID]
+ if selection.target == 'text/uri-list':
+ # Get hold of a reference so the temp file doesn't get deleted
+ self._temp_drag_file_path = model.get_file(uid)
+ logging.debug('putting %r in selection', self._temp_drag_file_path)
+ selection.set(selection.target, 8, self._temp_drag_file_path)
+ return True
+ elif selection.target == 'journal-object-id':
+ selection.set(selection.target, 8, uid)
+ return True
+
+ return False
+
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 4578853..251388d 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, 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
@@ -15,33 +15,50 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
-import traceback
-import sys
from gettext import gettext as _
import time
-import hippo
import gobject
import gtk
-import dbus
+import hippo
+import gconf
+import pango
from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon, Icon
+from sugar.graphics.icon import CanvasIcon, Icon, CellRendererIcon
+from sugar.graphics.xocolor import XoColor
+from sugar import util
-from jarabe.journal.collapsedentry import CollapsedEntry
+from jarabe.journal.listmodel import ListModel
+from jarabe.journal.palettes import ObjectPalette, BuddyPalette
from jarabe.journal import model
-
-DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
-DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
-DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
+from jarabe.journal import misc
UPDATE_INTERVAL = 300
-EMPTY_JOURNAL = _("Your Journal is empty")
-NO_MATCH = _("No matching entries ")
+MESSAGE_EMPTY_JOURNAL = 0
+MESSAGE_NO_MATCH = 1
-class BaseListView(gtk.HBox):
- __gtype_name__ = 'BaseListView'
+class TreeView(gtk.TreeView):
+ __gtype_name__ = 'JournalTreeView'
+
+ def __init__(self):
+ gtk.TreeView.__init__(self)
+
+ def do_size_request(self, requisition):
+ # HACK: We tell the model that the view is just resizing so it can avoid
+ # hitting both D-Bus and disk.
+ tree_model = self.get_model()
+ if tree_model is not None:
+ tree_model.view_is_resizing = True
+ try:
+ gtk.TreeView.do_size_request(self, requisition)
+ finally:
+ if tree_model is not None:
+ tree_model.view_is_resizing = False
+
+class BaseListView(gtk.Bin):
+ __gtype_name__ = 'JournalBaseListView'
__gsignals__ = {
'clear-clicked': (gobject.SIGNAL_RUN_FIRST,
@@ -51,57 +68,36 @@ class BaseListView(gtk.HBox):
def __init__(self):
self._query = {}
- self._result_set = None
- self._entries = []
- self._page_size = 0
- self._reflow_sid = 0
- self._do_scroll_hid = None
+ self._model = None
self._progress_bar = None
self._last_progress_bar_pulse = None
- gtk.HBox.__init__(self)
- self.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
- self.connect('key-press-event', self._key_press_event_cb)
-
- self._box = hippo.CanvasBox(
- orientation=hippo.ORIENTATION_VERTICAL,
- background_color=style.COLOR_WHITE.get_int())
-
- self._canvas = hippo.Canvas()
- self._canvas.set_root(self._box)
+ gobject.GObject.__init__(self)
- self.pack_start(self._canvas)
- self._canvas.show()
-
- self._vadjustment = gtk.Adjustment(value=0, lower=0, upper=0,
- step_incr=1, page_incr=0,
- page_size=0)
- self._vadjustment.connect('value-changed',
- self._vadjustment_value_changed_cb)
- self._vadjustment.connect('changed', self._vadjustment_changed_cb)
-
- self._vscrollbar = gtk.VScrollbar(self._vadjustment)
- self.pack_end(self._vscrollbar, expand=False, fill=False)
- self._vscrollbar.show()
-
- self.connect('scroll-event', self._scroll_event_cb)
self.connect('destroy', self.__destroy_cb)
- # DND stuff
- self._temp_file_path = None
- self._pressed_button = None
- self._press_start_x = None
- self._press_start_y = None
- self._last_clicked_entry = None
- self._canvas.drag_source_set(0, [], 0)
- self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK |
- gtk.gdk.POINTER_MOTION_HINT_MASK)
- self._canvas.connect_after("motion_notify_event",
- self._canvas_motion_notify_event_cb)
- self._canvas.connect("button_press_event",
- self._canvas_button_press_event_cb)
- self._canvas.connect("drag_end", self._drag_end_cb)
- self._canvas.connect("drag_data_get", self._drag_data_get_cb)
+ self._scrolled_window = gtk.ScrolledWindow()
+ self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self.add(self._scrolled_window)
+ self._scrolled_window.show()
+
+ self.tree_view = TreeView()
+ self.tree_view.props.fixed_height_mode = True
+ self.tree_view.modify_base(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+ self._scrolled_window.add(self.tree_view)
+ self.tree_view.show()
+
+ self.cell_title = None
+ self.cell_icon = None
+ self._title_column = None
+ self.date_column = None
+ self._add_columns()
+
+ self.tree_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
+ [('text/uri-list', 0, 0),
+ ('journal-object-id', 0, 0)],
+ gtk.gdk.ACTION_COPY)
# Auto-update stuff
self._fully_obscured = True
@@ -109,123 +105,204 @@ class BaseListView(gtk.HBox):
self._refresh_idle_handler = None
self._update_dates_timer = None
- bus = dbus.SessionBus()
- datastore = dbus.Interface(
- bus.get_object(DS_DBUS_SERVICE, DS_DBUS_PATH), DS_DBUS_INTERFACE)
- self._datastore_created_handler = \
- datastore.connect_to_signal('Created',
- self.__datastore_created_cb)
- self._datastore_updated_handler = \
- datastore.connect_to_signal('Updated',
- self.__datastore_updated_cb)
-
- self._datastore_deleted_handler = \
- datastore.connect_to_signal('Deleted',
- self.__datastore_deleted_cb)
+ model.created.connect(self.__model_created_cb)
+ model.updated.connect(self.__model_updated_cb)
+ model.deleted.connect(self.__model_deleted_cb)
- def __destroy_cb(self, widget):
- self._datastore_created_handler.remove()
- self._datastore_updated_handler.remove()
- self._datastore_deleted_handler.remove()
-
- def _vadjustment_changed_cb(self, vadjustment):
- if vadjustment.props.upper > self._page_size:
- self._vscrollbar.show()
- else:
- self._vscrollbar.hide()
+ def __model_created_cb(self, sender, **kwargs):
+ self._set_dirty()
- def _vadjustment_value_changed_cb(self, vadjustment):
- if self._do_scroll_hid is None:
- self._do_scroll_hid = gobject.idle_add(self._do_scroll)
+ def __model_updated_cb(self, sender, **kwargs):
+ self._set_dirty()
- def _do_scroll(self):
- current_position = int(self._vadjustment.props.value)
+ def __model_deleted_cb(self, sender, **kwargs):
+ self._set_dirty()
- self._result_set.seek(current_position)
- metadata_list = self._result_set.read(self._page_size)
+ def _add_columns(self):
+ cell_favorite = CellRendererFavorite(self.tree_view)
+ cell_favorite.connect('clicked', self.__favorite_clicked_cb)
+
+ column = gtk.TreeViewColumn('')
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ column.props.fixed_width = cell_favorite.props.width
+ column.pack_start(cell_favorite)
+ column.set_cell_data_func(cell_favorite, self.__favorite_set_data_cb)
+ self.tree_view.append_column(column)
+
+ self.cell_icon = CellRendererActivityIcon(self.tree_view)
+
+ column = gtk.TreeViewColumn('')
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ column.props.fixed_width = self.cell_icon.props.width
+ column.pack_start(self.cell_icon)
+ column.add_attribute(self.cell_icon, 'file-name', ListModel.COLUMN_ICON)
+ column.add_attribute(self.cell_icon, 'xo-color',
+ ListModel.COLUMN_ICON_COLOR)
+ self.tree_view.append_column(column)
+
+ self.cell_title = gtk.CellRendererText()
+ self.cell_title.props.ellipsize = pango.ELLIPSIZE_MIDDLE
+ self.cell_title.props.ellipsize_set = True
+
+ self._title_column = gtk.TreeViewColumn(_('Title'))
+ self._title_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ self._title_column.props.expand = True
+ self._title_column.props.clickable = True
+ self._title_column.pack_start(self.cell_title)
+ self._title_column.add_attribute(self.cell_title, 'markup',
+ ListModel.COLUMN_TITLE)
+ self._title_column.connect('clicked', self.__header_clicked_cb)
+ self.tree_view.append_column(self._title_column)
+
+ buddies_column = gtk.TreeViewColumn('')
+ buddies_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ self.tree_view.append_column(buddies_column)
+
+ for column_index in [ListModel.COLUMN_BUDDY_1, ListModel.COLUMN_BUDDY_2,
+ ListModel.COLUMN_BUDDY_3]:
+ cell_icon = CellRendererBuddy(self.tree_view,
+ column_index=column_index)
+ buddies_column.pack_start(cell_icon)
+ buddies_column.props.fixed_width += cell_icon.props.width
+ buddies_column.add_attribute(cell_icon, 'buddy', column_index)
+
+ cell_text = gtk.CellRendererText()
+ cell_text.props.xalign = 1
+
+ # Measure the required width for a date in the form of "10 hours, 10
+ # minutes ago"
+ timestamp = time.time() - 10 * 60 - 10 * 60 * 60
+ date = util.timestamp_to_elapsed_string(timestamp)
+ date_width = self._get_width_for_string(date)
+
+ self.date_column = gtk.TreeViewColumn(_('Date'))
+ self.date_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ self.date_column.props.fixed_width = date_width
+ self.date_column.set_alignment(1)
+ self.date_column.props.resizable = True
+ self.date_column.props.clickable = True
+ self.date_column.props.sort_indicator = True
+ self.date_column.props.sort_order = gtk.SORT_ASCENDING
+ self.date_column.pack_start(cell_text)
+ self.date_column.add_attribute(cell_text, 'text', ListModel.COLUMN_DATE)
+ self.date_column.connect('clicked', self.__header_clicked_cb)
+ self.tree_view.append_column(self.date_column)
+
+ def __header_clicked_cb(self, column_clicked):
+ if column_clicked == self._title_column:
+ if self._title_column.props.sort_indicator:
+ if self._title_column.props.sort_order == gtk.SORT_DESCENDING:
+ self._query['order_by'] = ['+title']
+ else:
+ self._query['order_by'] = ['-title']
+ else:
+ self._query['order_by'] = ['+title']
+ elif column_clicked == self.date_column:
+ if self.date_column.props.sort_indicator:
+ if self.date_column.props.sort_order == gtk.SORT_DESCENDING:
+ self._query['order_by'] = ['+timestamp']
+ else:
+ self._query['order_by'] = ['-timestamp']
+ else:
+ self._query['order_by'] = ['+timestamp']
- if self._result_set.length != self._vadjustment.props.upper:
- self._vadjustment.props.upper = self._result_set.length
- self._vadjustment.changed()
+ self.refresh()
- self._refresh_view(metadata_list)
- self._dirty = False
+ # Need to update the column indicators after the model has been reset
+ if self._query['order_by'] == ['-timestamp']:
+ self.date_column.props.sort_indicator = True
+ self._title_column.props.sort_indicator = False
+ self.date_column.props.sort_order = gtk.SORT_DESCENDING
+ elif self._query['order_by'] == ['+timestamp']:
+ self.date_column.props.sort_indicator = True
+ self._title_column.props.sort_indicator = False
+ self.date_column.props.sort_order = gtk.SORT_ASCENDING
+ elif self._query['order_by'] == ['-title']:
+ self.date_column.props.sort_indicator = False
+ self._title_column.props.sort_indicator = True
+ self._title_column.props.sort_order = gtk.SORT_DESCENDING
+ elif self._query['order_by'] == ['+title']:
+ self.date_column.props.sort_indicator = False
+ self._title_column.props.sort_indicator = True
+ self._title_column.props.sort_order = gtk.SORT_ASCENDING
+
+ def _get_width_for_string(self, text):
+ # Add some extra margin
+ text = text + 'aaaaa'
+
+ widget = gtk.Label('')
+ context = widget.get_pango_context()
+ layout = pango.Layout(context)
+ layout.set_text(text)
+ width, height_ = layout.get_size()
+ return pango.PIXELS(width)
- self._do_scroll_hid = None
- return False
+ def do_size_allocate(self, allocation):
+ self.allocation = allocation
+ self.child.size_allocate(allocation)
- def _refresh_view(self, metadata_list):
- logging.debug('ListView %r' % self)
- # Indicate when the Journal is empty
- if len(metadata_list) == 0:
- if self._is_query_empty():
- self._show_message(EMPTY_JOURNAL)
- else:
- self._show_message(NO_MATCH)
- return
+ def do_size_request(self, requisition):
+ requisition.width, requisition.height = self.child.size_request()
- # Refresh view and create the entries if they don't exist yet.
- for i in range(0, self._page_size):
- try:
- if i < len(metadata_list):
- if i >= len(self._entries):
- entry = self.create_entry()
- self._box.append(entry)
- self._entries.append(entry)
- entry.metadata = metadata_list[i]
- else:
- entry = self._entries[i]
- entry.metadata = metadata_list[i]
- entry.set_visible(True)
- elif i < len(self._entries):
- entry = self._entries[i]
- entry.set_visible(False)
- except Exception:
- logging.error('Exception while displaying entry:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
-
- def create_entry(self):
- """ Create a descendant of BaseCollapsedEntry
- """
- raise NotImplementedError
+ def __destroy_cb(self, widget):
+ if self._model is not None:
+ self._model.stop()
+
+ def __favorite_set_data_cb(self, column, cell, tree_model, tree_iter):
+ favorite = self._model[tree_iter][ListModel.COLUMN_FAVORITE]
+ if favorite:
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+ cell.props.xo_color = color
+ else:
+ cell.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ cell.props.fill_color = style.COLOR_WHITE.get_svg()
+
+ def __favorite_clicked_cb(self, cell, path):
+ row = self._model[path]
+ metadata = model.get(row[ListModel.COLUMN_UID])
+ if metadata['keep'] == '1':
+ metadata['keep'] = '0'
+ else:
+ metadata['keep'] = '1'
+ model.write(metadata, update_mtime=False)
def update_with_query(self, query_dict):
logging.debug('ListView.update_with_query')
self._query = query_dict
- if self._page_size > 0:
- self.refresh()
+
+ if 'order_by' not in self._query:
+ self._query['order_by'] = ['+timestamp']
+
+ self.refresh()
def refresh(self):
- logging.debug('ListView.refresh query %r' % self._query)
+ logging.debug('ListView.refresh query %r', self._query)
self._stop_progress_bar()
self._start_progress_bar()
- if self._result_set is not None:
- self._result_set.stop()
- self._result_set = model.find(self._query, self._page_size)
- self._result_set.ready.connect(self.__result_set_ready_cb)
- self._result_set.progress.connect(self.__result_set_progress_cb)
- self._result_set.setup()
+ if self._model is not None:
+ self._model.stop()
- def __result_set_ready_cb(self, **kwargs):
- if kwargs['sender'] != self._result_set:
- return
+ self._model = ListModel(self._query)
+ self._model.connect('ready', self.__model_ready_cb)
+ self._model.connect('progress', self.__model_progress_cb)
+ self._model.setup()
+ def __model_ready_cb(self, tree_model):
self._stop_progress_bar()
- self._vadjustment.props.upper = self._result_set.length
- self._vadjustment.changed()
+ # Cannot set it up earlier because will try to access the model and it
+ # needs to be ready.
+ self.tree_view.set_model(self._model)
- self._vadjustment.props.value = min(self._vadjustment.props.value,
- self._result_set.length - self._page_size)
- if self._result_set.length == 0:
+ if len(tree_model) == 0:
if self._is_query_empty():
- self._show_message(EMPTY_JOURNAL)
+ self._show_message(MESSAGE_EMPTY_JOURNAL)
else:
- self._show_message(NO_MATCH)
+ self._show_message(MESSAGE_NO_MATCH)
else:
self._clear_message()
- self._do_scroll()
def _is_query_empty(self):
# FIXME: This is a hack, we shouldn't have to update this every time
@@ -237,18 +314,16 @@ class BaseListView(gtk.HBox):
else:
return True
- def __result_set_progress_cb(self, **kwargs):
+ def __model_progress_cb(self, tree_model):
if time.time() - self._last_progress_bar_pulse > 0.05:
if self._progress_bar is not None:
self._progress_bar.pulse()
self._last_progress_bar_pulse = time.time()
def _start_progress_bar(self):
- self.remove(self._canvas)
- self.remove(self._vscrollbar)
-
alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5)
- self.pack_start(alignment)
+ self.remove(self.child)
+ self.add(alignment)
alignment.show()
self._progress_bar = gtk.ProgressBar()
@@ -258,102 +333,23 @@ class BaseListView(gtk.HBox):
self._progress_bar.show()
def _stop_progress_bar(self):
- for widget in self.get_children():
- self.remove(widget)
- self._progress_bar = None
-
- self.pack_start(self._canvas)
- self.pack_end(self._vscrollbar, expand=False, fill=False)
-
- def _scroll_event_cb(self, hbox, event):
- if event.direction == gtk.gdk.SCROLL_UP:
- if self._vadjustment.props.value > self._vadjustment.props.lower:
- self._vadjustment.props.value -= 1
- elif event.direction == gtk.gdk.SCROLL_DOWN:
- max_value = self._result_set.length - self._page_size
- if self._vadjustment.props.value < max_value:
- self._vadjustment.props.value += 1
-
- def do_focus(self, direction):
- if not self.is_focus():
- self.grab_focus()
- return True
- return False
-
- def _key_press_event_cb(self, widget, event):
- keyname = gtk.gdk.keyval_name(event.keyval)
-
- if keyname == 'Up':
- if self._vadjustment.props.value > self._vadjustment.props.lower:
- self._vadjustment.props.value -= 1
- elif keyname == 'Down':
- max_value = self._result_set.length - self._page_size
- if self._vadjustment.props.value < max_value:
- self._vadjustment.props.value += 1
- elif keyname == 'Page_Up' or keyname == 'KP_Page_Up':
- new_position = max(0,
- self._vadjustment.props.value - self._page_size)
- if new_position != self._vadjustment.props.value:
- self._vadjustment.props.value = new_position
- elif keyname == 'Page_Down' or keyname == 'KP_Page_Down':
- new_position = min(self._result_set.length - self._page_size,
- self._vadjustment.props.value + self._page_size)
- if new_position != self._vadjustment.props.value:
- self._vadjustment.props.value = new_position
- elif keyname == 'Home' or keyname == 'KP_Home':
- new_position = 0
- if new_position != self._vadjustment.props.value:
- self._vadjustment.props.value = new_position
- elif keyname == 'End' or keyname == 'KP_End':
- new_position = max(0, self._result_set.length - self._page_size)
- if new_position != self._vadjustment.props.value:
- self._vadjustment.props.value = new_position
- else:
- return False
-
- return True
-
- def do_size_allocate(self, allocation):
- gtk.HBox.do_size_allocate(self, allocation)
- new_page_size = int(allocation.height / style.GRID_CELL_SIZE)
-
- logging.debug("do_size_allocate: %r" % new_page_size)
-
- if new_page_size != self._page_size:
- self._page_size = new_page_size
- self._queue_reflow()
-
- def _queue_reflow(self):
- if not self._reflow_sid:
- self._reflow_sid = gobject.idle_add(self._reflow_idle_cb)
-
- def _reflow_idle_cb(self):
- self._box.clear()
- self._entries = []
-
- self._vadjustment.props.page_size = self._page_size
- self._vadjustment.props.page_increment = self._page_size
- self._vadjustment.changed()
-
- if self._result_set is not None:
- self._result_set.stop()
- self._result_set = model.find(self._query, self._page_size)
-
- max_value = max(0, self._result_set.length - self._page_size)
- if self._vadjustment.props.value > max_value:
- self._vadjustment.props.value = max_value
- else:
- self._do_scroll()
-
- self._reflow_sid = 0
+ if self.child != self._progress_bar:
+ return
+ self.remove(self.child)
+ self.add(self._scrolled_window)
def _show_message(self, message):
+ canvas = hippo.Canvas()
+ self.remove(self.child)
+ self.add(canvas)
+ canvas.show()
+
box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL,
background_color=style.COLOR_WHITE.get_int(),
yalign=hippo.ALIGNMENT_CENTER,
spacing=style.DEFAULT_SPACING,
padding_bottom=style.GRID_CELL_SIZE)
- self._canvas.set_root(box)
+ canvas.set_root(box)
icon = CanvasIcon(size=style.LARGE_ICON_SIZE,
icon_name='activity-journal',
@@ -361,141 +357,65 @@ class BaseListView(gtk.HBox):
fill_color = style.COLOR_TRANSPARENT.get_svg())
box.append(icon)
- text = hippo.CanvasText(text=message,
+ if message == MESSAGE_EMPTY_JOURNAL:
+ text = _('Your Journal is empty')
+ elif message == MESSAGE_NO_MATCH:
+ text = _('No matching entries')
+ else:
+ raise ValueError('Invalid message')
+
+ text = hippo.CanvasText(text=text,
xalign=hippo.ALIGNMENT_CENTER,
font_desc=style.FONT_BOLD.get_pango_desc(),
color = style.COLOR_BUTTON_GREY.get_int())
box.append(text)
- button = gtk.Button(label=_('Clear search'))
- button.connect('clicked', self.__clear_button_clicked_cb)
- button.props.image = Icon(icon_name='dialog-cancel',
- icon_size=gtk.ICON_SIZE_BUTTON)
- canvas_button = hippo.CanvasWidget(widget=button,
- xalign=hippo.ALIGNMENT_CENTER)
- box.append(canvas_button)
+ if message == MESSAGE_NO_MATCH:
+ button = gtk.Button(label=_('Clear search'))
+ button.connect('clicked', self.__clear_button_clicked_cb)
+ button.props.image = Icon(icon_name='dialog-cancel',
+ icon_size=gtk.ICON_SIZE_BUTTON)
+ canvas_button = hippo.CanvasWidget(widget=button,
+ xalign=hippo.ALIGNMENT_CENTER)
+ box.append(canvas_button)
def __clear_button_clicked_cb(self, button):
self.emit('clear-clicked')
def _clear_message(self):
- self._canvas.set_root(self._box)
-
- # TODO: Dnd methods. This should be merged somehow inside hippo-canvas.
- def _canvas_motion_notify_event_cb(self, widget, event):
- if not self._pressed_button:
- return True
-
- # if the mouse button is not pressed, no drag should occurr
- if not event.state & gtk.gdk.BUTTON1_MASK:
- self._pressed_button = None
- return True
-
- logging.debug("motion_notify_event_cb")
-
- if event.is_hint:
- x, y, state_ = event.window.get_pointer()
- else:
- x = event.x
- y = event.y
-
- if widget.drag_check_threshold(int(self._press_start_x),
- int(self._press_start_y),
- int(x),
- int(y)):
- context_ = widget.drag_begin([('text/uri-list', 0, 0),
- ('journal-object-id', 0, 0)],
- gtk.gdk.ACTION_COPY,
- 1,
- event)
- return True
-
- def _drag_end_cb(self, widget, drag_context):
- logging.debug("drag_end_cb")
- self._pressed_button = None
- self._press_start_x = None
- self._press_start_y = None
- self._last_clicked_entry = None
-
- # Release and delete the temp file
- self._temp_file_path = None
-
- def _drag_data_get_cb(self, widget, context, selection, target_type,
- event_time):
- logging.debug("drag_data_get_cb: requested target " + selection.target)
-
- metadata = self._last_clicked_entry.metadata
- if selection.target == 'text/uri-list':
- # Get hold of a reference so the temp file doesn't get deleted
- self._temp_file_path = model.get_file(metadata['uid'])
- selection.set(selection.target, 8, self._temp_file_path)
- elif selection.target == 'journal-object-id':
- selection.set(selection.target, 8, metadata['uid'])
-
- def _canvas_button_press_event_cb(self, widget, event):
- logging.debug("button_press_event_cb")
-
- if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS:
- self._last_clicked_entry = \
- self._get_entry_at_coords(event.x, event.y)
- if self._last_clicked_entry:
- self._pressed_button = event.button
- self._press_start_x = event.x
- self._press_start_y = event.y
-
- return False
-
- def _get_entry_at_coords(self, x, y):
- for entry in self._box.get_children():
- entry_x, entry_y = entry.get_context().translate_to_widget(entry)
- entry_width, entry_height = entry.get_allocation()
-
- if (x >= entry_x ) and (x <= entry_x + entry_width) and \
- (y >= entry_y ) and (y <= entry_y + entry_height):
- return entry
- return None
+ self.remove(self.child)
+ self.add(self._scrolled_window)
+ self._scrolled_window.show()
def update_dates(self):
logging.debug('ListView.update_dates')
- for entry in self._entries:
- if entry.get_visible():
- entry.update_date()
-
- def __datastore_created_cb(self, uid):
- self._set_dirty()
-
- def __datastore_updated_cb(self, uid):
- self._set_dirty()
-
- def __datastore_deleted_cb(self, uid):
- self._set_dirty()
+ visible_range = self.tree_view.get_visible_range()
+ if visible_range is None:
+ return
+ path, end_path = visible_range
+ while True:
+ x, y, width, height = self.tree_view.get_cell_area(path,
+ self.date_column)
+ x, y = self.tree_view.convert_tree_to_widget_coords(x, y)
+ self.tree_view.queue_draw_area(x, y, width, height)
+ if path == end_path:
+ break
+ else:
+ next_iter = self._model.iter_next(self._model.get_iter(path))
+ path = self._model.get_path(next_iter)
def _set_dirty(self):
if self._fully_obscured:
self._dirty = True
else:
- self._schedule_refresh()
-
- def _schedule_refresh(self):
- if self._refresh_idle_handler is None:
- logging.debug('Add refresh idle callback')
- self._refresh_idle_handler = \
- gobject.idle_add(self.__refresh_idle_cb)
-
- def __refresh_idle_cb(self):
- self.refresh()
- if self._refresh_idle_handler is not None:
- logging.debug('Remove refresh idle callback')
- gobject.source_remove(self._refresh_idle_handler)
- self._refresh_idle_handler = None
- return False
+ self.refresh()
def set_is_visible(self, visible):
- logging.debug('canvas_visibility_notify_event_cb %r' % visible)
+ logging.debug('canvas_visibility_notify_event_cb %r', visible)
if visible:
self._fully_obscured = False
if self._dirty:
- self._schedule_refresh()
+ self.refresh()
if self._update_dates_timer is None:
logging.debug('Adding date updating timer')
self._update_dates_timer = \
@@ -513,7 +433,7 @@ class BaseListView(gtk.HBox):
return True
class ListView(BaseListView):
- __gtype_name__ = 'ListView'
+ __gtype_name__ = 'JournalListView'
__gsignals__ = {
'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
@@ -524,11 +444,142 @@ class ListView(BaseListView):
def __init__(self):
BaseListView.__init__(self)
- def create_entry(self):
- entry = CollapsedEntry()
- entry.connect('detail-clicked', self.__entry_activated_cb)
- return entry
+ self.cell_title.props.editable = True
+ self.cell_title.connect('edited', self.__cell_title_edited_cb)
+
+ self.cell_icon.connect('clicked', self.__icon_clicked_cb)
+ self.cell_icon.connect('detail-clicked', self.__detail_clicked_cb)
+
+ cell_detail = CellRendererDetail(self.tree_view)
+ cell_detail.connect('clicked', self.__detail_cell_clicked_cb)
+
+ column = gtk.TreeViewColumn('')
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ column.props.fixed_width = cell_detail.props.width
+ column.pack_start(cell_detail)
+ self.tree_view.append_column(column)
+
+ def __detail_cell_clicked_cb(self, cell, path):
+ row = self.tree_view.get_model()[path]
+ self.emit('detail-clicked', row[ListModel.COLUMN_UID])
+
+ def __detail_clicked_cb(self, cell, uid):
+ self.emit('detail-clicked', uid)
+
+ def __icon_clicked_cb(self, cell, path):
+ row = self.tree_view.get_model()[path]
+ metadata = model.get(row[ListModel.COLUMN_UID])
+ misc.resume(metadata)
+
+ def __cell_title_edited_cb(self, cell, path, new_text):
+ row = self._model[path]
+ metadata = model.get(row[ListModel.COLUMN_UID])
+ metadata['title'] = new_text
+ model.write(metadata, update_mtime=False)
+
+class CellRendererFavorite(CellRendererIcon):
+ __gtype_name__ = 'JournalCellRendererFavorite'
+
+ def __init__(self, tree_view):
+ CellRendererIcon.__init__(self, tree_view)
+
+ self.props.width = style.GRID_CELL_SIZE
+ self.props.height = style.GRID_CELL_SIZE
+ self.props.size = style.SMALL_ICON_SIZE
+ self.props.icon_name = 'emblem-favorite'
+ self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+ self.props.prelit_stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.prelit_fill_color = style.COLOR_BUTTON_GREY.get_svg()
+
+class CellRendererDetail(CellRendererIcon):
+ __gtype_name__ = 'JournalCellRendererDetail'
+
+ def __init__(self, tree_view):
+ CellRendererIcon.__init__(self, tree_view)
+
+ self.props.width = style.GRID_CELL_SIZE
+ self.props.height = style.GRID_CELL_SIZE
+ self.props.size = style.SMALL_ICON_SIZE
+ self.props.icon_name = 'go-right'
+ self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+ self.props.stroke_color = style.COLOR_TRANSPARENT.get_svg()
+ self.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.prelit_stroke_color = style.COLOR_TRANSPARENT.get_svg()
+ self.props.prelit_fill_color = style.COLOR_BLACK.get_svg()
+
+class CellRendererActivityIcon(CellRendererIcon):
+ __gtype_name__ = 'JournalCellRendererActivityIcon'
+
+ __gsignals__ = {
+ 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str])),
+ }
+
+ def __init__(self, tree_view):
+ self._show_palette = True
+
+ CellRendererIcon.__init__(self, tree_view)
+
+ self.props.width = style.GRID_CELL_SIZE
+ self.props.height = style.GRID_CELL_SIZE
+ self.props.size = style.STANDARD_ICON_SIZE
+ self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+
+ self.tree_view = tree_view
+
+ def create_palette(self):
+ if not self._show_palette:
+ return None
+
+ tree_model = self.tree_view.get_model()
+ metadata = tree_model.get_metadata(self.props.palette_invoker.path)
+
+ palette = ObjectPalette(metadata, detail=True)
+ palette.connect('detail-clicked',
+ self.__detail_clicked_cb)
+ return palette
+
+ def __detail_clicked_cb(self, palette, uid):
+ self.emit('detail-clicked', uid)
+
+ def set_show_palette(self, show_palette):
+ self._show_palette = show_palette
+
+ show_palette = gobject.property(type=bool, default=True,
+ setter=set_show_palette)
+
+class CellRendererBuddy(CellRendererIcon):
+ __gtype_name__ = 'JournalCellRendererBuddy'
+
+ def __init__(self, tree_view, column_index):
+ CellRendererIcon.__init__(self, tree_view)
+
+ self.props.width = style.STANDARD_ICON_SIZE
+ self.props.height = style.STANDARD_ICON_SIZE
+ self.props.size = style.STANDARD_ICON_SIZE
+ self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+
+ self.tree_view = tree_view
+ self._model_column_index = column_index
+
+ def create_palette(self):
+ tree_model = self.tree_view.get_model()
+ row = tree_model[self.props.palette_invoker.path]
+
+ if row[self._model_column_index] is not None:
+ nick, xo_color = row[self._model_column_index]
+ return BuddyPalette((nick, xo_color.to_string()))
+ else:
+ return None
+
+ def set_buddy(self, buddy):
+ if buddy is None:
+ self.props.icon_name = None
+ else:
+ nick_, xo_color = buddy
+ self.props.icon_name = 'computer-xo'
+ self.props.xo_color = xo_color
- def __entry_activated_cb(self, entry):
- self.emit('detail-clicked', entry)
+ buddy = gobject.property(type=object, setter=set_buddy)
diff --git a/src/jarabe/journal/misc.py b/src/jarabe/journal/misc.py
index b29b744..e6e5abf 100644
--- a/src/jarabe/journal/misc.py
+++ b/src/jarabe/journal/misc.py
@@ -17,7 +17,6 @@
import logging
import time
import traceback
-import sys
import os
from gettext import gettext as _
@@ -45,7 +44,7 @@ def _get_icon_for_mime(mime_type):
return file_name
icons = gio.content_type_get_icon(mime_type)
- logging.debug('icons for this file: %r' % icons.props.names)
+ logging.debug('icons for this file: %r', icons.props.names)
for icon_name in icons.props.names:
file_name = get_icon_file_name(icon_name)
if file_name is not None:
@@ -69,7 +68,7 @@ def get_icon_name(metadata):
try:
bundle = ActivityBundle(file_path)
file_name = bundle.get_icon()
- except:
+ except Exception:
logging.warning('Could not read bundle:\n' + \
traceback.format_exc())
@@ -97,27 +96,27 @@ def get_bundle(metadata):
if is_activity_bundle(metadata):
file_path = util.TempFilePath(model.get_file(metadata['uid']))
if not os.path.exists(file_path):
- logging.warning('Invalid path: %r' % file_path)
+ logging.warning('Invalid path: %r', file_path)
return None
return ActivityBundle(file_path)
elif is_content_bundle(metadata):
file_path = util.TempFilePath(model.get_file(metadata['uid']))
if not os.path.exists(file_path):
- logging.warning('Invalid path: %r' % file_path)
+ logging.warning('Invalid path: %r', file_path)
return None
return ContentBundle(file_path)
elif is_journal_bundle(metadata):
file_path = util.TempFilePath(model.get_file(metadata['uid']))
if not os.path.exists(file_path):
- logging.warning('Invalid path: %r' % file_path)
+ logging.warning('Invalid path: %r', file_path)
return None
return JournalEntryBundle(file_path)
else:
return None
except MalformedBundleException, e:
- logging.warning('Incorrect bundle: %r' % e)
+ logging.warning('Incorrect bundle: %r', e)
return None
def _get_activities_for_mime(mime_type):
@@ -197,7 +196,7 @@ def resume(metadata, bundle_id=None):
if bundle_id is None:
activities = get_activities(metadata)
if not activities:
- logging.warning('No activity can open this object, %s.' %
+ logging.warning('No activity can open this object, %s.',
metadata.get('mime_type', None))
return
bundle_id = activities[0].get_bundle_id()
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index b6d2bde..02ce26a 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -20,7 +20,6 @@ from datetime import datetime
import time
import shutil
from stat import S_IFMT, S_IFDIR, S_IFREG
-import traceback
import re
import gobject
@@ -41,7 +40,8 @@ PROPERTIES = ['uid', 'title', 'mtime', 'timestamp', 'keep', 'buddies',
'icon-color', 'mime_type', 'progress', 'activity', 'mountpoint',
'activity_id', 'bundle_id']
-PAGES_TO_CACHE = 5
+MIN_PAGES_TO_CACHE = 3
+MAX_PAGES_TO_CACHE = 5
class _Cache(object):
@@ -64,11 +64,10 @@ class _Cache(object):
self._dict[entry['uid']] = entry
def remove_all(self, entries):
- entries = entries[:]
- for entry in entries:
- obj = self._dict[entry['uid']]
+ for uid in [entry['uid'] for entry in entries]:
+ obj = self._dict[uid]
self._array.remove(obj)
- del self._dict[entry['uid']]
+ del self._dict[uid]
def __len__(self):
return len(self._array)
@@ -83,11 +82,11 @@ class BaseResultSet(object):
"""Encapsulates the result of a query
"""
- def __init__(self, query, cache_limit):
+ def __init__(self, query, page_size):
self._total_count = -1
self._position = -1
self._query = query
- self._cache_limit = cache_limit
+ self._page_size = page_size
self._offset = 0
self._cache = _Cache()
@@ -104,7 +103,7 @@ class BaseResultSet(object):
def get_length(self):
if self._total_count == -1:
query = self._query.copy()
- query['limit'] = self._cache_limit
+ query['limit'] = self._page_size * MIN_PAGES_TO_CACHE
entries, self._total_count = self.find(query)
self._cache.append_all(entries)
self._offset = 0
@@ -118,13 +117,8 @@ class BaseResultSet(object):
def seek(self, position):
self._position = position
- def read(self, max_count):
- logging.debug('ResultSet.read position: %r' % self._position)
-
- if max_count * PAGES_TO_CACHE > self._cache_limit:
- raise RuntimeError(
- 'max_count (%i) too big for self._cache_limit'
- ' (%i).' % (max_count, self._cache_limit))
+ def read(self):
+ logging.debug('ResultSet.read position: %r', self._position)
if self._position == -1:
self.seek(0)
@@ -142,31 +136,29 @@ class BaseResultSet(object):
last_cached_entry = self._offset + len(self._cache)
- if (remaining_forward_entries <= 0 and
- remaining_backwards_entries <= 0) or \
- max_count > self._cache_limit:
+ if remaining_forward_entries <= 0 and remaining_backwards_entries <= 0:
# Total cache miss: remake it
- offset = max(0, self._position - max_count)
- logging.debug('remaking cache, offset: %r limit: %r' % \
- (offset, max_count * 2))
+ limit = self._page_size * MIN_PAGES_TO_CACHE
+ offset = max(0, self._position - limit / 2)
+ logging.debug('remaking cache, offset: %r limit: %r', offset,
+ limit)
query = self._query.copy()
- query['limit'] = self._cache_limit
+ query['limit'] = limit
query['offset'] = offset
entries, self._total_count = self.find(query)
self._cache.remove_all(self._cache)
self._cache.append_all(entries)
self._offset = offset
-
- elif remaining_forward_entries < 2 * max_count and \
- last_cached_entry < self._total_count:
+
+ elif remaining_forward_entries <= 0 and remaining_backwards_entries > 0:
# Add one page to the end of cache
- logging.debug('appending one more page, offset: %r' % \
- last_cached_entry)
+ logging.debug('appending one more page, offset: %r',
+ last_cached_entry)
query = self._query.copy()
- query['limit'] = max_count
+ query['limit'] = self._page_size
query['offset'] = last_cached_entry
entries, self._total_count = self.find(query)
@@ -174,21 +166,23 @@ class BaseResultSet(object):
self._cache.append_all(entries)
# apply the cache limit
- objects_excess = len(self._cache) - self._cache_limit
+ cache_limit = self._page_size * MAX_PAGES_TO_CACHE
+ objects_excess = len(self._cache) - cache_limit
if objects_excess > 0:
self._offset += objects_excess
self._cache.remove_all(self._cache[:objects_excess])
- elif remaining_backwards_entries < 2 * max_count and self._offset > 0:
+ elif remaining_forward_entries > 0 and \
+ remaining_backwards_entries <= 0 and self._offset > 0:
# Add one page to the beginning of cache
- limit = min(self._offset, max_count)
- self._offset = max(0, self._offset - max_count)
+ limit = min(self._offset, self._page_size)
+ self._offset = max(0, self._offset - limit)
- logging.debug('prepending one more page, offset: %r limit: %r' %
- (self._offset, limit))
+ logging.debug('prepending one more page, offset: %r limit: %r',
+ self._offset, limit)
query = self._query.copy()
- query['limit'] = limit
+ query['limit'] = self._page_size
query['offset'] = self._offset
entries, self._total_count = self.find(query)
@@ -196,20 +190,19 @@ class BaseResultSet(object):
self._cache.prepend_all(entries)
# apply the cache limit
- objects_excess = len(self._cache) - self._cache_limit
+ cache_limit = self._page_size * MAX_PAGES_TO_CACHE
+ objects_excess = len(self._cache) - cache_limit
if objects_excess > 0:
self._cache.remove_all(self._cache[-objects_excess:])
else:
logging.debug('cache hit and no need to grow the cache')
- first_pos = self._position - self._offset
- last_pos = self._position - self._offset + max_count
- return self._cache[first_pos:last_pos]
+ return self._cache[self._position - self._offset]
class DatastoreResultSet(BaseResultSet):
"""Encapsulates the result of a query on the datastore
"""
- def __init__(self, query, cache_limit):
+ def __init__(self, query, page_size):
if query.get('query', '') and not query['query'].startswith('"'):
query_text = ''
@@ -222,7 +215,7 @@ class DatastoreResultSet(BaseResultSet):
query['query'] = query_text
- BaseResultSet.__init__(self, query, cache_limit)
+ BaseResultSet.__init__(self, query, page_size)
def find(self, query):
entries, total_count = _get_datastore().find(query, PROPERTIES,
@@ -236,8 +229,8 @@ class DatastoreResultSet(BaseResultSet):
class InplaceResultSet(BaseResultSet):
"""Encapsulates the result of a query on a mount point
"""
- def __init__(self, query, cache_limit, mount_point):
- BaseResultSet.__init__(self, query, cache_limit)
+ def __init__(self, query, page_size, mount_point):
+ BaseResultSet.__init__(self, query, page_size)
self._mount_point = mount_point
self._file_list = None
self._pending_directories = 0
@@ -295,7 +288,7 @@ class InplaceResultSet(BaseResultSet):
metadata['mountpoint'] = self._mount_point
entries.append(metadata)
- logging.debug('InplaceResultSet.find took %f s.' % (time.time() - t))
+ logging.debug('InplaceResultSet.find took %f s.', time.time() - t)
return entries, total_count
@@ -337,8 +330,7 @@ class InplaceResultSet(BaseResultSet):
self.progress.send(self)
except Exception:
- logging.error('Error reading file %r: %s' % \
- (full_path, traceback.format_exc()))
+ logging.exception('Error reading file %r', full_path)
if self._pending_directories == 0:
self.setup_ready()
@@ -389,11 +381,10 @@ def find(query, page_size):
if mount_points is None or len(mount_points) != 1:
raise ValueError('Exactly one mount point must be specified')
- cache_limit = page_size * PAGES_TO_CACHE
if mount_points[0] == '/':
- return DatastoreResultSet(query, cache_limit)
+ return DatastoreResultSet(query, page_size)
else:
- return InplaceResultSet(query, cache_limit, mount_points[0])
+ return InplaceResultSet(query, page_size, mount_points[0])
def _get_mount_point(path):
dir_path = os.path.dirname(path)
@@ -419,16 +410,31 @@ def get_file(object_id):
"""Returns the file for an object
"""
if os.path.exists(object_id):
- logging.debug('get_file asked for file with path %r' % object_id)
+ logging.debug('get_file asked for file with path %r', object_id)
return object_id
else:
- logging.debug('get_file asked for entry with id %r' % object_id)
+ logging.debug('get_file asked for entry with id %r', object_id)
file_path = _get_datastore().get_filename(object_id)
if file_path:
return util.TempFilePath(file_path)
else:
return None
+def get_file_size(object_id):
+ """Return the file size for an object
+ """
+ logging.debug('get_file_size %r', object_id)
+ if os.path.exists(object_id):
+ return os.stat(object_id).st_size
+
+ file_path = dbus_helpers.get_filename(object_id)
+ if file_path:
+ size = os.stat(file_path).st_size
+ os.remove(file_path)
+ return size
+
+ return 0
+
def get_unique_values(key):
"""Returns a list with the different values a property has taken
"""
@@ -457,8 +463,8 @@ def copy(metadata, mount_point):
def write(metadata, file_path='', update_mtime=True):
"""Creates or updates an entry for that id
"""
- logging.debug('model.write %r %r %r' % (metadata.get('uid', ''), file_path,
- update_mtime))
+ logging.debug('model.write %r %r %r', metadata.get('uid', ''), file_path,
+ update_mtime)
if update_mtime:
metadata['mtime'] = datetime.now().isoformat()
metadata['timestamp'] = int(time.time())
@@ -490,9 +496,11 @@ def write(metadata, file_path='', update_mtime=True):
def _get_file_name(title, mime_type):
file_name = title
- extension = '.' + mime.get_primary_extension(mime_type)
- if not file_name.endswith(extension):
- file_name += extension
+ extension = mime.get_primary_extension(mime_type)
+ if extension is not None and extension:
+ extension = '.' + extension
+ if not file_name.endswith(extension):
+ file_name += extension
# Invalid characters in VFAT filenames. From
# http://en.wikipedia.org/wiki/File_Allocation_Table
@@ -505,7 +513,7 @@ def _get_file_name(title, mime_type):
max_len = 250
if len(file_name) > max_len:
name, extension = os.path.splitext(file_name)
- file_name = name[0:max_len - extension] + extension
+ file_name = name[0:max_len - len(extension)] + extension
return file_name
diff --git a/src/jarabe/journal/objectchooser.py b/src/jarabe/journal/objectchooser.py
index b70cf72..31bdba8 100644
--- a/src/jarabe/journal/objectchooser.py
+++ b/src/jarabe/journal/objectchooser.py
@@ -19,14 +19,13 @@ import logging
import gobject
import gtk
-import hippo
import wnck
from sugar.graphics import style
from sugar.graphics.toolbutton import ToolButton
-from jarabe.journal.listview import ListView
-from jarabe.journal.collapsedentry import BaseCollapsedEntry
+from jarabe.journal.listview import BaseListView
+from jarabe.journal.listmodel import ListModel
from jarabe.journal.journaltoolbox import SearchToolbar
from jarabe.journal.volumestoolbar import VolumesToolbar
@@ -107,8 +106,8 @@ class ObjectChooser(gtk.Window):
if window.get_xid() == parent.xid:
self.destroy()
- def __entry_activated_cb(self, list_view, entry):
- self._selected_object_id = entry.metadata['uid']
+ def __entry_activated_cb(self, list_view, uid):
+ self._selected_object_id = uid
self.emit('response', gtk.RESPONSE_ACCEPT)
def __delete_event_cb(self, chooser, event):
@@ -128,12 +127,12 @@ 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)
+ logging.debug('visibility_notify_event_cb %r', self)
visible = event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED
self._list_view.set_is_visible(visible)
@@ -163,50 +162,38 @@ class TitleBox(VolumesToolbar):
self.insert(tool_item, -1)
tool_item.show()
-class ChooserCollapsedEntry(BaseCollapsedEntry):
- __gtype_name__ = 'ChooserCollapsedEntry'
+class ChooserListView(BaseListView):
+ __gtype_name__ = 'ChooserListView'
__gsignals__ = {
'entry-activated': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([]))
+ ([str])),
}
def __init__(self):
- BaseCollapsedEntry.__init__(self)
-
- self.connect_after('button-release-event',
- self.__button_release_event_cb)
- self.connect('motion-notify-event', self.__motion_notify_event_cb)
+ BaseListView.__init__(self)
- def __button_release_event_cb(self, entry, event):
- self.emit('entry-activated')
- return True
+ self.cell_icon.props.show_palette = False
+ self.tree_view.props.hover_selection = True
- def __motion_notify_event_cb(self, entry, event):
- if event.detail == hippo.MOTION_DETAIL_ENTER:
- self.props.background_color = style.COLOR_PANEL_GREY.get_int()
- elif event.detail == hippo.MOTION_DETAIL_LEAVE:
- self.props.background_color = style.COLOR_WHITE.get_int()
- return False
+ self.tree_view.connect('button-release-event',
+ self.__button_release_event_cb)
-class ChooserListView(ListView):
- __gtype_name__ = 'ChooserListView'
+ def __entry_activated_cb(self, entry):
+ self.emit('entry-activated', entry)
- __gsignals__ = {
- 'entry-activated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
- }
+ def __button_release_event_cb(self, tree_view, event):
+ if event.window != tree_view.get_bin_window():
+ return False
- def __init__(self):
- ListView.__init__(self)
+ pos = tree_view.get_path_at_pos(event.x, event.y)
+ if pos is None:
+ return False
- def create_entry(self):
- entry = ChooserCollapsedEntry()
- entry.connect('entry-activated', self.__entry_activated_cb)
- return entry
+ path, column_, x_, y_ = pos
+ uid = tree_view.get_model()[path][ListModel.COLUMN_UID]
+ self.emit('entry-activated', uid)
- def __entry_activated_cb(self, entry):
- self.emit('entry-activated', entry)
+ return False
diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py
index 2c15591..49cc676 100644
--- a/src/jarabe/journal/palettes.py
+++ b/src/jarabe/journal/palettes.py
@@ -41,7 +41,7 @@ class ObjectPalette(Palette):
__gsignals__ = {
'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([]))
+ ([str])),
}
def __init__(self, metadata, detail=False):
@@ -61,7 +61,7 @@ class ObjectPalette(Palette):
style.COLOR_TRANSPARENT.get_svg()))
if metadata.has_key('title'):
- title = metadata['title']
+ title = gobject.markup_escape_text(metadata['title'])
else:
title = _('Untitled')
@@ -126,7 +126,7 @@ class ObjectPalette(Palette):
def __clipboard_get_func_cb(self, clipboard, selection_data, info, data):
# Get hold of a reference so the temp file doesn't get deleted
self._temp_file_path = model.get_file(self._metadata['uid'])
- logging.debug('__clipboard_get_func_cb %r' % self._temp_file_path)
+ logging.debug('__clipboard_get_func_cb %r', self._temp_file_path)
selection_data.set_uris(['file://' + self._temp_file_path])
def __clipboard_clear_func_cb(self, clipboard, data):
@@ -142,7 +142,7 @@ class ObjectPalette(Palette):
model.delete(self._metadata['uid'])
def __detail_activate_cb(self, menu_item):
- self.emit('detail-clicked')
+ self.emit('detail-clicked', self._metadata['uid'])
def __friend_selected_cb(self, menu_item, buddy):
logging.debug('__friend_selected_cb')
diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py
index b21832e..6cb3f8d 100644
--- a/src/jarabe/journal/volumestoolbar.py
+++ b/src/jarabe/journal/volumestoolbar.py
@@ -76,7 +76,7 @@ class VolumesToolbar(gtk.Toolbar):
self._remove_button(mount)
def _add_button(self, mount):
- logging.debug('VolumeToolbar._add_button: %r' % mount.get_name())
+ logging.debug('VolumeToolbar._add_button: %r', mount.get_name())
button = VolumeButton(mount)
button.props.group = self._volume_buttons[0]
@@ -99,14 +99,14 @@ class VolumesToolbar(gtk.Toolbar):
mount.unmount(self.__unmount_cb)
def __unmount_cb(self, source, result):
- logging.debug('__unmount_cb %r %r' % (source, result))
+ logging.debug('__unmount_cb %r %r', source, result)
def _get_button_for_mount(self, mount):
mount_point = mount.get_root().get_path()
for button in self.get_children():
if button.mount_point == mount_point:
return button
- logging.error('Couldnt find button with mount_point %r' % mount_point)
+ logging.error('Couldnt find button with mount_point %r', mount_point)
return None
def _remove_button(self, mount):
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 068cf8d..1d23f0c 100644
--- a/src/jarabe/model/bundleregistry.py
+++ b/src/jarabe/model/bundleregistry.py
@@ -18,7 +18,6 @@
import os
import logging
import traceback
-import sys
import gobject
import gio
@@ -26,6 +25,7 @@ import cjson
from sugar.bundle.activitybundle import ActivityBundle
from sugar.bundle.contentbundle import ContentBundle
+from jarabe.journal.journalentrybundle import JournalEntryBundle
from sugar.bundle.bundle import MalformedBundleException, \
AlreadyInstalledException, RegistrationException
from sugar import env
@@ -68,8 +68,7 @@ class BundleRegistry(gobject.GObject):
try:
self._load_favorites()
except Exception:
- logging.error('Error while loading favorite_activities\n%s.' \
- % traceback.format_exc())
+ logging.exception('Error while loading favorite_activities.')
self._merge_default_favorites()
@@ -78,7 +77,7 @@ class BundleRegistry(gobject.GObject):
if not one_file.get_path().endswith('.activity'):
return
if event_type == gio.FILE_MONITOR_EVENT_CREATED:
- self.add_bundle(one_file.get_path())
+ self.add_bundle(one_file.get_path(), install_mime_type=True)
elif event_type == gio.FILE_MONITOR_EVENT_DELETED:
self.remove_bundle(one_file.get_path())
@@ -154,7 +153,7 @@ class BundleRegistry(gobject.GObject):
if max_version > -1 and key not in self._favorite_bundles:
self._favorite_bundles[key] = None
- logging.debug('After merging: %r' % self._favorite_bundles)
+ logging.debug('After merging: %r', self._favorite_bundles)
self._write_favorites_file()
@@ -168,6 +167,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
@@ -181,9 +183,10 @@ class BundleRegistry(gobject.GObject):
bundle_dir = os.path.join(path, f)
if os.path.isdir(bundle_dir):
bundles[bundle_dir] = os.stat(bundle_dir).st_mtime
- except Exception, e:
+ except Exception:
logging.error('Error while processing installed activity ' \
- 'bundle: %s, %s, %s' % (f, e.__class__, e))
+ 'bundle %s:\n%s' % \
+ (bundle_dir, traceback.format_exc()))
bundle_dirs = bundles.keys()
bundle_dirs.sort(lambda d1, d2: cmp(bundles[d1], bundles[d2]))
@@ -192,10 +195,11 @@ class BundleRegistry(gobject.GObject):
self._add_bundle(folder)
except Exception, e:
logging.error('Error while processing installed activity ' \
- 'bundle: %s, %s, %s' % (folder, e.__class__, e))
+ 'bundle %s:\n%s' % \
+ (folder, traceback.format_exc()))
- def add_bundle(self, bundle_path):
- bundle = self._add_bundle(bundle_path)
+ def add_bundle(self, bundle_path, install_mime_type=False):
+ bundle = self._add_bundle(bundle_path, install_mime_type)
if bundle is not None:
self._set_bundle_favorite(bundle.get_bundle_id(),
bundle.get_activity_version(),
@@ -205,13 +209,14 @@ class BundleRegistry(gobject.GObject):
else:
return False
- def _add_bundle(self, bundle_path):
- logging.debug('STARTUP: Adding bundle %r' % bundle_path)
+ def _add_bundle(self, bundle_path, install_mime_type=False):
+ logging.debug('STARTUP: Adding bundle %r', bundle_path)
try:
bundle = ActivityBundle(bundle_path)
+ if install_mime_type:
+ bundle.install_mime_type(bundle_path)
except MalformedBundleException:
- logging.error('Error loading bundle %r:\n%s' % (bundle_path,
- ''.join(traceback.format_exception(*sys.exc_info()))))
+ logging.exception('Error loading bundle %r', bundle_path)
return None
if self.get_bundle(bundle.get_bundle_id()):
@@ -313,7 +318,8 @@ class BundleRegistry(gobject.GObject):
def is_installed(self, bundle):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
- if isinstance(bundle, ContentBundle):
+ if isinstance(bundle, ContentBundle) or \
+ isinstance(bundle, JournalEntryBundle):
return bundle.is_installed()
for installed_bundle in self._bundles:
@@ -326,12 +332,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():
@@ -342,7 +345,8 @@ class BundleRegistry(gobject.GObject):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
- if isinstance(bundle, ContentBundle):
+ if isinstance(bundle, ContentBundle) or \
+ isinstance(bundle, JournalEntryBundle):
pass
elif not self.add_bundle(install_path):
raise RegistrationException
@@ -350,7 +354,8 @@ class BundleRegistry(gobject.GObject):
def uninstall(self, bundle, force=False):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
- if isinstance(bundle, ContentBundle):
+ if isinstance(bundle, ContentBundle) or \
+ isinstance(bundle, JournalEntryBundle):
if bundle.is_installed():
bundle.uninstall()
else:
@@ -377,13 +382,16 @@ class BundleRegistry(gobject.GObject):
act = self.get_bundle(bundle.get_bundle_id())
if act is None:
logging.warning('Activity not installed')
+ elif act.get_activity_version() == bundle.get_activity_version():
+ logging.debug('No upgrade needed, same version already installed.')
+ return
elif act.get_path().startswith(env.get_user_activities_path()):
try:
self.uninstall(bundle, force=True)
except Exception:
logging.error('Uninstall failed, still trying to install ' \
'newer bundle:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ traceback.format_exc())
else:
logging.warning('Unable to uninstall system activity, ' \
'installing upgraded version in user activities')
diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py
index 6419f28..3c188ff 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__ = {
@@ -146,7 +147,7 @@ class BaseFileTransfer(gobject.GObject):
handle)
def __transferred_bytes_changed_cb(self, transferred_bytes):
- logging.debug('__transferred_bytes_changed_cb %r' % transferred_bytes)
+ logging.debug('__transferred_bytes_changed_cb %r', transferred_bytes)
self.props.transferred_bytes = transferred_bytes
def _set_transferred_bytes(self, transferred_bytes):
@@ -159,11 +160,11 @@ class BaseFileTransfer(gobject.GObject):
getter=_get_transferred_bytes, setter=_set_transferred_bytes)
def __initial_offset_defined_cb(self, offset):
- logging.debug('__initial_offset_defined_cb %r' % offset)
+ logging.debug('__initial_offset_defined_cb %r', offset)
self.initial_offset = offset
def __state_changed_cb(self, state, reason):
- logging.debug('__state_changed_cb %r %r' % (state, reason))
+ logging.debug('__state_changed_cb %r %r', state, reason)
self.reason_last_change = reason
self.props.state = state
@@ -204,7 +205,7 @@ class IncomingFileTransfer(BaseFileTransfer):
SOCKET_ACCESS_CONTROL_LOCALHOST, '', 0, byte_arrays=True)
def __notify_state_cb(self, file_transfer, pspec):
- logging.debug('__notify_state_cb %r' % self.props.state)
+ logging.debug('__notify_state_cb %r', self.props.state)
if self.props.state == FT_STATE_OPEN:
# Need to hold a reference to the socket so that python doesn't
# close the fd when it goes out of scope
@@ -275,14 +276,14 @@ class OutgoingFileTransfer(BaseFileTransfer):
'org.laptop.Sugar.Presence.Buddy')
handles = ps_buddy.GetTelepathyHandles()
- logging.debug('_get_buddy_handle %r' % handles)
+ logging.debug('_get_buddy_handle %r', handles)
bus_name, object_path, handle = handles[0]
return handle
def __notify_state_cb(self, file_transfer, pspec):
- logging.debug('__notify_state_cb %r' % self.props.state)
+ logging.debug('__notify_state_cb %r', self.props.state)
if self.props.state == FT_STATE_OPEN:
# Need to hold a reference to the socket so that python doesn't
# closes the fd when it goes out of scope
@@ -290,7 +291,7 @@ class OutgoingFileTransfer(BaseFileTransfer):
self._socket.connect(self._socket_address)
output_stream = gio.unix.OutputStream(self._socket.fileno(), True)
- logging.debug('opening %s for reading' % self._file_name)
+ logging.debug('opening %s for reading', self._file_name)
input_stream = gio.File(self._file_name).read()
if self.initial_offset > 0:
input_stream.skip(self.initial_offset)
@@ -307,14 +308,14 @@ def _new_channels_cb(connection, channels):
if props[CHANNEL + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER and \
not props[CHANNEL + '.Requested']:
- logging.debug('__new_channels_cb %r' % object_path)
+ logging.debug('__new_channels_cb %r', object_path)
incoming_file_transfer = IncomingFileTransfer(connection,
object_path, props)
new_file_transfer.send(None, file_transfer=incoming_file_transfer)
def _monitor_connection(connection):
- logging.debug('connection added %r' % connection)
+ logging.debug('connection added %r', connection)
connection[CONNECTION_INTERFACE_REQUESTS].connect_to_signal('NewChannels',
lambda channels: _new_channels_cb(connection, channels))
@@ -322,7 +323,7 @@ def _connection_addded_cb(conn_watcher, connection):
_monitor_connection(connection)
def _connection_removed_cb(conn_watcher, connection):
- logging.debug('connection removed %r' % connection)
+ logging.debug('connection removed %r', connection)
_conn_watcher = None
diff --git a/src/jarabe/model/friends.py b/src/jarabe/model/friends.py
index 27a11dd..b7bf7f1 100644
--- a/src/jarabe/model/friends.py
+++ b/src/jarabe/model/friends.py
@@ -72,8 +72,8 @@ class Friends(gobject.GObject):
continue
buddy = BuddyModel(key=key, nick=cp.get(key, 'nick'))
self.add_friend(buddy)
- except Exception, exc:
- logging.error("Error parsing friends file: %s" % exc)
+ except Exception:
+ logging.exception('Error parsing friends file')
def save(self):
cp = ConfigParser()
@@ -98,8 +98,8 @@ class Friends(gobject.GObject):
pass
def friends_synced_error(e):
- logging.error("Error asking presence service to sync friends: %s"
- % e)
+ logging.error('Error asking presence service to sync friends: %s',
+ e)
keys = []
for friend in self:
diff --git a/src/jarabe/model/neighborhood.py b/src/jarabe/model/neighborhood.py
index f4ef9ac..2b223e4 100644
--- a/src/jarabe/model/neighborhood.py
+++ b/src/jarabe/model/neighborhood.py
@@ -122,16 +122,16 @@ class Neighborhood(gobject.GObject):
gconf_client = gconf.client_get_default()
key = '/desktop/sugar/collaboration/publish_gadget'
publish = gconf_client.get_bool(key)
- logging.debug("Gadget discovered on connection %s."
- " Publish our status: %r" %
- (conn.service_name.split('.')[-1], publish))
+ logging.debug('Gadget discovered on connection %s.'
+ ' Publish our status: %r', conn.service_name.split('.')[-1],
+ publish)
conn[CONN_INTERFACE_GADGET].Publish(publish)
self._request_random_buddies(conn, NB_RANDOM_BUDDIES)
self._request_random_activities(conn, NB_RANDOM_ACTIVITIES)
def _request_random_buddies(self, conn, nb):
- logging.debug("Request %d random buddies" % nb)
+ logging.debug('Request %d random buddies', nb)
path, props_ = conn[CONNECTION_INTERFACE_REQUESTS].CreateChannel(
{ 'org.freedesktop.Telepathy.Channel.ChannelType':
@@ -145,7 +145,7 @@ class Neighborhood(gobject.GObject):
lambda: self._request_random_buddies(conn, nb)))
def _request_random_activities(self, conn, nb):
- logging.debug("Request %d random activities" % nb)
+ logging.debug('Request %d random activities', nb)
path, props_ = conn[CONNECTION_INTERFACE_REQUESTS].CreateChannel(
{ 'org.freedesktop.Telepathy.Channel.ChannelType':
diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
index bff5197..4dbc309 100644
--- a/src/jarabe/model/network.py
+++ b/src/jarabe/model/network.py
@@ -1,4 +1,5 @@
# Copyright (C) 2008 Red Hat, Inc.
+# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
#
# 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
@@ -103,7 +104,7 @@ class Wireless(object):
self.ssid = None
self.security = None
self.mode = None
- self.channel = None
+ self.band = None
def get_dict(self):
wireless = {'ssid': self.ssid}
@@ -111,8 +112,8 @@ class Wireless(object):
wireless['security'] = self.security
if self.mode:
wireless['mode'] = self.mode
- if self.channel:
- wireless['channel'] = self.channel
+ if self.band:
+ wireless['band'] = self.band
return wireless
class Connection(object):
@@ -138,7 +139,6 @@ class IP4Config(object):
def get_dict(self):
ip4_config = {}
- print self.method
if self.method is not None:
ip4_config['method'] = self.method
return ip4_config
@@ -254,8 +254,8 @@ class NMSettingsConnection(dbus.service.Object):
if not config.read(config_path):
logging.error('Error reading the nm config file')
return
- except ConfigParser.ParsingError, e:
- logging.error('Error reading the nm config file: %s' % e)
+ except ConfigParser.ParsingError:
+ logging.exception('Error reading the nm config file')
return
identifier = self._settings.connection.id
@@ -269,7 +269,6 @@ class NMSettingsConnection(dbus.service.Object):
if self._settings.connection.timestamp is not None:
config.set(identifier, 'timestamp',
self._settings.connection.timestamp)
-
if self._settings.wireless_security is not None:
if self._settings.wireless_security.key_mgmt is not None:
config.set(identifier, 'key-mgmt',
@@ -293,13 +292,13 @@ class NMSettingsConnection(dbus.service.Object):
elif self._settings.wireless_security.key_mgmt == 'wpa-psk':
config.set(identifier, 'key', self._secrets.psk)
except ConfigParser.Error, e:
- logging.error('Error constructing %s: %s' % (identifier, e))
+ logging.exception('Error constructing %s', identifier)
else:
f = open(config_path, 'w')
try:
config.write(f)
- except ConfigParser.Error, e:
- logging.error('Can not write %s error: %s' % (config_path, e))
+ except ConfigParser.Error:
+ logging.exception('Can not write %s', config_path)
f.close()
@dbus.service.method(dbus_interface=NM_CONNECTION_IFACE,
@@ -311,16 +310,16 @@ class NMSettingsConnection(dbus.service.Object):
async_callbacks=('reply', 'error'),
in_signature='sasb', out_signature='a{sa{sv}}')
def GetSecrets(self, setting_name, hints, request_new, reply, error):
- logging.debug('Secrets requested for connection %s request_new=%s'
- % (self.path, request_new))
+ logging.debug('Secrets requested for connection %s request_new=%s',
+ self.path, request_new)
if request_new or self._secrets is None:
# request_new is for example the case when the pw on the AP changes
response = SecretsResponse(self, reply, error)
try:
self.secrets_request.send(self, response=response)
- except Exception, e:
- logging.error('Error requesting the secrets via dialog: %s' % e)
+ except Exception:
+ logging.exception('Error requesting the secrets via dialog')
else:
reply(self._secrets.get_dict())
@@ -329,8 +328,8 @@ def get_settings():
if _nm_settings is None:
try:
_nm_settings = NMSettings()
- except dbus.DBusException, e:
- logging.error('Cannot create the UserSettings service %s.', e)
+ except dbus.DBusException:
+ logging.exception('Cannot create the UserSettings service.')
load_connections()
return _nm_settings
@@ -368,8 +367,8 @@ def load_connections():
if not config.read(config_path):
logging.error('Error reading the nm config file')
return
- except ConfigParser.ParsingError, e:
- logging.error('Error reading the nm config file: %s' % e)
+ except ConfigParser.ParsingError:
+ logging.exception('Error reading the nm config file')
return
for section in config.sections():
@@ -413,7 +412,7 @@ def load_connections():
if config.has_option(section, 'pairwise'):
value = config.get(section, 'pairwise')
settings.wireless_security.pairwise = value
- except ConfigParser.Error, e:
- logging.error('Error reading section: %s' % e)
+ except ConfigParser.Error:
+ logging.exception('Error reading section')
else:
add_connection(ssid, settings, secrets)
diff --git a/src/jarabe/model/notifications.py b/src/jarabe/model/notifications.py
index da5c590..a2345f7 100644
--- a/src/jarabe/model/notifications.py
+++ b/src/jarabe/model/notifications.py
@@ -42,8 +42,8 @@ class NotificationService(dbus.service.Object):
def Notify(self, app_name, replaces_id, app_icon, summary, body, actions,
hints, expire_timeout):
- logging.debug('Received notification: %r' % ([app_name, replaces_id,
- app_icon, summary, body, actions, hints, expire_timeout]))
+ logging.debug('Received notification: %r', [app_name, replaces_id,
+ app_icon, summary, body, actions, hints, expire_timeout])
if replaces_id > 0:
notification_id = replaces_id
diff --git a/src/jarabe/model/owner.py b/src/jarabe/model/owner.py
index bdfd9a8..2075f08 100644
--- a/src/jarabe/model/owner.py
+++ b/src/jarabe/model/owner.py
@@ -62,8 +62,8 @@ class Owner(gobject.GObject):
raise RuntimeError("invalid buddy icon")
# Get the icon's hash
- import md5
- digest = md5.new(self._icon).digest()
+ import hashlib
+ digest = hashlib.md5(self._icon).digest()
self._icon_hash = util.printable_hash(digest)
self._pservice = presenceservice.get_instance()
diff --git a/src/jarabe/model/screen.py b/src/jarabe/model/screen.py
index 87dc370..4403c1c 100644
--- a/src/jarabe/model/screen.py
+++ b/src/jarabe/model/screen.py
@@ -22,9 +22,6 @@ _HARDWARE_MANAGER_INTERFACE = 'org.freedesktop.ohm.Keystore'
_HARDWARE_MANAGER_SERVICE = 'org.freedesktop.ohm'
_HARDWARE_MANAGER_OBJECT_PATH = '/org/freedesktop/ohm/Keystore'
-COLOR_MODE = 0
-B_AND_W_MODE = 1
-
_ohm_service = None
def _get_ohm():
@@ -44,21 +41,3 @@ def set_dcon_freeze(frozen):
except dbus.DBusException:
logging.error('Cannot unfreeze the DCON')
-def set_display_mode(mode):
- try:
- _get_ohm().SetKey("display.dcon_mode", mode)
- except dbus.DBusException:
- logging.error('Cannot change DCON mode')
-
-def set_display_brightness(level):
- try:
- _get_ohm().SetKey("backlight.hardware_brightness", level)
- except dbus.DBusException:
- logging.error('Cannot set display brightness')
-
-def get_display_brightness():
- try:
- return _get_ohm().GetKey("backlight.hardware_brightness")
- except dbus.DBusException:
- logging.error('Cannot get display brightness')
- return 0
diff --git a/src/jarabe/model/shell.py b/src/jarabe/model/shell.py
index 709eb03..ec315ca 100644
--- a/src/jarabe/model/shell.py
+++ b/src/jarabe/model/shell.py
@@ -269,7 +269,7 @@ class Activity(gobject.GObject):
pass
def _set_active_error(self, err):
- logging.error("set_active() failed: %s" % err)
+ logging.error("set_active() failed: %s", err)
class ShellModel(gobject.GObject):
"""Model of the shell (activity management)
@@ -457,6 +457,12 @@ class ShellModel(gobject.GObject):
if activity_id:
home_activity = self.get_activity_by_id(activity_id)
+ xid = window.get_xid()
+ gdk_window = gtk.gdk.window_foreign_new(xid)
+ gdk_window.set_decorations(0)
+
+ window.maximize()
+
if not home_activity:
home_activity = Activity(activity_info, activity_id, window)
self._add_activity(home_activity)
@@ -468,8 +474,8 @@ class ShellModel(gobject.GObject):
self.emit('launch-completed', home_activity)
startup_time = time.time() - home_activity.get_launch_time()
- logging.debug('%s launched in %f seconds.' %
- (home_activity.get_type(), startup_time))
+ logging.debug('%s launched in %f seconds.',
+ home_activity.get_type(), startup_time)
if self._active_activity is None:
self._set_active_activity(home_activity)
@@ -531,7 +537,7 @@ class ShellModel(gobject.GObject):
if home_activity:
self._remove_activity(home_activity)
else:
- logging.error('Model for window %d does not exist.' % xid)
+ logging.error('Model for window %d does not exist.', xid)
def notify_launch(self, activity_id, service_name):
registry = get_registry()
@@ -556,13 +562,13 @@ class ShellModel(gobject.GObject):
def notify_launch_failed(self, activity_id):
home_activity = self.get_activity_by_id(activity_id)
if home_activity:
- logging.debug("Activity %s (%s) launch failed" % \
- (activity_id, home_activity.get_type()))
+ logging.debug("Activity %s (%s) launch failed", activity_id,
+ home_activity.get_type())
home_activity.props.launching = False
self._remove_activity(home_activity)
else:
- logging.error('Model for activity id %s does not exist.'
- % activity_id)
+ logging.error('Model for activity id %s does not exist.',
+ activity_id)
self.emit('launch-failed', home_activity)
@@ -570,12 +576,12 @@ class ShellModel(gobject.GObject):
home_activity = self.get_activity_by_id(activity_id)
if not home_activity:
- logging.debug('Activity %s has been closed already.' % activity_id)
+ logging.debug('Activity %s has been closed already.', activity_id)
return False
if home_activity.props.launching:
- logging.debug('Activity %s still launching, assuming it failed...'
- % activity_id)
+ logging.debug('Activity %s still launching, assuming it failed.',
+ activity_id)
self.notify_launch_failed(activity_id)
return False
diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py
index 35a8301..e9e9f8e 100644
--- a/src/jarabe/view/buddymenu.py
+++ b/src/jarabe/view/buddymenu.py
@@ -79,8 +79,8 @@ class BuddyMenu(Palette):
self._update_invite_menu(activity)
def _add_my_items(self):
- item = MenuItem(_('My Settings'), 'preferences-system')
- item.connect('activate', self.__controlpanel_activate_cb)
+ item = MenuItem(_('Shutdown'), 'system-shutdown')
+ item.connect('activate', self.__shutdown_activate_cb)
self.menu.append(item)
item.show()
@@ -92,13 +92,8 @@ class BuddyMenu(Palette):
self.menu.append(item)
item.show()
- item = MenuItem(_('Restart'), 'system-restart')
- item.connect('activate', self.__reboot_activate_cb)
- self.menu.append(item)
- item.show()
-
- item = MenuItem(_('Shutdown'), 'system-shutdown')
- item.connect('activate', self.__shutdown_activate_cb)
+ item = MenuItem(_('My Settings'), 'preferences-system')
+ item.connect('activate', self.__controlpanel_activate_cb)
self.menu.append(item)
item.show()
diff --git a/src/jarabe/view/keyhandler.py b/src/jarabe/view/keyhandler.py
index 08856c0..26a7f26 100644
--- a/src/jarabe/view/keyhandler.py
+++ b/src/jarabe/view/keyhandler.py
@@ -22,12 +22,12 @@ import errno
import traceback
import sys
+import gconf
import dbus
import gtk
from sugar._sugarext import KeyGrabber
-from jarabe.model import screen
from jarabe.model import sound
from jarabe.model import shell
from jarabe.model import session
@@ -35,10 +35,9 @@ from jarabe.view.tabbinghandler import TabbingHandler
from jarabe.model.shell import ShellModel
from jarabe import config
from jarabe.journal import journalactivity
+from jarabe.desktop import homewindow
-_BRIGHTNESS_STEP = 2
_VOLUME_STEP = sound.VOLUME_STEP
-_BRIGHTNESS_MAX = 15
_VOLUME_MAX = 100
_TABBING_MODIFIER = gtk.gdk.MOD1_MASK
@@ -47,10 +46,6 @@ _actions_table = {
'F2' : 'zoom_group',
'F3' : 'zoom_home',
'F4' : 'zoom_activity',
- 'F9' : 'brightness_down',
- 'F10' : 'brightness_up',
- '<alt>F9' : 'brightness_min',
- '<alt>F10' : 'brightness_max',
'XF86AudioMute' : 'volume_mute',
'F11' : 'volume_down',
'XF86AudioLowerVolume' : 'volume_down',
@@ -59,7 +54,6 @@ _actions_table = {
'<alt>F11' : 'volume_min',
'<alt>F12' : 'volume_max',
'0x93' : 'frame',
- '0xEB' : 'rotate',
'<alt>Tab' : 'next_window',
'<alt><shift>Tab' : 'previous_window',
'<alt>Escape' : 'close_window',
@@ -69,8 +63,9 @@ _actions_table = {
'<alt><shift>q' : 'quit_emulator',
'XF86Search' : 'open_search',
'<alt><shift>o' : 'open_search',
- '<alt><shift>r' : 'rotate',
'<alt><shift>s' : 'say_text',
+ 'Alt_L' : 'disable_resume_mode',
+ 'Alt_R' : 'disable_resume_mode',
}
SPEECH_DBUS_SERVICE = 'org.laptop.Speech'
@@ -80,12 +75,13 @@ SPEECH_DBUS_INTERFACE = 'org.laptop.Speech'
class KeyHandler(object):
def __init__(self, frame):
self._frame = frame
- self._screen_rotation = 0
self._key_pressed = None
self._keycode_pressed = 0
self._keystate_pressed = 0
self._speech_proxy = None
+ self._ungrab_metacity_keys()
+
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed',
self._key_pressed_cb)
@@ -98,7 +94,7 @@ class KeyHandler(object):
if f.endswith('.py') and not f.startswith('__'):
module_name = f[:-3]
try:
- logging.debug('Loading module %r' % module_name)
+ logging.debug('Loading module %r', module_name)
module = __import__('globalkey.' + module_name, globals(),
locals(), [module_name])
for key in module.BOUND_KEYS:
@@ -111,6 +107,15 @@ class KeyHandler(object):
self._key_grabber.grab_keys(_actions_table.keys())
+ def _ungrab_metacity_keys(self):
+ """So we can grab those instead.
+ """
+ client = gconf.client_get_default()
+ for key in ['run_command_screenshot', 'switch_windows',
+ 'cycle_windows']:
+ key = '/apps/metacity/global_keybindings/' + key
+ client.set_string(key, 'disabled')
+
def _change_volume(self, step=None, value=None):
if step is not None:
volume = sound.get_volume() + step
@@ -122,20 +127,6 @@ class KeyHandler(object):
sound.set_volume(volume)
sound.set_muted(volume == 0)
- def _change_brightness(self, step=None, value=None):
- if step is not None:
- level = screen.get_display_brightness() + step
- elif value is not None:
- level = value
-
- level = min(max(0, level), _BRIGHTNESS_MAX)
-
- screen.set_display_brightness(level)
- if level == 0:
- screen.set_display_mode(screen.B_AND_W_MODE)
- else:
- screen.set_display_mode(screen.COLOR_MODE)
-
def _get_speech_proxy(self):
if self._speech_proxy is None:
bus = dbus.SessionBus()
@@ -146,126 +137,81 @@ class KeyHandler(object):
return self._speech_proxy
def _on_speech_err(self, ex):
- logging.error("An error occurred with the ESpeak service: %r" % (ex, ))
+ logging.error('An error occurred with the ESpeak service: %r', ex)
def _primary_selection_cb(self, clipboard, text, user_data):
- logging.debug('KeyHandler._primary_selection_cb: %r' % text)
+ logging.debug('KeyHandler._primary_selection_cb: %r', text)
if text:
self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \
error_handler=self._on_speech_err)
- def handle_say_text(self):
+ def handle_say_text(self, event_time):
clipboard = gtk.clipboard_get(selection="PRIMARY")
clipboard.request_text(self._primary_selection_cb)
- def handle_previous_window(self):
- self._tabbing_handler.previous_activity()
+ def handle_previous_window(self, event_time):
+ self._tabbing_handler.previous_activity(event_time)
- def handle_next_window(self):
- self._tabbing_handler.next_activity()
+ def handle_next_window(self, event_time):
+ self._tabbing_handler.next_activity(event_time)
- def handle_close_window(self):
+ def handle_close_window(self, event_time):
active_activity = shell.get_model().get_active_activity()
if active_activity.is_journal():
return
active_activity.get_window().close()
- def handle_zoom_mesh(self):
+ def handle_zoom_mesh(self, event_time):
shell.get_model().zoom_level = ShellModel.ZOOM_MESH
- def handle_zoom_group(self):
+ def handle_zoom_group(self, event_time):
shell.get_model().zoom_level = ShellModel.ZOOM_GROUP
- def handle_zoom_home(self):
+ def handle_zoom_home(self, event_time):
shell.get_model().zoom_level = ShellModel.ZOOM_HOME
- def handle_zoom_activity(self):
+ def handle_zoom_activity(self, event_time):
shell.get_model().zoom_level = ShellModel.ZOOM_ACTIVITY
- def handle_brightness_max(self):
- self._change_brightness(value=_BRIGHTNESS_MAX)
-
- def handle_brightness_min(self):
- self._change_brightness(value=0)
-
- def handle_volume_max(self):
+ def handle_volume_max(self, event_time):
self._change_volume(value=_VOLUME_MAX)
- def handle_volume_min(self):
+ def handle_volume_min(self, event_time):
self._change_volume(value=0)
- def handle_brightness_up(self):
- self._change_brightness(step=_BRIGHTNESS_STEP)
-
- def handle_brightness_down(self):
- self._change_brightness(step=-_BRIGHTNESS_STEP)
-
- def handle_volume_mute(self):
+ def handle_volume_mute(self, event_time):
if sound.get_muted() is True:
sound.set_muted(False)
else:
sound.set_muted(True)
- def handle_volume_up(self):
+ def handle_volume_up(self, event_time):
self._change_volume(step=_VOLUME_STEP)
- def handle_volume_down(self):
+ def handle_volume_down(self, event_time):
self._change_volume(step=-_VOLUME_STEP)
- def handle_frame(self):
+ def handle_frame(self, event_time):
self._frame.notify_key_press()
- def handle_rotate(self):
- """
- Handles rotation of the display (using xrandr) and of the d-pad.
-
- Notes: default mappings for keypad on MP
- KP_Up 80
- KP_Right 85
- KP_Down 88
- KP_Left 83
- """
-
- states = [ 'normal', 'left', 'inverted', 'right']
- keycodes = (80, 85, 88, 83, 80, 85, 88, 83)
- keysyms = ("KP_Up", "KP_Right", "KP_Down", "KP_Left")
-
- self._screen_rotation -= 1
- self._screen_rotation %= 4
-
- actual_keycodes = keycodes[self._screen_rotation:self._screen_rotation
- + 4]
- # code_pairs now contains a mapping of keycode -> keysym in the current
- # orientation
- code_pairs = zip(actual_keycodes, keysyms)
-
- # Using the mappings in code_pairs, we dynamically build up an xmodmap
- # command to rotate the dpad keys.
- argv = ['xmodmap']
- for arg in [('-e', 'keycode %i = %s' % p) for p in code_pairs]:
- argv.extend(arg)
-
- # If either the xmodmap or xrandr command fails, check_call will fail
- # with CalledProcessError, which we raise.
- try:
- subprocess.check_call(argv)
- subprocess.check_call(['xrandr', '-o',
- states[self._screen_rotation]])
- except OSError, e:
- if e.errno != errno.EINTR:
- raise
-
- def handle_quit_emulator(self):
+ def handle_quit_emulator(self, event_time):
session.get_session_manager().shutdown()
- def handle_open_search(self):
+ def handle_open_search(self, event_time):
journalactivity.get_journal().focus_search()
- def _key_pressed_cb(self, grabber, keycode, state):
+ def handle_disable_resume_mode(self, event_time):
+ # TODO: KeyHandler should be a singleton and interested parties
+ # would listen to it. That way it wouldn't need to reference half
+ # of the shell classes.
+ home_box = homewindow.get_instance().get_home_box()
+ home_box.set_resume_mode(False)
+
+ def _key_pressed_cb(self, grabber, keycode, state, event_time):
key = grabber.get_key(keycode, state)
- logging.debug('_key_pressed_cb: %i %i %s' % (keycode, state, key))
- if key:
+ logging.debug('_key_pressed_cb: %i %i %s', keycode, state, key)
+ if key is not None:
self._key_pressed = key
self._keycode_pressed = keycode
self._keystate_pressed = state
@@ -275,14 +221,14 @@ class KeyHandler(object):
# Only accept window tabbing events, everything else
# cancels the tabbing operation.
if not action in ["next_window", "previous_window"]:
- self._tabbing_handler.stop()
+ self._tabbing_handler.stop(event_time)
return True
if hasattr(action, 'handle_key_press'):
action.handle_key_press(key)
elif isinstance(action, basestring):
method = getattr(self, 'handle_' + action)
- method()
+ method(event_time)
else:
raise TypeError('Invalid action %r' % action)
@@ -291,17 +237,33 @@ class KeyHandler(object):
# If this is not a registered key, then cancel tabbing.
if self._tabbing_handler.is_tabbing():
if not grabber.is_modifier(keycode):
- self._tabbing_handler.stop()
+ self._tabbing_handler.stop(event_time)
return True
return False
- def _key_released_cb(self, grabber, keycode, state):
+ def _is_resume_mode_keycode(self, keycode):
+ """See if the physical key pressed matches one of the keys that modify
+ the resume mode of the favorites view.
+ """
+ keymap = gtk.gdk.keymap_get_default()
+ entries = keymap.get_entries_for_keycode(keycode)
+ for entry in entries:
+ if gtk.gdk.keyval_name(entry[0]) in ['Alt_L', 'Alt_R']:
+ return True
+ return False
+
+ def _key_released_cb(self, grabber, keycode, state, event_time):
+ logging.debug('_key_released_cb: %i %i' % (keycode, state))
+ if self._is_resume_mode_keycode(keycode):
+ home_box = homewindow.get_instance().get_home_box()
+ home_box.set_resume_mode(True)
+
if self._tabbing_handler.is_tabbing():
# We stop tabbing and switch to the new window as soon as the
# modifier key is raised again.
if grabber.is_modifier(keycode, mask=_TABBING_MODIFIER):
- self._tabbing_handler.stop()
+ self._tabbing_handler.stop(event_time)
return True
return False
diff --git a/src/jarabe/view/launcher.py b/src/jarabe/view/launcher.py
index 3071790..643baee 100644
--- a/src/jarabe/view/launcher.py
+++ b/src/jarabe/view/launcher.py
@@ -33,6 +33,7 @@ class LaunchWindow(gtk.Window):
gobject.GObject.__init__(self)
self.props.type_hint = gtk.gdk.WINDOW_TYPE_HINT_NORMAL
+ self.props.decorated = False
canvas = hippo.Canvas()
canvas.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())
@@ -156,4 +157,4 @@ def _destroy_launcher(home_activity):
_launchers[activity_id].destroy()
del _launchers[activity_id]
else:
- logging.error('Launcher for %s is missing' % activity_id)
+ logging.error('Launcher for %s is missing', activity_id)
diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py
index 9d45eb5..fe711fc 100644
--- a/src/jarabe/view/palettes.py
+++ b/src/jarabe/view/palettes.py
@@ -17,10 +17,9 @@
import os
import statvfs
from gettext import gettext as _
-import gconf
import logging
-import gobject
+import gconf
import gtk
from sugar import env
@@ -32,7 +31,6 @@ from sugar.graphics.xocolor import XoColor
from sugar.activity import activityfactory
from sugar.activity.activityhandle import ActivityHandle
-from jarabe.model import bundleregistry
from jarabe.model import shell
from jarabe.view import launcher
from jarabe.view.viewsource import setup_view_source
@@ -107,12 +105,9 @@ class CurrentActivityPalette(BasePalette):
class ActivityPalette(Palette):
__gtype_name__ = 'SugarActivityPalette'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self, activity_info):
+ self._activity_info = activity_info
+
client = gconf.client_get_default()
color = XoColor(client.get_string("/desktop/sugar/user/color"))
activity_icon = Icon(file=activity_info.get_icon(),
@@ -122,17 +117,9 @@ class ActivityPalette(Palette):
Palette.__init__(self, primary_text=activity_info.get_name(),
icon=activity_icon)
- registry = bundleregistry.get_registry()
-
- self._bundle = activity_info
- self._bundle_id = activity_info.get_bundle_id()
- self._version = activity_info.get_activity_version()
- self._favorite = registry.is_bundle_favorite(self._bundle_id,
- self._version)
-
xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
- menu_item = MenuItem(text_label=_('Start'),
+ menu_item = MenuItem(text_label=_('Start new'),
file_name=activity_info.get_icon(),
xo_color=xo_color)
menu_item.connect('activate', self.__start_activate_cb)
@@ -141,46 +128,6 @@ class ActivityPalette(Palette):
# TODO: start-with
- self._favorite_item = MenuItem('')
- self._favorite_icon = Icon(icon_name='emblem-favorite',
- icon_size=gtk.ICON_SIZE_MENU)
- self._favorite_item.set_image(self._favorite_icon)
- self._favorite_item.connect('activate',
- self.__change_favorite_activate_cb)
- self.menu.append(self._favorite_item)
- self._favorite_item.show()
-
- menu_item = MenuItem(_('Erase'), 'list-remove')
- menu_item.connect('activate', self.__erase_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- if not os.access(self._bundle.get_path(), os.W_OK):
- menu_item.props.sensitive = False
-
- registry = bundleregistry.get_registry()
- self._activity_changed_sid = registry.connect('bundle_changed',
- self.__activity_changed_cb)
- self._update_favorite_item()
-
- self.connect('destroy', self.__destroy_cb)
-
- def __destroy_cb(self, palette):
- self.disconnect(self._activity_changed_sid)
-
- def _update_favorite_item(self):
- label = self._favorite_item.child
- if self._favorite:
- label.set_text(_('Remove favorite'))
- xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
- style.COLOR_TRANSPARENT.get_svg()))
- else:
- label.set_text(_('Make favorite'))
- client = gconf.client_get_default()
- xo_color = XoColor(client.get_string("/desktop/sugar/user/color"))
-
- self._favorite_icon.props.xo_color = xo_color
-
def __start_activate_cb(self, menu_item):
self.popdown(immediate=True)
@@ -189,28 +136,11 @@ class ActivityPalette(Palette):
activity_id = activityfactory.create_activity_id()
launcher.add_launcher(activity_id,
- self._bundle.get_icon(),
+ self._activity_info.get_icon(),
xo_color)
handle = ActivityHandle(activity_id)
- activityfactory.create(self._bundle, handle)
-
- def __change_favorite_activate_cb(self, menu_item):
- registry = bundleregistry.get_registry()
- registry.set_bundle_favorite(self._bundle_id,
- self._version,
- not self._favorite)
-
- def __activity_changed_cb(self, activity_registry, activity_info):
- if activity_info.get_bundle_id() == self._bundle_id and \
- activity_info.get_activity_version() == self._version:
- registry = bundleregistry.get_registry()
- self._favorite = registry.is_bundle_favorite(self._bundle_id,
- self._version)
- self._update_favorite_item()
-
- def __erase_activate_cb(self, menu_item):
- self.emit('erase-activated', self._bundle_id)
+ activityfactory.create(self._activity_info, handle)
class JournalPalette(BasePalette):
def __init__(self, home_activity):
@@ -299,7 +229,7 @@ class VolumePalette(Palette):
self._mount.unmount(self.__unmount_cb)
def __unmount_cb(self, mount, result):
- logging.debug('__unmount_cb %r %r' % (mount, result))
+ logging.debug('__unmount_cb %r %r', mount, result)
mount.unmount_finish(result)
def __popup_cb(self, palette):
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 = ""
diff --git a/src/jarabe/view/tabbinghandler.py b/src/jarabe/view/tabbinghandler.py
index b1c85c6..bb95c26 100644
--- a/src/jarabe/view/tabbinghandler.py
+++ b/src/jarabe/view/tabbinghandler.py
@@ -15,8 +15,10 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
-import gtk
+import time
+
import gobject
+import gtk
from jarabe.model import shell
@@ -57,27 +59,28 @@ class TabbingHandler(object):
else:
self._frame.show(self._frame.MODE_NON_INTERACTIVE)
- def __timeout_cb(self):
- self._activate_current()
+ def __timeout_cb(self, event_time):
+ self._activate_current(event_time)
self._timeout = None
return False
- def _start_timeout(self):
+ def _start_timeout(self, event_time):
self._cancel_timeout()
- self._timeout = gobject.timeout_add(_RAISE_DELAY, self.__timeout_cb)
+ self._timeout = gobject.timeout_add(_RAISE_DELAY,
+ lambda: self.__timeout_cb(event_time))
def _cancel_timeout(self):
if self._timeout:
gobject.source_remove(self._timeout)
self._timeout = None
- def _activate_current(self):
+ def _activate_current(self, event_time):
home_model = shell.get_model()
activity = home_model.get_tabbing_activity()
if activity and activity.get_window():
- activity.get_window().activate(1)
+ activity.get_window().activate(event_time)
- def next_activity(self):
+ def next_activity(self, event_time):
if not self._tabbing:
first_switch = True
self._start_tabbing()
@@ -96,11 +99,11 @@ class TabbingHandler(object):
activity = shell_model.get_next_activity(current=activity)
shell_model.set_tabbing_activity(activity)
- self._start_timeout()
+ self._start_timeout(event_time)
else:
- self._activate_next_activity()
+ self._activate_next_activity(event_time)
- def previous_activity(self):
+ def previous_activity(self, event_time):
if not self._tabbing:
first_switch = True
self._start_tabbing()
@@ -119,16 +122,16 @@ class TabbingHandler(object):
activity = shell_model.get_previous_activity(current=activity)
shell_model.set_tabbing_activity(activity)
- self._start_timeout()
+ self._start_timeout(event_time)
else:
- self._activate_next_activity()
+ self._activate_next_activity(event_time)
- def _activate_next_activity(self):
+ def _activate_next_activity(self, event_time):
next_activity = shell.get_model().get_next_activity()
if next_activity:
- next_activity.get_window().activate(gtk.get_current_event_time())
+ next_activity.get_window().activate(event_time)
- def stop(self):
+ def stop(self, event_time):
gtk.gdk.keyboard_ungrab()
gtk.gdk.pointer_ungrab()
self._tabbing = False
@@ -136,7 +139,7 @@ class TabbingHandler(object):
self._frame.hide()
self._cancel_timeout()
- self._activate_current()
+ self._activate_current(event_time)
home_model = shell.get_model()
home_model.set_tabbing_activity(None)
diff --git a/src/jarabe/view/viewsource.py b/src/jarabe/view/viewsource.py
index 870b176..43f668a 100644
--- a/src/jarabe/view/viewsource.py
+++ b/src/jarabe/view/viewsource.py
@@ -64,8 +64,8 @@ def setup_view_source(activity):
bundle_path = activity.get_bundle_path()
if window_xid in map_activity_to_window:
- _logger.debug('Viewsource window already open for %s %s' % \
- (window_xid, bundle_path))
+ _logger.debug('Viewsource window already open for %s %s', window_xid,
+ bundle_path)
return
document_path = None
@@ -95,7 +95,7 @@ class ViewSource(gtk.Window):
def __init__(self, window_xid, bundle_path, document_path, title):
gtk.Window.__init__(self)
- logging.debug('ViewSource paths: %r %r' % (bundle_path, document_path))
+ logging.debug('ViewSource paths: %r %r', bundle_path, document_path)
self.set_decorated(False)
self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
@@ -248,7 +248,7 @@ class DocumentButton(RadioToolButton):
self._jobject.destroy()
def __internal_save_error_cb(self, err):
- logging.debug("Error saving Source object to datastore: %s" % err)
+ logging.debug('Error saving Source object to datastore: %s', err)
self._jobject.destroy()
class Toolbar(gtk.Toolbar):
@@ -439,7 +439,7 @@ class SourceDisplay(gtk.ScrolledWindow):
return
mime_type = mime.get_for_file(self._file_path)
- logging.debug('Detected mime type: %r' % mime_type)
+ logging.debug('Detected mime type: %r', mime_type)
language_manager = gtksourceview2.language_manager_get_default()
detected_language = None
@@ -450,7 +450,7 @@ class SourceDisplay(gtk.ScrolledWindow):
break
if detected_language is not None:
- logging.debug('Detected language: %r' % \
+ logging.debug('Detected language: %r',
detected_language.get_name())
self._buffer.set_language(detected_language)