Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README14
-rw-r--r--bin/.gitignore1
-rwxr-xr-x[-rw-r--r--]bin/sugar-session8
-rw-r--r--bin/sugar.in14
-rw-r--r--configure.ac4
-rw-r--r--data/icons/module-updater.svg8
-rw-r--r--data/sugar.schemas.in48
-rw-r--r--data/sugar.xml.in6
-rw-r--r--docs/controls.txt6
-rw-r--r--docs/release_howto.txt22
-rw-r--r--extensions/cpsection/aboutcomputer/__init__.py1
-rw-r--r--extensions/cpsection/aboutcomputer/model.py101
-rw-r--r--extensions/cpsection/aboutcomputer/view.py34
-rw-r--r--extensions/cpsection/aboutme/__init__.py2
-rw-r--r--extensions/cpsection/aboutme/model.py64
-rw-r--r--extensions/cpsection/aboutme/view.py257
-rw-r--r--extensions/cpsection/datetime/model.py26
-rw-r--r--extensions/cpsection/datetime/view.py42
-rw-r--r--extensions/cpsection/frame/model.py28
-rw-r--r--extensions/cpsection/frame/view.py76
-rw-r--r--extensions/cpsection/keyboard/__init__.py1
-rw-r--r--extensions/cpsection/keyboard/model.py24
-rw-r--r--extensions/cpsection/keyboard/view.py65
-rw-r--r--extensions/cpsection/language/__init__.py1
-rw-r--r--extensions/cpsection/language/model.py86
-rw-r--r--extensions/cpsection/language/view.py49
-rw-r--r--extensions/cpsection/modemconfiguration/__init__.py1
-rwxr-xr-xextensions/cpsection/modemconfiguration/model.py13
-rw-r--r--extensions/cpsection/modemconfiguration/view.py30
-rw-r--r--extensions/cpsection/network/__init__.py3
-rw-r--r--extensions/cpsection/network/model.py55
-rw-r--r--extensions/cpsection/network/view.py69
-rw-r--r--extensions/cpsection/power/__init__.py1
-rw-r--r--extensions/cpsection/power/model.py26
-rw-r--r--extensions/cpsection/power/view.py1
-rw-r--r--extensions/cpsection/updater/backends/aslo.py23
-rwxr-xr-xextensions/cpsection/updater/model.py28
-rw-r--r--extensions/cpsection/updater/view.py22
-rw-r--r--extensions/deviceicon/Makefile.am1
-rw-r--r--extensions/deviceicon/battery.py224
-rw-r--r--extensions/deviceicon/network.py516
-rw-r--r--extensions/deviceicon/speaker.py29
-rw-r--r--extensions/deviceicon/touchpad.py136
-rw-r--r--extensions/deviceicon/volume.py11
-rw-r--r--extensions/globalkey/screenshot.py7
-rw-r--r--extensions/globalkey/viewsource.py2
-rw-r--r--po/Makevars1
-rw-r--r--po/POTFILES.in5
-rw-r--r--po/ar.po22
-rw-r--r--po/de.po1053
-rw-r--r--po/es.po1213
-rw-r--r--po/fr.po1191
-rw-r--r--po/it.po1053
-rw-r--r--po/nl.po684
-rw-r--r--src/jarabe/__init__.py1
-rw-r--r--src/jarabe/config.py.in2
-rw-r--r--src/jarabe/controlpanel/__init__.py1
-rw-r--r--src/jarabe/controlpanel/cmd.py39
-rw-r--r--src/jarabe/controlpanel/gui.py69
-rw-r--r--src/jarabe/controlpanel/inlinealert.py10
-rw-r--r--src/jarabe/controlpanel/sectionview.py13
-rw-r--r--src/jarabe/controlpanel/toolbar.py9
-rw-r--r--src/jarabe/desktop/Makefile.am2
-rw-r--r--src/jarabe/desktop/__init__.py1
-rw-r--r--src/jarabe/desktop/activitieslist.py55
-rw-r--r--src/jarabe/desktop/favoriteslayout.py142
-rw-r--r--src/jarabe/desktop/favoritesview.py88
-rw-r--r--src/jarabe/desktop/friendview.py41
-rw-r--r--src/jarabe/desktop/grid.py11
-rw-r--r--src/jarabe/desktop/groupbox.py13
-rw-r--r--src/jarabe/desktop/homebox.py21
-rw-r--r--src/jarabe/desktop/homewindow.py37
-rw-r--r--src/jarabe/desktop/keydialog.py61
-rw-r--r--src/jarabe/desktop/meshbox.py865
-rw-r--r--src/jarabe/desktop/networkviews.py721
-rw-r--r--src/jarabe/desktop/schoolserver.py65
-rw-r--r--src/jarabe/desktop/snowflakelayout.py3
-rw-r--r--src/jarabe/desktop/spreadlayout.py4
-rw-r--r--src/jarabe/desktop/transitionbox.py13
-rw-r--r--src/jarabe/frame/__init__.py2
-rw-r--r--src/jarabe/frame/activitiestray.py308
-rw-r--r--src/jarabe/frame/clipboard.py20
-rw-r--r--src/jarabe/frame/clipboardicon.py8
-rw-r--r--src/jarabe/frame/clipboardmenu.py7
-rw-r--r--src/jarabe/frame/clipboardobject.py17
-rw-r--r--src/jarabe/frame/clipboardpanelwindow.py14
-rw-r--r--src/jarabe/frame/clipboardtray.py18
-rw-r--r--src/jarabe/frame/devicestray.py11
-rw-r--r--src/jarabe/frame/eventarea.py14
-rw-r--r--src/jarabe/frame/frame.py21
-rw-r--r--src/jarabe/frame/frameinvoker.py2
-rw-r--r--src/jarabe/frame/framewindow.py1
-rw-r--r--src/jarabe/frame/friendstray.py105
-rw-r--r--src/jarabe/frame/notification.py12
-rw-r--r--src/jarabe/frame/zoomtoolbar.py2
-rw-r--r--src/jarabe/intro/Makefile.am4
-rw-r--r--src/jarabe/intro/__init__.py1
-rw-r--r--src/jarabe/intro/colorpicker.py1
-rw-r--r--src/jarabe/intro/default-picture.pngbin10442 -> 0 bytes
-rw-r--r--src/jarabe/intro/window.py50
-rw-r--r--src/jarabe/journal/Makefile.am1
-rw-r--r--src/jarabe/journal/detailview.py4
-rw-r--r--src/jarabe/journal/expandedentry.py28
-rw-r--r--src/jarabe/journal/journalactivity.py49
-rw-r--r--src/jarabe/journal/journalentrybundle.py4
-rw-r--r--src/jarabe/journal/journaltoolbox.py98
-rw-r--r--src/jarabe/journal/journalwindow.py (renamed from src/jarabe/desktop/myicon.py)27
-rw-r--r--src/jarabe/journal/keepicon.py1
-rw-r--r--src/jarabe/journal/listmodel.py111
-rw-r--r--src/jarabe/journal/listview.py83
-rw-r--r--src/jarabe/journal/misc.py138
-rw-r--r--src/jarabe/journal/modalalert.py7
-rw-r--r--src/jarabe/journal/model.py201
-rw-r--r--src/jarabe/journal/objectchooser.py10
-rw-r--r--src/jarabe/journal/palettes.py54
-rw-r--r--src/jarabe/journal/volumestoolbar.py85
-rw-r--r--src/jarabe/model/Makefile.am7
-rw-r--r--src/jarabe/model/__init__.py1
-rw-r--r--src/jarabe/model/adhoc.py294
-rw-r--r--src/jarabe/model/buddy.py342
-rw-r--r--src/jarabe/model/bundleregistry.py90
-rw-r--r--src/jarabe/model/filetransfer.py68
-rw-r--r--src/jarabe/model/friends.py85
-rw-r--r--src/jarabe/model/invites.py276
-rw-r--r--src/jarabe/model/mimeregistry.py1
-rw-r--r--src/jarabe/model/neighborhood.py1149
-rw-r--r--src/jarabe/model/network.py389
-rw-r--r--src/jarabe/model/notifications.py21
-rw-r--r--src/jarabe/model/olpcmesh.py28
-rw-r--r--src/jarabe/model/owner.py113
-rw-r--r--src/jarabe/model/screen.py6
-rw-r--r--src/jarabe/model/session.py19
-rw-r--r--src/jarabe/model/shell.py255
-rw-r--r--src/jarabe/model/sound.py11
-rw-r--r--src/jarabe/model/telepathyclient.py103
-rw-r--r--src/jarabe/util/__init__.py1
-rw-r--r--src/jarabe/util/emulator.py54
-rw-r--r--src/jarabe/util/telepathy/__init__.py1
-rw-r--r--src/jarabe/util/telepathy/connection_watcher.py18
-rw-r--r--src/jarabe/view/__init__.py1
-rw-r--r--src/jarabe/view/buddyicon.py29
-rw-r--r--src/jarabe/view/buddymenu.py54
-rw-r--r--src/jarabe/view/keyhandler.py63
-rw-r--r--src/jarabe/view/launcher.py23
-rw-r--r--src/jarabe/view/palettes.py50
-rw-r--r--src/jarabe/view/pulsingicon.py4
-rw-r--r--src/jarabe/view/service.py69
-rw-r--r--src/jarabe/view/tabbinghandler.py3
-rw-r--r--src/jarabe/view/viewsource.py31
149 files changed, 9871 insertions, 5002 deletions
diff --git a/README b/README
index 821adef..1f89810 100644
--- a/README
+++ b/README
@@ -1,8 +1,18 @@
Building
========
-See:
-http://wiki.laptop.org/go/Sugar_with_sugar-jhbuild
+For details see: http://wiki.sugarlabs.org/go/Development_Team/Jhbuild
+
+Sugar-jhbuild will automatically download the latest of Sugar's
+dependencies as well as Sugar itself directly from their source
+repositories, rather than relying on source packages that may have
+become stale. These are generic instructions on how to use jhbuild
+to get up and running with Sugar.
+
+ $ cd sugar-jhbuild
+ $ ./sugar-jhbuild update
+ $ ./sugar-jhbuild depscheck
+ $ ./sugar-jhbuild buildgit
Running multiple instances on the same machine
==============================================
diff --git a/bin/.gitignore b/bin/.gitignore
deleted file mode 100644
index 9e78b64..0000000
--- a/bin/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-sugar-shell-service
diff --git a/bin/sugar-session b/bin/sugar-session
index 1582b65..91ebf6f 100644..100755
--- a/bin/sugar-session
+++ b/bin/sugar-session
@@ -20,6 +20,7 @@ import os
import sys
import time
import subprocess
+import shutil
if os.environ.get('SUGAR_LOGGER_LEVEL', '') == 'debug':
print '%r STARTUP: Starting the shell' % time.time()
@@ -78,7 +79,6 @@ def start_ui_service():
from jarabe.view.service import UIService
ui_service = UIService()
- ui_service.start()
def start_session_manager():
from jarabe.model.session import get_session_manager
@@ -203,6 +203,10 @@ def set_fonts():
def main():
try:
from sugar import env
+ # Remove temporary files. See http://bugs.sugarlabs.org/ticket/1876
+ data_dir = os.path.join(env.get_profile_path(), 'data')
+ shutil.rmtree(data_dir, ignore_errors=True)
+ os.makedirs(data_dir)
cleanup_logs(env.get_logs_path())
except OSError, e:
# logs cleanup is not critical; it should not prevent sugar from
@@ -225,7 +229,7 @@ def main():
client = gconf.client_get_default()
client.set_string('/apps/metacity/general/mouse_button_modifier',
- 'disabled')
+ '<Super>')
timezone = client.get_string('/desktop/sugar/date/timezone')
if timezone is not None and timezone:
diff --git a/bin/sugar.in b/bin/sugar.in
index 898bd59..12098db 100644
--- a/bin/sugar.in
+++ b/bin/sugar.in
@@ -36,6 +36,11 @@ while [ $# -ne 0 ] ; do
shift
done
+# Set default profile dir
+if test -z "$SUGAR_PROFILE"; then
+ export SUGAR_PROFILE=default
+fi
+
if test -z "$SUGAR_SCALING"; then
export SUGAR_SCALING=72
fi
@@ -54,6 +59,15 @@ fi
export LANG="${LANG:-en_US.utf8}"
export LANGUAGE="${LANGUAGE:-${LANG}}"
+# Set Sugar's telepathy accounts directory
+export MC_ACCOUNT_DIR=$HOME/.sugar/$SUGAR_PROFILE/accounts
+
+# Workaround until gnome-keyring-daemon lets dbus activate it
+# https://bugzilla.gnome.org/show_bug.cgi?id=628302
+if test "$SUGAR_EMULATOR" = "yes" -a "$(type gnome-keyring-daemon)"; then
+ gnome-keyring-daemon --components=secrets &
+fi
+
# Source language settings and debug definitions
if [ -f ~/.i18n ]; then
. ~/.i18n
diff --git a/configure.ac b/configure.ac
index 4f60892..151eaa5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT([Sugar],[0.88.0],[],[sugar])
+AC_INIT([Sugar],[0.90.3],[],[sugar])
AC_PREREQ([2.59])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([configure.ac])
-SUCROSE_VERSION="0.88.0"
+SUCROSE_VERSION="0.90.1"
AC_SUBST(SUCROSE_VERSION)
AM_INIT_AUTOMAKE([1.9 foreign dist-bzip2 no-dist-gzip])
diff --git a/data/icons/module-updater.svg b/data/icons/module-updater.svg
index 2329a4a..a521f61 100644
--- a/data/icons/module-updater.svg
+++ b/data/icons/module-updater.svg
@@ -1,15 +1,15 @@
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
<!ENTITY stroke_color "#000">
<!ENTITY fill_color "#fff">
-]><svg height="55px" viewBox="0 0 55 55" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" stroke="&stroke_color;" fill="&fill_color;">
+]><svg height="55px" viewBox="0 0 55 55" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g>
- <path d="M 31.752 7.088 C 41.935 9.118 49.609 18.107 49.609 28.887 C 49.609 41.173 39.65 51.129 27.374 51.129 C 15.086 51.129 5.133 41.173 5.133 28.887 C 5.133 19.648 10.768 11.723 18.801 8.365 " fill="none" stroke="&fill_color;" />
- <path d="M 36.134 15.154 L 31.752 7.088 L 40.439 4.13 " fill="none" stroke="&fill_color;" />
+ <path d="M 31.752 7.088 C 41.935 9.118 49.609 18.107 49.609 28.887 C 49.609 41.173 39.65 51.129 27.374 51.129 C 15.086 51.129 5.133 41.173 5.133 28.887 C 5.133 19.648 10.768 11.723 18.801 8.365 " fill="none" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" />
+ <path d="M 36.134 15.154 L 31.752 7.088 L 40.439 4.13 " fill="none" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" />
</g>
<g>
<g>
- <path d="M 38.57 25.886 C 37.597 25.886 36.718 26.282 36.082 26.918 L 31.021 31.979 L 31.02 17.022 C 31.018 16.124 30.675 15.221 29.99 14.533 C 28.613 13.159 26.383 13.159 25.01 14.533 C 24.321 15.222 23.98 16.122 23.979 17.023 L 23.977 31.978 L 18.918 26.918 C 18.281 26.281 17.4 25.886 16.429 25.887 C 14.484 25.885 12.908 27.465 12.908 29.408 C 12.907 30.381 13.304 31.263 13.936 31.899 L 27.5 45.463 L 41.062 31.898 C 41.697 31.262 42.09 30.382 42.093 29.41 C 42.094 27.463 40.516 25.885 38.57 25.886 Z " stroke="none" />
+ <path d="M 38.57 25.886 C 37.597 25.886 36.718 26.282 36.082 26.918 L 31.021 31.979 L 31.02 17.022 C 31.018 16.124 30.675 15.221 29.99 14.533 C 28.613 13.159 26.383 13.159 25.01 14.533 C 24.321 15.222 23.98 16.122 23.979 17.023 L 23.977 31.978 L 18.918 26.918 C 18.281 26.281 17.4 25.886 16.429 25.887 C 14.484 25.885 12.908 27.465 12.908 29.408 C 12.907 30.381 13.304 31.263 13.936 31.899 L 27.5 45.463 L 41.062 31.898 C 41.697 31.262 42.09 30.382 42.093 29.41 C 42.094 27.463 40.516 25.885 38.57 25.886 Z " fill="&fill_color;" stroke="none" />
</g>
</g>
</g>
diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in
index b9606ba..b13f746 100644
--- a/data/sugar.schemas.in
+++ b/data/sugar.schemas.in
@@ -31,9 +31,9 @@
<default></default>
<locale name="C">
<short>User Color</short>
- <long>Color for the XO icon that is used throughout the
- desktop. The string is composed of the stroke color and fill
- color, format is that of rbg colors. Example: #AC32FF,#9A5200
+ <long>Color for the XO icon that is used throughout the
+ desktop. The string is composed of the stroke color and fill
+ color, format is that of rgb colors. Example: #AC32FF,#9A5200
</long>
</locale>
</schema>
@@ -78,7 +78,7 @@
<applyto>/desktop/sugar/date/timezone</applyto>
<owner>sugar</owner>
<type>string</type>
- <default>UTC</default>
+ <default></default>
<locale name="C">
<short>Timezone</short>
<long>Timezone setting for the system.</long>
@@ -192,6 +192,18 @@
</schema>
<schema>
+ <key>/schemas/desktop/sugar/show_restart</key>
+ <applyto>/desktop/sugar/show_restart</applyto>
+ <owner>sugar</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Show Restart</short>
+ <long>If TRUE, Sugar will show a "Restart" option.</long>
+ </locale>
+ </schema>
+
+ <schema>
<key>/schemas/desktop/sugar/peripherals/keyboard/layouts</key>
<applyto>/desktop/sugar/peripherals/keyboard/layouts</applyto>
<owner>sugar</owner>
@@ -329,5 +341,33 @@
</locale>
</schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/adhoc</key>
+ <applyto>/desktop/sugar/network/adhoc</applyto>
+ <owner>sugar</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Show Sugar Ad-hoc networks</short>
+ <long>If TRUE, Sugar will show default Ad-hoc networks for
+ channel 1,6 and 11. If Sugar sees no "known" network when
+ it starts, it does autoconnect to an Ad-hoc network.</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/desktop/sugar/protected_activities</key>
+ <applyto>/desktop/sugar/protected_activities</applyto>
+ <owner>sugar</owner>
+ <type>list</type>
+ <list_type>string</list_type>
+ <default>[]</default>
+ <locale name="C">
+ <short>Bundle IDs of protected activities</short>
+ <long>Users will not be allowed to erase these
+ activities through the list view.</long>
+ </locale>
+ </schema>
+
</schemalist>
</gconfschemafile>
diff --git a/data/sugar.xml.in b/data/sugar.xml.in
index 6a7f253..9300a45 100644
--- a/data/sugar.xml.in
+++ b/data/sugar.xml.in
@@ -8,4 +8,8 @@
<_comment>Sugar content bundle</_comment>
<glob pattern="*.xol"/>
</mime-type>
-</mime-info> \ No newline at end of file
+ <mime-type type="application/vnd.olpc-journal-entry">
+ <_comment>Sugar Journal entry bundle</_comment>
+ <glob pattern="*.xoj"/>
+ </mime-type>
+</mime-info>
diff --git a/docs/controls.txt b/docs/controls.txt
index 52591ea..fa977ef 100644
--- a/docs/controls.txt
+++ b/docs/controls.txt
@@ -52,7 +52,7 @@ sugar.ToolButton (support for rollovers)
* There is no palette but a tooltip.
* Normal: Button grey rounded filled rectangle
* Inactive: Button grey rounded stroked rectangle
-
+
sugar.ToggleIconButton
* Toggled should be like Pressed
@@ -184,10 +184,10 @@ Palettes in ToolIconButton, IconButton
* The popup will have a setPrimaryState(label, accelerator) method. For action buttons would be a MenuItem, for the others would only be a Label.
* The primary state should already have the same width as the secondary state and the expandable areas.
* Primary states appear and disappear automatically (with a short delay). A click outside makes it disappear instantly.
-* Secondary states appear after a delay, or with a single click on the icon.
+* Secondary states appear after a delay, or with a single click on the icon.
* Secondary disappears with the esc key, clicking outside the popup or clicking on a button inside.
-Toolbox
+Toolbox
* When an activity opens, the activity tab should be opened and the focus on the activity title.
* We must provide an activity tab in the toolbox and would be good to also provide an standard Edit tab.
diff --git a/docs/release_howto.txt b/docs/release_howto.txt
index db877e0..841809a 100644
--- a/docs/release_howto.txt
+++ b/docs/release_howto.txt
@@ -1,7 +1,7 @@
-''' This is the release process of the sugar tarballs sugar(shell),
+""" This is the release process of the sugar tarballs sugar(shell),
sugar-toolkit and sugar-base described in a pytish way and
instructions for sugar packagers
-'''
+"""
# Release sugar tarballs
@@ -10,9 +10,9 @@ for package in [sugar, sugar-toolkit, sugar-base, sugar-artwork]:
Pull the latest sources.
Increase the version number in configure.ac
# this will create you a tarball and does a check if it builds fine
- # e.g. it will check if all the files containing translations are
+ # e.g. it will check if all the files containing translations are
# in po/POTFILES.in
- make distcheck
+ make distcheck
if that succeed:
# commit the change, log it as "Release [version_number]" (e.g. 0.79.1)
@@ -26,7 +26,7 @@ for package in [sugar, sugar-toolkit, sugar-base, sugar-artwork]:
break
# Upload the package
- Upload the tarball to
+ Upload the tarball to
shell.sugarlabs.org:/pub/sugarlabs/sources/sucrose/glucose/$name/$name-$version
# Verify the upload of the package
@@ -34,16 +34,16 @@ for package in [sugar, sugar-toolkit, sugar-base, sugar-artwork]:
http://download.sugarlabs.org/sources/sucrose/glucose/$name/$name-$version
# Package sugar for Fedora
-# - For announcements of the Sucrose release subscribe at the sugar-devel
+# - For announcements of the Sucrose release subscribe at the sugar-devel
# mailing list; you can filter for the [ANNOUNCE] tag
-# - Uploaded tarballs can be found at:
+# - Uploaded tarballs can be found at:
# glucose: http://download.sugarlabs.org/sources/sucrose/glucose/$name/$name-$version
# fructose: http://download.sugarlabs.org/sources/sucrose/fructose/$name/$name-$version
# more about the taxonomy: http://sugarlabs.org/go/Taxonomy
-
-# more info on fedora packaging:
+
+# more info on fedora packaging:
# http://fedoraproject.org/wiki/PackageMaintainers/UpdatingPackageHowTo
-# request permissions to contribute to the fedora package:
+# request permissions to contribute to the fedora package:
# https://admin.fedoraproject.org/pkgdb/packages/name/[package]
if not cvs_package:
@@ -70,4 +70,4 @@ cvs commit -F clog
make tag
make build
-# Do the same for the other branches e.g. devel
+# Do the same for the other branches e.g. devel
diff --git a/extensions/cpsection/aboutcomputer/__init__.py b/extensions/cpsection/aboutcomputer/__init__.py
index ceb515a..faf814d 100644
--- a/extensions/cpsection/aboutcomputer/__init__.py
+++ b/extensions/cpsection/aboutcomputer/__init__.py
@@ -19,4 +19,3 @@ from gettext import gettext as _
CLASS = 'AboutComputer'
ICON = 'module-about_my_computer'
TITLE = _('About my Computer')
-
diff --git a/extensions/cpsection/aboutcomputer/model.py b/extensions/cpsection/aboutcomputer/model.py
index 898d79c..3219dd1 100644
--- a/extensions/cpsection/aboutcomputer/model.py
+++ b/extensions/cpsection/aboutcomputer/model.py
@@ -22,31 +22,45 @@ import subprocess
from gettext import gettext as _
import errno
+import dbus
+
from jarabe import config
+
+_NM_SERVICE = 'org.freedesktop.NetworkManager'
+_NM_PATH = '/org/freedesktop/NetworkManager'
+_NM_IFACE = 'org.freedesktop.NetworkManager'
+_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
+_NM_DEVICE_TYPE_WIFI = 2
+
_logger = logging.getLogger('ControlPanel - AboutComputer')
_not_available = _('Not available')
+
def get_aboutcomputer():
msg = 'Serial Number: %s \nBuild Number: %s \nFirmware Number: %s \n' \
% (get_serial_number(), get_build_number(), get_firmware_number())
return msg
+
def print_aboutcomputer():
print get_aboutcomputer()
+
def get_serial_number():
serial_no = _read_file('/ofw/serial-number')
if serial_no is None:
serial_no = _not_available
return serial_no
-
+
+
def print_serial_number():
serial_no = get_serial_number()
if serial_no is None:
serial_no = _not_available
print serial_no
+
def get_build_number():
build_no = _read_file('/boot/olpc_build')
@@ -68,38 +82,88 @@ def get_build_number():
return build_no
+
def print_build_number():
print get_build_number()
-def get_firmware_number():
+
+def get_firmware_number():
firmware_no = _read_file('/ofw/openprom/model')
if firmware_no is None:
firmware_no = _not_available
else:
- firmware_no = re.split(" +", firmware_no)
+ firmware_no = re.split(' +', firmware_no)
if len(firmware_no) == 3:
firmware_no = firmware_no[1]
- return firmware_no
+ return firmware_no
+
-def print_firmware_number():
+def print_firmware_number():
print get_firmware_number()
-def get_wireless_firmware():
+
+def _get_wireless_interfaces():
try:
- info = subprocess.Popen(["/usr/sbin/ethtool", "-i", "eth0"],
- stdout=subprocess.PIPE).stdout.readlines()
- except OSError:
+ bus = dbus.SystemBus()
+ manager_object = bus.get_object(_NM_SERVICE, _NM_PATH)
+ network_manager = dbus.Interface(manager_object, _NM_IFACE)
+ except dbus.DBusException:
+ _logger.warning('Cannot connect to NetworkManager, falling back to'
+ ' static list of devices')
+ return ['wlan0', 'eth0']
+
+ interfaces = []
+ for device_path in network_manager.GetDevices():
+ device_object = bus.get_object(_NM_SERVICE, device_path)
+ properties = dbus.Interface(device_object,
+ 'org.freedesktop.DBus.Properties')
+ device_type = properties.Get(_NM_DEVICE_IFACE, 'DeviceType')
+ if device_type != _NM_DEVICE_TYPE_WIFI:
+ continue
+
+ interfaces.append(properties.Get(_NM_DEVICE_IFACE, 'Interface'))
+
+ return interfaces
+
+
+def get_wireless_firmware():
+ environment = os.environ.copy()
+ environment['PATH'] = '%s:/usr/sbin' % (environment['PATH'], )
+ firmware_info = {}
+ for interface in _get_wireless_interfaces():
+ try:
+ output = subprocess.Popen(['ethtool', '-i', interface],
+ stdout=subprocess.PIPE,
+ env=environment).stdout.readlines()
+ except OSError:
+ _logger.exception('Error running ethtool for %r', interface)
+ continue
+
+ try:
+ version = ([line for line in output
+ if line.startswith('firmware')][0].split()[1])
+ except IndexError:
+ _logger.exception('Error parsing ethtool output for %r',
+ interface)
+ continue
+
+ firmware_info[interface] = version
+
+ if not firmware_info:
return _not_available
- try:
- wireless_firmware = [line for line in info
- if line.startswith('firmware')][0].split()[1]
- except IndexError:
- wireless_firmware = _not_available
- return wireless_firmware
+
+ if len(firmware_info) == 1:
+ return firmware_info.values()[0]
+
+ return ', '.join([_('%(interface)s: %(version)s') %
+ {'interface': interface, 'version': version}
+ for interface, version in firmware_info.items()])
+
def print_wireless_firmware():
print get_wireless_firmware()
+
def _read_file(path):
if os.access(path, os.R_OK) == 0:
return None
@@ -114,17 +178,18 @@ def _read_file(path):
_logger.debug('No information in file or directory: %s', path)
return None
+
def get_license():
license_file = os.path.join(config.data_path, 'GPLv2')
lang = os.environ['LANG']
- if lang.endswith("UTF-8"):
+ if lang.endswith('UTF-8'):
lang = lang[:-6]
- try_file = license_file + "." + lang
+ try_file = license_file + '.' + lang
if os.path.isfile(try_file):
license_file = try_file
else:
- try_file = license_file + "." + lang.split("_")[0]
+ try_file = license_file + '.' + lang.split('_')[0]
if os.path.isfile(try_file):
license_file = try_file
diff --git a/extensions/cpsection/aboutcomputer/view.py b/extensions/cpsection/aboutcomputer/view.py
index dd4f8f3..e5f2f32 100644
--- a/extensions/cpsection/aboutcomputer/view.py
+++ b/extensions/cpsection/aboutcomputer/view.py
@@ -26,6 +26,7 @@ from sugar.graphics import style
from jarabe import config
from jarabe.controlpanel.sectionview import SectionView
+
class AboutComputer(SectionView):
def __init__(self, model, alerts=None):
SectionView.__init__(self)
@@ -68,7 +69,7 @@ class AboutComputer(SectionView):
box_identity = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_serial = gtk.Label(_('Serial Number:'))
label_serial.set_alignment(1, 0)
- label_serial.modify_fg(gtk.STATE_NORMAL,
+ label_serial.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_identity.pack_start(label_serial, expand=False)
self._group.add_widget(label_serial)
@@ -83,7 +84,7 @@ class AboutComputer(SectionView):
self._vbox.pack_start(vbox_identity, expand=False)
vbox_identity.show()
- def _setup_software(self):
+ def _setup_software(self):
separator_software = gtk.HSeparator()
self._vbox.pack_start(separator_software, expand=False)
separator_software.show()
@@ -99,7 +100,7 @@ class AboutComputer(SectionView):
box_build = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_build = gtk.Label(_('Build:'))
label_build.set_alignment(1, 0)
- label_build.modify_fg(gtk.STATE_NORMAL,
+ label_build.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_build.pack_start(label_build, expand=False)
self._group.add_widget(label_build)
@@ -130,7 +131,7 @@ class AboutComputer(SectionView):
box_firmware = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_firmware = gtk.Label(_('Firmware:'))
label_firmware.set_alignment(1, 0)
- label_firmware.modify_fg(gtk.STATE_NORMAL,
+ label_firmware.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_firmware.pack_start(label_firmware, expand=False)
self._group.add_widget(label_firmware)
@@ -174,27 +175,30 @@ class AboutComputer(SectionView):
vbox_copyright.set_border_width(style.DEFAULT_SPACING * 2)
vbox_copyright.set_spacing(style.DEFAULT_SPACING)
- label_copyright = gtk.Label("© 2006-2009 One Laptop per Child "
- "Association Inc; Red Hat Inc; Collabora Ltd; "
- "and Contributors.")
+ copyright_text = '© 2006-2011 One Laptop per Child Association Inc,' \
+ ' Sugar Labs Inc, Red Hat Inc, Collabora Ltd and' \
+ ' Contributors.'
+ label_copyright = gtk.Label(copyright_text)
label_copyright.set_alignment(0, 0)
+ label_copyright.set_size_request(gtk.gdk.screen_width() / 2, -1)
+ label_copyright.set_line_wrap(True)
label_copyright.show()
vbox_copyright.pack_start(label_copyright, expand=False)
- label_info = gtk.Label(_("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 change it and/or "
- "distribute copies of it under certain "
- "conditions described therein."))
+ info_text = _('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'
+ ' change it and/or distribute copies of it under'
+ ' certain conditions described therein.')
+ label_info = gtk.Label(info_text)
label_info.set_alignment(0, 0)
label_info.set_line_wrap(True)
label_info.set_size_request(gtk.gdk.screen_width() / 2, -1)
label_info.show()
vbox_copyright.pack_start(label_info, expand=False)
- expander = gtk.Expander(_("Full license:"))
- expander.connect("notify::expanded", self.license_expander_cb)
+ expander = gtk.Expander(_('Full license:'))
+ expander.connect('notify::expanded', self.license_expander_cb)
expander.show()
vbox_copyright.pack_start(expander, expand=True)
diff --git a/extensions/cpsection/aboutme/__init__.py b/extensions/cpsection/aboutme/__init__.py
index 98843e1..7ded428 100644
--- a/extensions/cpsection/aboutme/__init__.py
+++ b/extensions/cpsection/aboutme/__init__.py
@@ -24,5 +24,3 @@ ICON = 'module-about_me'
TITLE = _('About Me')
client = gconf.client_get_default()
COLOR = XoColor(client.get_string('/desktop/sugar/user/color'))
-
-
diff --git a/extensions/cpsection/aboutme/model.py b/extensions/cpsection/aboutme/model.py
index 8500799..fb4c2f1 100644
--- a/extensions/cpsection/aboutme/model.py
+++ b/extensions/cpsection/aboutme/model.py
@@ -18,38 +18,45 @@
from gettext import gettext as _
import gconf
-_COLORS = {'red': {'dark':'#b20008', 'medium':'#e6000a', 'light':'#ffadce'},
- 'orange': {'dark':'#9a5200', 'medium':'#c97e00', 'light':'#ffc169'},
- 'yellow': {'dark':'#807500', 'medium':'#be9e00', 'light':'#fffa00'},
- 'green': {'dark':'#008009', 'medium':'#00b20d', 'light':'#8bff7a'},
- 'blue': {'dark':'#00588c', 'medium':'#005fe4', 'light':'#bccdff'},
- 'purple': {'dark':'#5e008c', 'medium':'#7f00bf', 'light':'#d1a3ff'}
- }
+
+_COLORS = {
+ 'red': {'dark': '#b20008', 'medium': '#e6000a', 'light': '#ffadce'},
+ 'orange': {'dark': '#9a5200', 'medium': '#c97e00', 'light': '#ffc169'},
+ 'yellow': {'dark': '#807500', 'medium': '#be9e00', 'light': '#fffa00'},
+ 'green': {'dark': '#008009', 'medium': '#00b20d', 'light': '#8bff7a'},
+ 'blue': {'dark': '#00588c', 'medium': '#005fe4', 'light': '#bccdff'},
+ 'purple': {'dark': '#5e008c', 'medium': '#7f00bf', 'light': '#d1a3ff'},
+}
_MODIFIERS = ('dark', 'medium', 'light')
-
+
+
def get_nick():
client = gconf.client_get_default()
- return client.get_string("/desktop/sugar/user/nick")
+ return client.get_string('/desktop/sugar/user/nick')
+
def print_nick():
print get_nick()
-
+
+
def set_nick(nick):
"""Set the nickname.
nick : e.g. 'walter'
"""
if not nick:
- raise ValueError(_("You must enter a name."))
+ raise ValueError(_('You must enter a name.'))
if not isinstance(nick, unicode):
nick = unicode(nick, 'utf-8')
client = gconf.client_get_default()
- client.set_string("/desktop/sugar/user/nick", nick)
+ client.set_string('/desktop/sugar/user/nick', nick)
return 1
+
def get_color():
client = gconf.client_get_default()
- return client.get_string("/desktop/sugar/user/color")
+ return client.get_string('/desktop/sugar/user/color')
+
def print_color():
color_string = get_color()
@@ -64,16 +71,17 @@ def print_color():
if _COLORS[color][hue] == tmp[1]:
fill_tuple = (color, hue)
- if stroke_tuple is not None:
- print _('stroke: color=%s hue=%s') % (stroke_tuple[0],
+ if stroke_tuple is not None:
+ print _('stroke: color=%s hue=%s') % (stroke_tuple[0],
stroke_tuple[1])
else:
- print _('stroke: %s') % (tmp[0])
- if fill_tuple is not None:
+ print _('stroke: %s') % (tmp[0])
+ if fill_tuple is not None:
print _('fill: color=%s hue=%s') % (fill_tuple[0], fill_tuple[1])
else:
print _('fill: %s') % (tmp[1])
-
+
+
def set_color(stroke, fill, stroke_modifier='medium', fill_modifier='medium'):
"""Set the system color by setting a fill and stroke color.
fill : [red, orange, yellow, blue, green, purple]
@@ -81,35 +89,37 @@ def set_color(stroke, fill, stroke_modifier='medium', fill_modifier='medium'):
hue stroke : [dark, medium, light] (optional)
hue fill : [dark, medium, light] (optional)
"""
-
+
if stroke_modifier not in _MODIFIERS or fill_modifier not in _MODIFIERS:
- print (_("Error in specified color modifiers."))
+ print (_('Error in specified color modifiers.'))
return
if stroke not in _COLORS or fill not in _COLORS:
- print (_("Error in specified colors."))
+ print (_('Error in specified colors.'))
return
-
+
if stroke_modifier == fill_modifier:
if fill_modifier == 'medium':
fill_modifier = 'light'
else:
fill_modifier = 'medium'
-
+
color = _COLORS[stroke][stroke_modifier] + ',' \
+ _COLORS[fill][fill_modifier]
client = gconf.client_get_default()
- client.set_string("/desktop/sugar/user/color", color)
+ client.set_string('/desktop/sugar/user/color', color)
return 1
+
def get_color_xo():
client = gconf.client_get_default()
- return client.get_string("/desktop/sugar/user/color")
+ return client.get_string('/desktop/sugar/user/color')
+
def set_color_xo(color):
- """Set a color with an XoColor
+ """Set a color with an XoColor
This method is used by the graphical user interface
"""
client = gconf.client_get_default()
- client.set_string("/desktop/sugar/user/color", color)
+ client.set_string('/desktop/sugar/user/color', color)
return 1
diff --git a/extensions/cpsection/aboutme/view.py b/extensions/cpsection/aboutme/view.py
index cabd66a..84daec7 100644
--- a/extensions/cpsection/aboutme/view.py
+++ b/extensions/cpsection/aboutme/view.py
@@ -1,4 +1,5 @@
# Copyright (C) 2008, OLPC
+# Copyright (C) 2010, 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
@@ -20,18 +21,104 @@ from gettext import gettext as _
from sugar.graphics.icon import Icon
from sugar.graphics import style
-from sugar.graphics.xocolor import XoColor
+from sugar.graphics.xocolor import XoColor, colors
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
+_STROKE_COLOR = 0
+_FILL_COLOR = 1
+
+
+def _get_next_stroke_color(color):
+ """ Return the next color pair in the list that shares the same fill
+ as color. """
+ current_index = _get_current_index(color)
+ if current_index == -1:
+ return '%s,%s' % (color.stroke, color.fill)
+ next_index = _next_index(current_index)
+ while(colors[next_index][_FILL_COLOR] != \
+ colors[current_index][_FILL_COLOR]):
+ next_index = _next_index(next_index)
+ return '%s,%s' % (colors[next_index][_STROKE_COLOR],
+ colors[next_index][_FILL_COLOR])
+
+
+def _get_previous_stroke_color(color):
+ """ Return the previous color pair in the list that shares the same fill
+ as color. """
+ current_index = _get_current_index(color)
+ if current_index == -1:
+ return '%s,%s' % (color.stroke, color.fill)
+ previous_index = _previous_index(current_index)
+ while (colors[previous_index][_FILL_COLOR] != \
+ colors[current_index][_FILL_COLOR]):
+ previous_index = _previous_index(previous_index)
+ return '%s,%s' % (colors[previous_index][_STROKE_COLOR],
+ colors[previous_index][_FILL_COLOR])
+
+
+def _get_next_fill_color(color):
+ """ Return the next color pair in the list that shares the same stroke
+ as color. """
+ current_index = _get_current_index(color)
+ if current_index == -1:
+ return '%s,%s' % (color.stroke, color.fill)
+ next_index = _next_index(current_index)
+ while (colors[next_index][_STROKE_COLOR] != \
+ colors[current_index][_STROKE_COLOR]):
+ next_index = _next_index(next_index)
+ return '%s,%s' % (colors[next_index][_STROKE_COLOR],
+ colors[next_index][_FILL_COLOR])
+
+
+def _get_previous_fill_color(color):
+ """ Return the previous color pair in the list that shares the same stroke
+ as color. """
+ current_index = _get_current_index(color)
+ if current_index == -1:
+ return '%s,%s' % (color.stroke, color.fill)
+ previous_index = _previous_index(current_index)
+ while (colors[previous_index][_STROKE_COLOR] != \
+ colors[current_index][_STROKE_COLOR]):
+ previous_index = _previous_index(previous_index)
+ return '%s,%s' % (colors[previous_index][_STROKE_COLOR],
+ colors[previous_index][_FILL_COLOR])
+
+
+def _next_index(current_index):
+ next_index = current_index + 1
+ if next_index == len(colors):
+ next_index = 0
+ return next_index
+
+
+def _previous_index(current_index):
+ previous_index = current_index - 1
+ if previous_index < 0:
+ previous_index = len(colors) - 1
+ return previous_index
+
+
+def _get_current_index(color):
+ return colors.index([color.stroke, color.fill])
+
+
+_PREVIOUS_FILL_COLOR = 0
+_NEXT_FILL_COLOR = 1
+_CURRENT_COLOR = 2
+_NEXT_STROKE_COLOR = 3
+_PREVIOUS_STROKE_COLOR = 4
+
+
class EventIcon(gtk.EventBox):
- __gtype_name__ = "SugarEventIcon"
- def __init__(self, **kwargs):
+ __gtype_name__ = 'SugarEventIcon'
+
+ def __init__(self, **kwargs):
gtk.EventBox.__init__(self)
- self.icon = Icon(pixel_size = style.XLARGE_ICON_SIZE, **kwargs)
-
+ self.icon = Icon(pixel_size=style.XLARGE_ICON_SIZE, **kwargs)
+
self.set_visible_window(False)
self.set_app_paintable(True)
self.set_events(gtk.gdk.BUTTON_PRESS_MASK)
@@ -39,28 +126,45 @@ class EventIcon(gtk.EventBox):
self.add(self.icon)
self.icon.show()
+
class ColorPicker(EventIcon):
__gsignals__ = {
'color-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([str]))
- }
- def __init__(self, xocolor=None):
+ ([object])),
+ }
+
+ def __init__(self, picker):
EventIcon.__init__(self)
- self.icon.props.xo_color = xocolor
+
self.icon.props.icon_name = 'computer-xo'
+ self._picker = picker
+ self._color = None
+
self.icon.props.pixel_size = style.XLARGE_ICON_SIZE
- self.connect('button_press_event', self.__pressed_cb)
- def __pressed_cb(self, button, event):
- self._set_random_colors()
+ self.connect('button_press_event', self.__pressed_cb, picker)
+
+ def update(self, color):
+ if self._picker == _PREVIOUS_FILL_COLOR:
+ self._color = XoColor(_get_previous_fill_color(color))
+ elif self._picker == _PREVIOUS_STROKE_COLOR:
+ self._color = XoColor(_get_previous_stroke_color(color))
+ elif self._picker == _NEXT_FILL_COLOR:
+ self._color = XoColor(_get_next_fill_color(color))
+ elif self._picker == _NEXT_STROKE_COLOR:
+ self._color = XoColor(_get_next_stroke_color(color))
+ else:
+ self._color = color
+ self.icon.props.xo_color = self._color
+
+ def __pressed_cb(self, button, event, picker):
+ if picker != _CURRENT_COLOR:
+ self.emit('color-changed', self._color)
- def _set_random_colors(self):
- xocolor = XoColor()
- self.icon.props.xo_color = xocolor
- self.emit('color-changed', xocolor.to_string())
class AboutMe(SectionView):
+
def __init__(self, model, alerts):
SectionView.__init__(self)
@@ -69,44 +173,44 @@ class AboutMe(SectionView):
self._nick_sid = 0
self._color_valid = True
self._nick_valid = True
- self._color_change_handler = None
- self._nick_change_handler = None
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+ self._color_label = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self._color_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self._color_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self._color_alert = None
+
+ self._pickers = {
+ _PREVIOUS_FILL_COLOR: ColorPicker(_PREVIOUS_FILL_COLOR),
+ _NEXT_FILL_COLOR: ColorPicker(_NEXT_FILL_COLOR),
+ _CURRENT_COLOR: ColorPicker(_CURRENT_COLOR),
+ _NEXT_STROKE_COLOR: ColorPicker(_NEXT_STROKE_COLOR),
+ _PREVIOUS_STROKE_COLOR: ColorPicker(_PREVIOUS_STROKE_COLOR),
+ }
+
+ self._setup_color()
+ initial_color = XoColor(self._model.get_color_xo())
+ self._update_pickers(initial_color)
+
self._nick_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
self._nick_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
self._nick_entry = None
self._nick_alert = None
self._setup_nick()
-
- self._color_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
- self._color_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
- self._color_picker = None
- self._color_alert = None
- self._setup_color()
-
self.setup()
def _setup_nick(self):
- label_entry = gtk.Label(_('Name:'))
- label_entry.modify_fg(gtk.STATE_NORMAL,
- style.COLOR_SELECTION_GREY.get_gdk_color())
- self._group.add_widget(label_entry)
- label_entry.set_alignment(1, 0.5)
- self._nick_box.pack_start(label_entry, expand=False)
- label_entry.show()
-
- self._nick_entry = gtk.Entry()
- self._nick_entry.modify_bg(gtk.STATE_INSENSITIVE,
+ self._nick_entry = gtk.Entry()
+ self._nick_entry.modify_bg(gtk.STATE_INSENSITIVE,
style.COLOR_WHITE.get_gdk_color())
- self._nick_entry.modify_base(gtk.STATE_INSENSITIVE,
+ self._nick_entry.modify_base(gtk.STATE_INSENSITIVE,
style.COLOR_WHITE.get_gdk_color())
self._nick_entry.set_width_chars(25)
self._nick_box.pack_start(self._nick_entry, expand=False)
- self._nick_entry.show()
+ self._nick_entry.show()
label_entry_error = gtk.Label()
self._group.add_widget(label_entry_error)
@@ -119,22 +223,36 @@ class AboutMe(SectionView):
self._nick_alert.props.msg = self.restart_msg
self._nick_alert.show()
- self.pack_start(self._nick_box, False)
+ self._center_in_panel = gtk.Alignment(0.5)
+ self._center_in_panel.add(self._nick_box)
+ self.pack_start(self._center_in_panel, False)
self.pack_start(self._nick_alert_box, False)
self._nick_box.show()
self._nick_alert_box.show()
-
- def _setup_color(self):
+ self._center_in_panel.show()
+
+ def _setup_color(self):
label_color = gtk.Label(_('Click to change your color:'))
- label_color.modify_fg(gtk.STATE_NORMAL,
+ label_color.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
self._group.add_widget(label_color)
- self._color_box.pack_start(label_color, expand=False)
+ self._color_label.pack_start(label_color, expand=False)
label_color.show()
-
- self._color_picker = ColorPicker()
- self._color_box.pack_start(self._color_picker, expand=False)
- self._color_picker.show()
+
+ for picker_index in sorted(self._pickers.keys()):
+ if picker_index == _CURRENT_COLOR:
+ left_separator = gtk.SeparatorToolItem()
+ left_separator.show()
+ self._color_box.pack_start(left_separator, expand=False)
+
+ picker = self._pickers[picker_index]
+ picker.show()
+ self._color_box.pack_start(picker, expand=False)
+
+ if picker_index == _CURRENT_COLOR:
+ right_separator = gtk.SeparatorToolItem()
+ right_separator.show()
+ self._color_box.pack_start(right_separator, expand=False)
label_color_error = gtk.Label()
self._group.add_widget(label_color_error)
@@ -147,30 +265,34 @@ class AboutMe(SectionView):
self._color_alert.props.msg = self.restart_msg
self._color_alert.show()
- self.pack_start(self._color_box, False)
- self.pack_start(self._color_alert_box, False)
+ self._center_in_panel = gtk.Alignment(0.5)
+ self._center_in_panel.add(self._color_box)
+ self.pack_start(self._color_label, False)
+ self.pack_start(self._center_in_panel, False)
+ self.pack_start(self._color_alert_box, False)
+ self._color_label.show()
self._color_box.show()
self._color_alert_box.show()
-
+ self._center_in_panel.show()
+
def setup(self):
self._nick_entry.set_text(self._model.get_nick())
- color = XoColor(self._model.get_color_xo())
- self._color_picker.icon.props.xo_color = color
-
self._color_valid = True
self._nick_valid = True
self.needs_restart = False
- self._nick_change_handler = self._nick_entry.connect( \
- 'changed', self.__nick_changed_cb)
- self._color_change_handler = self._color_picker.connect( \
- 'color-changed', self.__color_changed_cb)
+
+ self._nick_entry.connect('changed', self.__nick_changed_cb)
+ for picker in self._pickers.values():
+ picker.connect('color-changed', self.__color_changed_cb)
def undo(self):
- self._color_picker.disconnect(self._color_change_handler)
- self._nick_entry.disconnect(self._nick_change_handler)
self._model.undo()
self._nick_alert.hide()
- self._color_alert.hide()
+ self._color_alert.hide()
+
+ def _update_pickers(self, color):
+ for picker in self._pickers.values():
+ picker.update(color)
def _validate(self):
if self._nick_valid and self._color_valid:
@@ -178,13 +300,13 @@ class AboutMe(SectionView):
else:
self.props.is_valid = False
- def __nick_changed_cb(self, widget, data=None):
+ def __nick_changed_cb(self, widget, data=None):
if self._nick_sid:
gobject.source_remove(self._nick_sid)
- self._nick_sid = gobject.timeout_add(self._APPLY_TIMEOUT,
+ self._nick_sid = gobject.timeout_add(self._APPLY_TIMEOUT,
self.__nick_timeout_cb, widget)
- def __nick_timeout_cb(self, widget):
+ def __nick_timeout_cb(self, widget):
self._nick_sid = 0
if widget.get_text() == self._model.get_nick():
@@ -193,18 +315,18 @@ class AboutMe(SectionView):
self._model.set_nick(widget.get_text())
except ValueError, detail:
self._nick_alert.props.msg = detail
- self._nick_valid = False
+ self._nick_valid = False
else:
self._nick_alert.props.msg = self.restart_msg
- self._nick_valid = True
+ self._nick_valid = True
self.needs_restart = True
self.restart_alerts.append('nick')
self._validate()
self._nick_alert.show()
return False
- def __color_changed_cb(self, colorpicker, xocolor):
- self._model.set_color_xo(xocolor)
+ def __color_changed_cb(self, colorpicker, color):
+ self._model.set_color_xo(color.to_string())
self.needs_restart = True
self._color_alert.props.msg = self.restart_msg
self._color_valid = True
@@ -212,4 +334,7 @@ class AboutMe(SectionView):
self._validate()
self._color_alert.show()
+
+ self._update_pickers(color)
+
return False
diff --git a/extensions/cpsection/datetime/model.py b/extensions/cpsection/datetime/model.py
index 76064e4..84e1259 100644
--- a/extensions/cpsection/datetime/model.py
+++ b/extensions/cpsection/datetime/model.py
@@ -26,18 +26,20 @@ import gconf
_zone_tab = '/usr/share/zoneinfo/zone.tab'
+
def _initialize():
- '''Initialize the docstring of the set function'''
+ """Initialize the docstring of the set function"""
if set_timezone.__doc__ is None:
# when running under 'python -OO', all __doc__ fields are None,
# so += would fail -- and this function would be unnecessary anyway.
return
- timezones = read_all_timezones()
+ timezones = read_all_timezones()
for timezone in timezones:
- set_timezone.__doc__ += timezone + '\n'
-
+ set_timezone.__doc__ += timezone + '\n'
+
+
def read_all_timezones(fn=_zone_tab):
- fd = open (fn, 'r')
+ fd = open(fn, 'r')
lines = fd.readlines()
fd.close()
timezones = []
@@ -48,7 +50,7 @@ def read_all_timezones(fn=_zone_tab):
if len(line) > 1:
timezones.append(line[2])
timezones.sort()
-
+
for offset in xrange(-12, 13):
if offset < 0:
tz = 'GMT%d' % offset
@@ -56,7 +58,7 @@ def read_all_timezones(fn=_zone_tab):
tz = 'GMT+%d' % offset
else:
tz = 'GMT'
- timezones.append(tz)
+ timezones.append(tz)
for offset in xrange(-12, 13):
if offset < 0:
tz = 'UTC%d' % offset
@@ -64,16 +66,19 @@ def read_all_timezones(fn=_zone_tab):
tz = 'UTC+%d' % offset
else:
tz = 'UTC'
- timezones.append(tz)
+ timezones.append(tz)
return timezones
+
def get_timezone():
client = gconf.client_get_default()
return client.get_string('/desktop/sugar/date/timezone')
+
def print_timezone():
print get_timezone()
+
def set_timezone(timezone):
"""Set the system timezone
timezone : e.g. 'America/Los_Angeles'
@@ -84,9 +89,8 @@ def set_timezone(timezone):
client = gconf.client_get_default()
client.set_string('/desktop/sugar/date/timezone', timezone)
else:
- raise ValueError(_("Error timezone does not exist."))
+ raise ValueError(_('Error timezone does not exist.'))
return 1
-# inilialize the docstrings for the timezone
+# inilialize the docstrings for the timezone
_initialize()
-
diff --git a/extensions/cpsection/datetime/view.py b/extensions/cpsection/datetime/view.py
index 58719b4..1cef78f 100644
--- a/extensions/cpsection/datetime/view.py
+++ b/extensions/cpsection/datetime/view.py
@@ -24,36 +24,38 @@ from sugar.graphics import iconentry
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
+
class TimeZone(SectionView):
def __init__(self, model, alerts):
SectionView.__init__(self)
self._model = model
self.restart_alerts = alerts
- self._zone_sid = 0
+ self._zone_sid = 0
self._cursor_change_handler = None
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
- self.connect("realize", self.__realize_cb)
+ self.connect('realize', self.__realize_cb)
self._entry = iconentry.IconEntry()
self._entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
'system-search')
self._entry.add_clear_button()
- self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
style.COLOR_WHITE.get_gdk_color())
- self._entry.modify_base(gtk.STATE_INSENSITIVE,
- style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
self.pack_start(self._entry, False)
- self._entry.show()
+ self._entry.show()
self._scrolled_window = gtk.ScrolledWindow()
- self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self._scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
self._scrolled_window.set_shadow_type(gtk.SHADOW_IN)
- self._store = gtk.ListStore(gobject.TYPE_STRING)
+ self._store = gtk.ListStore(gobject.TYPE_STRING)
zones = model.read_all_timezones()
for zone in zones:
self._store.append([zone])
@@ -91,16 +93,16 @@ class TimeZone(SectionView):
zone = self._model.get_timezone()
for row in self._store:
if zone == row[0]:
- self._treeview.set_cursor(row.path, self._timezone_column,
+ self._treeview.set_cursor(row.path, self._timezone_column,
False)
- self._treeview.scroll_to_cell(row.path, self._timezone_column,
- True, 0.5, 0.5)
+ self._treeview.scroll_to_cell(row.path, self._timezone_column,
+ True, 0.5, 0.5)
break
-
- self.needs_restart = False
+
+ self.needs_restart = False
self._cursor_change_handler = self._treeview.connect( \
- "cursor-changed", self.__zone_changed_cd)
-
+ 'cursor-changed', self.__zone_changed_cd)
+
def undo(self):
self._treeview.disconnect(self._cursor_change_handler)
self._model.undo()
@@ -121,18 +123,18 @@ class TimeZone(SectionView):
return False
if self._model.get_timezone() == self._store.get_value(row, 0):
return False
-
+
if self._zone_sid:
gobject.source_remove(self._zone_sid)
- self._zone_sid = gobject.timeout_add(self._APPLY_TIMEOUT,
+ self._zone_sid = gobject.timeout_add(self._APPLY_TIMEOUT,
self.__zone_timeout_cb, row)
return True
- def __zone_timeout_cb(self, row):
- self._zone_sid = 0
+ def __zone_timeout_cb(self, row):
+ self._zone_sid = 0
self._model.set_timezone(self._store.get_value(row, 0))
self.restart_alerts.append('zone')
- self.needs_restart = True
+ self.needs_restart = True
self._zone_alert.props.msg = self.restart_msg
self._zone_alert.show()
return False
diff --git a/extensions/cpsection/frame/model.py b/extensions/cpsection/frame/model.py
index 9eea9ad..4796062 100644
--- a/extensions/cpsection/frame/model.py
+++ b/extensions/cpsection/frame/model.py
@@ -17,47 +17,53 @@
from gettext import gettext as _
import gconf
-
+
+
def get_corner_delay():
client = gconf.client_get_default()
corner_delay = client.get_int('/desktop/sugar/frame/corner_delay')
return corner_delay
+
def print_corner_delay():
print get_corner_delay()
-
+
+
def set_corner_delay(delay):
"""Set a delay for the activation of the frame using hot corners.
instantaneous: 0 (0 milliseconds)
- delay: 100 (100 milliseconds)
+ delay: 100 (100 milliseconds)
never: 1000 (disable activation)
"""
try:
int(delay)
- except ValueError:
- raise ValueError(_("Value must be an integer."))
+ except ValueError:
+ raise ValueError(_('Value must be an integer.'))
client = gconf.client_get_default()
client.set_int('/desktop/sugar/frame/corner_delay', int(delay))
return 0
-
+
+
def get_edge_delay():
client = gconf.client_get_default()
edge_delay = client.get_int('/desktop/sugar/frame/edge_delay')
return edge_delay
+
def print_edge_delay():
print get_edge_delay()
-
+
+
def set_edge_delay(delay):
- """Set a delay for the activation of the frame using warm edges.
+ """Set a delay for the activation of the frame using warm edges.
instantaneous: 0 (0 milliseconds)
- delay: 100 (100 milliseconds)
+ delay: 100 (100 milliseconds)
never: 1000 (disable activation)
"""
try:
int(delay)
- except ValueError:
- raise ValueError(_("Value must be an integer."))
+ except ValueError:
+ raise ValueError(_('Value must be an integer.'))
client = gconf.client_get_default()
client.set_int('/desktop/sugar/frame/edge_delay', int(delay))
return 0
diff --git a/extensions/cpsection/frame/view.py b/extensions/cpsection/frame/view.py
index cbe43bb..8b4ab83 100644
--- a/extensions/cpsection/frame/view.py
+++ b/extensions/cpsection/frame/view.py
@@ -23,11 +23,13 @@ from sugar.graphics import style
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
-_never = _('never')
+
+_never = _('never')
_instantaneous = _('instantaneous')
_seconds_label = _('%s seconds')
_MAX_DELAY = 1000
+
class Frame(SectionView):
def __init__(self, model, alerts):
SectionView.__init__(self)
@@ -43,7 +45,7 @@ class Frame(SectionView):
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
- self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+ self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
separator = gtk.HSeparator()
self.pack_start(separator, expand=False)
@@ -67,25 +69,25 @@ class Frame(SectionView):
self._setup_edge()
self.pack_start(self._box_sliders, expand=False)
- self._box_sliders.show()
+ self._box_sliders.show()
self.setup()
- def _setup_corner(self):
+ def _setup_corner(self):
box_delay = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_delay = gtk.Label(_('Corner'))
label_delay.set_alignment(1, 0.75)
- label_delay.modify_fg(gtk.STATE_NORMAL,
+ label_delay.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_delay.pack_start(label_delay, expand=False)
self._group.add_widget(label_delay)
- label_delay.show()
-
- adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY,
+ label_delay.show()
+
+ adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY,
step_incr=100, page_incr=100, page_size=0)
self._corner_delay_slider = gtk.HScale(adj)
self._corner_delay_slider.set_digits(0)
- self._corner_delay_slider.connect('format-value',
+ self._corner_delay_slider.connect('format-value',
self.__corner_delay_format_cb)
box_delay.pack_start(self._corner_delay_slider)
self._corner_delay_slider.show()
@@ -105,22 +107,22 @@ class Frame(SectionView):
if 'corner_delay' in self.restart_alerts:
self._corner_delay_alert.props.msg = self.restart_msg
self._corner_delay_alert.show()
-
- def _setup_edge(self):
+
+ def _setup_edge(self):
box_delay = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_delay = gtk.Label(_('Edge'))
label_delay.set_alignment(1, 0.75)
- label_delay.modify_fg(gtk.STATE_NORMAL,
+ label_delay.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_delay.pack_start(label_delay, expand=False)
self._group.add_widget(label_delay)
- label_delay.show()
-
- adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY,
+ label_delay.show()
+
+ adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY,
step_incr=100, page_incr=100, page_size=0)
self._edge_delay_slider = gtk.HScale(adj)
self._edge_delay_slider.set_digits(0)
- self._edge_delay_slider.connect('format-value',
+ self._edge_delay_slider.connect('format-value',
self.__edge_delay_format_cb)
box_delay.pack_start(self._edge_delay_slider)
self._edge_delay_slider.show()
@@ -140,24 +142,24 @@ class Frame(SectionView):
if 'edge_delay' in self.restart_alerts:
self._edge_delay_alert.props.msg = self.restart_msg
self._edge_delay_alert.show()
-
+
def setup(self):
self._corner_delay_slider.set_value(self._model.get_corner_delay())
self._edge_delay_slider.set_value(self._model.get_edge_delay())
self._corner_delay_is_valid = True
self._edge_delay_is_valid = True
self.needs_restart = False
- self._corner_delay_change_handler = self._corner_delay_slider.connect( \
+ self._corner_delay_change_handler = self._corner_delay_slider.connect(
'value-changed', self.__corner_delay_changed_cb)
- self._edge_delay_change_handler = self._edge_delay_slider.connect( \
+ self._edge_delay_change_handler = self._edge_delay_slider.connect(
'value-changed', self.__edge_delay_changed_cb)
-
- def undo(self):
+
+ def undo(self):
self._corner_delay_slider.disconnect(self._corner_delay_change_handler)
self._edge_delay_slider.disconnect(self._edge_delay_change_handler)
self._model.undo()
- self._corner_delay_alert.hide()
- self._edge_delay_alert.hide()
+ self._corner_delay_alert.hide()
+ self._edge_delay_alert.hide()
def _validate(self):
if self._edge_delay_is_valid and self._corner_delay_is_valid:
@@ -165,15 +167,15 @@ class Frame(SectionView):
else:
self.props.is_valid = False
- def __corner_delay_changed_cb(self, scale, data=None):
+ def __corner_delay_changed_cb(self, scale, data=None):
if self._corner_delay_sid:
gobject.source_remove(self._corner_delay_sid)
self._corner_delay_sid = gobject.timeout_add( \
self._APPLY_TIMEOUT, self.__corner_delay_timeout_cb, scale)
-
- def __corner_delay_timeout_cb(self, scale):
+
+ def __corner_delay_timeout_cb(self, scale):
self._corner_delay_sid = 0
- if scale.get_value() == self._model.get_corner_delay():
+ if scale.get_value() == self._model.get_corner_delay():
return
try:
self._model.set_corner_delay(scale.get_value())
@@ -182,12 +184,12 @@ class Frame(SectionView):
self._corner_delay_is_valid = False
else:
self._corner_delay_alert.props.msg = self.restart_msg
- self._corner_delay_is_valid = True
+ self._corner_delay_is_valid = True
self.needs_restart = True
self.restart_alerts.append('corner_delay')
-
+
self._validate()
- self._corner_delay_alert.show()
+ self._corner_delay_alert.show()
return False
def __corner_delay_format_cb(self, scale, value):
@@ -198,15 +200,15 @@ class Frame(SectionView):
else:
return _seconds_label % (value / _MAX_DELAY)
- def __edge_delay_changed_cb(self, scale, data=None):
+ def __edge_delay_changed_cb(self, scale, data=None):
if self._edge_delay_sid:
gobject.source_remove(self._edge_delay_sid)
self._edge_delay_sid = gobject.timeout_add( \
self._APPLY_TIMEOUT, self.__edge_delay_timeout_cb, scale)
-
- def __edge_delay_timeout_cb(self, scale):
+
+ def __edge_delay_timeout_cb(self, scale):
self._edge_delay_sid = 0
- if scale.get_value() == self._model.get_edge_delay():
+ if scale.get_value() == self._model.get_edge_delay():
return
try:
self._model.set_edge_delay(scale.get_value())
@@ -215,12 +217,12 @@ class Frame(SectionView):
self._edge_delay_is_valid = False
else:
self._edge_delay_alert.props.msg = self.restart_msg
- self._edge_delay_is_valid = True
+ self._edge_delay_is_valid = True
self.needs_restart = True
self.restart_alerts.append('edge_delay')
-
+
self._validate()
- self._edge_delay_alert.show()
+ self._edge_delay_alert.show()
return False
def __edge_delay_format_cb(self, scale, value):
diff --git a/extensions/cpsection/keyboard/__init__.py b/extensions/cpsection/keyboard/__init__.py
index 568e7a5..065086c 100644
--- a/extensions/cpsection/keyboard/__init__.py
+++ b/extensions/cpsection/keyboard/__init__.py
@@ -19,4 +19,3 @@ from gettext import gettext as _
CLASS = 'Keyboard'
ICON = 'module-keyboard'
TITLE = _('Keyboard')
-
diff --git a/extensions/cpsection/keyboard/model.py b/extensions/cpsection/keyboard/model.py
index 9d61c0c..1f92973 100644
--- a/extensions/cpsection/keyboard/model.py
+++ b/extensions/cpsection/keyboard/model.py
@@ -20,12 +20,13 @@ import xklavier
import gconf
-_GROUP_NAME = 'grp' # The XKB name for group switch options
+_GROUP_NAME = 'grp' # The XKB name for group switch options
_LAYOUTS_KEY = '/desktop/sugar/peripherals/keyboard/layouts'
_OPTIONS_KEY = '/desktop/sugar/peripherals/keyboard/options'
_MODEL_KEY = '/desktop/sugar/peripherals/keyboard/model'
+
class KeyboardManager(object):
def __init__(self, display):
self._engine = xklavier.Engine(display)
@@ -33,7 +34,7 @@ class KeyboardManager(object):
self._configregistry.load(False)
self._configrec = xklavier.ConfigRec()
self._configrec.get_from_server(self._engine)
-
+
self._gconf_client = gconf.client_get_default()
def _populate_one(self, config_registry, item, store):
@@ -48,7 +49,7 @@ class KeyboardManager(object):
else:
description = 'Default layout, %s' % item.get_description()
variant = ''
-
+
store.append([description, ('%s(%s)' % (layout, variant))])
def get_models(self):
@@ -76,8 +77,8 @@ class KeyboardManager(object):
def get_options_group(self):
"""Return list of supported options for switching keyboard group"""
options = []
- self._configregistry.foreach_option(_GROUP_NAME, self._populate_one, \
- options)
+ self._configregistry.foreach_option(_GROUP_NAME, self._populate_one,
+ options)
options.sort()
return options
@@ -96,7 +97,7 @@ class KeyboardManager(object):
layouts = self._gconf_client.get_list(_LAYOUTS_KEY, gconf.VALUE_STRING)
if layouts:
return layouts
-
+
layouts = self._configrec.get_layouts()
variants = self._configrec.get_variants()
@@ -126,7 +127,7 @@ class KeyboardManager(object):
return option
return None
-
+
def get_max_layouts(self):
"""Return the maximum number of layouts supported simultaneously"""
return self._engine.get_max_num_groups()
@@ -142,8 +143,10 @@ class KeyboardManager(object):
def set_option_group(self, option_group):
"""Sets the supplied option for switching keyboard group"""
#XXX: Merge, not overwrite previous options
- if option_group is None or not option_group:
+ if not option_group:
options = ['']
+ elif isinstance(option_group, list):
+ options = option_group
else:
options = [option_group]
self._gconf_client.set_list(_OPTIONS_KEY, gconf.VALUE_STRING, options)
@@ -160,8 +163,7 @@ class KeyboardManager(object):
for layout in layouts:
layouts_list.append(layout.split('(')[0])
variants_list.append(layout.split('(')[1][:-1])
-
+
self._configrec.set_layouts(layouts_list)
self._configrec.set_variants(variants_list)
- self._configrec.activate(self._engine)
-
+ self._configrec.activate(self._engine)
diff --git a/extensions/cpsection/keyboard/view.py b/extensions/cpsection/keyboard/view.py
index dd62a85..e349255 100644
--- a/extensions/cpsection/keyboard/view.py
+++ b/extensions/cpsection/keyboard/view.py
@@ -26,6 +26,7 @@ from sugar.graphics.icon import Icon
from jarabe.controlpanel.sectionview import SectionView
+
CLASS = 'Language'
ICON = 'module-keyboard'
TITLE = _('Keyboard')
@@ -37,6 +38,7 @@ _APPLY_TIMEOUT = 500
# once python-xklavier has been packaged for all major distributions
# For more information, see: http://dev.sugarlabs.org/ticket/407
+
class LayoutCombo(gtk.HBox):
"""
@@ -45,8 +47,8 @@ class LayoutCombo(gtk.HBox):
"""
__gsignals__ = {
- 'selection-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
- (gobject.TYPE_STRING, gobject.TYPE_INT))
+ 'selection-changed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_STRING, gobject.TYPE_INT)),
}
def __init__(self, keyboard_manager, n):
@@ -57,7 +59,7 @@ class LayoutCombo(gtk.HBox):
self.set_border_width(style.DEFAULT_SPACING)
self.set_spacing(style.DEFAULT_SPACING)
- label = gtk.Label(' <b>%s</b> ' % str(n+1))
+ label = gtk.Label(' <b>%s</b> ' % str(n + 1))
label.set_use_markup(True)
label.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
@@ -69,7 +71,7 @@ class LayoutCombo(gtk.HBox):
for description, name in self._keyboard_manager.get_languages():
self._klang_store.append([name, description])
- self._klang_combo = gtk.ComboBox(model = self._klang_store)
+ self._klang_combo = gtk.ComboBox(model=self._klang_store)
self._klang_combo_changed_id = \
self._klang_combo.connect('changed', self._klang_combo_changed_cb)
cell = gtk.CellRendererText()
@@ -77,10 +79,10 @@ class LayoutCombo(gtk.HBox):
cell.props.ellipsize_set = True
self._klang_combo.pack_start(cell)
self._klang_combo.add_attribute(cell, 'text', 1)
- self.pack_start(self._klang_combo, expand=True, fill = True)
+ self.pack_start(self._klang_combo, expand=True, fill=True)
self._kvariant_store = None
- self._kvariant_combo = gtk.ComboBox(model = None)
+ self._kvariant_combo = gtk.ComboBox(model=None)
self._kvariant_combo_changed_id = \
self._kvariant_combo.connect('changed', \
self._kvariant_combo_changed_cb)
@@ -89,7 +91,7 @@ class LayoutCombo(gtk.HBox):
cell.props.ellipsize_set = True
self._kvariant_combo.pack_start(cell)
self._kvariant_combo.add_attribute(cell, 'text', 1)
- self.pack_start(self._kvariant_combo, expand=True, fill = True)
+ self.pack_start(self._kvariant_combo, expand=True, fill=True)
self._klang_combo.set_active(self._index)
@@ -129,7 +131,7 @@ class LayoutCombo(gtk.HBox):
model = combobox.get_model()
lang = model.get(it, 0)[0]
self._set_kvariant_store(lang)
-
+
def _kvariant_combo_changed_cb(self, combobox):
it = combobox.get_active_iter()
model = combobox.get_model()
@@ -145,18 +147,17 @@ class Keyboard(SectionView):
self._kmodel = None
self._selected_kmodel = None
-
+
self._klayouts = []
self._selected_klayouts = []
-
+
self._group_switch_option = None
self._selected_group_switch_option = None
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
-
- self._layout_table = gtk.Table(rows = 4, columns = 2, \
- homogeneous = False)
+
+ self._layout_table = gtk.Table(rows=4, columns=2, homogeneous=False)
self._keyboard_manager = model.KeyboardManager(self.get_display())
self._layout_combo_list = []
@@ -169,7 +170,7 @@ class Keyboard(SectionView):
self._vbox = gtk.VBox()
scrollwindow.add_with_viewport(self._vbox)
-
+
self.__kmodel_sid = None
self.__layout_sid = None
self.__group_switch_sid = None
@@ -177,7 +178,7 @@ class Keyboard(SectionView):
self._setup_kmodel()
self._setup_layouts()
self._setup_group_switch_option()
-
+
self._vbox.show()
def _setup_kmodel(self):
@@ -200,7 +201,7 @@ class Keyboard(SectionView):
for description, name in self._keyboard_manager.get_models():
kmodel_store.append([name, description])
- kmodel_combo = gtk.ComboBox(model = kmodel_store)
+ kmodel_combo = gtk.ComboBox(model=kmodel_store)
cell = gtk.CellRendererText()
cell.props.ellipsize = pango.ELLIPSIZE_MIDDLE
cell.props.ellipsize_set = True
@@ -214,7 +215,7 @@ class Keyboard(SectionView):
kmodel_combo.set_active_iter(row.iter)
break
- box_kmodel.pack_start(kmodel_combo, expand = False)
+ box_kmodel.pack_start(kmodel_combo, expand=False)
self._vbox.pack_start(box_kmodel, expand=False)
box_kmodel.show_all()
@@ -223,7 +224,7 @@ class Keyboard(SectionView):
def __kmodel_changed_cb(self, combobox):
if self.__kmodel_sid is not None:
gobject.source_remove(self.__kmodel_sid)
- self.__kmodel_sid = gobject.timeout_add(_APPLY_TIMEOUT,
+ self.__kmodel_sid = gobject.timeout_add(_APPLY_TIMEOUT,
self.__kmodel_timeout_cb, combobox)
def __kmodel_timeout_cb(self, combobox):
@@ -240,7 +241,8 @@ class Keyboard(SectionView):
return False
def _setup_group_switch_option(self):
- """Adds the controls for changing the group switch option of keyboard"""
+ """Adds the controls for changing the group switch option of keyboard
+ """
separator_group_option = gtk.HSeparator()
self._vbox.pack_start(separator_group_option, expand=False)
separator_group_option.show_all()
@@ -259,7 +261,7 @@ class Keyboard(SectionView):
for description, name in self._keyboard_manager.get_options_group():
group_option_store.append([name, description])
- group_option_combo = gtk.ComboBox(model = group_option_store)
+ group_option_combo = gtk.ComboBox(model=group_option_store)
cell = gtk.CellRendererText()
cell.props.ellipsize = pango.ELLIPSIZE_MIDDLE
cell.props.ellipsize_set = True
@@ -280,7 +282,7 @@ class Keyboard(SectionView):
if not found:
group_option_combo.set_active(0)
- box_group_option.pack_start(group_option_combo, expand = False)
+ box_group_option.pack_start(group_option_combo, expand=False)
self._vbox.pack_start(box_group_option, expand=False)
box_group_option.show_all()
@@ -290,7 +292,7 @@ class Keyboard(SectionView):
def __group_switch_changed_cb(self, combobox):
if self.__group_switch_sid is not None:
gobject.source_remove(self.__group_switch_sid)
- self.__group_switch_sid = gobject.timeout_add(_APPLY_TIMEOUT,
+ self.__group_switch_sid = gobject.timeout_add(_APPLY_TIMEOUT,
self.__group_switch_timeout_cb, combobox)
def __group_switch_timeout_cb(self, combobox):
@@ -306,7 +308,6 @@ class Keyboard(SectionView):
except Exception:
logging.exception('Could not set new keyboard group switch option')
-
return False
def _setup_layouts(self):
@@ -324,18 +325,18 @@ class Keyboard(SectionView):
for i in range(0, self._keyboard_manager.get_max_layouts()):
add_remove_box = self.__create_add_remove_box()
self._layout_addremovebox_list.append(add_remove_box)
- self._layout_table.attach(add_remove_box, 1, 2, i, i+1)
+ self._layout_table.attach(add_remove_box, 1, 2, i, i + 1)
layout_combo = LayoutCombo(self._keyboard_manager, i)
layout_combo.connect('selection-changed', \
self.__layout_combo_selection_changed_cb)
self._layout_combo_list.append(layout_combo)
- self._layout_table.attach(layout_combo, 0, 1, i, i+1)
+ self._layout_table.attach(layout_combo, 0, 1, i, i + 1)
if i < len(self._klayouts):
layout_combo.show_all()
layout_combo.select_layout(self._klayouts[i])
-
+
self._vbox.pack_start(self._layout_table, expand=False)
self._layout_table.set_size_request(self._vbox.size_request()[0], -1)
self._layout_table.show()
@@ -359,15 +360,15 @@ class Keyboard(SectionView):
i += 1
def __create_add_remove_box(self):
- '''Creates gtk.Hbox with add/remove buttons'''
- add_icon = Icon(icon_name='list-add')
+ """Creates gtk.Hbox with add/remove buttons"""
+ add_icon = Icon(icon_name='list-add')
add_button = gtk.Button()
add_button.set_image(add_icon)
add_button.connect('clicked',
self.__add_button_clicked_cb)
- remove_icon = Icon(icon_name='list-remove')
+ remove_icon = Icon(icon_name='list-remove')
remove_button = gtk.Button()
remove_button.set_image(remove_icon)
remove_button.connect('clicked',
@@ -387,7 +388,7 @@ class Keyboard(SectionView):
def __add_button_clicked_cb(self, button):
self._layout_combo_list[len(self._selected_klayouts)].show_all()
self._update_klayouts()
-
+
def __remove_button_clicked_cb(self, button):
self._layout_combo_list[len(self._selected_klayouts) - 1].hide()
self._update_klayouts()
@@ -403,7 +404,7 @@ class Keyboard(SectionView):
if self.__layout_sid is not None:
gobject.source_remove(self.__layout_sid)
- self.__layout_sid = gobject.timeout_add(_APPLY_TIMEOUT,
+ self.__layout_sid = gobject.timeout_add(_APPLY_TIMEOUT,
self.__layout_timeout_cb)
def __layout_timeout_cb(self):
@@ -417,10 +418,8 @@ class Keyboard(SectionView):
return False
-
def undo(self):
"""Reverts back to the original keyboard configuration"""
self._keyboard_manager.set_model(self._kmodel)
self._keyboard_manager.set_layouts(self._klayouts)
self._keyboard_manager.set_option_group(self._group_switch_option)
-
diff --git a/extensions/cpsection/language/__init__.py b/extensions/cpsection/language/__init__.py
index a8f9f08..c93b7d1 100644
--- a/extensions/cpsection/language/__init__.py
+++ b/extensions/cpsection/language/__init__.py
@@ -19,4 +19,3 @@ from gettext import gettext as _
CLASS = 'Language'
ICON = 'module-language'
TITLE = _('Language')
-
diff --git a/extensions/cpsection/language/model.py b/extensions/cpsection/language/model.py
index fe80410..240e562 100644
--- a/extensions/cpsection/language/model.py
+++ b/extensions/cpsection/language/model.py
@@ -21,11 +21,14 @@
#
import os
+import locale
from gettext import gettext as _
import subprocess
-_default_lang = 'en_US.utf8'
-_standard_msg = _("Could not access ~/.i18n. Create standard settings.")
+
+_default_lang = '%s.%s' % locale.getdefaultlocale()
+_standard_msg = _('Could not access ~/.i18n. Create standard settings.')
+
def read_all_languages():
fdp = subprocess.Popen(['locale', '-av'], stdout=subprocess.PIPE)
@@ -42,7 +45,7 @@ def read_all_languages():
if locale.endswith('utf8') and len(lang):
locales.append((lang, territory, locale))
- #FIXME: This is a temporary workaround for locales that are essential to
+ #FIXME: This is a temporary workaround for locales that are essential to
# OLPC, but are not in Glibc yet.
locales.append(('Kreyol', 'Haiti', 'ht_HT.utf8'))
locales.append(('Dari', 'Afghanistan', 'fa_AF.utf8'))
@@ -51,7 +54,8 @@ def read_all_languages():
locales.sort()
return locales
-def _initialize():
+
+def _initialize():
if set_languages.__doc__ is None:
# when running under 'python -OO', all __doc__ fields are None,
# so += would fail -- and this function would be unnecessary anyway.
@@ -59,13 +63,12 @@ def _initialize():
languages = read_all_languages()
set_languages.__doc__ += '\n'
for lang in languages:
- set_languages.__doc__ += '%s \n' % (lang[0].replace(' ', '_') + '/' +
+ set_languages.__doc__ += '%s \n' % (lang[0].replace(' ', '_') + '/' +
lang[1].replace(' ', '_'))
-
-def _write_i18n(langs):
- colon = ':'
- langstr = colon.join(langs)
- path = os.path.join(os.environ.get("HOME"), '.i18n')
+
+
+def _write_i18n(lang_env, language_env):
+ path = os.path.join(os.environ.get('HOME'), '.i18n')
if not os.access(path, os.W_OK):
print _standard_msg
fd = open(path, 'w')
@@ -74,32 +77,33 @@ def _write_i18n(langs):
fd.close()
else:
fd = open(path, 'w')
- fd.write('LANG="%s"\n' % langs[0].strip("\n"))
- fd.write('LANGUAGE="%s"\n' % langstr)
+ fd.write('LANG="%s"\n' % lang_env)
+ fd.write('LANGUAGE="%s"\n' % language_env)
fd.close()
+
def get_languages():
- path = os.path.join(os.environ.get("HOME"), '.i18n')
+ path = os.path.join(os.environ.get('HOME', ''), '.i18n')
if not os.access(path, os.R_OK):
- print _standard_msg
+ print _standard_msg
fd = open(path, 'w')
fd.write('LANG="%s"\n' % _default_lang)
fd.write('LANGUAGE="%s"\n' % _default_lang)
fd.close()
return [_default_lang]
-
- fd = open(path, "r")
+
+ fd = open(path, 'r')
lines = fd.readlines()
fd.close()
langlist = None
for line in lines:
- if line.startswith("LANGUAGE="):
+ if line.startswith('LANGUAGE='):
lang = line[9:].replace('"', '')
lang = lang.strip()
langlist = lang.split(':')
- elif line.startswith("LANG="):
+ elif line.startswith('LANG='):
lang = line[5:].replace('"', '')
# There might be cases where .i18n may not contain a LANGUAGE field
@@ -108,6 +112,7 @@ def get_languages():
else:
return langlist
+
def print_languages():
codes = get_languages()
@@ -121,30 +126,35 @@ def print_languages():
found_lang = True
break
if not found_lang:
- print (_("Language for code=%s could not be determined.") % code)
-
+ print (_('Language for code=%s could not be determined.') % code)
+
+
def set_languages(languages):
"""Set the system language.
- languages :
+ languages :
"""
- if isinstance(languages, str):
- # This came from the commandline
- #TODO: Support multiple languages from the command line
- if languages.endswith('utf8'):
- _write_i18n(languages)
- return 1
- else:
- langs = read_all_languages()
- for lang, territory, locale in langs:
- code = lang.replace(' ', '_') + '/' \
- + territory.replace(' ', '_')
- if code == languages:
- _write_i18n(locale)
- return 1
- print (_("Sorry I do not speak \'%s\'.") % languages)
+
+ if languages.endswith('utf8'):
+ set_languages_list([languages])
+ return 1
else:
- _write_i18n(languages)
+ langs = read_all_languages()
+ for lang, territory, locale in langs:
+ code = lang.replace(' ', '_') + '/' \
+ + territory.replace(' ', '_')
+ if code == languages:
+ set_languages_list([locale])
+ return 1
+ print (_("Sorry I do not speak \'%s\'.") % languages)
+
+
+def set_languages_list(languages):
+ """Set the system language using a list of preferred languages"""
+ colon = ':'
+ language_env = colon.join(languages)
+ lang_env = languages[0].strip('\n')
+ _write_i18n(lang_env, language_env)
+
# inilialize the docstrings for the language
_initialize()
-
diff --git a/extensions/cpsection/language/view.py b/extensions/cpsection/language/view.py
index d1a49cf..1553959 100644
--- a/extensions/cpsection/language/view.py
+++ b/extensions/cpsection/language/view.py
@@ -25,13 +25,14 @@ from sugar.graphics.icon import Icon
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
-_translate_language = lambda msg: gettext.dgettext('iso_639', msg)
+_translate_language = lambda msg: gettext.dgettext('iso_639', msg)
_translate_country = lambda msg: gettext.dgettext('iso_3166', msg)
CLASS = 'Language'
ICON = 'module-language'
TITLE = gettext.gettext('Language')
+
class Language(SectionView):
def __init__(self, model, alerts):
SectionView.__init__(self)
@@ -53,9 +54,9 @@ 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.")
+ 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_width_chars(100)
self._text.set_line_wrap(True)
@@ -69,14 +70,14 @@ class Language(SectionView):
self.pack_start(scrolled, expand=True)
self._table = gtk.Table(rows=1, columns=3, homogeneous=False)
- self._table.set_border_width(style.DEFAULT_SPACING * 2)
+ self._table.set_border_width(style.DEFAULT_SPACING * 2)
self._table.show()
scrolled.add_with_viewport(self._table)
self._lang_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
self.pack_start(self._lang_alert_box, False)
- self._lang_alert = InlineAlert()
+ self._lang_alert = InlineAlert()
self._lang_alert_box.pack_start(self._lang_alert)
if 'lang' in self.restart_alerts:
self._lang_alert.props.msg = self.restart_msg
@@ -86,10 +87,10 @@ class Language(SectionView):
self.setup()
def _add_row(self, locale_code=None):
- '''Adds a row to the table'''
+ """Adds a row to the table"""
self._selected_lang_count += 1
-
+
self._table.resize(self._selected_lang_count, 3)
label = gtk.Label(str=str(self._selected_lang_count))
@@ -98,8 +99,7 @@ class Language(SectionView):
self._labels.append(label)
self._attach_to_table(label, 0, 1, padding=1)
label.show()
-
-
+
store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
for language, country, code in self._available_locales:
description = '%s (%s)' % (_translate_language(language), \
@@ -115,7 +115,7 @@ class Language(SectionView):
for row in store:
lang = locale_code.split('.')[0]
lang_column = row[0].split('.')[0]
- if lang in lang_column:
+ if lang in lang_column:
combobox.set_active_iter(row.iter)
break
else:
@@ -132,7 +132,7 @@ class Language(SectionView):
self._attach_to_table(add_remove_box, 2, 3)
add_remove_box.show_all()
-
+
if self._selected_lang_count > 1:
previous_add_removes = self._add_remove_boxes[-2]
previous_add_removes.hide_all()
@@ -149,12 +149,12 @@ class Language(SectionView):
ypadding=padding)
def _delete_last_row(self):
- '''Deletes the last row of the table'''
+ """Deletes the last row of the table"""
self._selected_lang_count -= 1
label, add_remove_box, combobox, store_ = self._get_last_row()
-
+
label.destroy()
add_remove_box.destroy()
combobox.destroy()
@@ -180,15 +180,15 @@ class Language(SectionView):
self._lang_alert.hide()
def _create_add_remove_box(self):
- '''Creates gtk.Hbox with add/remove buttons'''
- add_icon = Icon(icon_name='list-add')
+ """Creates gtk.Hbox with add/remove buttons"""
+ add_icon = Icon(icon_name='list-add')
add_button = gtk.Button()
add_button.set_image(add_icon)
add_button.connect('clicked',
self.__add_button_clicked_cb)
- remove_icon = Icon(icon_name='list-remove')
+ remove_icon = Icon(icon_name='list-remove')
remove_button = gtk.Button()
remove_button.set_image(remove_icon)
remove_button.connect('clicked',
@@ -217,8 +217,8 @@ class Language(SectionView):
selected_langs = self._get_selected_langs()
last_lang = selected_langs[-1]
- self._determine_add_remove_visibility(last_lang = last_lang)
-
+ self._determine_add_remove_visibility(last_lang=last_lang)
+
self._changed = (selected_langs != self._selected_locales)
if self._changed == False:
@@ -245,10 +245,10 @@ class Language(SectionView):
model = combobox.get_model()
lang_code = model.get(it, 0)[0]
new_codes.append(lang_code)
-
+
return new_codes
- def _determine_add_remove_visibility(self, last_lang = None):
+ def _determine_add_remove_visibility(self, last_lang=None):
# We should not let users add fallback languages for English (USA)
# This is because the software is not usually _translated_ into English
# which means that the fallback gets selected automatically
@@ -256,11 +256,11 @@ class Language(SectionView):
if last_lang is None:
selected_langs = self._get_selected_langs()
last_lang = selected_langs[-1]
-
+
add_remove_box = self._add_remove_boxes[-1]
buttons = add_remove_box.get_children()
add_button, remove_button = buttons
-
+
if last_lang.startswith('en_US'):
add_button.props.visible = False
else:
@@ -271,10 +271,9 @@ class Language(SectionView):
else:
remove_button.props.visible = True
-
def __lang_timeout_cb(self, codes):
self._lang_sid = 0
- self._model.set_languages(codes)
+ self._model.set_languages_list(codes)
self.restart_alerts.append('lang')
self.needs_restart = True
self._lang_alert.props.msg = self.restart_msg
diff --git a/extensions/cpsection/modemconfiguration/__init__.py b/extensions/cpsection/modemconfiguration/__init__.py
index 8a219dc..61f5904 100644
--- a/extensions/cpsection/modemconfiguration/__init__.py
+++ b/extensions/cpsection/modemconfiguration/__init__.py
@@ -19,4 +19,3 @@ from gettext import gettext as _
CLASS = 'ModemConfiguration'
ICON = 'module-modemconfiguration'
TITLE = _('Modem Configuration')
-
diff --git a/extensions/cpsection/modemconfiguration/model.py b/extensions/cpsection/modemconfiguration/model.py
index 2545ce1..1e83c44 100755
--- a/extensions/cpsection/modemconfiguration/model.py
+++ b/extensions/cpsection/modemconfiguration/model.py
@@ -20,51 +20,62 @@ from jarabe.model.network import GSM_USERNAME_PATH, GSM_PASSWORD_PATH, \
GSM_NUMBER_PATH, GSM_APN_PATH, GSM_PIN_PATH, \
GSM_PUK_PATH
+
def get_username():
client = gconf.client_get_default()
return client.get_string(GSM_USERNAME_PATH) or ''
+
def get_password():
client = gconf.client_get_default()
return client.get_string(GSM_PASSWORD_PATH) or ''
+
def get_number():
client = gconf.client_get_default()
return client.get_string(GSM_NUMBER_PATH) or ''
+
def get_apn():
client = gconf.client_get_default()
return client.get_string(GSM_APN_PATH) or ''
+
def get_pin():
client = gconf.client_get_default()
return client.get_string(GSM_PIN_PATH) or ''
+
def get_puk():
client = gconf.client_get_default()
return client.get_string(GSM_PUK_PATH) or ''
+
def set_username(username):
client = gconf.client_get_default()
client.set_string(GSM_USERNAME_PATH, username)
+
def set_password(password):
client = gconf.client_get_default()
client.set_string(GSM_PASSWORD_PATH, password)
+
def set_number(number):
client = gconf.client_get_default()
client.set_string(GSM_NUMBER_PATH, number)
+
def set_apn(apn):
client = gconf.client_get_default()
client.set_string(GSM_APN_PATH, apn)
+
def set_pin(pin):
client = gconf.client_get_default()
client.set_string(GSM_PIN_PATH, pin)
+
def set_puk(puk):
client = gconf.client_get_default()
client.set_string(GSM_PUK_PATH, puk)
-
diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsection/modemconfiguration/view.py
index b236f3f..c31edba 100644
--- a/extensions/cpsection/modemconfiguration/view.py
+++ b/extensions/cpsection/modemconfiguration/view.py
@@ -25,10 +25,12 @@ from sugar.graphics import style
from jarabe.controlpanel.sectionview import SectionView
+
APPLY_TIMEOUT = 1000
+
class EntryWithLabel(gtk.HBox):
- __gtype_name__ = "SugarEntryWithLabel"
+ __gtype_name__ = 'SugarEntryWithLabel'
def __init__(self, label_text):
gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
@@ -53,7 +55,7 @@ class EntryWithLabel(gtk.HBox):
def __entry_changed_cb(self, widget, data=None):
if self._timeout_sid:
gobject.source_remove(self._timeout_sid)
- self._timeout_sid = gobject.timeout_add(APPLY_TIMEOUT,
+ self._timeout_sid = gobject.timeout_add(APPLY_TIMEOUT,
self.__timeout_cb)
def __timeout_cb(self):
@@ -63,7 +65,7 @@ class EntryWithLabel(gtk.HBox):
return False
try:
- self.set_value(self._entry.get_text())
+ self.set_value(self._entry.get_text())
except ValueError:
self._is_valid = False
else:
@@ -74,18 +76,19 @@ class EntryWithLabel(gtk.HBox):
return False
def set_text_from_model(self):
- self._entry.set_text(self.get_value())
+ self._entry.set_text(self.get_value())
def get_value(self):
raise NotImplementedError
def set_value(self):
- raise NotImplementedError
+ raise NotImplementedError
def _get_is_valid(self):
return self._is_valid
is_valid = gobject.property(type=bool, getter=_get_is_valid, default=True)
+
class UsernameEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Username:'))
@@ -97,6 +100,7 @@ class UsernameEntry(EntryWithLabel):
def set_value(self, username):
self._model.set_username(username)
+
class PasswordEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Password:'))
@@ -108,6 +112,7 @@ class PasswordEntry(EntryWithLabel):
def set_value(self, password):
self._model.set_password(password)
+
class NumberEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Number:'))
@@ -119,6 +124,7 @@ class NumberEntry(EntryWithLabel):
def set_value(self, number):
self._model.set_number(number)
+
class ApnEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Access Point Name (APN):'))
@@ -130,6 +136,7 @@ class ApnEntry(EntryWithLabel):
def set_value(self, apn):
self._model.set_apn(apn)
+
class PinEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Personal Identity Number (PIN):'))
@@ -141,6 +148,7 @@ class PinEntry(EntryWithLabel):
def set_value(self, pin):
self._model.set_pin(pin)
+
class PukEntry(EntryWithLabel):
def __init__(self, model):
EntryWithLabel.__init__(self, _('Personal Unblocking Key (PUK):'))
@@ -164,10 +172,9 @@ class ModemConfiguration(SectionView):
self.set_spacing(style.DEFAULT_SPACING)
self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
- explanation = _("You will need to provide the following " \
- "information to set up a mobile " \
- "broadband connection to a cellular "\
- "(3G) network.")
+ explanation = _('You will need to provide the following information'
+ ' to set up a mobile broadband connection to a'
+ ' cellular (3G) network.')
self._text = gtk.Label(explanation)
self._text.set_width_chars(100)
self._text.set_line_wrap(True)
@@ -209,12 +216,12 @@ class ModemConfiguration(SectionView):
self._group.add_widget(self._pin_entry.label)
self.pack_start(self._pin_entry, expand=False)
self._pin_entry.show()
-
+
self._puk_entry = PukEntry(model)
self._puk_entry.connect('notify::is-valid',
self.__notify_is_valid_cb)
self._group.add_widget(self._puk_entry.label)
- self.pack_start(self._puk_entry, expand=False)
+ self.pack_start(self._puk_entry, expand=False)
self._puk_entry.show()
self.setup()
@@ -247,4 +254,3 @@ class ModemConfiguration(SectionView):
if entry.is_valid:
self.needs_restart = True
self._validate()
-
diff --git a/extensions/cpsection/network/__init__.py b/extensions/cpsection/network/__init__.py
index 8fea274..86546f7 100644
--- a/extensions/cpsection/network/__init__.py
+++ b/extensions/cpsection/network/__init__.py
@@ -20,6 +20,3 @@ CLASS = 'Network'
ICON = 'module-network'
TITLE = _('Network')
KEYWORDS = ['network', 'jabber', 'radio', 'server']
-
-
-
diff --git a/extensions/cpsection/network/model.py b/extensions/cpsection/network/model.py
index e1c3dab..916ce8c 100644
--- a/extensions/cpsection/network/model.py
+++ b/extensions/cpsection/network/model.py
@@ -19,25 +19,33 @@ import dbus
from gettext import gettext as _
import gconf
+from jarabe.model import network
+
+
_NM_SERVICE = 'org.freedesktop.NetworkManager'
_NM_PATH = '/org/freedesktop/NetworkManager'
_NM_IFACE = 'org.freedesktop.NetworkManager'
KEYWORDS = ['network', 'jabber', 'radio', 'server']
+
class ReadError(Exception):
def __init__(self, value):
self.value = value
+
def __str__(self):
return repr(self.value)
+
def get_jabber():
client = gconf.client_get_default()
return client.get_string('/desktop/sugar/collaboration/jabber_server')
+
def print_jabber():
print get_jabber()
+
def set_jabber(server):
"""Set the jabber server
server : e.g. 'olpc.collabora.co.uk'
@@ -45,30 +53,14 @@ def set_jabber(server):
client = gconf.client_get_default()
client.set_string('/desktop/sugar/collaboration/jabber_server', server)
- _restart_jabber()
return 0
-def _restart_jabber():
- """Call Sugar Presence Service to restart Telepathy CMs.
-
- This allows restarting the jabber server connection when we change it.
- """
- _PS_SERVICE = "org.laptop.Sugar.Presence"
- _PS_INTERFACE = "org.laptop.Sugar.Presence"
- _PS_PATH = "/org/laptop/Sugar/Presence"
- bus = dbus.SessionBus()
- try:
- ps = dbus.Interface(bus.get_object(_PS_SERVICE, _PS_PATH),
- _PS_INTERFACE)
- except dbus.DBusException:
- raise ReadError('%s service not available' % _PS_SERVICE)
- ps.RestartServerConnection()
def get_radio():
try:
bus = dbus.SystemBus()
obj = bus.get_object(_NM_SERVICE, _NM_PATH)
- nm_props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ nm_props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
except dbus.DBusException:
raise ReadError('%s service not available' % _NM_SERVICE)
@@ -78,18 +70,20 @@ def get_radio():
else:
raise ReadError(_('State is unknown.'))
+
def print_radio():
print ('off', 'on')[get_radio()]
-
+
+
def set_radio(state):
"""Turn Radio 'on' or 'off'
state : 'on/off'
- """
+ """
if state == 'on' or state == 1:
try:
bus = dbus.SystemBus()
obj = bus.get_object(_NM_SERVICE, _NM_PATH)
- nm_props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ nm_props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
except dbus.DBusException:
raise ReadError('%s service not available' % _NM_SERVICE)
nm_props.Set(_NM_IFACE, 'WirelessEnabled', True)
@@ -97,15 +91,16 @@ def set_radio(state):
try:
bus = dbus.SystemBus()
obj = bus.get_object(_NM_SERVICE, _NM_PATH)
- nm_props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ nm_props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
except dbus.DBusException:
raise ReadError('%s service not available' % _NM_SERVICE)
nm_props.Set(_NM_IFACE, 'WirelessEnabled', False)
else:
- raise ValueError(_("Error in specified radio argument use on/off."))
+ raise ValueError(_('Error in specified radio argument use on/off.'))
return 0
+
def clear_registration():
"""Clear the registration with the schoolserver
"""
@@ -113,28 +108,36 @@ def clear_registration():
client.set_string('/desktop/sugar/backup_url', '')
return 1
+
def clear_networks():
"""Clear saved passwords and network configurations.
"""
- pass
+ network.clear_wifi_connections()
+
+
+def have_networks():
+ return network.have_wifi_connections()
+
def get_publish_information():
client = gconf.client_get_default()
publish = client.get_bool('/desktop/sugar/collaboration/publish_gadget')
return publish
-
+
+
def print_publish_information():
print get_publish_information()
+
def set_publish_information(value):
- """ If set to true, Sugar will make you searchable for
+ """ If set to true, Sugar will make you searchable for
the other users of the Jabber server.
value: 0/1
"""
try:
value = (False, True)[int(value)]
except:
- raise ValueError(_("Error in specified argument use 0/1."))
+ raise ValueError(_('Error in specified argument use 0/1.'))
client = gconf.client_get_default()
client.set_bool('/desktop/sugar/collaboration/publish_gadget', value)
diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
index 588daeb..381dcb6 100644
--- a/extensions/cpsection/network/view.py
+++ b/extensions/cpsection/network/view.py
@@ -23,18 +23,20 @@ from sugar.graphics import style
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
+
CLASS = 'Network'
ICON = 'module-network'
TITLE = _('Network')
_APPLY_TIMEOUT = 3000
+
class Network(SectionView):
def __init__(self, model, alerts):
SectionView.__init__(self)
- self._model = model
- self.restart_alerts = alerts
+ self._model = model
+ self.restart_alerts = alerts
self._jabber_sid = 0
self._jabber_valid = True
self._radio_valid = True
@@ -64,8 +66,8 @@ class Network(SectionView):
box_wireless.set_border_width(style.DEFAULT_SPACING * 2)
box_wireless.set_spacing(style.DEFAULT_SPACING)
- radio_info = gtk.Label(_("Turn off the wireless radio to save "
- "battery life"))
+ radio_info = gtk.Label(_('Turn off the wireless radio to save battery'
+ ' life'))
radio_info.set_alignment(0, 0)
radio_info.set_line_wrap(True)
radio_info.show()
@@ -93,8 +95,8 @@ class Network(SectionView):
self._radio_alert.props.msg = self.restart_msg
self._radio_alert.show()
- history_info = gtk.Label(_("Discard network history if you "
- "have trouble connecting to the network"))
+ history_info = gtk.Label(_('Discard network history if you have'
+ ' trouble connecting to the network'))
history_info.set_alignment(0, 0)
history_info.set_line_wrap(True)
history_info.show()
@@ -104,6 +106,8 @@ class Network(SectionView):
self._clear_history_button = gtk.Button()
self._clear_history_button.set_label(_('Discard network history'))
box_clear_history.pack_start(self._clear_history_button, expand=False)
+ if not self._model.have_networks():
+ self._clear_history_button.set_sensitive(False)
self._clear_history_button.show()
box_wireless.pack_start(box_clear_history, expand=False)
box_clear_history.show()
@@ -135,24 +139,24 @@ class Network(SectionView):
box_server = gtk.HBox(spacing=style.DEFAULT_SPACING)
label_server = gtk.Label(_('Server:'))
label_server.set_alignment(1, 0.5)
- label_server.modify_fg(gtk.STATE_NORMAL,
+ label_server.modify_fg(gtk.STATE_NORMAL,
style.COLOR_SELECTION_GREY.get_gdk_color())
box_server.pack_start(label_server, expand=False)
group.add_widget(label_server)
- label_server.show()
+ label_server.show()
self._entry = gtk.Entry()
self._entry.set_alignment(0)
- self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
style.COLOR_WHITE.get_gdk_color())
- self._entry.modify_base(gtk.STATE_INSENSITIVE,
- style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
self._entry.set_size_request(int(gtk.gdk.screen_width() / 3), -1)
box_server.pack_start(self._entry, expand=False)
- self._entry.show()
+ self._entry.show()
box_mesh.pack_start(box_server, expand=False)
box_server.show()
-
- self._jabber_alert = InlineAlert()
+
+ self._jabber_alert = InlineAlert()
label_jabber_error = gtk.Label()
group.add_widget(label_jabber_error)
self._jabber_alert_box.pack_start(label_jabber_error, expand=False)
@@ -176,13 +180,13 @@ class Network(SectionView):
self.setup()
def setup(self):
- self._entry.set_text(self._model.get_jabber())
- try:
- radio_state = self._model.get_radio()
+ self._entry.set_text(self._model.get_jabber())
+ try:
+ radio_state = self._model.get_radio()
except self._model.ReadError, detail:
- self._radio_alert.props.msg = detail
+ self._radio_alert.props.msg = detail
self._radio_alert.show()
- else:
+ else:
self._button.set_active(radio_state)
self._jabber_valid = True
@@ -195,13 +199,13 @@ class Network(SectionView):
self._network_configuration_reset_handler = \
self._clear_history_button.connect( \
'clicked', self.__network_configuration_reset_cb)
-
+
def undo(self):
self._button.disconnect(self._radio_change_handler)
self._entry.disconnect(self._jabber_change_handler)
self._model.undo()
self._jabber_alert.hide()
- self._radio_alert.hide()
+ self._radio_alert.hide()
def _validate(self):
if self._jabber_valid and self._radio_valid:
@@ -209,7 +213,7 @@ class Network(SectionView):
else:
self.props.is_valid = False
- def __radio_toggled_cb(self, widget, data=None):
+ def __radio_toggled_cb(self, widget, data=None):
radio_state = widget.get_active()
try:
self._model.set_radio(radio_state)
@@ -217,18 +221,21 @@ class Network(SectionView):
self._radio_alert.props.msg = detail
self._radio_valid = False
else:
- self._radio_valid = True
+ self._radio_valid = True
+ if self._model.have_networks():
+ self._clear_history_button.set_sensitive(True)
self._validate()
return False
- def __jabber_changed_cb(self, widget, data=None):
+ def __jabber_changed_cb(self, widget, data=None):
if self._jabber_sid:
gobject.source_remove(self._jabber_sid)
- self._jabber_sid = gobject.timeout_add(_APPLY_TIMEOUT,
- self.__jabber_timeout_cb, widget)
-
- def __jabber_timeout_cb(self, widget):
+ self._jabber_sid = gobject.timeout_add(_APPLY_TIMEOUT,
+ self.__jabber_timeout_cb,
+ widget)
+
+ def __jabber_timeout_cb(self, widget):
self._jabber_sid = 0
if widget.get_text() == self._model.get_jabber:
return
@@ -240,11 +247,15 @@ class Network(SectionView):
self._jabber_alert.show()
self.restart_alerts.append('jabber')
else:
- self._jabber_valid = True
+ self._jabber_valid = True
self._jabber_alert.hide()
self._validate()
return False
def __network_configuration_reset_cb(self, widget):
+ # FIXME: takes effect immediately, not after CP is closed with
+ # confirmation button
self._model.clear_networks()
+ if not self._model.have_networks():
+ self._clear_history_button.set_sensitive(False)
diff --git a/extensions/cpsection/power/__init__.py b/extensions/cpsection/power/__init__.py
index 8b2e85f..35f7efd 100644
--- a/extensions/cpsection/power/__init__.py
+++ b/extensions/cpsection/power/__init__.py
@@ -20,4 +20,3 @@ CLASS = 'Power'
ICON = 'module-power'
TITLE = _('Power')
KEYWORDS = ['automatic', 'extreme', 'power', 'suspend', 'battery']
-
diff --git a/extensions/cpsection/power/model.py b/extensions/cpsection/power/model.py
index 48d05de..041e5cf 100644
--- a/extensions/cpsection/power/model.py
+++ b/extensions/cpsection/power/model.py
@@ -35,14 +35,17 @@ _logger = logging.getLogger('ControlPanel - Power')
class ReadError(Exception):
def __init__(self, value):
self.value = value
+
def __str__(self):
return repr(self.value)
+
def using_powerd():
# directory exists if powerd running, and it's recent
# enough to be controllable.
return os.access(POWERD_FLAG_DIR, os.W_OK)
+
def get_automatic_pm():
if using_powerd():
return not os.access(POWERD_INHIBIT_FLAG, os.R_OK)
@@ -51,9 +54,11 @@ def get_automatic_pm():
client = gconf.client_get_default()
return client.get_bool('/desktop/sugar/power/automatic')
+
def print_automatic_pm():
print ('off', 'on')[get_automatic_pm()]
+
def set_automatic_pm(enabled):
"""Automatic suspends on/off."""
@@ -74,42 +79,45 @@ def set_automatic_pm(enabled):
bus = dbus.SystemBus()
proxy = bus.get_object(OHM_SERVICE_NAME, OHM_SERVICE_PATH)
keystore = dbus.Interface(proxy, OHM_SERVICE_IFACE)
-
+
if enabled == 'on' or enabled == 1:
- keystore.SetKey("suspend.automatic_pm", 1)
+ keystore.SetKey('suspend.automatic_pm', 1)
enabled = True
elif enabled == 'off' or enabled == 0:
- keystore.SetKey("suspend.automatic_pm", 0)
+ keystore.SetKey('suspend.automatic_pm', 0)
enabled = False
else:
- raise ValueError(_("Error in automatic pm argument, use on/off."))
+ raise ValueError(_('Error in automatic pm argument, use on/off.'))
client = gconf.client_get_default()
client.set_bool('/desktop/sugar/power/automatic', enabled)
return
+
def get_extreme_pm():
client = gconf.client_get_default()
return client.get_bool('/desktop/sugar/power/extreme')
+
def print_extreme_pm():
print ('off', 'on')[get_extreme_pm()]
+
def set_extreme_pm(enabled):
"""Extreme power management on/off."""
-
+
bus = dbus.SystemBus()
proxy = bus.get_object(OHM_SERVICE_NAME, OHM_SERVICE_PATH)
keystore = dbus.Interface(proxy, OHM_SERVICE_IFACE)
-
+
if enabled == 'on' or enabled == 1:
- keystore.SetKey("suspend.extreme_pm", 1)
+ keystore.SetKey('suspend.extreme_pm', 1)
enabled = True
elif enabled == 'off' or enabled == 0:
- keystore.SetKey("suspend.extreme_pm", 0)
+ keystore.SetKey('suspend.extreme_pm', 0)
enabled = False
else:
- raise ValueError(_("Error in extreme pm argument, use on/off."))
+ raise ValueError(_('Error in extreme pm argument, use on/off.'))
client = gconf.client_get_default()
client.set_bool('/desktop/sugar/power/extreme', enabled)
diff --git a/extensions/cpsection/power/view.py b/extensions/cpsection/power/view.py
index 8f1ed56..fd89efa 100644
--- a/extensions/cpsection/power/view.py
+++ b/extensions/cpsection/power/view.py
@@ -22,6 +22,7 @@ from sugar.graphics import style
from jarabe.controlpanel.sectionview import SectionView
from jarabe.controlpanel.inlinealert import InlineAlert
+
class Power(SectionView):
def __init__(self, model, alerts):
SectionView.__init__(self)
diff --git a/extensions/cpsection/updater/backends/aslo.py b/extensions/cpsection/updater/backends/aslo.py
index 5f257f9..6504e9e 100644
--- a/extensions/cpsection/updater/backends/aslo.py
+++ b/extensions/cpsection/updater/backends/aslo.py
@@ -15,7 +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
-'''Activity information microformat parser.
+"""Activity information microformat parser.
Activity information is embedded in HTML/XHTML/XML pages using a
Resource Description Framework (RDF) http://www.w3.org/RDF/ .
@@ -46,7 +46,7 @@ An example::
</RDF:Description>
</em:targetApplication>
</RDF:Description></RDF:RDF>
-'''
+"""
import logging
from xml.etree.ElementTree import XML
@@ -54,6 +54,9 @@ import traceback
import gio
+from sugar.bundle.bundleversion import NormalizedVersion
+from sugar.bundle.bundleversion import InvalidVersionError
+
from jarabe import config
_FIND_DESCRIPTION = \
@@ -127,17 +130,17 @@ class _UpdateFetcher(object):
size = None
else:
try:
- version = int(document.find(_FIND_VERSION).text)
- except ValueError:
- logging.error(traceback.format_exc())
- version = 0
+ version = NormalizedVersion(document.find(_FIND_VERSION).text)
+ except InvalidVersionError:
+ logging.exception('Exception occured while parsing version')
+ version = '0'
link = document.find(_FIND_LINK).text
try:
size = long(document.find(_FIND_SIZE).text) * 1024
except ValueError:
- logging.error(traceback.format_exc())
+ logging.exception('Exception occured while parsing size')
size = 0
global _fetcher
@@ -146,13 +149,13 @@ class _UpdateFetcher(object):
def fetch_update_info(bundle, completion_cb):
- '''Queries the server for a newer version of the ActivityBundle.
+ """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:
diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py
index 9845371..7ea445f 100755
--- a/extensions/cpsection/updater/model.py
+++ b/extensions/cpsection/updater/model.py
@@ -14,12 +14,12 @@
# 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.
+"""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
@@ -33,6 +33,7 @@ import gio
from sugar import env
from sugar.datastore import datastore
from sugar.bundle.activitybundle import ActivityBundle
+from sugar.bundle.bundleversion import NormalizedVersion
from jarabe.model import bundleregistry
@@ -64,14 +65,16 @@ class UpdateModel(gobject.GObject):
def check_updates(self):
self.updates = []
- self._bundles_to_check = \
- [bundle for bundle in bundleregistry.get_registry()]
+ self._bundles_to_check = list(bundleregistry.get_registry())
self._check_next_update()
def _check_next_update(self):
total = len(bundleregistry.get_registry())
current = total - len(self._bundles_to_check)
+ if not self._bundles_to_check:
+ return False
+
bundle = self._bundles_to_check.pop()
self.emit('progress', UpdateModel.ACTION_CHECKING, bundle.get_name(),
current, total)
@@ -83,7 +86,8 @@ class UpdateModel(gobject.GObject):
logging.error('Error getting update information from server:\n'
'%s' % error_message)
- if version is not None and version > bundle.get_activity_version():
+ if version is not None and \
+ version > NormalizedVersion(bundle.get_activity_version()):
self.updates.append(BundleUpdate(bundle, version, link, size))
if self._cancelling:
@@ -196,14 +200,17 @@ class UpdateModel(gobject.GObject):
logging.debug('UpdateModel._cancel_checking')
total = len(bundleregistry.get_registry())
current = total - len(self._bundles_to_check)
- self.emit('progress', UpdateModel.ACTION_CHECKING, '', current, current)
+ self.emit('progress', UpdateModel.ACTION_CHECKING, '', current,
+ current)
self._bundles_to_check = None
self._cancelling = False
def _cancel_updating(self):
logging.debug('UpdateModel._cancel_updating')
- current = self._total_bundles_to_update - len(self._bundles_to_update) - 1
- self.emit('progress', UpdateModel.ACTION_UPDATING, '', current, current)
+ current = (self._total_bundles_to_update -
+ len(self._bundles_to_update) - 1)
+ self.emit('progress', UpdateModel.ACTION_UPDATING, '', current,
+ current)
if self._downloader is not None:
self._downloader.cancel()
@@ -216,6 +223,7 @@ class UpdateModel(gobject.GObject):
self._bundles_to_update = None
self._cancelling = False
+
class BundleUpdate(object):
def __init__(self, bundle, version, link, size):
@@ -226,7 +234,7 @@ class BundleUpdate(object):
class _Downloader(gobject.GObject):
- _CHUNK_SIZE = 10240 # 10K
+ _CHUNK_SIZE = 10240 # 10K
__gsignals__ = {
'progress': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
@@ -262,7 +270,7 @@ class _Downloader(gobject.GObject):
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()
diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater/view.py
index 2164c0b..814658f 100644
--- a/extensions/cpsection/updater/view.py
+++ b/extensions/cpsection/updater/view.py
@@ -145,7 +145,7 @@ class ActivityUpdater(SectionView):
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:
@@ -161,7 +161,8 @@ class ActivityUpdater(SectionView):
self._model.check_updates()
def __install_button_clicked_cb(self, button):
- self._top_label.set_markup('<big>%s</big>' % _('Installing updates...'))
+ text = '<big>%s</big>' % _('Installing updates...')
+ self._top_label.set_markup(text)
self._model.update(self._update_box.get_bundles_to_update())
def __cancel_button_clicked_cb(self, button):
@@ -176,12 +177,13 @@ class ActivityUpdater(SectionView):
self._top_label.set_markup('<big>%s</big>' % top_message)
self._clear_center()
- def undo(self):
+ def undo(self):
self._model.cancel()
+
class ProgressPane(gtk.VBox):
- '''Container which replaces the `ActivityPane` during refresh or
- install.'''
+ """Container which replaces the `ActivityPane` during refresh or
+ install."""
def __init__(self):
gtk.VBox.__init__(self)
@@ -359,7 +361,7 @@ class UpdateListModel(gtk.ListStore):
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 = _('From version %(current)s to %(new)s (Size: %(size)s)')
details = details % \
{'current': bundle_update.bundle.get_activity_version(),
'new': bundle_update.version,
@@ -374,9 +376,7 @@ class UpdateListModel(gtk.ListStore):
def _format_size(size):
- '''
- Convert a given size in bytes to a nicer better readable unit
- '''
+ """Convert a given size in bytes to a nicer better readable unit"""
if size == 0:
# TRANS: download size is 0
return _('None')
@@ -385,7 +385,7 @@ def _format_size(size):
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)
+ return locale.format_string(_('%.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)
+ return locale.format_string(_('%.1f MB'), size / 1024.0 / 1024)
diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am
index 8a2e765..118d866 100644
--- a/extensions/deviceicon/Makefile.am
+++ b/extensions/deviceicon/Makefile.am
@@ -5,4 +5,5 @@ sugar_PYTHON = \
battery.py \
network.py \
speaker.py \
+ touchpad.py \
volume.py
diff --git a/extensions/deviceicon/battery.py b/extensions/deviceicon/battery.py
index edfcce4..4c1ef37 100644
--- a/extensions/deviceicon/battery.py
+++ b/extensions/deviceicon/battery.py
@@ -16,8 +16,9 @@
import logging
from gettext import gettext as _
-import gconf
+import sys
+import gconf
import gobject
import gtk
import dbus
@@ -30,6 +31,7 @@ from sugar.graphics.xocolor import XoColor
from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
_ICON_NAME = 'battery'
_STATUS_CHARGING = 0
@@ -37,35 +39,38 @@ _STATUS_DISCHARGING = 1
_STATUS_FULLY_CHARGED = 2
_STATUS_NOT_PRESENT = 3
-_LEVEL_PROP = 'battery.charge_level.percentage'
-_CHARGING_PROP = 'battery.rechargeable.is_charging'
-_DISCHARGING_PROP = 'battery.rechargeable.is_discharging'
-_PRESENT_PROP = 'battery.present'
+_UP_DEVICE_IFACE = 'org.freedesktop.UPower.Device'
+
+_UP_TYPE_BATTERY = 2
+
+_UP_STATE_UNKNOWN = 0
+_UP_STATE_CHARGING = 1
+_UP_STATE_DISCHARGING = 2
+_UP_STATE_EMPTY = 3
+_UP_STATE_FULL = 4
+_UP_STATE_CHARGE_PENDING = 5
+_UP_STATE_DISCHARGE_PENDING = 6
+
+_WARN_MIN_PERCENTAGE = 15
+
class DeviceView(TrayIcon):
FRAME_POSITION_RELATIVE = 102
- def __init__(self, udi):
- client = gconf.client_get_default()
+ def __init__(self, battery):
+ client = gconf.client_get_default()
self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
self.set_palette_invoker(FrameWidgetInvoker(self))
- self._model = DeviceModel(udi)
+ self._model = DeviceModel(battery)
self.palette = BatteryPalette(_('My Battery'))
self.palette.set_group_id('frame')
-
- self._model.connect('notify::level',
- self._battery_status_changed_cb)
- self._model.connect('notify::charging',
- self._battery_status_changed_cb)
- self._model.connect('notify::discharging',
- self._battery_status_changed_cb)
- self._model.connect('notify::present',
- self._battery_status_changed_cb)
+ self._model.connect('updated',
+ self.__battery_status_changed_cb)
self._update_info()
def _update_info(self):
@@ -86,27 +91,30 @@ class DeviceView(TrayIcon):
style.COLOR_WHITE.get_svg()))
elif self._model.props.discharging:
status = _STATUS_DISCHARGING
- if current_level <= 15:
+ if current_level <= _WARN_MIN_PERCENTAGE:
badge_name = 'emblem-warning'
else:
status = _STATUS_FULLY_CHARGED
- self.icon.props.icon_name = get_icon_state(name, current_level, step=-5)
+ self.icon.props.icon_name = get_icon_state(name, current_level,
+ step=-5)
self.icon.props.xo_color = xo_color
self.icon.props.badge_name = badge_name
- self.palette.set_level(current_level)
- self.palette.set_status(status)
+ self.palette.set_info(current_level, self._model.props.time_remaining,
+ status)
- def _battery_status_changed_cb(self, pspec, param):
+ def __battery_status_changed_cb(self, model):
self._update_info()
+
class BatteryPalette(Palette):
def __init__(self, primary_text):
Palette.__init__(self, primary_text)
-
self._level = 0
+ self._time = 0
+ self._status = _STATUS_NOT_PRESENT
self._progress_bar = gtk.ProgressBar()
self._progress_bar.set_size_request(
style.zoom(style.GRID_CELL_SIZE * 4), -1)
@@ -122,130 +130,128 @@ class BatteryPalette(Palette):
self._progress_widget = vbox
self.set_content(self._progress_widget)
- def set_level(self, percent):
- self._level = percent
- fraction = percent / 100.0
- self._progress_bar.set_fraction(fraction)
+ def set_info(self, percentage, seconds, status):
+ self._level = percentage
+ self._time = seconds
+ self._status = status
+ self._progress_bar.set_fraction(percentage / 100.0)
+ self._update_secondary()
- def set_status(self, status):
- current_level = self._level
+ def _update_secondary(self):
secondary_text = ''
- status_text = '%s%%' % current_level
+ status_text = '%s%%' % (self._level, )
progress_widget = self._progress_widget
- if status == _STATUS_NOT_PRESENT:
+ if self._status == _STATUS_NOT_PRESENT:
secondary_text = _('Removed')
progress_widget = None
- elif status == _STATUS_CHARGING:
+ elif self._status == _STATUS_CHARGING:
secondary_text = _('Charging')
- elif status == _STATUS_DISCHARGING:
- if current_level <= 15:
+ elif self._status == _STATUS_DISCHARGING:
+ if self._level <= _WARN_MIN_PERCENTAGE:
secondary_text = _('Very little power remaining')
else:
- #TODO: make this less of an wild/educated guess
- minutes_remaining = int(current_level / 0.59)
- remaining_hourpart = minutes_remaining / 60
+ minutes_remaining = self._time // 60
+ remaining_hourpart = minutes_remaining // 60
remaining_minpart = minutes_remaining % 60
secondary_text = _('%(hour)d:%(min).2d remaining') % \
{'hour': remaining_hourpart, 'min': remaining_minpart}
else:
secondary_text = _('Charged')
+
self.set_content(progress_widget)
self.props.secondary_text = secondary_text
self._status_label.set_text(status_text)
+
class DeviceModel(gobject.GObject):
__gproperties__ = {
- 'level' : (int, None, None, 0, 100, 0,
- gobject.PARAM_READABLE),
- 'charging' : (bool, None, None, False,
- gobject.PARAM_READABLE),
- 'discharging' : (bool, None, None, False,
- gobject.PARAM_READABLE),
- 'present' : (bool, None, None, False,
- gobject.PARAM_READABLE)
+ 'level': (int, None, None, 0, 100, 0, gobject.PARAM_READABLE),
+ 'time-remaining': (int, None, None, 0, sys.maxint, 0,
+ gobject.PARAM_READABLE), # unit: seconds
+ 'charging': (bool, None, None, False, gobject.PARAM_READABLE),
+ 'discharging': (bool, None, None, False, gobject.PARAM_READABLE),
+ 'present': (bool, None, None, False, gobject.PARAM_READABLE),
}
- def __init__(self, udi):
- gobject.GObject.__init__(self)
-
- bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
- proxy = bus.get_object('org.freedesktop.Hal', udi,
- follow_name_owner_changes=True)
- self._battery = dbus.Interface(proxy, 'org.freedesktop.Hal.Device')
- bus.add_signal_receiver(self._battery_changed,
- 'PropertyModified',
- 'org.freedesktop.Hal.Device',
- 'org.freedesktop.Hal',
- udi)
-
- self._level = self._get_level()
- self._charging = self._get_charging()
- self._discharging = self._get_discharging()
- self._present = self._get_present()
-
- def _get_level(self):
- try:
- return self._battery.GetProperty(_LEVEL_PROP)
- except dbus.DBusException:
- 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)
- return False
+ __gsignals__ = {
+ 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ }
- def _get_discharging(self):
+ def __init__(self, battery):
+ gobject.GObject.__init__(self)
+ self._battery = battery
+ self._battery_props_iface = dbus.Interface(self._battery,
+ dbus.PROPERTIES_IFACE)
+ self._battery.connect_to_signal('Changed',
+ self.__battery_properties_changed_cb,
+ dbus_interface=_UP_DEVICE_IFACE)
+ self._fetch_properties_from_upower()
+
+ def _fetch_properties_from_upower(self):
+ """Get current values from UPower."""
+ # pylint: disable=W0201
try:
- return self._battery.GetProperty(_DISCHARGING_PROP)
+ dbus_props = self._battery_props_iface.GetAll(_UP_DEVICE_IFACE)
except dbus.DBusException:
- logging.error('Cannot access %s', _DISCHARGING_PROP)
- return False
+ logging.error('Cannot access battery properties')
+ dbus_props = {}
- def _get_present(self):
- try:
- return self._battery.GetProperty(_PRESENT_PROP)
- except dbus.DBusException:
- logging.error('Cannot access %s', _PRESENT_PROP)
- return False
+ self._level = dbus_props.get('Percentage', 0)
+ self._state = dbus_props.get('State', _UP_STATE_UNKNOWN)
+ self._present = dbus_props.get('IsPresent', False)
+ self._time_to_empty = dbus_props.get('TimeToEmpty', 0)
+ self._time_to_full = dbus_props.get('TimeToFull', 0)
def do_get_property(self, pspec):
+ """Return current value of given GObject property."""
if pspec.name == 'level':
- return self._level
+ return self._level
if pspec.name == 'charging':
- return self._charging
+ return self._state == _UP_STATE_CHARGING
if pspec.name == 'discharging':
- return self._discharging
+ return self._state == _UP_STATE_DISCHARGING
if pspec.name == 'present':
return self._present
+ if pspec.name == 'time-remaining':
+ if self._state == _UP_STATE_CHARGING:
+ return self._time_to_full
+ if self._state == _UP_STATE_DISCHARGING:
+ return self._time_to_empty
+ return 0
def get_type(self):
return 'battery'
- def _battery_changed(self, num_changes, changes_list):
- for change in changes_list:
- if change[0] == _LEVEL_PROP:
- self._level = self._get_level()
- self.notify('level')
- elif change[0] == _CHARGING_PROP:
- self._charging = self._get_charging()
- self.notify('charging')
- elif change[0] == _DISCHARGING_PROP:
- self._discharging = self._get_discharging()
- self.notify('discharging')
- elif change[0] == _PRESENT_PROP:
- self._present = self._get_present()
- self.notify('present')
+ def __battery_properties_changed_cb(self):
+ old_level = self._level
+ old_state = self._state
+ old_present = self._present
+ old_time = self.props.time_remaining
+ self._fetch_properties_from_upower()
+ if self._level != old_level:
+ self.notify('level')
+ if self._state != old_state:
+ self.notify('charging')
+ self.notify('discharging')
+ if self._present != old_present:
+ self.notify('present')
+ if self.props.time_remaining != old_time:
+ self.notify('time-remaining')
+
+ self.emit('updated')
+
def setup(tray):
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
- proxy = bus.get_object('org.freedesktop.Hal',
- '/org/freedesktop/Hal/Manager')
- hal_manager = dbus.Interface(proxy, 'org.freedesktop.Hal.Manager')
-
- for udi in hal_manager.FindDeviceByCapability('battery'):
- tray.add_device(DeviceView(udi))
+ up_proxy = bus.get_object('org.freedesktop.UPower',
+ '/org/freedesktop/UPower')
+ upower = dbus.Interface(up_proxy, 'org.freedesktop.UPower')
+
+ for device_path in upower.EnumerateDevices():
+ device = bus.get_object('org.freedesktop.UPower', device_path)
+ device_prop_iface = dbus.Interface(device, dbus.PROPERTIES_IFACE)
+ device_type = device_prop_iface.Get(_UP_DEVICE_IFACE, 'Type')
+ if device_type == _UP_TYPE_BATTERY:
+ tray.add_device(DeviceView(device))
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
index 94a4293..4c4f339 100644
--- a/extensions/deviceicon/network.py
+++ b/extensions/deviceicon/network.py
@@ -23,10 +23,10 @@ import logging
import hashlib
import socket
import struct
-import re
import datetime
import time
import gtk
+import glib
import gobject
import gconf
import dbus
@@ -36,17 +36,17 @@ from sugar.graphics import style
from sugar.graphics.palette import Palette
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.tray import TrayIcon
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.icon import Icon
from sugar.graphics import xocolor
-from sugar.util import unique_id
from sugar import profile
from jarabe.model import network
-from jarabe.model.network import Settings
-from jarabe.model.network import IP4Config
from jarabe.frame.frameinvoker import FrameWidgetInvoker
from jarabe.view.pulsingicon import PulsingIcon
-IP_ADDRESS_TEXT_TEMPLATE = _("IP address: %s")
+
+IP_ADDRESS_TEXT_TEMPLATE = _('IP address: %s')
_NM_SERVICE = 'org.freedesktop.NetworkManager'
_NM_IFACE = 'org.freedesktop.NetworkManager'
@@ -63,26 +63,18 @@ _GSM_STATE_NOT_READY = 0
_GSM_STATE_DISCONNECTED = 1
_GSM_STATE_CONNECTING = 2
_GSM_STATE_CONNECTED = 3
-_GSM_STATE_NEED_AUTH = 4
+_GSM_STATE_FAILED = 4
-def frequency_to_channel(frequency):
- ftoc = { 2412: 1, 2417: 2, 2422: 3, 2427: 4,
- 2432: 5, 2437: 6, 2442: 7, 2447: 8,
- 2452: 9, 2457: 10, 2462: 11, 2467: 12,
- 2472: 13}
- return ftoc[frequency]
class WirelessPalette(Palette):
__gtype_name__ = 'SugarWirelessPalette'
__gsignals__ = {
- 'deactivate-connection' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
- 'create-connection' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
+ 'deactivate-connection': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([])),
}
- def __init__(self, primary_text, can_create=True):
+ def __init__(self, primary_text):
Palette.__init__(self, label=primary_text)
self._disconnect_item = None
@@ -109,16 +101,13 @@ class WirelessPalette(Palette):
self._info.pack_start(_padded(self._ip_address_label))
self._info.show_all()
- self._disconnect_item = gtk.MenuItem(_('Disconnect...'))
- self._disconnect_item.connect('activate', self.__disconnect_activate_cb)
+ self._disconnect_item = MenuItem(_('Disconnect...'))
+ icon = Icon(icon_size=gtk.ICON_SIZE_MENU, icon_name='media-eject')
+ self._disconnect_item.set_image(icon)
+ self._disconnect_item.connect('activate',
+ self.__disconnect_activate_cb)
self.menu.append(self._disconnect_item)
- if can_create:
- self._adhoc_item = gtk.MenuItem(_('Create new wireless network'))
- self._adhoc_item.connect('activate', self.__adhoc_activate_cb)
- self.menu.append(self._adhoc_item)
- self._adhoc_item.show()
-
def set_connecting(self):
self.props.secondary_text = _('Connecting...')
@@ -145,18 +134,12 @@ class WirelessPalette(Palette):
def __disconnect_activate_cb(self, menuitem):
self.emit('deactivate-connection')
- def __adhoc_activate_cb(self, menuitem):
- self.emit('create-connection')
-
def _set_frequency(self, frequency):
- try:
- channel = frequency_to_channel(frequency)
- except KeyError:
- channel = 0
+ channel = network.frequency_to_channel(frequency)
self._set_channel(channel)
def _set_channel(self, channel):
- self._channel_label.set_text("%s: %d" % (_("Channel"), channel))
+ self._channel_label.set_text('%s: %d' % (_('Channel'), channel))
def _set_ip_address(self, ip_address):
if ip_address is not None:
@@ -204,7 +187,7 @@ class WiredPalette(Palette):
def _inet_ntoa(self, iaddress):
address = ['%s' % ((iaddress >> i) % 256) for i in [0, 8, 16, 24]]
- return ".".join(address)
+ return '.'.join(address)
def _set_ip_address(self, ip_address):
if ip_address is not None:
@@ -214,14 +197,13 @@ class WiredPalette(Palette):
ip_address_text = ""
self._ip_address_label.set_text(ip_address_text)
+
class GsmPalette(Palette):
__gtype_name__ = 'SugarGsmPalette'
__gsignals__ = {
- 'gsm-connect' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
- 'gsm-disconnect' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
+ 'gsm-connect': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'gsm-disconnect': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self):
@@ -229,34 +211,53 @@ class GsmPalette(Palette):
Palette.__init__(self, label=_('Wireless modem'))
self._current_state = None
+ self._failed_connection = False
- self._toggle_state_item = gtk.MenuItem('')
+ self._toggle_state_item = MenuItem('')
self._toggle_state_item.connect('activate', self.__toggle_state_cb)
self.menu.append(self._toggle_state_item)
self._toggle_state_item.show()
- self.set_state(_GSM_STATE_NOT_READY)
-
self.info_box = gtk.VBox()
- self.data_label = gtk.Label()
- self.data_label.props.xalign = 0.0
- label_alignment = self._add_widget_with_padding(self.data_label)
- self.info_box.pack_start(label_alignment)
- self.data_label.show()
+ self.error_title_label = gtk.Label("")
+ self.error_title_label.set_alignment(0, 0.5)
+ self.error_title_label.set_line_wrap(True)
+ self.info_box.pack_start(self.error_title_label)
+ self.error_description_label = gtk.Label("")
+ self.error_description_label.set_alignment(0, 0.5)
+ self.error_description_label.set_line_wrap(True)
+ self.info_box.pack_start(self.error_description_label)
+
+ self.connection_info_box = gtk.HBox()
+ icon = Icon(icon_name='data-upload', icon_size=gtk.ICON_SIZE_MENU)
+ self.connection_info_box.pack_start(icon)
+ icon.show()
+
+ self._data_label_up = gtk.Label()
+ self._data_label_up.props.xalign = 0.0
+ label_alignment = self._add_widget_with_padding(self._data_label_up)
+ self.connection_info_box.pack_start(label_alignment)
+ self._data_label_up.show()
label_alignment.show()
- self.connection_time_label = gtk.Label()
- self.connection_time_label.props.xalign = 0.0
- label_alignment = self._add_widget_with_padding( \
- self.connection_time_label)
- self.info_box.pack_start(label_alignment)
- self.connection_time_label.show()
+ icon = Icon(icon_name='data-download', icon_size=gtk.ICON_SIZE_MENU)
+ self.connection_info_box.pack_start(icon)
+ icon.show()
+ self._data_label_down = gtk.Label()
+ self._data_label_down.props.xalign = 0.0
+ label_alignment = self._add_widget_with_padding(self._data_label_down)
+ self.connection_info_box.pack_start(label_alignment)
+ self._data_label_down.show()
label_alignment.show()
+ self.info_box.pack_start(self.connection_info_box)
+
self.info_box.show()
self.set_content(self.info_box)
+ self.set_state(_GSM_STATE_NOT_READY)
+
def _add_widget_with_padding(self, child, xalign=0, yalign=0.5):
alignment = gtk.Alignment(xalign=xalign, yalign=yalign,
xscale=1, yscale=0.33)
@@ -267,31 +268,41 @@ class GsmPalette(Palette):
alignment.add(child)
return alignment
- def set_state(self, state):
+ def update_state(self, state, reason=0):
self._current_state = state
- self._update_label_and_text()
+ self._update_label_and_text(reason)
- def _update_label_and_text(self):
+ def _update_label_and_text(self, reason=0):
if self._current_state == _GSM_STATE_NOT_READY:
self._toggle_state_item.get_child().set_label('...')
self.props.secondary_text = _('Please wait...')
elif self._current_state == _GSM_STATE_DISCONNECTED:
- self._toggle_state_item.get_child().set_label(_('Connect'))
+ if not self._failed_connection:
+ self._toggle_state_item.get_child().set_label(_('Connect'))
self.props.secondary_text = _('Disconnected')
+ icon = Icon(icon_name='dialog-ok', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
elif self._current_state == _GSM_STATE_CONNECTING:
self._toggle_state_item.get_child().set_label(_('Cancel'))
self.props.secondary_text = _('Connecting...')
+ icon = Icon(icon_name='dialog-cancel', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
elif self._current_state == _GSM_STATE_CONNECTED:
+ self._failed_connection = False
self._toggle_state_item.get_child().set_label(_('Disconnect'))
- self.props.secondary_text = _('Connected')
-
- elif self._current_state == _GSM_STATE_NEED_AUTH:
- self._toggle_state_item.get_child().set_label(_('Sim requires Pin/Puk'))
- self.props.secondary_text = _('Authentication Error')
-
+ self.update_connection_time()
+ icon = Icon(icon_name='media-eject', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
+
+ elif self._current_state == _GSM_STATE_FAILED:
+ message_error = self._get_error_by_nm_reason(reason)
+ self.add_alert(message_error[0], message_error[1])
else:
raise ValueError('Invalid GSM state while updating label and ' \
'text, %s' % str(self._current_state))
@@ -300,21 +311,64 @@ class GsmPalette(Palette):
if self._current_state == _GSM_STATE_NOT_READY:
pass
elif self._current_state == _GSM_STATE_DISCONNECTED:
+ self.error_title_label.hide()
+ self.error_description_label.hide()
self.emit('gsm-connect')
elif self._current_state == _GSM_STATE_CONNECTING:
self.emit('gsm-disconnect')
elif self._current_state == _GSM_STATE_CONNECTED:
self.emit('gsm-disconnect')
- elif self._current_state == _GSM_STATE_NEED_AUTH:
- self.emit('gsm-disconnect')
else:
raise ValueError('Invalid GSM state while emitting signal, %s' % \
str(self._current_state))
+ def add_alert(self, error, suggestion):
+ self._failed_connection = True
+ action = _('Try connection again')
+ self._toggle_state_item.get_child().set_label(action)
+
+ title = _('Error: %s') % error
+ self.error_title_label.set_markup('<b>%s</b>' % title)
+ self.error_title_label.show()
+
+ message = _('Suggestion: %s') % suggestion
+ self.error_description_label.set_text(message)
+ self.error_description_label.show()
+
+ def update_connection_time(self, connection_time=None):
+ if connection_time is not None:
+ self.props.secondary_text = _('Connected for %s') % \
+ connection_time.strftime('%H:%M:%S')
+ else:
+ self.props.secondary_text = _('Connected for %s') % '00:00:00'
+
+ def update_stats(self, in_bytes, out_bytes):
+ in_KBytes = in_bytes / 1024
+ out_KBytes = out_bytes / 1024
+ self._data_label_up.set_text(_('%d KB') % (out_KBytes))
+ self._data_label_down.set_text(_('%d KB') % (in_KBytes))
+
+ def _get_error_by_nm_reason(self, reason):
+ if reason in [network.NM_DEVICE_STATE_REASON_NO_SECRETS,
+ network.NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED]:
+ message = _('Check your Pin/Puk configuration.')
+ elif reason in [network.NM_DEVICE_STATE_REASON_PPP_DISCONNECT,
+ network.NM_DEVICE_STATE_REASON_PPP_FAILED]:
+ message = _('Check your Access Point Name ' \
+ '(APN) configuration')
+ elif reason in [network.NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER,
+ network.NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT]:
+ message = _('Check the Number configuration.')
+ elif reason == network.NM_DEVICE_STATE_REASON_CONFIG_FAILED:
+ message = _('Check your configuration.')
+ else:
+ message = ''
+ message_tuple = (network.get_error_by_reason(reason), message)
+ return message_tuple
+
class WirelessDeviceView(ToolButton):
- _ICON_NAME = 'network-wireless'
FRAME_POSITION_RELATIVE = 302
def __init__(self, device):
@@ -333,9 +387,9 @@ class WirelessDeviceView(ToolButton):
self._active_ap_op = None
self._icon = PulsingIcon()
- self._icon.props.icon_name = get_icon_state(self._ICON_NAME, 0)
- self._inactive_color = xocolor.XoColor( \
- "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
+ self._icon.props.icon_name = get_icon_state('network-wireless', 0)
+ self._inactive_color = xocolor.XoColor(
+ '%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
self._icon.props.pulse_color = self._inactive_color
self._icon.props.base_color = self._inactive_color
@@ -347,14 +401,12 @@ class WirelessDeviceView(ToolButton):
self._palette = WirelessPalette(self._name)
self._palette.connect('deactivate-connection',
self.__deactivate_connection_cb)
- self._palette.connect('create-connection',
- self.__create_connection_cb)
self.set_palette(self._palette)
self._palette.set_group_id('frame')
- self._device_props = dbus.Interface(self._device,
- 'org.freedesktop.DBus.Properties')
- self._device_props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
+ self._device_props = dbus.Interface(self._device,
+ dbus.PROPERTIES_IFACE)
+ self._device_props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
reply_handler=self.__get_device_props_reply_cb,
error_handler=self.__get_device_props_error_cb)
@@ -394,7 +446,7 @@ class WirelessDeviceView(ToolButton):
return
self._active_ap_op = active_ap_op
active_ap = self._bus.get_object(_NM_SERVICE, active_ap_op)
- props = dbus.Interface(active_ap, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(active_ap, dbus.PROPERTIES_IFACE)
props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True,
reply_handler=self.__get_all_ap_props_reply_cb,
@@ -418,11 +470,6 @@ class WirelessDeviceView(ToolButton):
def __ap_properties_changed_cb(self, properties):
self._update_properties(properties)
- def _name_encodes_colors(self):
- """Match #XXXXXX,#YYYYYY at the end of the network name"""
- return self._name[-7] == '#' and self._name[-8] == ',' \
- and self._name[-15] == '#'
-
def _update_properties(self, properties):
if 'Mode' in properties:
self._mode = properties['Mode']
@@ -438,11 +485,9 @@ class WirelessDeviceView(ToolButton):
self._frequency = properties['Frequency']
if self._color == None:
- if self._mode == network.NM_802_11_MODE_ADHOC \
- and self._name_encodes_colors():
- 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_ADHOC and \
+ network.is_sugar_adhoc_network(self._name):
+ self._color = profile.get_color()
else:
sha_hash = hashlib.sha1()
data = self._name + hex(self._flags)
@@ -450,7 +495,7 @@ class WirelessDeviceView(ToolButton):
digest = hash(sha_hash.digest())
index = digest % len(xocolor.colors)
- self._color = xocolor.XoColor('%s,%s' %
+ self._color = xocolor.XoColor('%s,%s' %
(xocolor.colors[index][0],
xocolor.colors[index][1]))
self._update()
@@ -463,11 +508,11 @@ class WirelessDeviceView(ToolButton):
def _update(self):
if self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:
- self._icon.props.badge_name = "emblem-locked"
+ self._icon.props.badge_name = 'emblem-locked'
else:
self._icon.props.badge_name = None
- self._palette.props.primary_text = self._name
+ self._palette.props.primary_text = glib.markup_escape_text(self._name)
self._update_state()
self._update_color()
@@ -478,14 +523,24 @@ class WirelessDeviceView(ToolButton):
else:
state = network.DEVICE_STATE_UNKNOWN
- if state == network.DEVICE_STATE_ACTIVATED:
- icon_name = '%s-connected' % self._ICON_NAME
- else:
- icon_name = self._ICON_NAME
+ if self._mode != network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._name) == False:
+ if state == network.DEVICE_STATE_ACTIVATED:
+ icon_name = '%s-connected' % 'network-wireless'
+ else:
+ icon_name = 'network-wireless'
- icon_name = get_icon_state(icon_name, self._strength)
- if icon_name:
- self._icon.props.icon_name = icon_name
+ icon_name = get_icon_state(icon_name, self._strength)
+ if icon_name:
+ self._icon.props.icon_name = icon_name
+ else:
+ channel = network.frequency_to_channel(self._frequency)
+ if state == network.DEVICE_STATE_ACTIVATED:
+ self._icon.props.icon_name = 'network-adhoc-%s-connected' \
+ % channel
+ else:
+ self._icon.props.icon_name = 'network-adhoc-%s' % channel
+ self._icon.props.base_color = profile.get_color()
if state == network.DEVICE_STATE_PREPARE or \
state == network.DEVICE_STATE_CONFIG or \
@@ -495,7 +550,8 @@ class WirelessDeviceView(ToolButton):
self._icon.props.pulsing = True
elif state == network.DEVICE_STATE_ACTIVATED:
address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
- self._palette.set_connected_with_frequency(self._frequency, address)
+ self._palette.set_connected_with_frequency(self._frequency,
+ address)
self._icon.props.pulsing = False
else:
self._icon.props.badge_name = None
@@ -508,69 +564,12 @@ class WirelessDeviceView(ToolButton):
self._icon.props.base_color = self._color
def __deactivate_connection_cb(self, palette, data=None):
- if self._active_ap_op is not None:
- obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
- netmgr = dbus.Interface(obj, _NM_IFACE)
- netmgr_props = dbus.Interface(
- netmgr, 'org.freedesktop.DBus.Properties')
- active_connections_o = netmgr_props.Get(_NM_IFACE,
- 'ActiveConnections')
-
- for conn_o in active_connections_o:
- obj = self._bus.get_object(_NM_IFACE, conn_o)
- props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
- ap_op = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
- if ap_op == self._active_ap_op:
- netmgr.DeactivateConnection(conn_o)
- break
-
- def __create_connection_cb(self, palette, data=None):
- """Create an 802.11 IBSS network.
-
- The user's color is encoded at the end of the network name. The network
- name is truncated so that it does not exceed the 32 byte SSID limit.
- """
- client = gconf.client_get_default()
- nick = client.get_string('/desktop/sugar/user/nick').decode('utf-8')
- color = client.get_string('/desktop/sugar/user/color')
- color_suffix = ' %s' % color
-
- format = _('%s\'s network').encode('utf-8')
- extra_length = (len(format) - len('%s')) + len(color_suffix)
- name_limit = 32 - extra_length
-
- # truncate the nick and use a regex to drop any partial characters
- # at the end
- nick = nick.encode('utf-8')[:name_limit]
- pattern = "([\xf6-\xf7][\x80-\xbf]{0,2}|[\xe0-\xef][\x80-\xbf]{0,1}|[\xc0-\xdf])$"
- nick = re.sub(pattern, '', nick)
-
- connection_name = format % nick
- connection_name += color_suffix
-
- connection = network.find_connection_by_ssid(connection_name)
- if connection is None:
- settings = Settings()
- settings.connection.id = 'Auto ' + connection_name
- uuid = settings.connection.uuid = unique_id()
- settings.connection.type = '802-11-wireless'
- settings.wireless.ssid = dbus.ByteArray(connection_name)
- settings.wireless.band = 'bg'
- settings.wireless.mode = 'adhoc'
- settings.ip4_config = IP4Config()
- settings.ip4_config.method = 'link-local'
-
- connection = network.add_connection(uuid, settings)
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection = network.find_connection_by_ssid(self._name)
+ if connection:
+ connection.disable_autoconnect()
- obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
- netmgr = dbus.Interface(obj, _NM_IFACE)
-
- netmgr.ActivateConnection(network.SETTINGS_SERVICE,
- connection.path,
- self._device.object_path,
- '/',
- reply_handler=self.__activate_reply_cb,
- error_handler=self.__activate_error_cb)
+ network.disconnect_access_points([self._active_ap_op])
def __activate_reply_cb(self, connection):
logging.debug('Network created: %s', connection)
@@ -583,7 +582,7 @@ class OlpcMeshDeviceView(ToolButton):
_ICON_NAME = 'network-mesh'
FRAME_POSITION_RELATIVE = 302
- def __init__(self, device):
+ def __init__(self, device, state):
ToolButton.__init__(self)
self._bus = dbus.SystemBus()
@@ -593,57 +592,41 @@ class OlpcMeshDeviceView(ToolButton):
self._channel = 0
self._icon = PulsingIcon(icon_name=self._ICON_NAME)
- self._icon.props.pulse_color = xocolor.XoColor( \
- "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
+ self._inactive_color = xocolor.XoColor(
+ '%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
- self._icon.props.base_color = profile.get_color()
+ self._icon.props.pulse_color = profile.get_color()
+ self._icon.props.base_color = self._inactive_color
self.set_icon_widget(self._icon)
self._icon.show()
self.set_palette_invoker(FrameWidgetInvoker(self))
- self._palette = WirelessPalette(_("Mesh Network"))
+ self._palette = WirelessPalette(_('Mesh Network'))
self._palette.connect('deactivate-connection',
self.__deactivate_connection)
self.set_palette(self._palette)
self._palette.set_group_id('frame')
+ self.update_state(state)
+
self._device_props = dbus.Interface(self._device,
- 'org.freedesktop.DBus.Properties')
- self._device_props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
- reply_handler=self.__get_device_props_reply_cb,
- error_handler=self.__get_device_props_error_cb)
+ dbus.PROPERTIES_IFACE)
self._device_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
reply_handler=self.__get_active_channel_reply_cb,
error_handler=self.__get_active_channel_error_cb)
- self._bus.add_signal_receiver(self.__state_changed_cb,
- signal_name='StateChanged',
- path=self._device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
signal_name='PropertiesChanged',
path=device.object_path,
dbus_interface=_NM_OLPC_MESH_IFACE)
def disconnect(self):
- self._bus.remove_signal_receiver(self.__state_changed_cb,
- signal_name='StateChanged',
- path=self._device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
signal_name='PropertiesChanged',
path=self._device.object_path,
dbus_interface=_NM_OLPC_MESH_IFACE)
- def __get_device_props_reply_cb(self, properties):
- if 'State' in properties:
- self._device_state = properties['State']
- self._update()
-
- def __get_device_props_error_cb(self, err):
- logging.error('Error getting the device properties: %s', err)
-
def __get_active_channel_reply_cb(self, channel):
self._channel = channel
self._update_text()
@@ -661,7 +644,8 @@ class OlpcMeshDeviceView(ToolButton):
self._update_text()
def _update_text(self):
- text = _("Mesh Network") + " " + str(self._channel)
+ channel = str(self._channel)
+ text = _('Mesh Network %s') % glib.markup_escape_text(channel)
self._palette.props.primary_text = text
def _update(self):
@@ -671,31 +655,39 @@ class OlpcMeshDeviceView(ToolButton):
network.DEVICE_STATE_CONFIG,
network.DEVICE_STATE_NEED_AUTH,
network.DEVICE_STATE_IP_CONFIG]:
+ self._icon.props.base_color = self._inactive_color
+ self._icon.props.pulse_color = profile.get_color()
self._palette.set_connecting()
self._icon.props.pulsing = True
elif state == network.DEVICE_STATE_ACTIVATED:
address = self._device_props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
self._palette.set_connected_with_channel(self._channel, address)
+ self._icon.props.base_color = profile.get_color()
self._icon.props.pulsing = False
+ self._update_text()
+
+ def update_state(self, state):
+ self._device_state = state
+ self._update()
def __deactivate_connection(self, palette, data=None):
obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
netmgr = dbus.Interface(obj, _NM_IFACE)
- netmgr_props = dbus.Interface(netmgr, 'org.freedesktop.DBus.Properties')
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
active_connections_o = netmgr_props.Get(_NM_IFACE,
'ActiveConnections')
for conn_o in active_connections_o:
# The connection path for a mesh connection is the device itself.
obj = self._bus.get_object(_NM_IFACE, conn_o)
- props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
ap_op = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
try:
obj = self._bus.get_object(_NM_IFACE, ap_op)
- props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
- type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
- if type == network.DEVICE_TYPE_802_11_OLPC_MESH:
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
+ if device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
netmgr.DeactivateConnection(conn_o)
break
except dbus.exceptions.DBusException:
@@ -747,6 +739,7 @@ class GsmDeviceView(TrayIcon):
signal_name='PppStats',
path=self._device.object_path,
dbus_interface=_NM_SERIAL_IFACE)
+
def create_palette(self):
palette = GsmPalette()
@@ -756,7 +749,7 @@ class GsmDeviceView(TrayIcon):
self._palette = palette
- props = dbus.Interface(self._device, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
reply_handler=self.__current_state_check_cb,
error_handler=self.__current_state_check_error_cb)
@@ -774,6 +767,10 @@ class GsmDeviceView(TrayIcon):
'/',
reply_handler=self.__connect_cb,
error_handler=self.__connect_error_cb)
+ else:
+ self._palette.add_alert(_('No GSM connection available.'), \
+ _('Create a connection in the ' \
+ 'control panel.'))
def __connect_cb(self, active_connection):
logging.debug('Connected successfully to gsm device, %s',
@@ -785,12 +782,12 @@ class GsmDeviceView(TrayIcon):
def __gsm_disconnect_cb(self, palette, data=None):
obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
netmgr = dbus.Interface(obj, _NM_IFACE)
- netmgr_props = dbus.Interface(netmgr, 'org.freedesktop.DBus.Properties')
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
active_connections_o = netmgr_props.Get(_NM_IFACE, 'ActiveConnections')
for conn_o in active_connections_o:
obj = self._bus.get_object(_NM_IFACE, conn_o)
- props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
devices = props.Get(_NM_ACTIVE_CONN_IFACE, 'Devices')
if self._device.object_path in devices:
netmgr.DeactivateConnection(
@@ -806,16 +803,17 @@ class GsmDeviceView(TrayIcon):
raise RuntimeError('Error when disconnecting gsm device, %s' % error)
def __state_changed_cb(self, new_state, old_state, reason):
- logging.debug('State: %s to %s, reason %s', old_state, new_state, reason)
- self._update_state(int(new_state))
+ logging.debug('State: %s to %s, reason %s', old_state,
+ new_state, reason)
+ self._update_state(int(new_state), int(old_state), int(reason))
def __current_state_check_cb(self, properties):
- self._update_state(int(properties['State']))
+ self._update_state(int(properties['State']), 0, 0)
def __current_state_check_error_cb(self, error):
raise RuntimeError('Error when checking gsm device state, %s' % error)
- def _update_state(self, state):
+ def _update_state(self, state, old_state, reason):
gsm_state = None
if state is network.DEVICE_STATE_ACTIVATED:
@@ -823,20 +821,22 @@ class GsmDeviceView(TrayIcon):
connection = network.find_gsm_connection()
if connection is not None:
connection.set_connected()
- self._connection_timestamp = time.time() - \
+ self._connection_timestamp = time.time() - \
connection.get_settings().connection.timestamp
self._connection_time_handler = gobject.timeout_add_seconds( \
1, self.__connection_timecount_cb)
- self._update_stats(0, 0)
- self._update_connection_time()
- self._palette.info_box.show()
+ self._palette.update_connection_time()
+ self._palette.update_stats(0, 0)
+ if self._palette is not None:
+ self._palette.connection_info_box.show()
elif state is network.DEVICE_STATE_DISCONNECTED:
gsm_state = _GSM_STATE_DISCONNECTED
self._connection_timestamp = 0
if self._connection_time_handler is not None:
gobject.source_remove(self._connection_time_handler)
- self._palette.info_box.hide()
+ if self._palette is not None:
+ self._palette.connection_info_box.hide()
elif state in [network.DEVICE_STATE_UNMANAGED,
network.DEVICE_STATE_UNAVAILABLE,
@@ -845,14 +845,15 @@ class GsmDeviceView(TrayIcon):
elif state in [network.DEVICE_STATE_PREPARE,
network.DEVICE_STATE_CONFIG,
- network.DEVICE_STATE_IP_CONFIG]:
+ network.DEVICE_STATE_IP_CONFIG,
+ network.DEVICE_STATE_NEED_AUTH]:
gsm_state = _GSM_STATE_CONNECTING
-
- elif state in [network.DEVICE_STATE_NEED_AUTH]:
- gsm_state = _GSM_STATE_NEED_AUTH
-
+
+ elif state == network.DEVICE_STATE_FAILED:
+ gsm_state = _GSM_STATE_FAILED
+
if self._palette is not None:
- self._palette.set_state(gsm_state)
+ self._palette.update_state(gsm_state, reason)
def disconnect(self):
self._bus.remove_signal_receiver(self.__state_changed_cb,
@@ -861,38 +862,22 @@ class GsmDeviceView(TrayIcon):
dbus_interface=_NM_DEVICE_IFACE)
def __ppp_stats_changed_cb(self, in_bytes, out_bytes):
- self._update_stats(in_bytes, out_bytes)
-
- def _update_stats(self, in_bytes, out_bytes):
- in_KBytes = in_bytes / 1024
- out_KBytes = out_bytes / 1024
- text = _("Data sent %d KB / received %d KB") % (out_KBytes, in_KBytes)
- self._palette.data_label.set_text(text)
+ self._palette.update_stats(in_bytes, out_bytes)
def __connection_timecount_cb(self):
self._connection_timestamp = self._connection_timestamp + 1
- self._update_connection_time()
+ connection_time = \
+ datetime.datetime.fromtimestamp(self._connection_timestamp)
+ self._palette.update_connection_time(connection_time)
return True
- def _update_connection_time(self):
- connection_time = datetime.datetime.fromtimestamp( \
- self._connection_timestamp)
- text = _("Connection time ") + connection_time.strftime('%H : %M : %S')
- self._palette.connection_time_label.set_text(text)
class WirelessDeviceObserver(object):
- def __init__(self, device, tray, device_type):
+ def __init__(self, device, tray):
self._device = device
self._device_view = None
self._tray = tray
-
- if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
- self._device_view = WirelessDeviceView(self._device)
- elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
- self._device_view = OlpcMeshDeviceView(self._device)
- else:
- raise ValueError('Unimplemented device type %d' % device_type)
-
+ self._device_view = WirelessDeviceView(self._device)
self._tray.add_device(self._device_view)
def disconnect(self):
@@ -902,6 +887,63 @@ class WirelessDeviceObserver(object):
self._device_view = None
+class MeshDeviceObserver(object):
+ def __init__(self, device, tray):
+ self._bus = dbus.SystemBus()
+ self._device = device
+ self._device_view = None
+ self._tray = tray
+
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
+ props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
+ reply_handler=self.__get_device_props_reply_cb,
+ error_handler=self.__get_device_props_error_cb)
+
+ self._bus.add_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+
+ def _remove_device_view(self):
+ self._device_view.disconnect()
+ self._tray.remove_device(self._device_view)
+ self._device_view = None
+
+ def disconnect(self):
+ if self._device_view is not None:
+ self._remove_device_view()
+
+ self._bus.remove_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+
+ def __get_device_props_reply_cb(self, properties):
+ if 'State' in properties:
+ self._update_state(properties['State'])
+
+ def __get_device_props_error_cb(self, err):
+ logging.error('Error getting the device properties: %s', err)
+
+ def __state_changed_cb(self, new_state, old_state, reason):
+ self._update_state(new_state)
+
+ def _update_state(self, state):
+ if state in (network.DEVICE_STATE_PREPARE, network.DEVICE_STATE_CONFIG,
+ network.DEVICE_STATE_NEED_AUTH,
+ network.DEVICE_STATE_IP_CONFIG,
+ network.DEVICE_STATE_ACTIVATED):
+ if self._device_view is not None:
+ self._device_view.update_state(state)
+ return
+
+ self._device_view = OlpcMeshDeviceView(self._device, state)
+ self._tray.add_device(self._device_view)
+ else:
+ if self._device_view is not None:
+ self._remove_device_view()
+
+
class WiredDeviceObserver(object):
def __init__(self, device, tray):
self._bus = dbus.SystemBus()
@@ -910,7 +952,7 @@ class WiredDeviceObserver(object):
self._device_view = None
self._tray = tray
- props = dbus.Interface(self._device, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
reply_handler=self.__get_device_props_reply_cb,
error_handler=self.__get_device_props_error_cb)
@@ -938,8 +980,7 @@ class WiredDeviceObserver(object):
def _update_state(self, state):
if state == network.DEVICE_STATE_ACTIVATED:
- props = dbus.Interface(self._device,
- 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
address = props.Get(_NM_DEVICE_IFACE, 'Ip4Address')
speed = props.Get(_NM_WIRED_IFACE, 'Speed')
self._device_view = WiredDeviceView(speed, address)
@@ -950,6 +991,7 @@ class WiredDeviceObserver(object):
del self._device_view
self._device_view = None
+
class GsmDeviceObserver(object):
def __init__(self, device, tray):
self._device = device
@@ -964,6 +1006,7 @@ class GsmDeviceObserver(object):
self._tray.remove_device(self._device_view)
self._device_view = None
+
class NetworkManagerObserver(object):
def __init__(self, tray):
self._bus = dbus.SystemBus()
@@ -997,15 +1040,17 @@ class NetworkManagerObserver(object):
def _check_device(self, device_op):
nm_device = self._bus.get_object(_NM_SERVICE, device_op)
- props = dbus.Interface(nm_device, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(nm_device, dbus.PROPERTIES_IFACE)
device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
if device_type == network.DEVICE_TYPE_802_3_ETHERNET:
device = WiredDeviceObserver(nm_device, self._tray)
self._devices[device_op] = device
- elif device_type in [network.DEVICE_TYPE_802_11_WIRELESS,
- network.DEVICE_TYPE_802_11_OLPC_MESH]:
- device = WirelessDeviceObserver(nm_device, self._tray, device_type)
+ elif device_type == network.DEVICE_TYPE_802_11_WIRELESS:
+ device = WirelessDeviceObserver(nm_device, self._tray)
+ self._devices[device_op] = device
+ elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
+ device = MeshDeviceObserver(nm_device, self._tray)
self._devices[device_op] = device
elif device_type == network.DEVICE_TYPE_GSM_MODEM:
device = GsmDeviceObserver(nm_device, self._tray)
@@ -1020,5 +1065,6 @@ class NetworkManagerObserver(object):
device.disconnect()
del self._devices[device_op]
+
def setup(tray):
device_observer = NetworkManagerObserver(tray)
diff --git a/extensions/deviceicon/speaker.py b/extensions/deviceicon/speaker.py
index 3a54464..d396bfb 100644
--- a/extensions/deviceicon/speaker.py
+++ b/extensions/deviceicon/speaker.py
@@ -32,12 +32,13 @@ from jarabe.model import sound
_ICON_NAME = 'speaker'
+
class DeviceView(TrayIcon):
FRAME_POSITION_RELATIVE = 103
def __init__(self):
- client = gconf.client_get_default()
+ client = gconf.client_get_default()
self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
@@ -70,22 +71,24 @@ class DeviceView(TrayIcon):
xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
style.COLOR_WHITE.get_svg()))
- self.icon.props.icon_name = get_icon_state(name, current_level, step=-1)
+ self.icon.props.icon_name = get_icon_state(name, current_level,
+ step=-1)
self.icon.props.xo_color = xo_color
def __button_release_event_cb(self, widget, event):
- if event.button == 1:
- self._model.props.muted = not self._model.props.muted
- return True
- else:
+ if event.button != 1:
return False
+ self.palette_invoker.notify_right_click()
+ return True
+
def __expose_event_cb(self, *args):
self._update_info()
def __speaker_status_changed_cb(self, pspec_, param_):
self._update_info()
+
class SpeakerPalette(Palette):
def __init__(self, primary_text, model):
@@ -167,10 +170,11 @@ class SpeakerPalette(Palette):
self._update_level()
self._update_muted()
+
class DeviceModel(gobject.GObject):
__gproperties__ = {
- 'level' : (int, None, None, 0, 100, 0, gobject.PARAM_READWRITE),
- 'muted' : (bool, None, None, False, gobject.PARAM_READWRITE),
+ 'level': (int, None, None, 0, 100, 0, gobject.PARAM_READWRITE),
+ 'muted': (bool, None, None, False, gobject.PARAM_READWRITE),
}
def __init__(self):
@@ -201,16 +205,17 @@ class DeviceModel(gobject.GObject):
return 'speaker'
def do_get_property(self, pspec):
- if pspec.name == "level":
+ if pspec.name == 'level':
return self._get_level()
- elif pspec.name == "muted":
+ elif pspec.name == 'muted':
return self._get_muted()
def do_set_property(self, pspec, value):
- if pspec.name == "level":
+ if pspec.name == 'level':
self._set_level(value)
- elif pspec.name == "muted":
+ elif pspec.name == 'muted':
self._set_muted(value)
+
def setup(tray):
tray.add_device(DeviceView())
diff --git a/extensions/deviceicon/touchpad.py b/extensions/deviceicon/touchpad.py
new file mode 100644
index 0000000..b3b34f5
--- /dev/null
+++ b/extensions/deviceicon/touchpad.py
@@ -0,0 +1,136 @@
+# Copyright (C) 2010, Walter Bender, 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
+
+
+from gettext import gettext as _
+import os
+
+import gtk
+import gconf
+
+import logging
+
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.palette import Palette
+from sugar.graphics import style
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
+TOUCHPAD_MODE_CAPACITIVE = 'capacitive'
+TOUCHPAD_MODE_RESISTIVE = 'resistive'
+TOUCHPAD_MODES = [TOUCHPAD_MODE_CAPACITIVE, TOUCHPAD_MODE_RESISTIVE]
+STATUS_TEXT = {
+ TOUCHPAD_MODE_CAPACITIVE: _('finger'),
+ TOUCHPAD_MODE_RESISTIVE: _('stylus'),
+}
+STATUS_ICON = {
+ TOUCHPAD_MODE_CAPACITIVE: 'touchpad-' + TOUCHPAD_MODE_CAPACITIVE,
+ TOUCHPAD_MODE_RESISTIVE: 'touchpad-' + TOUCHPAD_MODE_RESISTIVE,
+}
+# NODE_PATH is used to communicate with the touchpad device.
+NODE_PATH = '/sys/devices/platform/i8042/serio1/ptmode'
+
+
+class DeviceView(TrayIcon):
+ """ Manage the touchpad mode from the device palette on the Frame. """
+
+ FRAME_POSITION_RELATIVE = 500
+
+ def __init__(self):
+ """ Create the icon that represents the touchpad. """
+ icon_name = STATUS_ICON[_read_touchpad_mode()]
+
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+ TrayIcon.__init__(self, icon_name=icon_name, xo_color=color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ def create_palette(self):
+ """ Create a palette for this icon; called by the Sugar framework
+ when a palette needs to be displayed. """
+ self.palette = ResourcePalette(_('My touchpad'), self.icon)
+ self.palette.set_group_id('frame')
+ return self.palette
+
+ def __button_release_event_cb(self, widget, event):
+ """ Callback for button release event; used to invoke touchpad-mode
+ change. """
+ self.palette.toggle_mode()
+ return True
+
+
+class ResourcePalette(Palette):
+ """ Palette attached to the decive icon that represents the touchpas. """
+
+ def __init__(self, primary_text, icon):
+ """ Create the palette and initilize with current touchpad status. """
+ Palette.__init__(self, label=primary_text)
+
+ self._icon = icon
+
+ vbox = gtk.VBox()
+ self.set_content(vbox)
+
+ self._status_text = gtk.Label()
+ vbox.pack_start(self._status_text, padding=style.DEFAULT_PADDING)
+ self._status_text.show()
+
+ vbox.show()
+
+ self._mode = _read_touchpad_mode()
+ self._update()
+
+ def _update(self):
+ """ Update the label and icon based on the current mode. """
+ self._status_text.set_label(STATUS_TEXT[self._mode])
+ self._icon.props.icon_name = STATUS_ICON[self._mode]
+
+ def toggle_mode(self):
+ """ Toggle the touchpad mode. """
+ self._mode = TOUCHPAD_MODES[1 - TOUCHPAD_MODES.index(self._mode)]
+ _write_touchpad_mode(self._mode)
+ self._update()
+
+
+def setup(tray):
+ """ Initialize the devic icon; called by the shell when initializing the
+ Frame. """
+ if os.path.exists(NODE_PATH):
+ tray.add_device(DeviceView())
+ _write_touchpad_mode(TOUCHPAD_MODE_CAPACITIVE)
+
+
+def _read_touchpad_mode():
+ """ Read the touchpad mode from the node path. """
+ node_file_handle = open(NODE_PATH, 'r')
+ text = node_file_handle.read()
+ node_file_handle.close()
+
+ return TOUCHPAD_MODES[int(text[0])]
+
+
+def _write_touchpad_mode(touchpad):
+ """ Write the touchpad mode to the node path. """
+ try:
+ node_file_handle = open(NODE_PATH, 'w')
+ except IOError, e:
+ logging.error('Error opening %s for writing: %s', NODE_PATH, e)
+ return
+ node_file_handle.write(str(TOUCHPAD_MODES.index(touchpad)))
+ node_file_handle.close()
diff --git a/extensions/deviceicon/volume.py b/extensions/deviceicon/volume.py
index e7f62a2..ea2377d 100644
--- a/extensions/deviceicon/volume.py
+++ b/extensions/deviceicon/volume.py
@@ -28,8 +28,10 @@ from jarabe.journal import journalactivity
from jarabe.view.palettes import VolumePalette
from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
_icons = {}
+
class DeviceView(TrayIcon):
FRAME_POSITION_RELATIVE = 500
@@ -70,9 +72,11 @@ class DeviceView(TrayIcon):
journal.reveal()
return True
+
def setup(tray):
gobject.idle_add(_setup_volumes, tray)
+
def _setup_volumes(tray):
volume_monitor = gio.volume_monitor_get()
@@ -86,9 +90,11 @@ def _setup_volumes(tray):
volume_monitor.connect('mount-added', _mount_added_cb, tray)
volume_monitor.connect('mount-removed', _mount_removed_cb, tray)
+
def _volume_added_cb(volume_monitor, volume, tray):
_mount(volume, tray)
+
def _mount(volume, tray):
# Follow Nautilus behaviour here
# since it has the same issue with removable device
@@ -102,20 +108,23 @@ def _mount(volume, tray):
#TODO: pass None as mount_operation, or better, SugarMountOperation
volume.mount(gtk.MountOperation(tray.get_toplevel()), _mount_cb)
+
def _mount_cb(volume, result):
logging.debug('_mount_cb %r %r', volume, result)
volume.mount_finish(result)
+
def _mount_added_cb(volume_monitor, mount, tray):
_add_device(mount, tray)
+
def _mount_removed_cb(volume_monitor, mount, tray):
icon = _icons[mount]
tray.remove_device(icon)
del _icons[mount]
+
def _add_device(mount, tray):
icon = DeviceView(mount)
_icons[mount] = icon
tray.add_device(icon)
-
diff --git a/extensions/globalkey/screenshot.py b/extensions/globalkey/screenshot.py
index 8b4d4c2..0afe964 100644
--- a/extensions/globalkey/screenshot.py
+++ b/extensions/globalkey/screenshot.py
@@ -17,7 +17,6 @@
import os
import tempfile
-import time
from gettext import gettext as _
import gtk
@@ -31,6 +30,7 @@ from jarabe.model import shell
BOUND_KEYS = ['<alt>1', 'Print']
+
def handle_key_press(key):
tmp_dir = os.path.join(env.get_profile_path(), 'data')
fd, file_path = tempfile.mkstemp(dir=tmp_dir)
@@ -45,7 +45,7 @@ def handle_key_press(key):
height=height)
screenshot.get_from_drawable(window, window.get_colormap(), x_orig,
y_orig, 0, 0, width, height)
- screenshot.save(file_path, "png")
+ screenshot.save(file_path, 'png')
client = gconf.client_get_default()
color = client.get_string('/desktop/sugar/user/color')
@@ -87,14 +87,15 @@ def handle_key_press(key):
jobject.destroy()
del jobject
+
def _get_preview_data(screenshot):
preview = screenshot.scale_simple(style.zoom(300), style.zoom(225),
gtk.gdk.INTERP_BILINEAR)
preview_data = []
+
def save_func(buf, data):
data.append(buf)
preview.save_to_callback(save_func, 'png', user_data=preview_data)
return dbus.ByteArray(''.join(preview_data))
-
diff --git a/extensions/globalkey/viewsource.py b/extensions/globalkey/viewsource.py
index df3cd9e..96e7c6b 100644
--- a/extensions/globalkey/viewsource.py
+++ b/extensions/globalkey/viewsource.py
@@ -18,8 +18,10 @@
from jarabe.view.viewsource import setup_view_source
from jarabe.model import shell
+
BOUND_KEYS = ['0xEC', '<alt><shift>v']
+
def handle_key_press(key):
shell_model = shell.get_model()
activity = shell_model.get_active_activity()
diff --git a/po/Makevars b/po/Makevars
new file mode 100644
index 0000000..da4dba6
--- /dev/null
+++ b/po/Makevars
@@ -0,0 +1 @@
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=Q_ --keyword=g_dgettext:2 --keyword=g_dngettext:2,3 --keyword=g_dpgettext:2 --keyword=g_dpgettext2=2c,3 --keyword=pgettext:1c,2
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e9a7f1c..9e46831 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -28,6 +28,7 @@ extensions/cpsection/updater/view.py
extensions/deviceicon/battery.py
extensions/deviceicon/network.py
extensions/deviceicon/speaker.py
+extensions/deviceicon/touchpad.py
extensions/deviceicon/volume.py
extensions/globalkey/screenshot.py
data/sugar.schemas.in
@@ -41,6 +42,7 @@ src/jarabe/desktop/favoritesview.py
src/jarabe/desktop/homebox.py
src/jarabe/desktop/keydialog.py
src/jarabe/desktop/meshbox.py
+src/jarabe/desktop/networkviews.py
src/jarabe/desktop/schoolserver.py
src/jarabe/frame/activitiestray.py
src/jarabe/frame/clipboardmenu.py
@@ -52,15 +54,18 @@ src/jarabe/journal/detailview.py
src/jarabe/journal/expandedentry.py
src/jarabe/journal/journalactivity.py
src/jarabe/journal/journaltoolbox.py
+src/jarabe/journal/listmodel.py
src/jarabe/journal/listview.py
src/jarabe/journal/misc.py
src/jarabe/journal/modalalert.py
src/jarabe/journal/objectchooser.py
src/jarabe/journal/palettes.py
src/jarabe/journal/volumestoolbar.py
+src/jarabe/model/network.py
src/jarabe/view/buddymenu.py
src/jarabe/view/keyhandler.py
src/jarabe/view/launcher.py
src/jarabe/view/palettes.py
src/jarabe/view/viewsource.py
+src/jarabe/util/emulator.py
diff --git a/po/ar.po b/po/ar.po
index df5b323..d1fb825 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: sugar\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-30 00:31-0500\n"
-"PO-Revision-Date: 2010-02-07 17:12+0200\n"
+"PO-Revision-Date: 2011-01-24 00:25+0200\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n"
"Language: ar\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-"X-Generator: Pootle 2.0.1\n"
+"X-Generator: Pootle 2.0.3\n"
"Nplurals=6; Plural=N==0 ? 0: n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 "
": n%100>=11 && n%100<=99 ? 4 : 5;\n"
@@ -321,12 +321,12 @@ msgstr "برمجيات محدّثة"
#, python-format
msgid "You can install %s update"
msgid_plural "You can install %s updates"
-msgstr[0] "يمكنك تنزيل %s تحديث"
-msgstr[1] "يمكنك تنزيل تحديث واحد"
-msgstr[2] "يمكنك تنزيل تحديثين"
-msgstr[3] "يمكنك تنزيل %s تحديثات"
-msgstr[4] "يمكنك تنزيل %s تحديثا"
-msgstr[5] "يمكنك تنزيل %s تحديث"
+msgstr[0] "لا تحديثات لتثبيتها%.0s"
+msgstr[1] "يمكنك تثبيت تحديث واحد%.0s"
+msgstr[2] "يمكنك تثبيت تحديثين%.0s"
+msgstr[3] "يمكنك تثبيت %s تحديثات"
+msgstr[4] "يمكنك تثبيت %s تحديثا"
+msgstr[5] "يمكنك تثبيت %s تحديث"
#: ../extensions/cpsection/updater/view.py:159
msgid "Checking for updates..."
@@ -340,9 +340,9 @@ msgstr "يُثبّت التحديثات..."
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
-msgstr[0] "لم تُثبّت أي تحديثات"
-msgstr[1] "ثُبّت تحديث واحد"
-msgstr[2] "ثُبّت تحديثين"
+msgstr[0] "لم تُثبّت أي تحديثات%.0s"
+msgstr[1] "ثُبّت تحديث واحد%.0s"
+msgstr[2] "ثُبّت تحديثين%.0s"
msgstr[3] "ثُبّتت %s تحديثات"
msgstr[4] "ثُبّت %s تحديثا"
msgstr[5] "ثُبّت %s تحديث"
diff --git a/po/de.po b/po/de.po
index 5ca6d88..1624694 100644
--- a/po/de.po
+++ b/po/de.po
@@ -10,14 +10,42 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# This file is distributed under the same license as the PACKAGE package.
# Fabian Affolter <fab@fedoraproject.org>, 2007.
msgid ""
msgstr ""
"Project-Id-Version: sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-03-11 00:31-0500\n"
-"PO-Revision-Date: 2010-03-29 01:49+0200\n"
+"POT-Creation-Date: 2010-12-23 01:15-0500\n"
+"PO-Revision-Date: 2011-01-18 10:34+0200\n"
"Last-Translator: Markus <m.slg@gmx.de>\n"
"Language-Team: German <fedora-trans-de@redhat.com>\n"
"Language: de\n"
@@ -32,43 +60,39 @@ msgstr ""
msgid "About Me"
msgstr "Über mich"
-#: ../extensions/cpsection/aboutme/model.py:43
+#: ../extensions/cpsection/aboutme/model.py:48
msgid "You must enter a name."
msgstr "Bitte einen Namen eingeben."
-#: ../extensions/cpsection/aboutme/model.py:68
+#: ../extensions/cpsection/aboutme/model.py:75
#, python-format
msgid "stroke: color=%s hue=%s"
msgstr "Linie: Farbe=%s Farbton=%s"
-#: ../extensions/cpsection/aboutme/model.py:71
+#: ../extensions/cpsection/aboutme/model.py:78
#, python-format
msgid "stroke: %s"
msgstr "Linie: %s"
-#: ../extensions/cpsection/aboutme/model.py:73
+#: ../extensions/cpsection/aboutme/model.py:80
#, python-format
msgid "fill: color=%s hue=%s"
-msgstr "Füllung: Farbe=%s Farbton=%s"
+msgstr "Füllung: Farbe=%s Farbton=%s"
-#: ../extensions/cpsection/aboutme/model.py:75
+#: ../extensions/cpsection/aboutme/model.py:82
#, python-format
msgid "fill: %s"
-msgstr "Füllung: %s"
+msgstr "Füllung: %s"
-#: ../extensions/cpsection/aboutme/model.py:86
+#: ../extensions/cpsection/aboutme/model.py:94
msgid "Error in specified color modifiers."
msgstr "Fehler in den angegebenen Farbänderungen."
-#: ../extensions/cpsection/aboutme/model.py:89
+#: ../extensions/cpsection/aboutme/model.py:97
msgid "Error in specified colors."
msgstr "Fehler in den angegebenen Farben."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:93
-msgid "Name:"
-msgstr "Name:"
-
-#: ../extensions/cpsection/aboutme/view.py:128
+#: ../extensions/cpsection/aboutme/view.py:235
msgid "Click to change your color:"
msgstr "Klicken, um deine Farbe zu wechseln:"
@@ -76,43 +100,43 @@ msgstr "Klicken, um deine Farbe zu wechseln:"
msgid "About my Computer"
msgstr "Über meinen Computer"
-#: ../extensions/cpsection/aboutcomputer/model.py:28
+#: ../extensions/cpsection/aboutcomputer/model.py:29
msgid "Not available"
msgstr "Nicht verfügbar"
-#: ../extensions/cpsection/aboutcomputer/view.py:60
+#: ../extensions/cpsection/aboutcomputer/view.py:61
msgid "Identity"
msgstr "Identität"
-#: ../extensions/cpsection/aboutcomputer/view.py:69
+#: ../extensions/cpsection/aboutcomputer/view.py:70
msgid "Serial Number:"
msgstr "Seriennummer:"
-#: ../extensions/cpsection/aboutcomputer/view.py:91
+#: ../extensions/cpsection/aboutcomputer/view.py:92
msgid "Software"
msgstr "Software"
-#: ../extensions/cpsection/aboutcomputer/view.py:100
+#: ../extensions/cpsection/aboutcomputer/view.py:101
msgid "Build:"
msgstr "Version:"
-#: ../extensions/cpsection/aboutcomputer/view.py:115
+#: ../extensions/cpsection/aboutcomputer/view.py:116
msgid "Sugar:"
msgstr "Sugar:"
-#: ../extensions/cpsection/aboutcomputer/view.py:131
+#: ../extensions/cpsection/aboutcomputer/view.py:132
msgid "Firmware:"
msgstr "Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:146
+#: ../extensions/cpsection/aboutcomputer/view.py:147
msgid "Wireless Firmware:"
msgstr "WLAN-Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:169
+#: ../extensions/cpsection/aboutcomputer/view.py:170
msgid "Copyright and License"
msgstr "Copyright und Lizenz"
-#: ../extensions/cpsection/aboutcomputer/view.py:184
+#: ../extensions/cpsection/aboutcomputer/view.py:188
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 "
@@ -124,7 +148,7 @@ msgstr ""
"darin festgelegten Bedingungen ist es erlaubt, die Software zu verändern "
"und/oder Kopien davon zu erstellen und zu verteilen."
-#: ../extensions/cpsection/aboutcomputer/view.py:196
+#: ../extensions/cpsection/aboutcomputer/view.py:200
msgid "Full license:"
msgstr "Vollständige Lizenz:"
@@ -132,11 +156,11 @@ msgstr "Vollständige Lizenz:"
msgid "Date & Time"
msgstr "Datum & Uhrzeit"
-#: ../extensions/cpsection/datetime/model.py:87
+#: ../extensions/cpsection/datetime/model.py:92
msgid "Error timezone does not exist."
msgstr "Fehler: unbekannte Zeitzone."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:47
+#: ../extensions/cpsection/datetime/view.py:70 ../data/sugar.schemas.in.h:52
msgid "Timezone"
msgstr "Zeitzone"
@@ -145,51 +169,51 @@ msgstr "Zeitzone"
msgid "Frame"
msgstr "Rahmen"
-#: ../extensions/cpsection/frame/model.py:38
-#: ../extensions/cpsection/frame/model.py:60
+#: ../extensions/cpsection/frame/model.py:41
+#: ../extensions/cpsection/frame/model.py:66
msgid "Value must be an integer."
msgstr "Der Wert muss ganzzahlig sein."
-#: ../extensions/cpsection/frame/view.py:26
+#: ../extensions/cpsection/frame/view.py:27
msgid "never"
msgstr "nie"
# (Markus S.) ' unmittelbar'?
-#: ../extensions/cpsection/frame/view.py:27
+#: ../extensions/cpsection/frame/view.py:28
msgid "instantaneous"
msgstr "sofort"
-#: ../extensions/cpsection/frame/view.py:28
+#: ../extensions/cpsection/frame/view.py:29
#, python-format
msgid "%s seconds"
msgstr "%s Sekunden"
-#: ../extensions/cpsection/frame/view.py:52
+#: ../extensions/cpsection/frame/view.py:54
msgid "Activation Delay"
msgstr "Aktivierungsverzögerung"
-#: ../extensions/cpsection/frame/view.py:76
+#: ../extensions/cpsection/frame/view.py:78
msgid "Corner"
msgstr "Ecke"
-#: ../extensions/cpsection/frame/view.py:111
+#: ../extensions/cpsection/frame/view.py:113
msgid "Edge"
-msgstr "Kante"
+msgstr "Rand"
#: ../extensions/cpsection/keyboard/__init__.py:21
-#: ../extensions/cpsection/keyboard/view.py:31
+#: ../extensions/cpsection/keyboard/view.py:32
msgid "Keyboard"
msgstr "Tastatur"
-#: ../extensions/cpsection/keyboard/view.py:189
+#: ../extensions/cpsection/keyboard/view.py:190
msgid "Keyboard Model"
msgstr "Tastaturmodell"
-#: ../extensions/cpsection/keyboard/view.py:248
+#: ../extensions/cpsection/keyboard/view.py:250
msgid "Key(s) to change layout"
msgstr "Taste(n) zum Layoutwechsel"
-#: ../extensions/cpsection/keyboard/view.py:318
+#: ../extensions/cpsection/keyboard/view.py:319
msgid "Keyboard Layout(s)"
msgstr "Tastaturlayout(s)"
@@ -198,21 +222,21 @@ msgstr "Tastaturlayout(s)"
msgid "Language"
msgstr "Sprache"
-#: ../extensions/cpsection/language/model.py:28
+#: ../extensions/cpsection/language/model.py:30
msgid "Could not access ~/.i18n. Create standard settings."
msgstr "Zugriff auf ~/.i18n nicht möglich. Erzeuge daher Standardeinstellungen."
-#: ../extensions/cpsection/language/model.py:124
+#: ../extensions/cpsection/language/model.py:131
#, python-format
msgid "Language for code=%s could not be determined."
msgstr "Sprache für Code=%s konnte nicht ermittelt werden."
-#: ../extensions/cpsection/language/model.py:144
+#: ../extensions/cpsection/language/model.py:152
#, python-format
msgid "Sorry I do not speak '%s'."
msgstr "Entschuldigung, ich spreche nicht '%s'."
-#: ../extensions/cpsection/language/view.py:56
+#: ../extensions/cpsection/language/view.py:57
msgid ""
"Add languages in the order you prefer. If a translation is not available, "
"the next in the list will be used."
@@ -224,31 +248,31 @@ msgstr ""
msgid "Modem Configuration"
msgstr "Modem-Konfiguration"
-#: ../extensions/cpsection/modemconfiguration/view.py:91
+#: ../extensions/cpsection/modemconfiguration/view.py:94
msgid "Username:"
msgstr "Benutzername:"
-#: ../extensions/cpsection/modemconfiguration/view.py:102
+#: ../extensions/cpsection/modemconfiguration/view.py:106
msgid "Password:"
msgstr "Passwort:"
-#: ../extensions/cpsection/modemconfiguration/view.py:113
+#: ../extensions/cpsection/modemconfiguration/view.py:118
msgid "Number:"
msgstr "Nummer:"
-#: ../extensions/cpsection/modemconfiguration/view.py:124
+#: ../extensions/cpsection/modemconfiguration/view.py:130
msgid "Access Point Name (APN):"
-msgstr "Access-Point-Name (APN):"
+msgstr "Name des Zugangspunktes (APN):"
-#: ../extensions/cpsection/modemconfiguration/view.py:135
+#: ../extensions/cpsection/modemconfiguration/view.py:142
msgid "Personal Identity Number (PIN):"
msgstr "Persönliche Kennnumer (PIN):"
-#: ../extensions/cpsection/modemconfiguration/view.py:146
+#: ../extensions/cpsection/modemconfiguration/view.py:154
msgid "Personal Unblocking Key (PUK):"
msgstr "Persönlicher Freischaltschlüssel (PUK):"
-#: ../extensions/cpsection/modemconfiguration/view.py:167
+#: ../extensions/cpsection/modemconfiguration/view.py:175
msgid ""
"You will need to provide the following information to set up a mobile "
"broadband connection to a cellular (3G) network."
@@ -257,52 +281,52 @@ msgstr ""
"Mobilfunknetz (3G) aufzubauen."
#: ../extensions/cpsection/network/__init__.py:21
-#: ../extensions/cpsection/network/view.py:28
+#: ../extensions/cpsection/network/view.py:29
msgid "Network"
msgstr "Netzwerk"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:68
msgid "State is unknown."
msgstr "Status ist nicht bekannt."
# (Markus S.) vgl. http://lists.laptop.org/pipermail/localization/2008-July/001232.html
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:96
msgid "Error in specified radio argument use on/off."
msgstr "Fehler im angegebenen Funknetzparameter -- on/off verwenden."
# (Markus S.) vgl. http://lists.laptop.org/pipermail/localization/2008-July/001232.html
-#: ../extensions/cpsection/network/model.py:137
+#: ../extensions/cpsection/network/model.py:133
msgid "Error in specified argument use 0/1."
msgstr "Fehler im angegebenen Parameter -- 0/1 verwenden."
-#: ../extensions/cpsection/network/view.py:59
+#: ../extensions/cpsection/network/view.py:61
msgid "Wireless"
msgstr "Funknetzwerk"
-#: ../extensions/cpsection/network/view.py:67
+#: ../extensions/cpsection/network/view.py:69
msgid "Turn off the wireless radio to save battery life"
msgstr "Schalte das Funknetz aus, um die Lebensdauer der Batterie zu erhöhen."
# (Markus S,) war 'Radio:'
-#: ../extensions/cpsection/network/view.py:80
+#: ../extensions/cpsection/network/view.py:82
msgid "Radio"
msgstr "Funknetz"
-#: ../extensions/cpsection/network/view.py:96
+#: ../extensions/cpsection/network/view.py:98
msgid "Discard network history if you have trouble connecting to the network"
msgstr ""
"Verwirf die Netzwerk-Chronik, wenn du Schwierigkeiten hast, dich mit dem "
"Netzwerk zu verbinden."
-#: ../extensions/cpsection/network/view.py:105
+#: ../extensions/cpsection/network/view.py:107
msgid "Discard network history"
msgstr "Netzwerk-Chronik verwerfen"
-#: ../extensions/cpsection/network/view.py:118
+#: ../extensions/cpsection/network/view.py:120
msgid "Collaboration"
msgstr "Zusammenarbeit"
-#: ../extensions/cpsection/network/view.py:126
+#: ../extensions/cpsection/network/view.py:128
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 "
@@ -312,7 +336,7 @@ msgstr ""
"Server können einander sehen, selbst wenn sie sich nicht im selben Netzwerk "
"befinden."
-#: ../extensions/cpsection/network/view.py:136
+#: ../extensions/cpsection/network/view.py:138
msgid "Server:"
msgstr "Server:"
@@ -321,25 +345,25 @@ msgid "Power"
msgstr "Energieversorgung"
# (Markus S.) vgl. http://lists.laptop.org/pipermail/localization/2008-July/001232.html
-#: ../extensions/cpsection/power/model.py:85
+#: ../extensions/cpsection/power/model.py:90
msgid "Error in automatic pm argument, use on/off."
msgstr ""
"Fehler im automatischen Energieverwaltungsparameter -- on/off verwenden."
# (Markus S.) vgl. http://lists.laptop.org/pipermail/localization/2008-July/001232.html
-#: ../extensions/cpsection/power/model.py:112
+#: ../extensions/cpsection/power/model.py:120
msgid "Error in extreme pm argument, use on/off."
msgstr "Fehler im extremen Energieverwaltungsparameter -- on/off verwenden."
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:48
msgid "Power management"
msgstr "Energieverwaltung"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:58
msgid "Automatic power management (increases battery life)"
msgstr "Automatische Energieverwaltung (erhöht die Lebensdauer der Batterie)"
-#: ../extensions/cpsection/power/view.py:85
+#: ../extensions/cpsection/power/view.py:86
msgid ""
"Extreme power management (disableswireless radio, increases battery life)"
msgstr ""
@@ -381,37 +405,37 @@ msgstr "Deine Software ist auf dem neuesten Stand."
#, python-format
msgid "You can install %s update"
msgid_plural "You can install %s updates"
-msgstr[0] "Du kannst %s Update installieren."
-msgstr[1] "Du kannst %s Updates installieren."
+msgstr[0] "Du kannst %s Aktualisierung installieren."
+msgstr[1] "Du kannst %s Aktualisierungen installieren."
#: ../extensions/cpsection/updater/view.py:159
msgid "Checking for updates..."
-msgstr "Suche nach Updates..."
+msgstr "Suche nach Aktualisierungen..."
#: ../extensions/cpsection/updater/view.py:164
msgid "Installing updates..."
-msgstr "Installiere Updates..."
+msgstr "Installiere Aktualisierungen..."
-#: ../extensions/cpsection/updater/view.py:172
+#: ../extensions/cpsection/updater/view.py:173
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
-msgstr[0] "%s Update wurde installiert."
-msgstr[1] "%s Updates wurden installiert."
+msgstr[0] "%s Aktualisierung wurde installiert."
+msgstr[1] "%s Aktualisierungen wurden installiert."
-#: ../extensions/cpsection/updater/view.py:253
+#: ../extensions/cpsection/updater/view.py:255
msgid "Install selected"
msgstr "Auswahl installieren"
-#: ../extensions/cpsection/updater/view.py:274
+#: ../extensions/cpsection/updater/view.py:276
#, python-format
msgid "Download size: %s"
msgstr "Downloadgröße: %s"
-#: ../extensions/cpsection/updater/view.py:362
+#: ../extensions/cpsection/updater/view.py:364
#, python-format
-msgid "From version %(current)d to %(new)s (Size: %(size)s)"
-msgstr "Von Version %(current)d auf %(new)s (Größe: %(size)s)"
+msgid "From version %(current)s to %(new)s (Size: %(size)s)"
+msgstr "Von Version %(current)s auf %(new)s (Größe: %(size)s)"
#. TRANS: download size is 0
#: ../extensions/cpsection/updater/view.py:382
@@ -435,28 +459,28 @@ msgstr "%.0f KB"
msgid "%.1f MB"
msgstr "%.1f MB"
-#: ../extensions/deviceicon/battery.py:58
+#: ../extensions/deviceicon/battery.py:60
msgid "My Battery"
msgstr "Meine Batterie"
-#: ../extensions/deviceicon/battery.py:137
+#: ../extensions/deviceicon/battery.py:141
msgid "Removed"
msgstr "Entfernt"
-#: ../extensions/deviceicon/battery.py:140
+#: ../extensions/deviceicon/battery.py:144
msgid "Charging"
-msgstr "Aufladen"
+msgstr "Wird aufgeladen"
-#: ../extensions/deviceicon/battery.py:143
+#: ../extensions/deviceicon/battery.py:147
msgid "Very little power remaining"
msgstr "Sehr wenig Ladung verbleibend"
-#: ../extensions/deviceicon/battery.py:149
+#: ../extensions/deviceicon/battery.py:153
#, python-format
msgid "%(hour)d:%(min).2d remaining"
msgstr "%(hour)d:%(min).2d verbleibend"
-#: ../extensions/deviceicon/battery.py:152
+#: ../extensions/deviceicon/battery.py:156
msgid "Charged"
msgstr "Aufgeladen"
@@ -469,134 +493,180 @@ msgstr "IP-Addresse: %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:112
+#: ../extensions/deviceicon/network.py:104
msgid "Disconnect..."
msgstr "Verbindung trennen..."
-#: ../extensions/deviceicon/network.py:117
-msgid "Create new wireless network"
-msgstr "Neues Funknetzwerk erstellen"
-
# 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:123
-#: ../extensions/deviceicon/network.py:285
-#: ../src/jarabe/desktop/meshbox.py:247 ../src/jarabe/desktop/meshbox.py:536
+#: ../extensions/deviceicon/network.py:112
+#: ../extensions/deviceicon/network.py:290
+#: ../src/jarabe/desktop/networkviews.py:239
+#: ../src/jarabe/desktop/networkviews.py:538
+#: ../src/jarabe/desktop/networkviews.py:667
msgid "Connecting..."
msgstr "Verbinde..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:127
-#: ../extensions/deviceicon/network.py:199
-#: ../extensions/deviceicon/network.py:289
-#: ../src/jarabe/desktop/meshbox.py:253 ../src/jarabe/desktop/meshbox.py:542
+#: ../extensions/deviceicon/network.py:116
+#: ../extensions/deviceicon/network.py:182
+#: ../src/jarabe/desktop/networkviews.py:249
+#: ../src/jarabe/desktop/networkviews.py:544
+#: ../src/jarabe/desktop/networkviews.py:673
msgid "Connected"
msgstr "Verbunden"
-#: ../extensions/deviceicon/network.py:159
+#: ../extensions/deviceicon/network.py:142
msgid "Channel"
msgstr "Kanal"
-#: ../extensions/deviceicon/network.py:174
+#: ../extensions/deviceicon/network.py:157
msgid "Wired Network"
msgstr "Kabelnetzwerk"
# (Markus S.) War 'Geschwindigkeit'
-#: ../extensions/deviceicon/network.py:202
+#: ../extensions/deviceicon/network.py:185
msgid "Speed"
msgstr "Übertragungsrate"
-#: ../extensions/deviceicon/network.py:229
+#: ../extensions/deviceicon/network.py:211
msgid "Wireless modem"
msgstr "Funkmodem"
-#: ../extensions/deviceicon/network.py:277
+#: ../extensions/deviceicon/network.py:278
msgid "Please wait..."
msgstr "Bitte warten..."
-#: ../extensions/deviceicon/network.py:280
-#: ../src/jarabe/desktop/meshbox.py:163 ../src/jarabe/desktop/meshbox.py:493
+#: ../extensions/deviceicon/network.py:282
+#: ../src/jarabe/desktop/networkviews.py:149
+#: ../src/jarabe/desktop/networkviews.py:492
+#: ../src/jarabe/desktop/networkviews.py:624
msgid "Connect"
msgstr "Verbinden"
-#: ../extensions/deviceicon/network.py:281
+#: ../extensions/deviceicon/network.py:283
msgid "Disconnected"
msgstr "Nicht verbunden"
-#: ../extensions/deviceicon/network.py:284
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:68
-#: ../src/jarabe/frame/activitiestray.py:700
-#: ../src/jarabe/frame/activitiestray.py:799
-#: ../src/jarabe/frame/activitiestray.py:827
+#: ../extensions/deviceicon/network.py:289
+#: ../src/jarabe/controlpanel/toolbar.py:119
+#: ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/frame/activitiestray.py:587
+#: ../src/jarabe/frame/activitiestray.py:687
+#: ../src/jarabe/frame/activitiestray.py:715
msgid "Cancel"
msgstr "Abbrechen"
# (mschlager) war 'Nicht verbunden', ich würde aber eher erwarten, dass das die Beschriftung eines Menüeintrags ist, mit dem man eine Verbindung trennt, was dann in der Folge die Meldung 'Disconnecting...' liefert.
-#: ../extensions/deviceicon/network.py:288
-#: ../src/jarabe/desktop/meshbox.py:167
+#: ../extensions/deviceicon/network.py:297
+#: ../src/jarabe/desktop/networkviews.py:153
+#: ../src/jarabe/desktop/networkviews.py:496
msgid "Disconnect"
msgstr "Verbindung trennen"
-#: ../extensions/deviceicon/network.py:292
-msgid "Sim requires Pin/Puk"
-msgstr "Sim benötigt PIN/PUK"
+#: ../extensions/deviceicon/network.py:327
+msgid "Try connection again"
+msgstr "Erneuter Verbindungsversuch"
+
+#: ../extensions/deviceicon/network.py:330
+#, python-format
+msgid "Error: %s"
+msgstr "Fehler: %s"
+
+#: ../extensions/deviceicon/network.py:334
+#, python-format
+msgid "Suggestion: %s"
+msgstr "Vorschlag: %s"
-#: ../extensions/deviceicon/network.py:293
-msgid "Authentication Error"
-msgstr "Authentifizierungsfehler"
+#: ../extensions/deviceicon/network.py:340
+#: ../extensions/deviceicon/network.py:343
+#, python-format
+msgid "Connected for %s"
+msgstr "Verbunden für %s"
-#: ../extensions/deviceicon/network.py:538
+#: ../extensions/deviceicon/network.py:348
+#: ../extensions/deviceicon/network.py:349
#, python-format
-msgid "%s's network"
-msgstr "Netzwerk von %s"
+msgid "%d KB"
+msgstr "%d KB"
+
+#: ../extensions/deviceicon/network.py:354
+msgid "Check your Pin/Puk configuration."
+msgstr "Überprüfe deine Pin/Puk-Einstellungen."
+
+#: ../extensions/deviceicon/network.py:357
+msgid "Check your Access Point Name (APN) configuration"
+msgstr "Überprüfe deine Einstellung für den Namen des Zugangspunktes (APN)."
+
+#: ../extensions/deviceicon/network.py:361
+msgid "Check the Number configuration."
+msgstr "Überprüfe deine Einstellung der Nummer."
-#: ../extensions/deviceicon/network.py:605
-#: ../extensions/deviceicon/network.py:664
+#: ../extensions/deviceicon/network.py:363
+msgid "Check your configuration."
+msgstr "Überprüfe deine Einstellungen."
+
+#: ../extensions/deviceicon/network.py:615
msgid "Mesh Network"
msgstr "Maschennetzwerk"
-#: ../extensions/deviceicon/network.py:869
+#: ../extensions/deviceicon/network.py:658
#, python-format
-msgid "Data sent %d KB / received %d KB"
-msgstr "gesendet %d KB / empfangen %d KB"
+msgid "Mesh Network %s"
+msgstr "Maschennetzwerk %s"
+
+#: ../extensions/deviceicon/network.py:782
+msgid "No GSM connection available."
+msgstr "Keine GSM-Verbindung verfügbar."
-#: ../extensions/deviceicon/network.py:880
-msgid "Connection time "
-msgstr "Verbindungsdauer "
+#: ../extensions/deviceicon/network.py:783
+msgid "Create a connection in the control panel."
+msgstr "Verbindung in der Menüleiste erstellen."
-#: ../extensions/deviceicon/speaker.py:59
+#: ../extensions/deviceicon/speaker.py:60
msgid "My Speakers"
msgstr "Meine Lautsprecher"
-#: ../extensions/deviceicon/speaker.py:133
+#: ../extensions/deviceicon/speaker.py:136
msgid "Unmute"
msgstr "Laut schalten"
-#: ../extensions/deviceicon/speaker.py:136
+#: ../extensions/deviceicon/speaker.py:139
msgid "Mute"
msgstr "Stumm schalten"
+#: ../extensions/deviceicon/touchpad.py:37
+msgid "finger"
+msgstr "Finger"
+
+#: ../extensions/deviceicon/touchpad.py:38
+msgid "stylus"
+msgstr "Stift"
+
+#: ../extensions/deviceicon/touchpad.py:67
+msgid "My touchpad"
+msgstr "Mein Touchpad"
+
# (Markus S.) 'Zelle'?
#: ../extensions/globalkey/screenshot.py:59
msgid "Mesh"
msgstr "Masche"
#: ../extensions/globalkey/screenshot.py:61
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "Group"
msgstr "Gruppe"
# (Markus S.) war 'Zuhause', vgl. stuffer-sheet
#: ../extensions/globalkey/screenshot.py:63
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "Home"
msgstr "Startbildschirm"
#: ../extensions/globalkey/screenshot.py:69
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "Activity"
msgstr "Aktivität"
@@ -627,6 +697,10 @@ msgid "Backup URL"
msgstr "Backup-URL"
#: ../data/sugar.schemas.in.h:4
+msgid "Bundle IDs of protected activities"
+msgstr "IDs geschützter Aktivitäten bündeln"
+
+#: ../data/sugar.schemas.in.h:5
msgid ""
"Color for the XO icon that is used throughout the desktop. The string is "
"composed of the stroke color and fill color, format is that of rbg colors. "
@@ -636,103 +710,103 @@ msgstr ""
"Zeichenkette setzt sich aus der Linien- und der Füllfarbe zusammen, die "
"jeweils als RGB-Farben angeben werden. Beispiel: #AC32FF,#9A5200"
-#: ../data/sugar.schemas.in.h:5
+#: ../data/sugar.schemas.in.h:6
msgid "Corner Delay"
msgstr "Eckenverzögerung"
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:7
msgid "Default font face"
msgstr "Standard-Schriftart"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:8
msgid "Default font size"
msgstr "Standard-Schriftgröße"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:9
msgid "Default nick"
msgstr "Standard-Benutzername"
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:10
msgid "Delay for the activation of the frame using the corners."
msgstr "Verzögerung bei der Aktivierung eines Rahmens über die Ecken."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:11
msgid "Delay for the activation of the frame using the edges."
msgstr "Verzögerung bei der Aktivierung eines Rahmens über die Ränder."
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:12
msgid "Directory to search for translations"
msgstr "Verzeichnis zur Suche nach Übersetzungen"
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:13
msgid "Edge Delay"
msgstr "Randverzögerung"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:14
msgid "Favorites Layout"
msgstr "Favoriten-Layout"
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:15
msgid "Favorites resume mode"
msgstr "Favoriten-Wiederaufnahmemodus"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:16
msgid "Font face that is used throughout the desktop."
msgstr "Schriftart, die auf dem Desktop benutzt wird."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:17
msgid "Font size that is used throughout the desktop."
msgstr "Schriftgröße, die auf dem Desktop benutzt wird."
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:18
msgid "GSM network APN"
msgstr "GSM-Netzwerk APN"
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:19
msgid "GSM network PIN"
msgstr "GSM-Netzwerk PIN"
-#: ../data/sugar.schemas.in.h:19
+#: ../data/sugar.schemas.in.h:20
msgid "GSM network PUK"
msgstr "GSM-Netzwerk PUK"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:21
msgid "GSM network access point name configuration"
-msgstr "GSM-Netzwerk Namenseinstellung Zugriffspunkt"
+msgstr "GSM-Netzwerk Einstellung des Namens des Zugangspunktes"
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:22
msgid "GSM network number"
msgstr "GSM-Netzwerk Nummer"
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:23
msgid "GSM network password"
msgstr "GSM-Netzwerk Passwort"
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:24
msgid "GSM network password configuration"
msgstr "GSM-Netzwerk Passworteinstellung"
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:25
msgid "GSM network personal identification number configuration"
msgstr "GSM-Netzwerk PIN-Einstellung"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:26
msgid "GSM network personal unlock key configuration"
msgstr "GSM-Netzwerk PUK-Einstellung"
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:27
msgid "GSM network telephone number configuration"
-msgstr "GSM-Netzwerk Telefonnummereinstellung"
+msgstr "GSM-Netzwerk Telefonnummerneinstellung"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:28
msgid "GSM network username"
msgstr "GSM-Netzwerk Benutzername"
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:29
msgid "GSM network username configuration"
-msgstr "GSM-Netzwerk Benutzernameneinstellung"
+msgstr "GSM-Netzwerk Benutzernamenseinstellung"
-#: ../data/sugar.schemas.in.h:29
+#: ../data/sugar.schemas.in.h:30
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
@@ -740,112 +814,140 @@ msgstr ""
"Falls WAHR, wird Sugar es für andere Nutzer des Jabber-Servers zulassen, uns "
"zu suchen."
-#: ../data/sugar.schemas.in.h:30
+#: ../data/sugar.schemas.in.h:31
msgid "If TRUE, Sugar will show a \"Log out\" option."
msgstr "Falls WAHR, wird Sugar die Option \"Abmelden\" anzeigen."
-#: ../data/sugar.schemas.in.h:31
+#: ../data/sugar.schemas.in.h:32
+msgid "If TRUE, Sugar will show a \"Restart\" option."
+msgstr "Falls WAHR, wird Sugar die Option \"Neustart\" anzeigen."
+
+#: ../data/sugar.schemas.in.h:33
+msgid ""
+"If TRUE, Sugar will show default Ad-hoc networks for channel 1,6 and 11. If "
+"Sugar sees no \"known\" network when it starts, it does autoconnect to an Ad-"
+"hoc network."
+msgstr ""
+"Falls WAHR, wird Sugar standardmäßige Ad-hoc-Netzwerke für Kanal 1, 6 und 11 "
+"anzeigen. Wenn Sugar beim Start kein \"bekanntes\" Netzwerk erkennt, "
+"verbindet es sich automatisch mit einem Ad-hoc-Netzwerk."
+
+#: ../data/sugar.schemas.in.h:34
msgid "Jabber Server"
msgstr "Jabber-Server"
-#: ../data/sugar.schemas.in.h:32
+#: ../data/sugar.schemas.in.h:35
msgid "Keyboard layouts"
msgstr "Tastaturlayouts"
-#: ../data/sugar.schemas.in.h:33
+#: ../data/sugar.schemas.in.h:36
msgid "Keyboard model"
msgstr "Tastaturmodell"
-#: ../data/sugar.schemas.in.h:34
+#: ../data/sugar.schemas.in.h:37
msgid "Keyboard options"
msgstr "Tastatureinstellungen"
-#: ../data/sugar.schemas.in.h:35
+#: ../data/sugar.schemas.in.h:38
msgid "Layout of the favorites view."
msgstr "Layout der Favoriten-Ansicht."
-#: ../data/sugar.schemas.in.h:36
+#: ../data/sugar.schemas.in.h:39
msgid ""
"List of keyboard layouts. Each entry should be in the form layout(variant)"
msgstr ""
"Liste der Tastaturlayouts. Jeder Eintrag sollte von der Form "
"Layout(Variante) sein."
-#: ../data/sugar.schemas.in.h:37
+#: ../data/sugar.schemas.in.h:40
msgid "List of keyboard options."
msgstr "Liste der Tastatureinstellungen."
-#: ../data/sugar.schemas.in.h:38
+#: ../data/sugar.schemas.in.h:41
msgid "Power Automatic"
msgstr "Automatische Energieverwaltung"
-#: ../data/sugar.schemas.in.h:39
+#: ../data/sugar.schemas.in.h:42
msgid "Power Automatic."
msgstr "Automatische Energieverwaltung."
# (Markus S.) war 'Extreme Energieverwaltung'
-#: ../data/sugar.schemas.in.h:40
+#: ../data/sugar.schemas.in.h:43
msgid "Power Extreme"
msgstr "Extremes Energiesparen"
# (Markus S.) war 'Extreme Energieverwaltung'
-#: ../data/sugar.schemas.in.h:41
+#: ../data/sugar.schemas.in.h:44
msgid "Power Extreme."
msgstr "Extremes Energiesparen."
-#: ../data/sugar.schemas.in.h:42
+#: ../data/sugar.schemas.in.h:45
msgid "Publish to Gadget"
msgstr "Veröffentlichen auf Gerät"
-#: ../data/sugar.schemas.in.h:43
+#: ../data/sugar.schemas.in.h:46
msgid "Setting for muting the sound device."
msgstr "Einstellung zum Stummschalten der Audio-Ausgabe."
-#: ../data/sugar.schemas.in.h:44
+#: ../data/sugar.schemas.in.h:47
msgid "Show Log out"
msgstr "Abmelden anzeigen"
-#: ../data/sugar.schemas.in.h:45
+#: ../data/sugar.schemas.in.h:48
+msgid "Show Restart"
+msgstr "Neustart anzeigen"
+
+#: ../data/sugar.schemas.in.h:49
+msgid "Show Sugar Ad-hoc networks"
+msgstr "Sugar-Ad-hoc-Netzwerke anzeigen"
+
+#: ../data/sugar.schemas.in.h:50
msgid "Sound Muted"
msgstr "Stummgeschaltet"
-#: ../data/sugar.schemas.in.h:46
+#: ../data/sugar.schemas.in.h:51
msgid "The keyboard model to be used"
msgstr "Das zu verwendende Tastaturmodell"
-#: ../data/sugar.schemas.in.h:48
+#: ../data/sugar.schemas.in.h:53
msgid "Timezone setting for the system."
msgstr "Zeitzoneneinstellung des Systems."
-#: ../data/sugar.schemas.in.h:49
+#: ../data/sugar.schemas.in.h:54
msgid "Url of the jabber server to use."
msgstr "URL des zu nutzenden Jabber-Servers."
-#: ../data/sugar.schemas.in.h:50
+#: ../data/sugar.schemas.in.h:55
msgid "Url where the backup is saved to."
msgstr "URL, unter der das Backup gespeichert wird."
-#: ../data/sugar.schemas.in.h:51
+#: ../data/sugar.schemas.in.h:56
msgid "User Color"
msgstr "Benutzerfarbe"
-#: ../data/sugar.schemas.in.h:52
+#: ../data/sugar.schemas.in.h:57
msgid "User Name"
msgstr "Benutzername"
-#: ../data/sugar.schemas.in.h:53
+#: ../data/sugar.schemas.in.h:58
msgid "User name that is used throughout the desktop."
msgstr "Benutzername, der überall auf dem Desktop benutzt wird."
-#: ../data/sugar.schemas.in.h:54
+#: ../data/sugar.schemas.in.h:59
+msgid ""
+"Users will not be allowed to erase these activities through the list view."
+msgstr ""
+"Benutzer werden diese Aktivitäten nicht in der Listenansicht löschen können."
+
+#: ../data/sugar.schemas.in.h:60
msgid "Volume Level"
msgstr "Lautstärke"
-#: ../data/sugar.schemas.in.h:55
+#: ../data/sugar.schemas.in.h:61
msgid "Volume level for the sound device."
msgstr "Lautstärkepegel für die Audio-Ausgabe."
-#: ../data/sugar.schemas.in.h:56
+#: ../data/sugar.schemas.in.h:62
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -877,7 +979,7 @@ msgstr "sugar-control-panel: %s"
# which must appear in the translated string (msgstr) as well.
#. TRANS: Translators, there's a empty line at the end of this string,
#. which must appear in the translated string (msgstr) as well.
-#: ../src/jarabe/controlpanel/cmd.py:37
+#: ../src/jarabe/controlpanel/cmd.py:38
msgid ""
"Usage: sugar-control-panel [ option ] key [ args ... ] \n"
" Control for the sugar environment. \n"
@@ -899,52 +1001,54 @@ msgstr ""
" -g Parameter Den aktuellen Wert für diesen Parameter auslesen\n"
" -s Parameter Den aktuellen Wert für diesen Parameter festlegen\n"
" -c Parameter Den aktuellen Wert für diesen Parameter zurücksetzen\n"
-" "
+" "
-#: ../src/jarabe/controlpanel/cmd.py:50
+#: ../src/jarabe/controlpanel/cmd.py:52
msgid "To apply your changes you have to restart sugar.\n"
msgstr "Um die Änderungen zu übernehmen, muss Sugar neu gestartet werden.\n"
-#: ../src/jarabe/controlpanel/gui.py:281
+#: ../src/jarabe/controlpanel/gui.py:285
+#: ../src/jarabe/journal/journaltoolbox.py:437
+#: ../src/jarabe/journal/volumestoolbar.py:158
msgid "Warning"
msgstr "Warnung"
-#: ../src/jarabe/controlpanel/gui.py:282
-#: ../src/jarabe/controlpanel/sectionview.py:42
+#: ../src/jarabe/controlpanel/gui.py:286
+#: ../src/jarabe/controlpanel/sectionview.py:41
msgid "Changes require restart"
msgstr "Neustart zur Übernahme der Änderungen notwendig"
-#: ../src/jarabe/controlpanel/gui.py:285
+#: ../src/jarabe/controlpanel/gui.py:289
msgid "Cancel changes"
msgstr "Änderungen verwerfen"
-#: ../src/jarabe/controlpanel/gui.py:290 ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/controlpanel/gui.py:294 ../src/jarabe/desktop/homebox.py:72
msgid "Later"
msgstr "Später"
-#: ../src/jarabe/controlpanel/gui.py:294
+#: ../src/jarabe/controlpanel/gui.py:298
msgid "Restart now"
msgstr "Jetzt neu starten"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:206
+#: ../src/jarabe/controlpanel/toolbar.py:63 ../src/jarabe/intro/window.py:211
msgid "Done"
msgstr "Fertig"
-#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:333
+#: ../src/jarabe/controlpanel/toolbar.py:125
+#: ../src/jarabe/desktop/favoritesview.py:336
msgid "Ok"
msgstr "Ok"
-#: ../src/jarabe/desktop/activitieslist.py:236
+#: ../src/jarabe/desktop/activitieslist.py:230
#, python-format
msgid "Version %s"
msgstr "Version %s"
-#: ../src/jarabe/desktop/activitieslist.py:357
+#: ../src/jarabe/desktop/activitieslist.py:354
msgid "Confirm erase"
msgstr "Löschen bestätigen"
-#: ../src/jarabe/desktop/activitieslist.py:359
+#: ../src/jarabe/desktop/activitieslist.py:356
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
msgstr "Löschen bestätigen: Willst du %s wirklich dauerhaft löschen?"
@@ -953,388 +1057,444 @@ msgstr "Löschen bestätigen: Willst du %s wirklich dauerhaft löschen?"
# 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:363
-#: ../src/jarabe/frame/clipboardmenu.py:63
-#: ../src/jarabe/view/viewsource.py:218
+#: ../src/jarabe/desktop/activitieslist.py:360
+#: ../src/jarabe/frame/clipboardmenu.py:64
+#: ../src/jarabe/view/viewsource.py:221
msgid "Keep"
msgstr "Behalten"
-#: ../src/jarabe/desktop/activitieslist.py:366
-#: ../src/jarabe/desktop/activitieslist.py:409
-#: ../src/jarabe/journal/journaltoolbox.py:360
-#: ../src/jarabe/journal/palettes.py:106
+#: ../src/jarabe/desktop/activitieslist.py:363
+#: ../src/jarabe/desktop/activitieslist.py:417
+#: ../src/jarabe/journal/journaltoolbox.py:391
+#: ../src/jarabe/journal/palettes.py:112
msgid "Erase"
msgstr "Löschen"
-#: ../src/jarabe/desktop/activitieslist.py:430
+#: ../src/jarabe/desktop/activitieslist.py:432
msgid "Remove favorite"
msgstr "Favorit entfernen"
-#: ../src/jarabe/desktop/activitieslist.py:434
+#: ../src/jarabe/desktop/activitieslist.py:436
msgid "Make favorite"
msgstr "Zum Favoriten machen"
# 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
+#: ../src/jarabe/desktop/favoriteslayout.py:127
msgid "Freeform"
msgstr "Freie Form"
# 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:198
+#: ../src/jarabe/desktop/favoriteslayout.py:215
msgid "Ring"
msgstr "Ring"
# 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:337
+#: ../src/jarabe/desktop/favoriteslayout.py:402
msgid "Spiral"
msgstr "Spirale"
# 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:404
+#: ../src/jarabe/desktop/favoriteslayout.py:472
msgid "Box"
msgstr "Rechteck"
# 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:445
+#: ../src/jarabe/desktop/favoriteslayout.py:515
msgid "Triangle"
msgstr "Dreieck"
-#: ../src/jarabe/desktop/favoritesview.py:324
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "Registration Failed"
msgstr "Anmeldung fehlgeschlagen"
-#: ../src/jarabe/desktop/favoritesview.py:325
+#: ../src/jarabe/desktop/favoritesview.py:328
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:327
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Successful"
msgstr "Anmeldung erfolgreich"
-#: ../src/jarabe/desktop/favoritesview.py:328
+#: ../src/jarabe/desktop/favoritesview.py:331
msgid "You are now registered with your school server."
msgstr "Du bist nun an deinem Schulserver angemeldet."
-#: ../src/jarabe/desktop/favoritesview.py:631
+#: ../src/jarabe/desktop/favoritesview.py:626
msgid "Register"
msgstr "Am Schulserver anmelden"
-#: ../src/jarabe/desktop/homebox.py:63
+#: ../src/jarabe/desktop/homebox.py:65
msgid "Software Update"
msgstr "Software-Aktualisierung"
-#: ../src/jarabe/desktop/homebox.py:64
+#: ../src/jarabe/desktop/homebox.py:66
msgid "Update your activities to ensure compatibility with your new software"
msgstr ""
"Aktualisiere deine Aktivitäten, um die Kompatibilität mit deiner neuen "
"Software sicherzustellen."
-#: ../src/jarabe/desktop/homebox.py:73
+#: ../src/jarabe/desktop/homebox.py:75
msgid "Check now"
msgstr "Jetzt prüfen"
-#: ../src/jarabe/desktop/homebox.py:192
+#: ../src/jarabe/desktop/homebox.py:193
msgid "List view"
msgstr "Listenansicht"
-#: ../src/jarabe/desktop/homebox.py:193
+#: ../src/jarabe/desktop/homebox.py:194
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:255
+#: ../src/jarabe/desktop/homebox.py:257
msgid "Favorites view"
msgstr "Favoritenansicht"
-#: ../src/jarabe/desktop/homebox.py:256
+#: ../src/jarabe/desktop/homebox.py:258
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:135
+#: ../src/jarabe/desktop/keydialog.py:143
msgid "Key Type:"
msgstr "Schlüsseltyp:"
-#: ../src/jarabe/desktop/keydialog.py:155
+#: ../src/jarabe/desktop/keydialog.py:163
msgid "Authentication Type:"
msgstr "Authentifizierungstyp:"
-#: ../src/jarabe/desktop/keydialog.py:220
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "WPA & WPA2 Personal"
msgstr "WPA & WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:229
+#: ../src/jarabe/desktop/keydialog.py:238
msgid "Wireless Security:"
msgstr "WLAN-Sicherheit:"
-#: ../src/jarabe/desktop/meshbox.py:491
-#, python-format
-msgid "Mesh Network %d"
-msgstr "Maschennetzwerk %d"
-
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:628
-#: ../src/jarabe/frame/activitiestray.py:735
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:67
+#: ../src/jarabe/desktop/meshbox.py:109
+#: ../src/jarabe/frame/activitiestray.py:622
+#: ../src/jarabe/journal/journaltoolbox.py:485
+#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:78
msgid "Resume"
msgstr "Fortsetzen"
-#: ../src/jarabe/desktop/meshbox.py:633
-#: ../src/jarabe/frame/activitiestray.py:233
+#: ../src/jarabe/desktop/meshbox.py:114
+#: ../src/jarabe/frame/activitiestray.py:173
msgid "Join"
-msgstr "Mitmachen"
+msgstr "Beitreten"
+
+#: ../src/jarabe/desktop/networkviews.py:489
+#, python-format
+msgid "Ad-hoc Network %d"
+msgstr "Ad-hoc-Netzwerk %d"
+
+#: ../src/jarabe/desktop/networkviews.py:622
+#, python-format
+msgid "Mesh Network %d"
+msgstr "Maschennetzwerk %d"
-#: ../src/jarabe/desktop/schoolserver.py:104
+#: ../src/jarabe/desktop/schoolserver.py:131
msgid "Cannot connect to the server."
msgstr "Kann nicht mit dem Server verbinden."
-#: ../src/jarabe/desktop/schoolserver.py:109
+#: ../src/jarabe/desktop/schoolserver.py:136
msgid "The server could not complete the request."
msgstr "Der Server konnte die Anforderung nicht erfüllen."
-#: ../src/jarabe/frame/activitiestray.py:238
-#: ../src/jarabe/frame/activitiestray.py:672
+#: ../src/jarabe/frame/activitiestray.py:178
+#: ../src/jarabe/frame/activitiestray.py:559
msgid "Decline"
msgstr "Ablehnen"
-#: ../src/jarabe/frame/activitiestray.py:624
+#: ../src/jarabe/frame/activitiestray.py:509
#, python-format
msgid "%dB"
msgstr "%dB"
-#: ../src/jarabe/frame/activitiestray.py:626
+#: ../src/jarabe/frame/activitiestray.py:511
#, python-format
msgid "%dKB"
msgstr "%dKB"
-#: ../src/jarabe/frame/activitiestray.py:628
+#: ../src/jarabe/frame/activitiestray.py:513
#, python-format
msgid "%dMB"
msgstr "%dMB"
-#: ../src/jarabe/frame/activitiestray.py:645
+#: ../src/jarabe/frame/activitiestray.py:530
#, python-format
msgid "%s of %s"
msgstr "%s von %s"
-#: ../src/jarabe/frame/activitiestray.py:657
+#: ../src/jarabe/frame/activitiestray.py:544
#, python-format
msgid "Transfer from %r"
msgstr "Übertragung von %r"
-#: ../src/jarabe/frame/activitiestray.py:667
+#: ../src/jarabe/frame/activitiestray.py:554
msgid "Accept"
msgstr "Akzeptieren"
-#: ../src/jarabe/frame/activitiestray.py:690
-#: ../src/jarabe/frame/activitiestray.py:817
+#: ../src/jarabe/frame/activitiestray.py:577
+#: ../src/jarabe/frame/activitiestray.py:705
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:724
-#: ../src/jarabe/frame/activitiestray.py:852
+#: ../src/jarabe/frame/activitiestray.py:611
+#: ../src/jarabe/frame/activitiestray.py:740
msgid "Dismiss"
msgstr "Verwerfen"
-#: ../src/jarabe/frame/activitiestray.py:787
+#: ../src/jarabe/frame/activitiestray.py:675
#, python-format
msgid "Transfer to %r"
msgstr "Übertragung zu %r"
-#: ../src/jarabe/frame/clipboardmenu.py:53 ../src/jarabe/view/palettes.py:221
+#: ../src/jarabe/frame/clipboardmenu.py:54 ../src/jarabe/view/palettes.py:220
msgid "Remove"
msgstr "Entfernen"
-#: ../src/jarabe/frame/clipboardmenu.py:58
-#: ../src/jarabe/frame/clipboardmenu.py:81
+#: ../src/jarabe/frame/clipboardmenu.py:59
+#: ../src/jarabe/frame/clipboardmenu.py:82
msgid "Open"
msgstr "Öffnen"
-#: ../src/jarabe/frame/clipboardmenu.py:86
+#: ../src/jarabe/frame/clipboardmenu.py:87
msgid "Open with"
msgstr "Öffnen mit"
# (Markus S.) 'clipping', nicht 'clipped'
-#: ../src/jarabe/frame/clipboardobject.py:49
+#: ../src/jarabe/frame/clipboardobject.py:50
#, python-format
msgid "%s clipping"
msgstr "%s ausgeschnitten"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "Neighborhood"
msgstr "Umgebung"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "F1"
msgstr "F1"
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "F2"
msgstr "F2"
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "F3"
msgstr "F3"
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "F4"
msgstr "F4"
-#: ../src/jarabe/intro/window.py:128
+#: ../src/jarabe/intro/window.py:96
+msgid "Name:"
+msgstr "Name:"
+
+#: ../src/jarabe/intro/window.py:132
msgid "Click to change color:"
msgstr "Klicken zum Wechseln der Farbe:"
-#: ../src/jarabe/intro/window.py:192 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:197 ../src/jarabe/journal/detailview.py:105
msgid "Back"
msgstr "Zurück"
# (Markus S.) war 'Nächste'
-#: ../src/jarabe/intro/window.py:209
+#: ../src/jarabe/intro/window.py:214
msgid "Next"
msgstr "Vor"
-#: ../src/jarabe/journal/expandedentry.py:151
-#: ../src/jarabe/journal/palettes.py:60
+#: ../src/jarabe/journal/expandedentry.py:154
+#: ../src/jarabe/journal/listmodel.py:144 ../src/jarabe/journal/palettes.py:59
msgid "Untitled"
msgstr "Ohne Titel"
-#: ../src/jarabe/journal/expandedentry.py:242
+#: ../src/jarabe/journal/expandedentry.py:243
msgid "No preview"
msgstr "Keine Vorschau"
-#: ../src/jarabe/journal/expandedentry.py:261
+#: ../src/jarabe/journal/expandedentry.py:262
#, python-format
msgid "Kind: %s"
msgstr "Art: %s"
-#: ../src/jarabe/journal/expandedentry.py:261
+#: ../src/jarabe/journal/expandedentry.py:262
+#: ../src/jarabe/journal/listmodel.py:150
+#: ../src/jarabe/journal/listmodel.py:157
+#: ../src/jarabe/journal/listmodel.py:165
msgid "Unknown"
msgstr "Unbekannt"
-#: ../src/jarabe/journal/expandedentry.py:262
+#: ../src/jarabe/journal/expandedentry.py:263
#, python-format
msgid "Date: %s"
msgstr "Datum: %s"
-#: ../src/jarabe/journal/expandedentry.py:263
+#: ../src/jarabe/journal/expandedentry.py:264
#, python-format
msgid "Size: %s"
msgstr "Größe: %s"
-#: ../src/jarabe/journal/expandedentry.py:285 ../src/jarabe/journal/misc.py:93
+#: ../src/jarabe/journal/expandedentry.py:292
+#: ../src/jarabe/journal/misc.py:108
msgid "No date"
msgstr "Kein Datum"
-#: ../src/jarabe/journal/expandedentry.py:292
+#: ../src/jarabe/journal/expandedentry.py:299
msgid "Participants:"
msgstr "Teilnehmer:"
-#: ../src/jarabe/journal/expandedentry.py:315
+#: ../src/jarabe/journal/expandedentry.py:321
msgid "Description:"
msgstr "Beschreibung:"
-#: ../src/jarabe/journal/expandedentry.py:340
+#: ../src/jarabe/journal/expandedentry.py:346
msgid "Tags:"
msgstr "Stichwörter:"
-#: ../src/jarabe/journal/journalactivity.py:108
-#: ../src/jarabe/journal/volumestoolbar.py:47
+#: ../src/jarabe/journal/journalactivity.py:115
+#: ../src/jarabe/journal/journaltoolbox.py:456
+#: ../src/jarabe/journal/volumestoolbar.py:50
msgid "Journal"
msgstr "Tagebuch"
-#: ../src/jarabe/journal/journaltoolbox.py:67
+#: ../src/jarabe/journal/journaltoolbox.py:69
msgid "Search"
msgstr "Suchen"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:136
msgid "Anytime"
msgstr "Beliebiges Datum"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:138
msgid "Today"
msgstr "Heute"
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:140
msgid "Since yesterday"
msgstr "Seit gestern"
# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
-#: ../src/jarabe/journal/journaltoolbox.py:132
+#: ../src/jarabe/journal/journaltoolbox.py:142
msgid "Past week"
msgstr "Vergangene Woche"
# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
-#: ../src/jarabe/journal/journaltoolbox.py:134
+#: ../src/jarabe/journal/journaltoolbox.py:144
msgid "Past month"
msgstr "Vergangener Monat"
# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
-#: ../src/jarabe/journal/journaltoolbox.py:136
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "Past year"
msgstr "Vergangenes Jahr"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:153
msgid "Anyone"
msgstr "Alle"
-#: ../src/jarabe/journal/journaltoolbox.py:145
+#: ../src/jarabe/journal/journaltoolbox.py:155
msgid "My friends"
msgstr "Meine Freunde"
-#: ../src/jarabe/journal/journaltoolbox.py:146
+#: ../src/jarabe/journal/journaltoolbox.py:156
msgid "My class"
msgstr "Meine Klasse"
# TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:274
+#: ../src/jarabe/journal/journaltoolbox.py:298
msgid "Anything"
msgstr "Alles"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:350
-#: ../src/jarabe/journal/palettes.py:84
+#: ../src/jarabe/journal/journaltoolbox.py:381
+#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Kopieren"
+#: ../src/jarabe/journal/journaltoolbox.py:436
+#: ../src/jarabe/journal/volumestoolbar.py:157
+msgid "Entries without a file cannot be copied."
+msgstr "Einträge ohne eine Datei lassen sich nicht kopieren."
+
+#: ../src/jarabe/journal/journaltoolbox.py:445
+#: ../src/jarabe/journal/volumestoolbar.py:166
+#, python-format
+msgid "Error while copying the entry. %s"
+msgstr "Fehler beim Kopieren des Eintrags. %s"
+
+#: ../src/jarabe/journal/journaltoolbox.py:446
+#: ../src/jarabe/journal/volumestoolbar.py:167
+msgid "Error"
+msgstr "Fehler"
+
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:431
+#: ../src/jarabe/journal/journaltoolbox.py:488
#: ../src/jarabe/journal/palettes.py:69
msgid "Start"
msgstr "Start"
-#: ../src/jarabe/journal/listview.py:373
+#: ../src/jarabe/journal/journaltoolbox.py:516
+msgid "Sort by date modified"
+msgstr "Nach Änderungsdatum sortieren"
+
+#: ../src/jarabe/journal/journaltoolbox.py:517
+msgid "Sort by date created"
+msgstr "Nach Erstellungsdatum sortieren"
+
+#: ../src/jarabe/journal/journaltoolbox.py:518
+msgid "Sort by size"
+msgstr "Nach Größe sortieren"
+
+#: ../src/jarabe/journal/journaltoolbox.py:527
+msgid "Sort view"
+msgstr "Ansicht sortieren"
+
+#: ../src/jarabe/journal/listview.py:380
msgid "Your Journal is empty"
msgstr "Dein Tagebuch ist leer."
-#: ../src/jarabe/journal/listview.py:375
+#: ../src/jarabe/journal/listview.py:382
msgid "No matching entries"
msgstr "Keine passenden Einträge"
-#: ../src/jarabe/journal/listview.py:386
+#: ../src/jarabe/journal/listview.py:393
msgid "Clear search"
msgstr "Suchfeld leeren"
-#: ../src/jarabe/journal/modalalert.py:63
+#: ../src/jarabe/journal/misc.py:273
+#, python-format
+msgid "Older Version Of %s Activity"
+msgstr "Ältere Version von %s Aktivität"
+
+#: ../src/jarabe/journal/misc.py:274
+#, python-format
+msgid "Do you want to downgrade to version %s "
+msgstr "Möchtest Du auf Version %s zurücksetzen?"
+
+#: ../src/jarabe/journal/modalalert.py:64
msgid "Your Journal is full"
msgstr "Dein Tagebuch ist voll."
-#: ../src/jarabe/journal/modalalert.py:67
+#: ../src/jarabe/journal/modalalert.py:68
msgid "Please delete some old Journal entries to make space for new ones."
msgstr ""
"Lösche bitte einige alte Tagebucheinträge, um Platz für neue zu schaffen."
-#: ../src/jarabe/journal/modalalert.py:79
+#: ../src/jarabe/journal/modalalert.py:80
msgid "Show Journal"
msgstr "Tagebuch anzeigen"
@@ -1343,7 +1503,7 @@ msgid "Choose an object"
msgstr "Ein Objekt auswählen"
#: ../src/jarabe/journal/objectchooser.py:151
-#: ../src/jarabe/view/viewsource.py:310
+#: ../src/jarabe/view/viewsource.py:311
msgid "Close"
msgstr "Schließen"
@@ -1355,102 +1515,306 @@ msgstr "Fortsetzen mit"
msgid "Start with"
msgstr "Beginnen mit"
-#: ../src/jarabe/journal/palettes.py:92
+#: ../src/jarabe/journal/palettes.py:83 ../src/jarabe/journal/palettes.py:216
+msgid "No activity to start entry"
+msgstr "Keine Aktivität, um den Eintrag zu beginnen"
+
+#: ../src/jarabe/journal/palettes.py:98
msgid "Send to"
msgstr "Senden an"
-#: ../src/jarabe/journal/palettes.py:101
+#: ../src/jarabe/journal/palettes.py:107
msgid "View Details"
msgstr "Details betrachten"
-#: ../src/jarabe/journal/palettes.py:179
+#: ../src/jarabe/journal/palettes.py:181
msgid "No friends present"
msgstr "Keine Freunde anwesend"
-#: ../src/jarabe/journal/palettes.py:184
+#: ../src/jarabe/journal/palettes.py:186
msgid "No valid connection found"
msgstr "Keine gültige Verbindung gefunden"
-#: ../src/jarabe/journal/palettes.py:212
+#: ../src/jarabe/journal/palettes.py:214
msgid "No activity to resume entry"
msgstr "Keine Aktivität, um den Eintrag fortzusetzen"
-#: ../src/jarabe/journal/palettes.py:214
-msgid "No activity to start entry"
-msgstr "Keine Aktivität, um den Eintrag zu beginnen"
+#: ../src/jarabe/model/network.py:158
+msgid "The reason for the device state change is unknown."
+msgstr "Der Grund für die Zustandsänderung des Gerätes ist unbekannt."
+
+#: ../src/jarabe/model/network.py:160
+msgid "The state change is normal."
+msgstr "Die Zustandsänderung ist normal."
+
+#: ../src/jarabe/model/network.py:162
+msgid "The device is now managed."
+msgstr "Das Gerät wird nun verwaltet."
+
+#: ../src/jarabe/model/network.py:164
+msgid "The device is no longer managed."
+msgstr "Das Gerät wird nicht länger verwaltet."
+
+#: ../src/jarabe/model/network.py:166
+msgid "The device could not be readied for configuration."
+msgstr "Das Gerät konnte nicht für die Konfiguration bereitgestellt werden."
+
+#: ../src/jarabe/model/network.py:168
+msgid ""
+"IP configuration could not be reserved (no available address, timeout, etc)."
+msgstr ""
+"Die IP-Konfiguration konnte nicht umgesetzt werden (keine Adresse verfügbar, "
+"Zeitüberschreitung etc.)."
+
+#: ../src/jarabe/model/network.py:171
+msgid "The IP configuration is no longer valid."
+msgstr "Die IP-Konfiguration ist nicht länger gültig."
+
+#: ../src/jarabe/model/network.py:173
+msgid "Secrets were required, but not provided."
+msgstr "Geheimnisse wurden angefordert, aber nicht bereitgestellt."
+
+#: ../src/jarabe/model/network.py:175
+msgid ""
+"The 802.1X supplicant disconnected from the access point or authentication "
+"server."
+msgstr ""
+"Der 802.1X-Supplikant wurde vom Zugangspunkt oder Authentifizierungsserver "
+"getrennt."
+
+#: ../src/jarabe/model/network.py:178
+msgid "Configuration of the 802.1X supplicant failed."
+msgstr "Konfiguration des 802.1X-Supplikanten schlug fehl."
+
+#: ../src/jarabe/model/network.py:180
+msgid "The 802.1X supplicant quit or failed unexpectedly."
+msgstr "Der 802.1X-Supplikant brach ab oder scheiterte unerwartet."
+
+#: ../src/jarabe/model/network.py:182
+msgid "The 802.1X supplicant took too long to authenticate."
+msgstr "Der 802.1X-Supplikant benötigte zu lange für die Authentifizierung."
+
+#: ../src/jarabe/model/network.py:184
+msgid "The PPP service failed to start within the allowed time."
+msgstr "Der PPP-Dienst startete nicht in der vorgegebenen Zeit."
+
+#: ../src/jarabe/model/network.py:186
+msgid "The PPP service disconnected unexpectedly."
+msgstr "Der PPP-Dienst wurde unerwartet getrennt."
+
+#: ../src/jarabe/model/network.py:188
+msgid "The PPP service quit or failed unexpectedly."
+msgstr "Der PPP-Dienst brach ab oder scheiterte unerwartet."
+
+#: ../src/jarabe/model/network.py:190
+msgid "The DHCP service failed to start within the allowed time."
+msgstr "Der DHCP-Dienst startete nicht in der vorgegebenen Zeit."
+
+#: ../src/jarabe/model/network.py:192
+msgid "The DHCP service reported an unexpected error."
+msgstr "Der DHCP-Dienst meldete einen unerwarteten Fehler."
+
+#: ../src/jarabe/model/network.py:194
+msgid "The DHCP service quit or failed unexpectedly."
+msgstr "Der DHCP-Dienst brach ab oder scheiterte unerwartet."
+
+#: ../src/jarabe/model/network.py:196
+msgid "The shared connection service failed to start."
+msgstr "Der Dienst für geteilte Verbindungen konnte nicht starten."
+
+#: ../src/jarabe/model/network.py:198
+msgid "The shared connection service quit or failed unexpectedly."
+msgstr ""
+"Der Dienst für geteilte Verbindungen brach ab oder scheiterte unerwartet."
-#: ../src/jarabe/view/buddymenu.py:62
+#: ../src/jarabe/model/network.py:201
+msgid "The AutoIP service failed to start."
+msgstr "Der AutoIP-Dienst konnte nicht starten."
+
+#: ../src/jarabe/model/network.py:203
+msgid "The AutoIP service reported an unexpected error."
+msgstr "Der AutoIP-Dienst meldete einen unerwarteten Fehler."
+
+#: ../src/jarabe/model/network.py:205
+msgid "The AutoIP service quit or failed unexpectedly."
+msgstr "Der AutoIP-Dienst brach ab oder scheiterte unerwartet."
+
+#: ../src/jarabe/model/network.py:207
+msgid "Dialing failed because the line was busy."
+msgstr "Die Einwahl schlug fehl, weil die Leitung belegt war."
+
+#: ../src/jarabe/model/network.py:209
+msgid "Dialing failed because there was no dial tone."
+msgstr "Die Einwahl schlug mangels Einwahlton fehl."
+
+#: ../src/jarabe/model/network.py:211
+msgid "Dialing failed because there was no carrier."
+msgstr "Die Einwahl schlug mangels Leitung fehl."
+
+#: ../src/jarabe/model/network.py:213
+msgid "Dialing timed out."
+msgstr "Zeitüberschreitung bei der Einwahl."
+
+#: ../src/jarabe/model/network.py:215
+msgid "Dialing failed."
+msgstr "Einwahl fehlgeschlagen."
+
+#: ../src/jarabe/model/network.py:217
+msgid "Modem initialization failed."
+msgstr "Initialisierung des Modems fehlgeschlagen."
+
+#: ../src/jarabe/model/network.py:219
+msgid "Failed to select the specified GSM APN"
+msgstr "Konnte den angegebenen GSM-Zugangspunkt (APN) nicht anwählen."
+
+#: ../src/jarabe/model/network.py:221
+msgid "Not searching for networks."
+msgstr "Suche nicht nach Netzwerken."
+
+#: ../src/jarabe/model/network.py:223
+msgid "Network registration was denied."
+msgstr "Die Netzwerkregistrierung wurde verweigert."
+
+#: ../src/jarabe/model/network.py:225
+msgid "Network registration timed out."
+msgstr "Zeitüberschreitung bei der Netzwerkregistrierung."
+
+#: ../src/jarabe/model/network.py:227
+msgid "Failed to register with the requested GSM network."
+msgstr "Registrierung bei dem angeforderten GSM-Netzwerk fehlgeschlagen."
+
+#: ../src/jarabe/model/network.py:229
+msgid "PIN check failed."
+msgstr "PIN-Überprüfung fehlgeschlagen."
+
+#: ../src/jarabe/model/network.py:231
+msgid "Necessary firmware for the device may be missing."
+msgstr "Möglicherweise fehlt die nötige Firmware für das Gerät."
+
+#: ../src/jarabe/model/network.py:233
+msgid "The device was removed."
+msgstr "Das Gerät wurde entfernt."
+
+#: ../src/jarabe/model/network.py:235
+msgid "NetworkManager went to sleep."
+msgstr "Der Netzwerk-Manager ist nun im Ruhezustand."
+
+#: ../src/jarabe/model/network.py:237
+msgid "The device's active connection was removed or disappeared."
+msgstr "Die aktive Verbindung des Gerätes wurde entfernt oder ist verschwunden."
+
+#: ../src/jarabe/model/network.py:240
+msgid "A user or client requested the disconnection."
+msgstr "Ein Benutzer oder Client forderte die Verbindungstrennung."
+
+#: ../src/jarabe/model/network.py:242
+msgid "The device's carrier/link changed."
+msgstr "Die Leitung/Verbindung des Gerätes hat sich geändert."
+
+#: ../src/jarabe/view/buddymenu.py:63
msgid "Remove friend"
msgstr "Freund entfernen"
-#: ../src/jarabe/view/buddymenu.py:65
+#: ../src/jarabe/view/buddymenu.py:66
msgid "Make friend"
msgstr "Freunde werden"
-#: ../src/jarabe/view/buddymenu.py:82
+#: ../src/jarabe/view/buddymenu.py:83
msgid "Shutdown"
-msgstr "Rechner Ausschalten"
+msgstr "Rechner ausschalten"
-#: ../src/jarabe/view/buddymenu.py:90
+#: ../src/jarabe/view/buddymenu.py:91
+msgid "Restart"
+msgstr "Rechner neu starten"
+
+#: ../src/jarabe/view/buddymenu.py:97
msgid "Logout"
-msgstr "Benutzer Abmelden"
+msgstr "Benutzer abmelden"
-#: ../src/jarabe/view/buddymenu.py:95
+#: ../src/jarabe/view/buddymenu.py:102
msgid "My Settings"
msgstr "Meine Einstellungen"
-#: ../src/jarabe/view/buddymenu.py:130
+#: ../src/jarabe/view/buddymenu.py:137
#, python-format
msgid "Invite to %s"
msgstr "Einladen zu %s"
-#: ../src/jarabe/view/launcher.py:192
+#: ../src/jarabe/view/launcher.py:190
#, python-format
msgid "<b>%s</b> failed to start."
msgstr "<b>%s</b> konnte nicht gestartet werden."
-#: ../src/jarabe/view/palettes.py:45
+#: ../src/jarabe/view/palettes.py:46
msgid "Starting..."
msgstr "Starte..."
+#: ../src/jarabe/view/palettes.py:56
+msgid "Activity failed to start"
+msgstr "Aktivität konnte nicht gestartet werden."
+
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:74
+#: ../src/jarabe/view/palettes.py:85
msgid "View Source"
-msgstr "Quelltext betrachten"
+msgstr "Quelltext anzeigen"
-#: ../src/jarabe/view/palettes.py:85
+#: ../src/jarabe/view/palettes.py:96
msgid "Stop"
msgstr "Beenden"
-#: ../src/jarabe/view/palettes.py:125
+#: ../src/jarabe/view/palettes.py:132
msgid "Start new"
msgstr "Neu beginnen"
-#: ../src/jarabe/view/palettes.py:174
+#: ../src/jarabe/view/palettes.py:172
msgid "Show contents"
msgstr "Inhalte anzeigen"
-#: ../src/jarabe/view/palettes.py:196 ../src/jarabe/view/palettes.py:246
+#: ../src/jarabe/view/palettes.py:194 ../src/jarabe/view/palettes.py:245
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB frei"
-#: ../src/jarabe/view/viewsource.py:208
+#: ../src/jarabe/view/viewsource.py:211
msgid "Instance Source"
msgstr "Quelltext der Instanz"
-#: ../src/jarabe/view/viewsource.py:233
+#: ../src/jarabe/view/viewsource.py:236
msgid "Source"
msgstr "Quelltext"
-#: ../src/jarabe/view/viewsource.py:294
+#: ../src/jarabe/view/viewsource.py:295
msgid "Activity Bundle Source"
msgstr "Quelltext des Aktivitätenbündels"
-#: ../src/jarabe/view/viewsource.py:301
+#: ../src/jarabe/view/viewsource.py:302
#, python-format
msgid "View source: %r"
-msgstr "Quelltext betrachten: %r"
+msgstr "Quelltext anzeigen: %r"
+
+#: ../src/jarabe/util/emulator.py:40
+msgid "Sugar in a window"
+msgstr "Sugar in einem Fenster"
+
+#~ msgid "Create new wireless network"
+#~ msgstr "Neues Funknetzwerk erstellen"
+
+#~ msgid "Sim requires Pin/Puk"
+#~ msgstr "Sim benötigt PIN/PUK"
+
+#~ msgid "Authentication Error"
+#~ msgstr "Authentifizierungsfehler"
+
+#, python-format
+#~ msgid "%s's network"
+#~ msgstr "Netzwerk von %s"
+
+#, python-format
+#~ msgid "Data sent %d KB / received %d KB"
+#~ msgstr "gesendet %d KB / empfangen %d KB"
+
+#~ msgid "Connection time "
+#~ msgstr "Verbindungsdauer "
#~ msgid "APN:"
#~ msgstr "APN (Zugangspunkt):"
@@ -1470,9 +1834,6 @@ msgstr "Quelltext betrachten: %r"
#~ msgid "Unmount"
#~ msgstr "Einbindung lösen"
-#~ msgid "Restart"
-#~ msgstr "Neustart"
-
#~ msgid ""
#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
#~ msgstr ""
diff --git a/po/es.po b/po/es.po
index 587608f..a28bcd9 100644
--- a/po/es.po
+++ b/po/es.po
@@ -2,20 +2,40 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
msgid ""
msgstr ""
"Project-Id-Version: olpc-sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-09-05 00:31-0400\n"
-"PO-Revision-Date: 2010-01-18 19:18+0200\n"
-"Last-Translator: Chris <cjl@laptop.org>\n"
+"POT-Creation-Date: 2011-02-06 00:30-0500\n"
+"PO-Revision-Date: 2011-02-10 18:26+0200\n"
+"Last-Translator: Gonzalo <godiard@sugarlabs.org>\n"
"Language-Team: Fedora Spanish <fedora-trans-es@redhat.com>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Pootle 2.0.1\n"
+"X-Generator: Pootle 2.0.3\n"
"X-Poedit-Language: Spanish\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Poedit-Basepath: .\n"
@@ -24,43 +44,39 @@ msgstr ""
msgid "About Me"
msgstr "Acerca de mí"
-#: ../extensions/cpsection/aboutme/model.py:43
+#: ../extensions/cpsection/aboutme/model.py:48
msgid "You must enter a name."
msgstr "Debe ingresar un nombre."
-#: ../extensions/cpsection/aboutme/model.py:68
+#: ../extensions/cpsection/aboutme/model.py:75
#, python-format
msgid "stroke: color=%s hue=%s"
msgstr "borde: color=%s tonalidad=%s"
-#: ../extensions/cpsection/aboutme/model.py:71
+#: ../extensions/cpsection/aboutme/model.py:78
#, python-format
msgid "stroke: %s"
msgstr "borde: %s"
-#: ../extensions/cpsection/aboutme/model.py:73
+#: ../extensions/cpsection/aboutme/model.py:80
#, python-format
msgid "fill: color=%s hue=%s"
msgstr "relleno: color=%s tonalidad=%s"
-#: ../extensions/cpsection/aboutme/model.py:75
+#: ../extensions/cpsection/aboutme/model.py:82
#, python-format
msgid "fill: %s"
msgstr "relleno: %s"
-#: ../extensions/cpsection/aboutme/model.py:86
+#: ../extensions/cpsection/aboutme/model.py:94
msgid "Error in specified color modifiers."
msgstr "Error en modificadores de color especificados."
-#: ../extensions/cpsection/aboutme/model.py:89
+#: ../extensions/cpsection/aboutme/model.py:97
msgid "Error in specified colors."
msgstr "Error en colores especificados."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:92
-msgid "Name:"
-msgstr "Nombre:"
-
-#: ../extensions/cpsection/aboutme/view.py:128
+#: ../extensions/cpsection/aboutme/view.py:235
msgid "Click to change your color:"
msgstr "Clic para cambiar su color:"
@@ -68,56 +84,61 @@ msgstr "Clic para cambiar su color:"
msgid "About my Computer"
msgstr "Acerca de mi computadora"
-#: ../extensions/cpsection/aboutcomputer/model.py:28
+#: ../extensions/cpsection/aboutcomputer/model.py:37
msgid "Not available"
msgstr "No disponible"
-#: ../extensions/cpsection/aboutcomputer/view.py:60
+#: ../extensions/cpsection/aboutcomputer/model.py:158
+#, python-format
+msgid "%(interface)s: %(version)s"
+msgstr "%(interface)s: %(version)s"
+
+#: ../extensions/cpsection/aboutcomputer/view.py:61
msgid "Identity"
msgstr "Identidad"
-#: ../extensions/cpsection/aboutcomputer/view.py:69
+#: ../extensions/cpsection/aboutcomputer/view.py:70
msgid "Serial Number:"
msgstr "Número de serie:"
-#: ../extensions/cpsection/aboutcomputer/view.py:91
+#: ../extensions/cpsection/aboutcomputer/view.py:92
msgid "Software"
msgstr "Software"
# Por ahora..
-#: ../extensions/cpsection/aboutcomputer/view.py:100
+#: ../extensions/cpsection/aboutcomputer/view.py:101
msgid "Build:"
msgstr "Ensamble:"
-#: ../extensions/cpsection/aboutcomputer/view.py:115
+#: ../extensions/cpsection/aboutcomputer/view.py:116
msgid "Sugar:"
msgstr "Azúcar:"
-#: ../extensions/cpsection/aboutcomputer/view.py:131
+#: ../extensions/cpsection/aboutcomputer/view.py:132
msgid "Firmware:"
msgstr "Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:146
+#: ../extensions/cpsection/aboutcomputer/view.py:147
msgid "Wireless Firmware:"
-msgstr "Firmware Wireless:"
+msgstr "Firmware de la red inalámbrica:"
-#: ../extensions/cpsection/aboutcomputer/view.py:169
+#: ../extensions/cpsection/aboutcomputer/view.py:170
msgid "Copyright and License"
-msgstr "Licencia y Copyright"
+msgstr "Licencia y derechos de autor"
-#: ../extensions/cpsection/aboutcomputer/view.py:184
+#: ../extensions/cpsection/aboutcomputer/view.py:188
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 "
"change it and/or distribute copies of it under certain conditions described "
"therein."
msgstr ""
-"Azucar es la interfaz gráfica de usuario que usted esta mirando. Azucar es "
+"Azucar es la interfaz gráfica de usuario que usted esta mirando. Azucar es "
"software libre, cubierto bajo la licencia GNU Licencia Publica General, y "
"esta invitado a cambiarla y/o distribuir copias bajo ciertas condiciones que "
"se describen en ella."
-#: ../extensions/cpsection/aboutcomputer/view.py:196
+#: ../extensions/cpsection/aboutcomputer/view.py:200
msgid "Full license:"
msgstr "Licencia completa:"
@@ -125,11 +146,11 @@ msgstr "Licencia completa:"
msgid "Date & Time"
msgstr "Fecha y hora"
-#: ../extensions/cpsection/datetime/model.py:87
+#: ../extensions/cpsection/datetime/model.py:92
msgid "Error timezone does not exist."
msgstr "Error, zona horaria no existe."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:27
+#: ../extensions/cpsection/datetime/view.py:70 ../data/sugar.schemas.in.h:52
msgid "Timezone"
msgstr "Zona horaria"
@@ -137,50 +158,50 @@ msgstr "Zona horaria"
msgid "Frame"
msgstr "Cuadro"
-#: ../extensions/cpsection/frame/model.py:38
-#: ../extensions/cpsection/frame/model.py:60
+#: ../extensions/cpsection/frame/model.py:41
+#: ../extensions/cpsection/frame/model.py:66
msgid "Value must be an integer."
msgstr "El valor debe ser un número entero."
-#: ../extensions/cpsection/frame/view.py:26
+#: ../extensions/cpsection/frame/view.py:27
msgid "never"
msgstr "nunca"
-#: ../extensions/cpsection/frame/view.py:27
+#: ../extensions/cpsection/frame/view.py:28
msgid "instantaneous"
msgstr "instantáneo"
-#: ../extensions/cpsection/frame/view.py:28
+#: ../extensions/cpsection/frame/view.py:29
#, python-format
msgid "%s seconds"
msgstr "%s segundos"
-#: ../extensions/cpsection/frame/view.py:52
+#: ../extensions/cpsection/frame/view.py:54
msgid "Activation Delay"
msgstr "Retraso de activación"
-#: ../extensions/cpsection/frame/view.py:76
+#: ../extensions/cpsection/frame/view.py:78
msgid "Corner"
msgstr "Esquina"
-#: ../extensions/cpsection/frame/view.py:111
+#: ../extensions/cpsection/frame/view.py:113
msgid "Edge"
msgstr "Borde"
#: ../extensions/cpsection/keyboard/__init__.py:21
-#: ../extensions/cpsection/keyboard/view.py:31
+#: ../extensions/cpsection/keyboard/view.py:32
msgid "Keyboard"
msgstr "Teclado"
-#: ../extensions/cpsection/keyboard/view.py:187
+#: ../extensions/cpsection/keyboard/view.py:190
msgid "Keyboard Model"
msgstr "Modelo de teclado"
-#: ../extensions/cpsection/keyboard/view.py:243
+#: ../extensions/cpsection/keyboard/view.py:250
msgid "Key(s) to change layout"
msgstr "Tecla(s) para cambiar el diseño"
-#: ../extensions/cpsection/keyboard/view.py:311
+#: ../extensions/cpsection/keyboard/view.py:319
msgid "Keyboard Layout(s)"
msgstr "Diseño(s) de teclado"
@@ -189,22 +210,22 @@ msgstr "Diseño(s) de teclado"
msgid "Language"
msgstr "Idioma"
-#: ../extensions/cpsection/language/model.py:28
+#: ../extensions/cpsection/language/model.py:30
msgid "Could not access ~/.i18n. Create standard settings."
msgstr ""
"No se puede acceder a ~/.i18n. Crear configuración internacional estándar."
-#: ../extensions/cpsection/language/model.py:124
+#: ../extensions/cpsection/language/model.py:131
#, python-format
msgid "Language for code=%s could not be determined."
msgstr "El lenguaje del código=%s no pudo ser determinado."
-#: ../extensions/cpsection/language/model.py:144
+#: ../extensions/cpsection/language/model.py:152
#, python-format
msgid "Sorry I do not speak '%s'."
msgstr "Lo siento, yo no hablo '%s'."
-#: ../extensions/cpsection/language/view.py:56
+#: ../extensions/cpsection/language/view.py:57
msgid ""
"Add languages in the order you prefer. If a translation is not available, "
"the next in the list will be used."
@@ -212,48 +233,84 @@ msgstr ""
"Añade idiomas en el orden que prefieres. Si una traducción no se encuentra "
"disponible, se usará la siguiente en la lista."
+#: ../extensions/cpsection/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Configuración del módem"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:94
+msgid "Username:"
+msgstr "Nombre de usuario:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:106
+msgid "Password:"
+msgstr "Contraseña:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:118
+msgid "Number:"
+msgstr "Número:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:130
+msgid "Access Point Name (APN):"
+msgstr "Nombre de punto de acceso (APN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:142
+msgid "Personal Identity Number (PIN):"
+msgstr "Número de identificación personal (PIN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:154
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Clave personal de desbloqueo (PUK):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:175
+msgid ""
+"You will need to provide the following information to set up a mobile "
+"broadband connection to a cellular (3G) network."
+msgstr ""
+"Necesitará proveer la información siguiente para configurar una conexión de "
+"banda ancha (3G) de red de telefonía celular."
+
#: ../extensions/cpsection/network/__init__.py:21
-#: ../extensions/cpsection/network/view.py:28
+#: ../extensions/cpsection/network/view.py:29
msgid "Network"
msgstr "Red"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:71
msgid "State is unknown."
msgstr "Estado desconocido."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:99
msgid "Error in specified radio argument use on/off."
msgstr "Error en argumento especificado de radio use on/off."
-#: ../extensions/cpsection/network/model.py:137
+#: ../extensions/cpsection/network/model.py:140
msgid "Error in specified argument use 0/1."
msgstr "Error en argumento especificado use 0/1."
-#: ../extensions/cpsection/network/view.py:59
+#: ../extensions/cpsection/network/view.py:61
msgid "Wireless"
msgstr "Inalámbrica"
-#: ../extensions/cpsection/network/view.py:67
+#: ../extensions/cpsection/network/view.py:69
msgid "Turn off the wireless radio to save battery life"
msgstr "Apague la radio inalámbrica y ahorre vida de batería"
-#: ../extensions/cpsection/network/view.py:80
+#: ../extensions/cpsection/network/view.py:82
msgid "Radio"
msgstr "Radio"
-#: ../extensions/cpsection/network/view.py:96
+#: ../extensions/cpsection/network/view.py:98
msgid "Discard network history if you have trouble connecting to the network"
msgstr "Descarte el historial de la red si tiene problemas de conexión"
-#: ../extensions/cpsection/network/view.py:105
+#: ../extensions/cpsection/network/view.py:107
msgid "Discard network history"
msgstr "Descarte historial de la red"
-#: ../extensions/cpsection/network/view.py:118
+#: ../extensions/cpsection/network/view.py:122
msgid "Collaboration"
msgstr "Colaboración"
-#: ../extensions/cpsection/network/view.py:126
+#: ../extensions/cpsection/network/view.py:130
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 "
@@ -262,7 +319,7 @@ msgstr ""
"El servidor es equivalente al cuarto en el cual se esta; la gente en el "
"mismo servidor podrá verse entre ellos, aun cuando no esten en la misma red."
-#: ../extensions/cpsection/network/view.py:136
+#: ../extensions/cpsection/network/view.py:140
msgid "Server:"
msgstr "Servidor:"
@@ -270,24 +327,24 @@ msgstr "Servidor:"
msgid "Power"
msgstr "Energía"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:90
msgid "Error in automatic pm argument, use on/off."
-msgstr "Error en argumento automático de pm, use on/off."
+msgstr "Error en argumento automático de manejo de energía, use on/off."
-#: ../extensions/cpsection/power/model.py:81
+#: ../extensions/cpsection/power/model.py:120
msgid "Error in extreme pm argument, use on/off."
-msgstr "Error en argumento extremo de pm, use on/off."
+msgstr "Error en argumento extremo de manejo de energía, use on/off."
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:48
msgid "Power management"
msgstr "Manejo de energía"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:58
msgid "Automatic power management (increases battery life)"
msgstr "Manejo automático de energía (incrementa la vida de la batería)"
# best translationfor now
-#: ../extensions/cpsection/power/view.py:85
+#: ../extensions/cpsection/power/view.py:86
msgid ""
"Extreme power management (disableswireless radio, increases battery life)"
msgstr ""
@@ -296,9 +353,9 @@ msgstr ""
#: ../extensions/cpsection/updater/__init__.py:21
msgid "Software update"
-msgstr "Actualización de Software"
+msgstr "Actualización de software"
-#: ../extensions/cpsection/updater/view.py:62
+#: ../extensions/cpsection/updater/view.py:63
msgid ""
"Software updates correct errors, eliminate security vulnerabilities, and "
"provide new features."
@@ -306,109 +363,109 @@ msgstr ""
"Las actualizaciones de software corrigen errores, eliminan vulnerabilidades "
"de seguridad y proveen nuevas características."
-#: ../extensions/cpsection/updater/view.py:122
+#: ../extensions/cpsection/updater/view.py:125
#, python-format
msgid "Checking %s..."
msgstr "Probando %s..."
-#: ../extensions/cpsection/updater/view.py:124
+#: ../extensions/cpsection/updater/view.py:127
#, python-format
msgid "Downloading %s..."
msgstr "Descargando %s..."
-#: ../extensions/cpsection/updater/view.py:126
+#: ../extensions/cpsection/updater/view.py:129
#, python-format
msgid "Updating %s..."
msgstr "Actualizando %s..."
-#: ../extensions/cpsection/updater/view.py:135
+#: ../extensions/cpsection/updater/view.py:139
msgid "Your software is up-to-date"
msgstr "Tu software esta actualizado"
-#: ../extensions/cpsection/updater/view.py:137
+#: ../extensions/cpsection/updater/view.py:141
#, python-format
msgid "You can install %s update"
msgid_plural "You can install %s updates"
msgstr[0] "Puedes instalar %s actualización"
msgstr[1] "Puedes instalar %s actualizaciones"
-#: ../extensions/cpsection/updater/view.py:155
+#: ../extensions/cpsection/updater/view.py:159
msgid "Checking for updates..."
msgstr "Buscando actualizaciones..."
-#: ../extensions/cpsection/updater/view.py:160
+#: ../extensions/cpsection/updater/view.py:164
msgid "Installing updates..."
msgstr "Instalando actualizaciones..."
-#: ../extensions/cpsection/updater/view.py:165
+#: ../extensions/cpsection/updater/view.py:173
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
msgstr[0] "%s actualización fue instalada"
msgstr[1] "%s actualizaciones fueron instaladas"
-#: ../extensions/cpsection/updater/view.py:244
+#: ../extensions/cpsection/updater/view.py:255
msgid "Install selected"
msgstr "Instalación seleccionada"
-#: ../extensions/cpsection/updater/view.py:265
+#: ../extensions/cpsection/updater/view.py:276
#, python-format
msgid "Download size: %s"
msgstr "Tamaño de descarga: %s"
-#: ../extensions/cpsection/updater/view.py:353
+#: ../extensions/cpsection/updater/view.py:364
#, python-format
-msgid "From version %(current)d to %(new)s (Size: %(size)s)"
-msgstr "Desde la version %(current)d hacia %(new)s (Size: %(size)s)"
+msgid "From version %(current)s to %(new)s (Size: %(size)s)"
+msgstr "Desde la version %(current)s hacia %(new)s (Size: %(size)s)"
#. TRANS: download size is 0
-#: ../extensions/cpsection/updater/view.py:373
+#: ../extensions/cpsection/updater/view.py:382
msgid "None"
msgstr "Ninguno"
#. TRANS: download size of very small updates
-#: ../extensions/cpsection/updater/view.py:376
+#: ../extensions/cpsection/updater/view.py:385
msgid "1 KB"
msgstr "1 KB"
#. TRANS: download size of small updates, e.g. '250 KB'
-#: ../extensions/cpsection/updater/view.py:379
+#: ../extensions/cpsection/updater/view.py:388
#, python-format
msgid "%.0f KB"
msgstr "%.0f KB"
#. TRANS: download size of updates, e.g. '2.3 MB'
-#: ../extensions/cpsection/updater/view.py:382
+#: ../extensions/cpsection/updater/view.py:391
#, python-format
msgid "%.1f MB"
msgstr "%.1f MB"
-#: ../extensions/deviceicon/battery.py:58
+#: ../extensions/deviceicon/battery.py:60
msgid "My Battery"
msgstr "Mi batería"
-#: ../extensions/deviceicon/battery.py:137
+#: ../extensions/deviceicon/battery.py:141
msgid "Removed"
msgstr "Eliminado"
-#: ../extensions/deviceicon/battery.py:140
+#: ../extensions/deviceicon/battery.py:144
msgid "Charging"
msgstr "Cargando"
-#: ../extensions/deviceicon/battery.py:143
+#: ../extensions/deviceicon/battery.py:147
msgid "Very little power remaining"
msgstr "Queda muy poca batería"
-#: ../extensions/deviceicon/battery.py:149
+#: ../extensions/deviceicon/battery.py:153
#, python-format
msgid "%(hour)d:%(min).2d remaining"
msgstr "Quedan %(hour)d:%(min).2d"
-#: ../extensions/deviceicon/battery.py:152
+#: ../extensions/deviceicon/battery.py:156
msgid "Charged"
msgstr "Cargada"
-#: ../extensions/deviceicon/network.py:44
+#: ../extensions/deviceicon/network.py:49
#, python-format
msgid "IP address: %s"
msgstr "Direccion IP: %s"
@@ -417,125 +474,336 @@ msgstr "Direccion 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:110
+#: ../extensions/deviceicon/network.py:104
msgid "Disconnect..."
msgstr "Desconectando..."
-#: ../extensions/deviceicon/network.py:114
-msgid "Create new wireless network"
-msgstr "Crear nueva red inalámbrica"
-
-#: ../extensions/deviceicon/network.py:120
-#: ../src/jarabe/desktop/meshbox.py:264
+#: ../extensions/deviceicon/network.py:112
+#: ../extensions/deviceicon/network.py:290
+#: ../src/jarabe/desktop/networkviews.py:241
+#: ../src/jarabe/desktop/networkviews.py:546
+#: ../src/jarabe/desktop/networkviews.py:673
msgid "Connecting..."
msgstr "Conectando..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:124
-#: ../extensions/deviceicon/network.py:186
-#: ../src/jarabe/desktop/meshbox.py:270
+#: ../extensions/deviceicon/network.py:116
+#: ../extensions/deviceicon/network.py:182
+#: ../src/jarabe/desktop/networkviews.py:251
+#: ../src/jarabe/desktop/networkviews.py:552
+#: ../src/jarabe/desktop/networkviews.py:679
msgid "Connected"
msgstr "Conectado"
-#: ../extensions/deviceicon/network.py:146
+#: ../extensions/deviceicon/network.py:142
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:161
+#: ../extensions/deviceicon/network.py:157
msgid "Wired Network"
msgstr "Red Cableada"
-#: ../extensions/deviceicon/network.py:189
+#: ../extensions/deviceicon/network.py:185
msgid "Speed"
msgstr "Velocidad"
-#: ../extensions/deviceicon/network.py:415
+#: ../extensions/deviceicon/network.py:211
+msgid "Wireless modem"
+msgstr "Módem inalámbrico"
+
+#: ../extensions/deviceicon/network.py:278
+msgid "Please wait..."
+msgstr "Espere por favor..."
+
+#: ../extensions/deviceicon/network.py:282
+#: ../src/jarabe/desktop/networkviews.py:134
+#: ../src/jarabe/desktop/networkviews.py:500
+#: ../src/jarabe/desktop/networkviews.py:630
+msgid "Connect"
+msgstr "Conectar"
+
+#: ../extensions/deviceicon/network.py:283
+msgid "Disconnected"
+msgstr "Desconectado"
+
+#: ../extensions/deviceicon/network.py:289
+#: ../src/jarabe/controlpanel/toolbar.py:119
+#: ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/frame/activitiestray.py:587
+#: ../src/jarabe/frame/activitiestray.py:687
+#: ../src/jarabe/frame/activitiestray.py:715
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: ../extensions/deviceicon/network.py:297
+#: ../src/jarabe/desktop/networkviews.py:138
+#: ../src/jarabe/desktop/networkviews.py:504
+msgid "Disconnect"
+msgstr "Desconectar"
+
+#: ../extensions/deviceicon/network.py:327
+msgid "Try connection again"
+msgstr "Probar la conexión nuevamente"
+
+#: ../extensions/deviceicon/network.py:330
+#, python-format
+msgid "Error: %s"
+msgstr "Error: %s"
+
+#: ../extensions/deviceicon/network.py:334
+#, python-format
+msgid "Suggestion: %s"
+msgstr "Sugerencia: %s"
+
+#: ../extensions/deviceicon/network.py:340
+#: ../extensions/deviceicon/network.py:343
+#, python-format
+msgid "Connected for %s"
+msgstr "Conectado a %s"
+
+#: ../extensions/deviceicon/network.py:348
+#: ../extensions/deviceicon/network.py:349
+#, python-format
+msgid "%d KB"
+msgstr "%d KB"
+
+#: ../extensions/deviceicon/network.py:354
+msgid "Check your Pin/Puk configuration."
+msgstr "Revise la configuración de su Pin/Puk."
+
+#: ../extensions/deviceicon/network.py:357
+msgid "Check your Access Point Name (APN) configuration"
+msgstr "Revise la configuración del nombre de su punto de acceso (APN)"
+
+#: ../extensions/deviceicon/network.py:361
+msgid "Check the Number configuration."
+msgstr "Revise el Número en la configuración."
+
+#: ../extensions/deviceicon/network.py:363
+msgid "Check your configuration."
+msgstr "Revise su configuración."
+
+#: ../extensions/deviceicon/network.py:605
+msgid "Mesh Network"
+msgstr "Red Malla"
+
+#: ../extensions/deviceicon/network.py:648
+#, python-format
+msgid "Mesh Network %s"
+msgstr "Red Malla %s"
+
+#: ../extensions/deviceicon/network.py:771
+msgid "No GSM connection available."
+msgstr "No se dispone de conexión GSM."
+
+#: ../extensions/deviceicon/network.py:772
+msgid "Create a connection in the control panel."
+msgstr "Crear una conexión en el panel de control."
+
+#: ../extensions/deviceicon/resources.py:48
+msgid "System resources"
+msgstr "Recursos del sistema"
+
+#: ../extensions/deviceicon/resources.py:170
#, python-format
-msgid "%s's network %s"
-msgstr "%s's red %s"
+msgid "CPU in use: %d%%"
+msgstr "CPU en uso: %d%%"
-#: ../extensions/deviceicon/speaker.py:59
+#: ../extensions/deviceicon/resources.py:172
+#, python-format
+msgid "Memory in use: %d%%"
+msgstr "Memoria en uso: %d%%"
+
+#: ../extensions/deviceicon/resources.py:202
+msgid "Cannot compute CPU and memory usage statistics!"
+msgstr "No puedo calcular las estadísticas de uso de CPU y memoria!"
+
+#: ../extensions/deviceicon/speaker.py:60
msgid "My Speakers"
msgstr "Mis parlantes"
# la traducción la tome del AlsaMixer de Gnome.
-#: ../extensions/deviceicon/speaker.py:133
+#: ../extensions/deviceicon/speaker.py:136
msgid "Unmute"
msgstr "Dar voz"
-#: ../extensions/deviceicon/speaker.py:136
+#: ../extensions/deviceicon/speaker.py:139
msgid "Mute"
msgstr "Silenciar"
-#: ../extensions/globalkey/screenshot.py:56
+#: ../extensions/deviceicon/touchpad.py:37
+msgid "finger"
+msgstr "dedo"
+
+#: ../extensions/deviceicon/touchpad.py:38
+msgid "stylus"
+msgstr "estilo"
+
+#: ../extensions/deviceicon/touchpad.py:67
+msgid "My touchpad"
+msgstr "Mi superficie táctil"
+
+#: ../extensions/globalkey/screenshot.py:59
msgid "Mesh"
msgstr "Malla"
-#: ../extensions/globalkey/screenshot.py:58
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../extensions/globalkey/screenshot.py:61
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "Group"
msgstr "Grupo"
-#: ../extensions/globalkey/screenshot.py:60
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../extensions/globalkey/screenshot.py:63
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "Home"
msgstr "Hogar"
-#: ../extensions/globalkey/screenshot.py:66
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../extensions/globalkey/screenshot.py:69
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "Activity"
msgstr "Actividad"
-#: ../extensions/globalkey/screenshot.py:69
+#: ../extensions/globalkey/screenshot.py:72
msgid "Screenshot"
msgstr "Captura de pantalla"
-#: ../extensions/globalkey/screenshot.py:71
+#: ../extensions/globalkey/screenshot.py:74
#, python-format
msgid "Screenshot of \"%s\""
msgstr "Captura pantalla de \"%s\""
#: ../data/sugar.schemas.in.h:1
+msgid ""
+"\"disabled\" to ask nick on initialization; \"system\" to reuse UNIX account "
+"long name."
+msgstr ""
+"\"disabled\" (desactivado) para preguntar apodo al inicio; \"system\" (sistema) "
+"para reutilizar el nombre largo de la cuenta UNIX."
+
+#: ../data/sugar.schemas.in.h:2
+msgid "Additional directories which can contain updated translations."
+msgstr "Directorios adicionales que pueden contener traducciones actualizadas."
+
+#: ../data/sugar.schemas.in.h:3
msgid "Backup URL"
msgstr "URL de Respaldo"
-#: ../data/sugar.schemas.in.h:2
+#: ../data/sugar.schemas.in.h:4
+msgid "Bundle IDs of protected activities"
+msgstr "Bundle IDs de actividades protegidas"
+
+#: ../data/sugar.schemas.in.h:5
msgid ""
"Color for the XO icon that is used throughout the desktop. The string is "
-"composed of the stroke color and fill color, format is that of rbg colors. "
+"composed of the stroke color and fill color, format is that of rgb colors. "
"Example: #AC32FF,#9A5200"
msgstr ""
"El color para el ícono del XO se utiliza en todo el escritorio. La cadena "
"está compuesta por el trazo y color de relleno de color, el formato es el de "
-"colores RBG. Ejemplo: #AC32FF, #9A5200"
+"colores RGB. Ejemplo: #AC32FF, #9A5200"
# es la mejor traduccion ?
-#: ../data/sugar.schemas.in.h:3
+#: ../data/sugar.schemas.in.h:6
msgid "Corner Delay"
msgstr "Retraso de las Esquinas"
-#: ../data/sugar.schemas.in.h:4
+#: ../data/sugar.schemas.in.h:7
+msgid "Default font face"
+msgstr "Tipo de letra predeterminado"
+
+#: ../data/sugar.schemas.in.h:8
+msgid "Default font size"
+msgstr "Tamaño de letra predeterminado"
+
+#: ../data/sugar.schemas.in.h:9
+msgid "Default nick"
+msgstr "Apodo predeterminado"
+
+#: ../data/sugar.schemas.in.h:10
msgid "Delay for the activation of the frame using the corners."
msgstr "Retraso para la activación del cuadro utilizando las esquinas."
-#: ../data/sugar.schemas.in.h:5
+#: ../data/sugar.schemas.in.h:11
msgid "Delay for the activation of the frame using the edges."
msgstr "Retraso para la activación del cuadro utilizando los bordes."
+#: ../data/sugar.schemas.in.h:12
+msgid "Directory to search for translations"
+msgstr "Directorio a buscar para traducciones"
+
# es la mejor traduccion ?
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:13
msgid "Edge Delay"
msgstr "Retraso del Borde"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:14
msgid "Favorites Layout"
msgstr "Diseño de favoritos"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:15
msgid "Favorites resume mode"
msgstr "Modo de reanudar favoritos"
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:16
+msgid "Font face that is used throughout the desktop."
+msgstr "Tipo de letra que se utiliza en todo el escritorio."
+
+#: ../data/sugar.schemas.in.h:17
+msgid "Font size that is used throughout the desktop."
+msgstr "Tamaño de letra que se utiliza en todo el escritorio."
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network APN"
+msgstr "APN de la red GSM"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network PIN"
+msgstr "PIN de la red GSM"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network PUK"
+msgstr "PUK de la red GSM"
+
+# Puede evitarse el texto "(APN)"; pero es bueno ser preciso en detallarlo para usuarios no técnicos.
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network access point name configuration"
+msgstr "Configuración del nombre del punto de acceso (APN) de la red GSM"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network number"
+msgstr "Número de la red GSM"
+
+# Puede usarse el sinónimo "Clave" mas hay que usar el término conocido por el usuario.
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network password"
+msgstr "Contraseña de la red GSM"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network password configuration"
+msgstr "Configuración de la contraseña de la red GSM"
+
+# Puede excluirse el texto "(PIN)" mas es de ser precisos para usuarios no técnicos.
+#: ../data/sugar.schemas.in.h:25
+msgid "GSM network personal identification number configuration"
+msgstr "Configuración del numero de identificación personal (PIN) de la red GSM"
+
+# Puede no usarse el texto "(PUK)" pero es válido ser específico.
+#: ../data/sugar.schemas.in.h:26
+msgid "GSM network personal unlock key configuration"
+msgstr "Configuración de la clave de desbloqueo personal (PUK) de la red GSM"
+
+#: ../data/sugar.schemas.in.h:27
+msgid "GSM network telephone number configuration"
+msgstr "Configuración del número de teléfono de la red GSM"
+
+#: ../data/sugar.schemas.in.h:28
+msgid "GSM network username"
+msgstr "Nombre de usuario de la red GSM"
+
+#: ../data/sugar.schemas.in.h:29
+msgid "GSM network username configuration"
+msgstr "Configuración del nombre de usuario de la red GSM"
+
+#: ../data/sugar.schemas.in.h:30
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
@@ -543,110 +811,139 @@ msgstr ""
"Si es TRUE, Azúcar habilitará que otros usuarios nos busquen en el servidor "
"Jabber."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:31
msgid "If TRUE, Sugar will show a \"Log out\" option."
msgstr "Si es TRUE, Azúcar mostrará una opción \"Terminar Sesión\"."
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:32
+msgid "If TRUE, Sugar will show a \"Restart\" option."
+msgstr "Si es TRUE, Azúcar mostrará una opción \"Reiniciar Sesión\"."
+
+#: ../data/sugar.schemas.in.h:33
+msgid ""
+"If TRUE, Sugar will show default Ad-hoc networks for channel 1,6 and 11. If "
+"Sugar sees no \"known\" network when it starts, it does autoconnect to an Ad-"
+"hoc network."
+msgstr ""
+"Sí es TRUE, Azúcar mostrara las redes ad-hoc predefinidas para canales 1,6, "
+"y 11. Sí Azúcar no ve redes conocidas cuando inicia, se conectará "
+"automáticamente a una red ad-hoc."
+
+#: ../data/sugar.schemas.in.h:34
msgid "Jabber Server"
msgstr "Servidor Jabber"
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:35
msgid "Keyboard layouts"
msgstr "Distribuciones del teclado"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:36
msgid "Keyboard model"
msgstr "Modelo del teclado"
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:37
msgid "Keyboard options"
msgstr "Opciones del teclado"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:38
msgid "Layout of the favorites view."
msgstr "Distribución de las actividades favoritas."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:39
msgid ""
"List of keyboard layouts. Each entry should be in the form layout(variant)"
msgstr ""
"Lista de las distribuciones de teclado. Cada entrada debe ser en la forma "
"distribución(variante)"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:40
msgid "List of keyboard options."
msgstr "Lista de las opciones del teclado."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:41
msgid "Power Automatic"
msgstr "Manejo automática de energía"
-#: ../data/sugar.schemas.in.h:19
+#: ../data/sugar.schemas.in.h:42
msgid "Power Automatic."
msgstr "Manejo automática de energía."
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:43
msgid "Power Extreme"
msgstr "Manejo extremo de energía"
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:44
msgid "Power Extreme."
msgstr "Manejo extremo de energía."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:45
msgid "Publish to Gadget"
msgstr "Publicar en Gadget"
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:46
msgid "Setting for muting the sound device."
msgstr "Configuración para silenciar el dispositivo de sonido."
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:47
msgid "Show Log out"
msgstr "Mostrar Terminar Sesión"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:48
+msgid "Show Restart"
+msgstr "Mostrar Reiniciar"
+
+#: ../data/sugar.schemas.in.h:49
+msgid "Show Sugar Ad-hoc networks"
+msgstr "Mostrar redes específicas de Azúcar"
+
+#: ../data/sugar.schemas.in.h:50
msgid "Sound Muted"
msgstr "Sonido silenciado"
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:51
msgid "The keyboard model to be used"
msgstr "El modelo del teclado que se utilizará"
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:53
msgid "Timezone setting for the system."
msgstr "Configuración de zona horaria para el sistema."
-#: ../data/sugar.schemas.in.h:29
+#: ../data/sugar.schemas.in.h:54
msgid "Url of the jabber server to use."
msgstr "URL del servidor de Jabber para usar."
-#: ../data/sugar.schemas.in.h:30
+#: ../data/sugar.schemas.in.h:55
msgid "Url where the backup is saved to."
msgstr "URL donde se guarda el backup."
-#: ../data/sugar.schemas.in.h:31
+#: ../data/sugar.schemas.in.h:56
msgid "User Color"
msgstr "Color del usuario"
-#: ../data/sugar.schemas.in.h:32
+#: ../data/sugar.schemas.in.h:57
msgid "User Name"
msgstr "Nombre de usuario"
-#: ../data/sugar.schemas.in.h:33
+#: ../data/sugar.schemas.in.h:58
msgid "User name that is used throughout the desktop."
msgstr "Nombre de usuario que se utiliza en todo el escritorio."
-#: ../data/sugar.schemas.in.h:34
+#: ../data/sugar.schemas.in.h:59
+msgid ""
+"Users will not be allowed to erase these activities through the list view."
+msgstr ""
+"Usuarios que no se les permitirá borrar actividades a través de la vista de "
+"lista."
+
+#: ../data/sugar.schemas.in.h:60
msgid "Volume Level"
msgstr "Nivel de volumen"
-#: ../data/sugar.schemas.in.h:35
+#: ../data/sugar.schemas.in.h:61
msgid "Volume level for the sound device."
msgstr "Nivel de volumen para el dispositivo de sonido."
-#: ../data/sugar.schemas.in.h:36
+#: ../data/sugar.schemas.in.h:62
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -677,7 +974,7 @@ msgstr "sugar-control-panel: %s"
# which must appear in the translated string (msgstr) as well.
#. TRANS: Translators, there's a empty line at the end of this string,
#. which must appear in the translated string (msgstr) as well.
-#: ../src/jarabe/controlpanel/cmd.py:37
+#: ../src/jarabe/controlpanel/cmd.py:38
msgid ""
"Usage: sugar-control-panel [ option ] key [ args ... ] \n"
" Control for the sugar environment. \n"
@@ -701,72 +998,52 @@ msgstr ""
" -c clave vaciar el valor actual de la clave \n"
" "
-#: ../src/jarabe/controlpanel/cmd.py:50
+#: ../src/jarabe/controlpanel/cmd.py:52
msgid "To apply your changes you have to restart sugar.\n"
msgstr "Para aplicar sus cambios tiene que reiniciar Azúcar.\n"
-#: ../src/jarabe/controlpanel/gui.py:280
+#: ../src/jarabe/controlpanel/gui.py:297
+#: ../src/jarabe/journal/journaltoolbox.py:437
+#: ../src/jarabe/journal/volumestoolbar.py:158
msgid "Warning"
msgstr "Advertencia"
-#: ../src/jarabe/controlpanel/gui.py:281
-#: ../src/jarabe/controlpanel/sectionview.py:42
+#: ../src/jarabe/controlpanel/gui.py:298
+#: ../src/jarabe/controlpanel/sectionview.py:41
msgid "Changes require restart"
msgstr "Los cambios requieren reiniciar"
-#: ../src/jarabe/controlpanel/gui.py:284
+#: ../src/jarabe/controlpanel/gui.py:301
msgid "Cancel changes"
msgstr "Cancelar cambios"
-#: ../src/jarabe/controlpanel/gui.py:289 ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/controlpanel/gui.py:306 ../src/jarabe/desktop/homebox.py:72
msgid "Later"
msgstr "Después"
-#: ../src/jarabe/controlpanel/gui.py:293
+#: ../src/jarabe/controlpanel/gui.py:310
msgid "Restart now"
msgstr "Reiniciar ahora"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:188
+#: ../src/jarabe/controlpanel/toolbar.py:63 ../src/jarabe/intro/window.py:212
msgid "Done"
msgstr "Hecho"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:68
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:822
-#: ../src/jarabe/frame/activitiestray.py:850
-msgid "Cancel"
-msgstr "Cancelar"
-
-#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:332
+#: ../src/jarabe/controlpanel/toolbar.py:125
+#: ../src/jarabe/desktop/favoritesview.py:336
msgid "Ok"
-msgstr "Ok"
-
-#: ../src/jarabe/desktop/activitieslist.py:80
-#: ../src/jarabe/journal/listview.py:147
-msgid "Title"
-msgstr "Título"
-
-#: ../src/jarabe/desktop/activitieslist.py:91
-msgid "Version"
-msgstr "Versión"
-
-#: ../src/jarabe/desktop/activitieslist.py:105
-#: ../src/jarabe/journal/listview.py:178
-msgid "Date"
-msgstr "Fecha"
+msgstr "Aceptar"
-#: ../src/jarabe/desktop/activitieslist.py:234
+#: ../src/jarabe/desktop/activitieslist.py:230
#, python-format
msgid "Version %s"
msgstr "Versión %s"
-#: ../src/jarabe/desktop/activitieslist.py:355
+#: ../src/jarabe/desktop/activitieslist.py:354
msgid "Confirm erase"
msgstr "Confirmar borrado"
-#: ../src/jarabe/desktop/activitieslist.py:357
+#: ../src/jarabe/desktop/activitieslist.py:356
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
msgstr "Confirmar el borrado: ¿Quiere borrar %s de forma permanente?"
@@ -775,390 +1052,448 @@ msgstr "Confirmar el borrado: ¿Quiere borrar %s de forma permanente?"
# 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
+#: ../src/jarabe/desktop/activitieslist.py:360
+#: ../src/jarabe/frame/clipboardmenu.py:64
+#: ../src/jarabe/view/viewsource.py:221
msgid "Keep"
msgstr "Guardar"
-#: ../src/jarabe/desktop/activitieslist.py:364
-#: ../src/jarabe/desktop/activitieslist.py:407
-#: ../src/jarabe/journal/journaltoolbox.py:360
+#: ../src/jarabe/desktop/activitieslist.py:363
+#: ../src/jarabe/desktop/activitieslist.py:417
+#: ../src/jarabe/journal/journaltoolbox.py:391
#: ../src/jarabe/journal/palettes.py:112
msgid "Erase"
msgstr "Borrar"
-#: ../src/jarabe/desktop/activitieslist.py:428
+#: ../src/jarabe/desktop/activitieslist.py:432
msgid "Remove favorite"
msgstr "Remover favorito"
-#: ../src/jarabe/desktop/activitieslist.py:432
+#: ../src/jarabe/desktop/activitieslist.py:436
msgid "Make favorite"
msgstr "Hacer favorito"
# 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
+#: ../src/jarabe/desktop/favoriteslayout.py:127
msgid "Freeform"
msgstr "Forma libre"
# 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:198
+#: ../src/jarabe/desktop/favoriteslayout.py:215
msgid "Ring"
msgstr "Anillo"
# 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:337
+#: ../src/jarabe/desktop/favoriteslayout.py:402
msgid "Spiral"
msgstr "Espiral"
# 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:404
+#: ../src/jarabe/desktop/favoriteslayout.py:472
msgid "Box"
msgstr "Caja"
# 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:445
+#: ../src/jarabe/desktop/favoriteslayout.py:515
msgid "Triangle"
msgstr "Triángulo"
-#: ../src/jarabe/desktop/favoritesview.py:323
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "Registration Failed"
-msgstr "Registro fallido"
+msgstr "Error al registrar"
-#: ../src/jarabe/desktop/favoritesview.py:324
+#: ../src/jarabe/desktop/favoritesview.py:328
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:326
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Successful"
msgstr "Registro exitoso"
-#: ../src/jarabe/desktop/favoritesview.py:327
+#: ../src/jarabe/desktop/favoritesview.py:331
msgid "You are now registered with your school server."
msgstr "Ahora estás registrado en el servidor de colegio."
-#: ../src/jarabe/desktop/favoritesview.py:671
+#: ../src/jarabe/desktop/favoritesview.py:627
msgid "Register"
msgstr "Registro"
-#: ../src/jarabe/desktop/homebox.py:63
+#: ../src/jarabe/desktop/favoritesview.py:629
+#: ../src/jarabe/desktop/favoritesview.py:646
+msgid "Register again"
+msgstr "Registrar nuevamente"
+
+#: ../src/jarabe/desktop/homebox.py:65
msgid "Software Update"
msgstr "Actualización de Software"
-#: ../src/jarabe/desktop/homebox.py:64
+#: ../src/jarabe/desktop/homebox.py:66
msgid "Update your activities to ensure compatibility with your new software"
msgstr ""
"Actualice sus actividades para asegurar compatibilidad con su nuevo software"
-#: ../src/jarabe/desktop/homebox.py:73
+#: ../src/jarabe/desktop/homebox.py:75
msgid "Check now"
msgstr "Pruebe ahora"
-#: ../src/jarabe/desktop/homebox.py:192
+#: ../src/jarabe/desktop/homebox.py:193
msgid "List view"
msgstr "Vista en lista"
-#: ../src/jarabe/desktop/homebox.py:193
+#: ../src/jarabe/desktop/homebox.py:194
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:255
+#: ../src/jarabe/desktop/homebox.py:257
msgid "Favorites view"
msgstr "Vista de favoritos"
-#: ../src/jarabe/desktop/homebox.py:256
+#: ../src/jarabe/desktop/homebox.py:258
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
# This is an encryption key type, not a keyboard key
-#: ../src/jarabe/desktop/keydialog.py:131
+#: ../src/jarabe/desktop/keydialog.py:143
msgid "Key Type:"
msgstr "Tipo de clave:"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:163
msgid "Authentication Type:"
msgstr "Tipo de autenticación:"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "WPA & WPA2 Personal"
msgstr "WPA y WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:238
msgid "Wireless Security:"
msgstr "Seguridad inalámbrica:"
-#: ../src/jarabe/desktop/meshbox.py:136
-msgid "Connect"
-msgstr "Conectar"
-
-#: ../src/jarabe/desktop/meshbox.py:140
-msgid "Disconnect"
-msgstr "Desconectar"
-
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:466
-#: ../src/jarabe/frame/activitiestray.py:761
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:64
+#: ../src/jarabe/desktop/meshbox.py:109
+#: ../src/jarabe/frame/activitiestray.py:622
+#: ../src/jarabe/journal/journaltoolbox.py:485
+#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:78
msgid "Resume"
msgstr "Retomar"
-#: ../src/jarabe/desktop/meshbox.py:471
-#: ../src/jarabe/frame/activitiestray.py:235
+#: ../src/jarabe/desktop/meshbox.py:114
+#: ../src/jarabe/frame/activitiestray.py:173
msgid "Join"
msgstr "Unirse"
-#: ../src/jarabe/desktop/schoolserver.py:103
+#: ../src/jarabe/desktop/networkviews.py:497
+#, python-format
+msgid "Ad-hoc Network %d"
+msgstr "Red específica %d"
+
+#: ../src/jarabe/desktop/networkviews.py:628
+#, python-format
+msgid "Mesh Network %d"
+msgstr "Red Malla %d"
+
+#: ../src/jarabe/desktop/schoolserver.py:131
msgid "Cannot connect to the server."
msgstr "No se puede conectar al servidor."
-#: ../src/jarabe/desktop/schoolserver.py:108
+#: ../src/jarabe/desktop/schoolserver.py:136
msgid "The server could not complete the request."
msgstr "El servidor no pudo completar el pedido."
-#: ../src/jarabe/frame/activitiestray.py:240
-#: ../src/jarabe/frame/activitiestray.py:698
+#: ../src/jarabe/frame/activitiestray.py:178
+#: ../src/jarabe/frame/activitiestray.py:559
msgid "Decline"
msgstr "Rechazar"
-#: ../src/jarabe/frame/activitiestray.py:650
+#: ../src/jarabe/frame/activitiestray.py:509
#, python-format
msgid "%dB"
msgstr "%dB"
-#: ../src/jarabe/frame/activitiestray.py:652
+#: ../src/jarabe/frame/activitiestray.py:511
#, python-format
msgid "%dKB"
msgstr "%dKB"
-#: ../src/jarabe/frame/activitiestray.py:654
+#: ../src/jarabe/frame/activitiestray.py:513
#, python-format
msgid "%dMB"
msgstr "%dMB"
-#: ../src/jarabe/frame/activitiestray.py:671
+#: ../src/jarabe/frame/activitiestray.py:530
#, python-format
msgid "%s of %s"
msgstr "%s de %s"
-#: ../src/jarabe/frame/activitiestray.py:683
+#: ../src/jarabe/frame/activitiestray.py:544
#, python-format
msgid "Transfer from %r"
msgstr "Transferencia desde %r"
-#: ../src/jarabe/frame/activitiestray.py:693
+#: ../src/jarabe/frame/activitiestray.py:554
msgid "Accept"
msgstr "Aceptar"
-#: ../src/jarabe/frame/activitiestray.py:716
-#: ../src/jarabe/frame/activitiestray.py:840
+#: ../src/jarabe/frame/activitiestray.py:577
+#: ../src/jarabe/frame/activitiestray.py:705
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:750
-#: ../src/jarabe/frame/activitiestray.py:875
+#: ../src/jarabe/frame/activitiestray.py:611
+#: ../src/jarabe/frame/activitiestray.py:740
msgid "Dismiss"
msgstr "Descartar"
-#: ../src/jarabe/frame/activitiestray.py:810
+#: ../src/jarabe/frame/activitiestray.py:675
#, python-format
msgid "Transfer to %r"
msgstr "Transferencia a %r"
-#: ../src/jarabe/frame/clipboardmenu.py:52 ../src/jarabe/view/palettes.py:218
+#: ../src/jarabe/frame/clipboardmenu.py:54 ../src/jarabe/view/palettes.py:220
msgid "Remove"
msgstr "Eliminar"
-#: ../src/jarabe/frame/clipboardmenu.py:57
-#: ../src/jarabe/frame/clipboardmenu.py:80
+#: ../src/jarabe/frame/clipboardmenu.py:59
+#: ../src/jarabe/frame/clipboardmenu.py:82
msgid "Open"
msgstr "Abrir"
-#: ../src/jarabe/frame/clipboardmenu.py:85
+#: ../src/jarabe/frame/clipboardmenu.py:87
msgid "Open with"
msgstr "Abrir con"
-#: ../src/jarabe/frame/clipboardobject.py:49
+#: ../src/jarabe/frame/clipboardobject.py:50
#, python-format
msgid "%s clipping"
msgstr "recorte de %s"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "Neighborhood"
msgstr "Vecindario"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "F1"
msgstr "F1"
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "F2"
msgstr "F2"
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "F3"
msgstr "F3"
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "F4"
msgstr "F4"
-#: ../src/jarabe/intro/window.py:124
+#: ../src/jarabe/intro/window.py:97
+msgid "Name:"
+msgstr "Nombre:"
+
+#: ../src/jarabe/intro/window.py:133
msgid "Click to change color:"
msgstr "Clic para cambiar de color:"
-#: ../src/jarabe/intro/window.py:174 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:198 ../src/jarabe/journal/detailview.py:105
msgid "Back"
msgstr "Atrás"
-#: ../src/jarabe/intro/window.py:191
+#: ../src/jarabe/intro/window.py:215
msgid "Next"
msgstr "Siguiente"
-#: ../src/jarabe/journal/expandedentry.py:164
-#: ../src/jarabe/journal/palettes.py:66
+#: ../src/jarabe/journal/expandedentry.py:154
+#: ../src/jarabe/journal/listmodel.py:144 ../src/jarabe/journal/palettes.py:59
msgid "Untitled"
msgstr "Sin título"
-#: ../src/jarabe/journal/expandedentry.py:210
+#: ../src/jarabe/journal/expandedentry.py:243
msgid "No preview"
msgstr "Sin vista previa"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:262
#, python-format
msgid "Kind: %s"
msgstr "Tipo: %s"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:262
+#: ../src/jarabe/journal/listmodel.py:150
+#: ../src/jarabe/journal/listmodel.py:158
+#: ../src/jarabe/journal/listmodel.py:166
msgid "Unknown"
msgstr "Desconocido"
-#: ../src/jarabe/journal/expandedentry.py:230
+#: ../src/jarabe/journal/expandedentry.py:263
#, python-format
msgid "Date: %s"
msgstr "Fecha: %s"
-#: ../src/jarabe/journal/expandedentry.py:231
+#: ../src/jarabe/journal/expandedentry.py:264
#, python-format
msgid "Size: %s"
msgstr "Tamaño: %s"
-#: ../src/jarabe/journal/expandedentry.py:253 ../src/jarabe/journal/misc.py:92
+#: ../src/jarabe/journal/expandedentry.py:292
+#: ../src/jarabe/journal/misc.py:108
msgid "No date"
msgstr "Sin fecha"
-#: ../src/jarabe/journal/expandedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:299
msgid "Participants:"
msgstr "Participantes:"
-#: ../src/jarabe/journal/expandedentry.py:283
+#: ../src/jarabe/journal/expandedentry.py:321
msgid "Description:"
msgstr "Descripción:"
-#: ../src/jarabe/journal/expandedentry.py:309
+#: ../src/jarabe/journal/expandedentry.py:346
msgid "Tags:"
msgstr "Etiquetas:"
-#: ../src/jarabe/journal/journalactivity.py:108
-#: ../src/jarabe/journal/volumestoolbar.py:47
+#: ../src/jarabe/journal/journalactivity.py:115
+#: ../src/jarabe/journal/journaltoolbox.py:456
+#: ../src/jarabe/journal/volumestoolbar.py:50
msgid "Journal"
msgstr "Diario"
-#: ../src/jarabe/journal/journaltoolbox.py:67
+#: ../src/jarabe/journal/journaltoolbox.py:69
msgid "Search"
msgstr "Buscar"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:136
msgid "Anytime"
msgstr "Cualquier momento"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:138
msgid "Today"
msgstr "Hoy"
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:140
msgid "Since yesterday"
msgstr "Desde ayer"
# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
-#: ../src/jarabe/journal/journaltoolbox.py:132
+#: ../src/jarabe/journal/journaltoolbox.py:142
msgid "Past week"
msgstr "Última semana"
# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
-#: ../src/jarabe/journal/journaltoolbox.py:134
+#: ../src/jarabe/journal/journaltoolbox.py:144
msgid "Past month"
msgstr "Último mes"
# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
-#: ../src/jarabe/journal/journaltoolbox.py:136
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "Past year"
msgstr "Último año"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:153
msgid "Anyone"
msgstr "Cualquiera"
-#: ../src/jarabe/journal/journaltoolbox.py:145
+#: ../src/jarabe/journal/journaltoolbox.py:155
msgid "My friends"
msgstr "Mis amigos"
-#: ../src/jarabe/journal/journaltoolbox.py:146
+#: ../src/jarabe/journal/journaltoolbox.py:156
msgid "My class"
msgstr "Mi clase"
# TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:274
+#: ../src/jarabe/journal/journaltoolbox.py:298
msgid "Anything"
msgstr "Cualquiera"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:350
+#: ../src/jarabe/journal/journaltoolbox.py:381
#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Copiar"
+#: ../src/jarabe/journal/journaltoolbox.py:436
+#: ../src/jarabe/journal/volumestoolbar.py:157
+msgid "Entries without a file cannot be copied."
+msgstr "Entradas sin un archivo no pueden ser copiadas."
+
+#: ../src/jarabe/journal/journaltoolbox.py:445
+#: ../src/jarabe/journal/volumestoolbar.py:166
+#, python-format
+msgid "Error while copying the entry. %s"
+msgstr "Error mientras se copiaba la entrada. %s"
+
+#: ../src/jarabe/journal/journaltoolbox.py:446
+#: ../src/jarabe/journal/volumestoolbar.py:167
+msgid "Error"
+msgstr "Error"
+
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:431
-#: ../src/jarabe/journal/palettes.py:75
+#: ../src/jarabe/journal/journaltoolbox.py:488
+#: ../src/jarabe/journal/palettes.py:69
msgid "Start"
msgstr "Iniciar"
-#: ../src/jarabe/journal/listview.py:361
+#: ../src/jarabe/journal/journaltoolbox.py:516
+msgid "Sort by date modified"
+msgstr "Ordenar por fecha de modificación"
+
+#: ../src/jarabe/journal/journaltoolbox.py:517
+msgid "Sort by date created"
+msgstr "Ordenar por fecha de creación"
+
+#: ../src/jarabe/journal/journaltoolbox.py:518
+msgid "Sort by size"
+msgstr "Ordenar por tamaño"
+
+#: ../src/jarabe/journal/journaltoolbox.py:527
+msgid "Sort view"
+msgstr "Ordenar vista"
+
+#: ../src/jarabe/journal/listview.py:380
msgid "Your Journal is empty"
msgstr "Su diario está vacío"
-#: ../src/jarabe/journal/listview.py:363
+#: ../src/jarabe/journal/listview.py:382
msgid "No matching entries"
msgstr "No hay entradas coincidentes"
-#: ../src/jarabe/journal/listview.py:374
+#: ../src/jarabe/journal/listview.py:393
msgid "Clear search"
msgstr "Limpiar búsqueda"
-#: ../src/jarabe/journal/modalalert.py:63
+#: ../src/jarabe/journal/misc.py:273
+#, python-format
+msgid "Older Version Of %s Activity"
+msgstr "Versión más antigua de la actividad %s"
+
+#: ../src/jarabe/journal/misc.py:274
+#, python-format
+msgid "Do you want to downgrade to version %s"
+msgstr "¿Desea instalar la versión %s, mas antigua? "
+
+#: ../src/jarabe/journal/modalalert.py:64
msgid "Your Journal is full"
-msgstr "Su diario está vacío"
+msgstr "Su diario está lleno"
-#: ../src/jarabe/journal/modalalert.py:67
+#: ../src/jarabe/journal/modalalert.py:68
msgid "Please delete some old Journal entries to make space for new ones."
msgstr ""
-"Por favor borre las entradas viejas del diario para hacer espacio a las "
+"Por favor borre algunas entradas viejas del diario para hacer espacio a las "
"nuevas entradas."
-#: ../src/jarabe/journal/modalalert.py:79
+#: ../src/jarabe/journal/modalalert.py:80
msgid "Show Journal"
msgstr "Mostrar diario"
@@ -1167,18 +1502,22 @@ msgid "Choose an object"
msgstr "Escoja un objeto"
#: ../src/jarabe/journal/objectchooser.py:151
-#: ../src/jarabe/view/viewsource.py:308
+#: ../src/jarabe/view/viewsource.py:311
msgid "Close"
msgstr "Cerrar"
-#: ../src/jarabe/journal/palettes.py:73
+#: ../src/jarabe/journal/palettes.py:67
msgid "Resume with"
msgstr "Reiniciar con"
-#: ../src/jarabe/journal/palettes.py:76
+#: ../src/jarabe/journal/palettes.py:70
msgid "Start with"
msgstr "Empezar con"
+#: ../src/jarabe/journal/palettes.py:83 ../src/jarabe/journal/palettes.py:216
+msgid "No activity to start entry"
+msgstr "No se encontró una actividad para iniciar la entrada"
+
#: ../src/jarabe/journal/palettes.py:98
msgid "Send to"
msgstr "Enviar a"
@@ -1187,103 +1526,313 @@ msgstr "Enviar a"
msgid "View Details"
msgstr "Ver detalles"
-#: ../src/jarabe/journal/palettes.py:185
+#: ../src/jarabe/journal/palettes.py:181
msgid "No friends present"
msgstr "No hay amigos presentes"
# tildes
-#: ../src/jarabe/journal/palettes.py:190
+#: ../src/jarabe/journal/palettes.py:186
msgid "No valid connection found"
msgstr "No se encontró una conexión válida"
# tildes...
-#: ../src/jarabe/journal/palettes.py:218
+#: ../src/jarabe/journal/palettes.py:214
msgid "No activity to resume entry"
msgstr "No se encontró una actividad para retomar la entrada"
-#: ../src/jarabe/journal/palettes.py:220
-msgid "No activity to start entry"
-msgstr "No se encontró una actividad para iniciar la entrada"
+#: ../src/jarabe/model/network.py:163
+msgid "The reason for the device state change is unknown."
+msgstr "La razón para el cambio de estado del dispositivo es desconocida."
+
+#: ../src/jarabe/model/network.py:165
+msgid "The state change is normal."
+msgstr "El cambio de estado es normal."
+
+#: ../src/jarabe/model/network.py:167
+msgid "The device is now managed."
+msgstr "El dispositivo está siendo administrado."
+
+#: ../src/jarabe/model/network.py:169
+msgid "The device is no longer managed."
+msgstr "El dispositivo ya no está siendo administrado."
+
+#: ../src/jarabe/model/network.py:171
+msgid "The device could not be readied for configuration."
+msgstr "El dispositivo no pudo ser preparado para su configuración."
+
+#: ../src/jarabe/model/network.py:173
+msgid ""
+"IP configuration could not be reserved (no available address, timeout, etc)."
+msgstr ""
+"La configuración IP no pudo ser reservada (no hay dirección disponible, "
+"tiempo fuera, etc)."
+
+#: ../src/jarabe/model/network.py:176
+msgid "The IP configuration is no longer valid."
+msgstr "La configuración IP ya no es válida."
+
+#: ../src/jarabe/model/network.py:178
+msgid "Secrets were required, but not provided."
+msgstr "Claves requeridas, pero no fueron suministradas."
+
+#: ../src/jarabe/model/network.py:180
+msgid ""
+"The 802.1X supplicant disconnected from the access point or authentication "
+"server."
+msgstr ""
+"El cliente 802.1X fué desconectado del punto de acceso o del server de "
+"autenticación."
+
+#: ../src/jarabe/model/network.py:183
+msgid "Configuration of the 802.1X supplicant failed."
+msgstr "Configuración del cliente 802.1X fallada."
+
+#: ../src/jarabe/model/network.py:185
+msgid "The 802.1X supplicant quit or failed unexpectedly."
+msgstr "El cliente 802.1X ha abandonado o fallado inesperadamente."
+
+#: ../src/jarabe/model/network.py:187
+msgid "The 802.1X supplicant took too long to authenticate."
+msgstr "El cliente 802.1X ha tomado demasiado tiempo para autenticar."
+
+#: ../src/jarabe/model/network.py:189
+msgid "The PPP service failed to start within the allowed time."
+msgstr "El servicio PPP ha fallado en comenzar en el tiempo permitido."
+
+#: ../src/jarabe/model/network.py:191
+msgid "The PPP service disconnected unexpectedly."
+msgstr "El servicio PPP se ha desconectado inesperadamente."
+
+#: ../src/jarabe/model/network.py:193
+msgid "The PPP service quit or failed unexpectedly."
+msgstr "El servicio PPP ha abandonado o fallado inesperadamente."
+
+#: ../src/jarabe/model/network.py:195
+msgid "The DHCP service failed to start within the allowed time."
+msgstr "El servicio DHCP ha fallado en comenzar en el tiempo permitido."
+
+#: ../src/jarabe/model/network.py:197
+msgid "The DHCP service reported an unexpected error."
+msgstr "El servicio DHCP ha reportado un error inesperado."
+
+#: ../src/jarabe/model/network.py:199
+msgid "The DHCP service quit or failed unexpectedly."
+msgstr "El servicio DHCP ha abandonado o fallado inesperadamente."
+
+#: ../src/jarabe/model/network.py:201
+msgid "The shared connection service failed to start."
+msgstr "El servicio de conexión compartida ha fallado al iniciar."
+
+#: ../src/jarabe/model/network.py:203
+msgid "The shared connection service quit or failed unexpectedly."
+msgstr ""
+"El servicio de conexión compartida ha abandonado o fallado inesperadamente."
+
+#: ../src/jarabe/model/network.py:206
+msgid "The AutoIP service failed to start."
+msgstr "El servicio AutoIP ha fallado al iniciar."
+
+#: ../src/jarabe/model/network.py:208
+msgid "The AutoIP service reported an unexpected error."
+msgstr "El servicio AutoIP ha reportado un error inesperado."
+
+#: ../src/jarabe/model/network.py:210
+msgid "The AutoIP service quit or failed unexpectedly."
+msgstr "El servicio AutoIP ha abandonado o fallado inesperadamente."
+
+#: ../src/jarabe/model/network.py:212
+msgid "Dialing failed because the line was busy."
+msgstr "Conexión fallada porque la línea estaba ocupada."
+
+#: ../src/jarabe/model/network.py:214
+msgid "Dialing failed because there was no dial tone."
+msgstr "Conexión fallada porque no había tono de llamada."
+
+#: ../src/jarabe/model/network.py:216
+msgid "Dialing failed because there was no carrier."
+msgstr "Conexión fallada porque no había portadora."
+
+#: ../src/jarabe/model/network.py:218
+msgid "Dialing timed out."
+msgstr "Llamada expiró por tiempo."
+
+#: ../src/jarabe/model/network.py:220
+msgid "Dialing failed."
+msgstr "Llamada falló."
+
+#: ../src/jarabe/model/network.py:222
+msgid "Modem initialization failed."
+msgstr "Fallo en inicialización de modem."
+
+#: ../src/jarabe/model/network.py:224
+msgid "Failed to select the specified GSM APN"
+msgstr "Fallo al seleccionar el punto de acceso (APN) GSM"
+
+#: ../src/jarabe/model/network.py:226
+msgid "Not searching for networks."
+msgstr "No se buscan redes."
+
+#: ../src/jarabe/model/network.py:228
+msgid "Network registration was denied."
+msgstr "Registración en la red fué rechazada."
+
+#: ../src/jarabe/model/network.py:230
+msgid "Network registration timed out."
+msgstr "Registración en la red expiró."
+
+#: ../src/jarabe/model/network.py:232
+msgid "Failed to register with the requested GSM network."
+msgstr "Falló la registración con la red GSM solicitada."
+
+#: ../src/jarabe/model/network.py:234
+msgid "PIN check failed."
+msgstr "Control de PIN fallado."
+
+#: ../src/jarabe/model/network.py:236
+msgid "Necessary firmware for the device may be missing."
+msgstr "Firmware necesario para el dispositivo puede faltar."
+
+#: ../src/jarabe/model/network.py:238
+msgid "The device was removed."
+msgstr "El dispositivo fué quitado."
+
+#: ../src/jarabe/model/network.py:240
+msgid "NetworkManager went to sleep."
+msgstr "NetworkManager fue dormido."
+
+#: ../src/jarabe/model/network.py:242
+msgid "The device's active connection was removed or disappeared."
+msgstr ""
+"Las conexiones activas del dispositivo fueron removidas o desaparecieron."
+
+#: ../src/jarabe/model/network.py:245
+msgid "A user or client requested the disconnection."
+msgstr "Un usuario o cliente solicitó la desconexión."
+
+#: ../src/jarabe/model/network.py:247
+msgid "The device's carrier/link changed."
+msgstr "La portadora/link del dispositivo a cambiado."
# "Eliminate friend"??? That's a bit harsh. Wouldn't "quitar amigo" be a better choice?--
# agree but i preffer remover :). that verbe has the exact meaning we are looking on here.
-#: ../src/jarabe/view/buddymenu.py:62
+#: ../src/jarabe/view/buddymenu.py:63
msgid "Remove friend"
msgstr "Remover amigo"
-#: ../src/jarabe/view/buddymenu.py:65
+#: ../src/jarabe/view/buddymenu.py:66
msgid "Make friend"
msgstr "Agregar amigo"
-#: ../src/jarabe/view/buddymenu.py:82
+#: ../src/jarabe/view/buddymenu.py:83
msgid "Shutdown"
msgstr "Apagar"
-#: ../src/jarabe/view/buddymenu.py:90
+#: ../src/jarabe/view/buddymenu.py:91
+msgid "Restart"
+msgstr "Reiniciar"
+
+#: ../src/jarabe/view/buddymenu.py:97
msgid "Logout"
msgstr "Salir"
-#: ../src/jarabe/view/buddymenu.py:95
+#: ../src/jarabe/view/buddymenu.py:102
msgid "My Settings"
msgstr "Mis ajustes"
-#: ../src/jarabe/view/buddymenu.py:130
+#: ../src/jarabe/view/buddymenu.py:137
#, python-format
msgid "Invite to %s"
msgstr "Invitar a %s"
-#: ../src/jarabe/view/palettes.py:45
+#: ../src/jarabe/view/launcher.py:190
+#, python-format
+msgid "<b>%s</b> failed to start."
+msgstr "<b>%s</b> falló al iniciar."
+
+#: ../src/jarabe/view/palettes.py:46
msgid "Starting..."
msgstr "Iniciando..."
+#: ../src/jarabe/view/palettes.py:56
+msgid "Activity failed to start"
+msgstr "Actividad falló al iniciar"
+
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:71
+#: ../src/jarabe/view/palettes.py:85
msgid "View Source"
msgstr "Ver fuente"
-#: ../src/jarabe/view/palettes.py:82
+#: ../src/jarabe/view/palettes.py:96
msgid "Stop"
msgstr "Parar"
-#: ../src/jarabe/view/palettes.py:122
+#: ../src/jarabe/view/palettes.py:132
msgid "Start new"
msgstr "Empezar nuevo"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:172
msgid "Show contents"
msgstr "Mostrar contenidos"
-#: ../src/jarabe/view/palettes.py:193 ../src/jarabe/view/palettes.py:243
+#: ../src/jarabe/view/palettes.py:194 ../src/jarabe/view/palettes.py:245
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB libres"
-#: ../src/jarabe/view/viewsource.py:208
+#: ../src/jarabe/view/viewsource.py:211
msgid "Instance Source"
msgstr "Fuente de la instancia"
-#: ../src/jarabe/view/viewsource.py:233
+#: ../src/jarabe/view/viewsource.py:236
msgid "Source"
msgstr "Fuente"
-#: ../src/jarabe/view/viewsource.py:292
+#: ../src/jarabe/view/viewsource.py:295
msgid "Activity Bundle Source"
msgstr "Fuente del paquete de la actividad"
-#: ../src/jarabe/view/viewsource.py:299
+#: ../src/jarabe/view/viewsource.py:302
#, python-format
msgid "View source: %r"
msgstr "Ver código fuente: %r"
+#: ../src/jarabe/util/emulator.py:40
+msgid "Sugar in a window"
+msgstr "Sugar en una ventana"
+
+# Access point name for GPRS network
+#~ msgid "APN:"
+#~ msgstr "Nombre del Punto de Acceso:"
+
+#~ msgid "Create new wireless network"
+#~ msgstr "Crear nueva red inalámbrica"
+
+#, python-format
+#~ msgid "%s's network"
+#~ msgstr "Red de %s"
+
+#, python-format
+#~ msgid "Data sent %d kb / received %d kb"
+#~ msgstr "Datos enviados %d kb / recibidos %d kb"
+
+#~ msgid "Connection time "
+#~ msgstr "Tiempo de conexión "
+
+#~ msgid "Title"
+#~ msgstr "Título"
+
+#~ msgid "Version"
+#~ msgstr "Versión"
+
+#~ msgid "Date"
+#~ msgstr "Fecha"
+
#~ msgid "Cannot obtain data needed for registration."
#~ msgstr "No se puede obtener datos necesarios para el registro"
#~ msgid "Unmount"
#~ msgstr "Desmontar"
-#~ msgid "Restart"
-#~ msgstr "Reiniciar"
-
#~ msgid ""
#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
#~ msgstr ""
@@ -1306,12 +1855,6 @@ msgstr "Ver código fuente: %r"
#~ msgid "Disconnecting..."
#~ msgstr "Desconectando..."
-#~ msgid "Mesh Network"
-#~ msgstr "Red Malla"
-
-#~ msgid "Disconnected"
-#~ msgstr "Desconectado"
-
#~ msgid "About my XO"
#~ msgstr "Acerca de mi XO"
diff --git a/po/fr.po b/po/fr.po
index 6f4f1bc..b383cc4 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,3 +1,15 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# translation of sugar.po to french
# Copyright (C) 2007 the Package Owner
# This file is distributed under the same license as the sugar graphical shell package.
@@ -6,8 +18,8 @@ msgid ""
msgstr ""
"Project-Id-Version: sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-09-05 00:31-0400\n"
-"PO-Revision-Date: 2009-09-16 01:37-0400\n"
+"POT-Creation-Date: 2011-02-06 00:30-0500\n"
+"PO-Revision-Date: 2011-02-11 18:23+0200\n"
"Last-Translator: samy boutayeb <s.boutayeb@free.fr>\n"
"Language-Team: French <traduc@traduc.org>\n"
"Language: fr\n"
@@ -15,49 +27,45 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Pootle 1.2.1\n"
+"X-Generator: Pootle 2.0.3\n"
#: ../extensions/cpsection/aboutme/__init__.py:24
msgid "About Me"
msgstr "Moi"
-#: ../extensions/cpsection/aboutme/model.py:43
+#: ../extensions/cpsection/aboutme/model.py:48
msgid "You must enter a name."
msgstr "Vous devez indiquer un nom."
-#: ../extensions/cpsection/aboutme/model.py:68
+#: ../extensions/cpsection/aboutme/model.py:75
#, python-format
msgid "stroke: color=%s hue=%s"
msgstr "stroke: color=%s hue=%s"
-#: ../extensions/cpsection/aboutme/model.py:71
+#: ../extensions/cpsection/aboutme/model.py:78
#, python-format
msgid "stroke: %s"
msgstr "stroke: %s"
-#: ../extensions/cpsection/aboutme/model.py:73
+#: ../extensions/cpsection/aboutme/model.py:80
#, python-format
msgid "fill: color=%s hue=%s"
msgstr "fill: color=%s hue=%s"
-#: ../extensions/cpsection/aboutme/model.py:75
+#: ../extensions/cpsection/aboutme/model.py:82
#, python-format
msgid "fill: %s"
msgstr "fill: %s"
-#: ../extensions/cpsection/aboutme/model.py:86
+#: ../extensions/cpsection/aboutme/model.py:94
msgid "Error in specified color modifiers."
msgstr "Erreur dans les modificateurs de couleur spécifiés."
-#: ../extensions/cpsection/aboutme/model.py:89
+#: ../extensions/cpsection/aboutme/model.py:97
msgid "Error in specified colors."
msgstr "Erreur dans les couleurs spécifiées."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:92
-msgid "Name:"
-msgstr "Nom :"
-
-#: ../extensions/cpsection/aboutme/view.py:128
+#: ../extensions/cpsection/aboutme/view.py:235
msgid "Click to change your color:"
msgstr "Cliquer pour changer de couleur :"
@@ -65,43 +73,48 @@ msgstr "Cliquer pour changer de couleur :"
msgid "About my Computer"
msgstr "Mon ordinateur"
-#: ../extensions/cpsection/aboutcomputer/model.py:28
+#: ../extensions/cpsection/aboutcomputer/model.py:37
msgid "Not available"
msgstr "Non disponible"
-#: ../extensions/cpsection/aboutcomputer/view.py:60
+#: ../extensions/cpsection/aboutcomputer/model.py:158
+#, python-format
+msgid "%(interface)s: %(version)s"
+msgstr "%(interface)s : %(version)s"
+
+#: ../extensions/cpsection/aboutcomputer/view.py:61
msgid "Identity"
msgstr "Identité"
-#: ../extensions/cpsection/aboutcomputer/view.py:69
+#: ../extensions/cpsection/aboutcomputer/view.py:70
msgid "Serial Number:"
msgstr "Numéro de série :"
-#: ../extensions/cpsection/aboutcomputer/view.py:91
+#: ../extensions/cpsection/aboutcomputer/view.py:92
msgid "Software"
msgstr "Logiciel"
-#: ../extensions/cpsection/aboutcomputer/view.py:100
+#: ../extensions/cpsection/aboutcomputer/view.py:101
msgid "Build:"
msgstr "Version :"
-#: ../extensions/cpsection/aboutcomputer/view.py:115
+#: ../extensions/cpsection/aboutcomputer/view.py:116
msgid "Sugar:"
msgstr "Sugar :"
-#: ../extensions/cpsection/aboutcomputer/view.py:131
+#: ../extensions/cpsection/aboutcomputer/view.py:132
msgid "Firmware:"
msgstr "Micrologiciel :"
-#: ../extensions/cpsection/aboutcomputer/view.py:146
+#: ../extensions/cpsection/aboutcomputer/view.py:147
msgid "Wireless Firmware:"
msgstr "Micrologiciel sans fil :"
-#: ../extensions/cpsection/aboutcomputer/view.py:169
+#: ../extensions/cpsection/aboutcomputer/view.py:170
msgid "Copyright and License"
msgstr "Copyright et licence"
-#: ../extensions/cpsection/aboutcomputer/view.py:184
+#: ../extensions/cpsection/aboutcomputer/view.py:188
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 "
@@ -113,7 +126,7 @@ msgstr ""
"License). Vous êtes autorisé à le modifier et/ou à en distribuer des copies "
"aux conditions spécifiées."
-#: ../extensions/cpsection/aboutcomputer/view.py:196
+#: ../extensions/cpsection/aboutcomputer/view.py:200
msgid "Full license:"
msgstr "Licence complète :"
@@ -121,11 +134,11 @@ msgstr "Licence complète :"
msgid "Date & Time"
msgstr "Date & heure"
-#: ../extensions/cpsection/datetime/model.py:87
+#: ../extensions/cpsection/datetime/model.py:92
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:27
+#: ../extensions/cpsection/datetime/view.py:70 ../data/sugar.schemas.in.h:52
msgid "Timezone"
msgstr "Fuseau horaire"
@@ -133,50 +146,50 @@ msgstr "Fuseau horaire"
msgid "Frame"
msgstr "Cadre"
-#: ../extensions/cpsection/frame/model.py:38
-#: ../extensions/cpsection/frame/model.py:60
+#: ../extensions/cpsection/frame/model.py:41
+#: ../extensions/cpsection/frame/model.py:66
msgid "Value must be an integer."
msgstr "La valeur doit être un entier."
-#: ../extensions/cpsection/frame/view.py:26
+#: ../extensions/cpsection/frame/view.py:27
msgid "never"
msgstr "jamais"
-#: ../extensions/cpsection/frame/view.py:27
+#: ../extensions/cpsection/frame/view.py:28
msgid "instantaneous"
msgstr "immédiat"
-#: ../extensions/cpsection/frame/view.py:28
+#: ../extensions/cpsection/frame/view.py:29
#, python-format
msgid "%s seconds"
msgstr "%s secondes"
-#: ../extensions/cpsection/frame/view.py:52
+#: ../extensions/cpsection/frame/view.py:54
msgid "Activation Delay"
msgstr "Délai d'activation"
-#: ../extensions/cpsection/frame/view.py:76
+#: ../extensions/cpsection/frame/view.py:78
msgid "Corner"
msgstr "Coin"
-#: ../extensions/cpsection/frame/view.py:111
+#: ../extensions/cpsection/frame/view.py:113
msgid "Edge"
msgstr "Bord"
#: ../extensions/cpsection/keyboard/__init__.py:21
-#: ../extensions/cpsection/keyboard/view.py:31
+#: ../extensions/cpsection/keyboard/view.py:32
msgid "Keyboard"
msgstr "Clavier"
-#: ../extensions/cpsection/keyboard/view.py:187
+#: ../extensions/cpsection/keyboard/view.py:190
msgid "Keyboard Model"
msgstr "Modèle de clavier"
-#: ../extensions/cpsection/keyboard/view.py:243
+#: ../extensions/cpsection/keyboard/view.py:250
msgid "Key(s) to change layout"
msgstr "Touche(s) de modification de la disposition"
-#: ../extensions/cpsection/keyboard/view.py:311
+#: ../extensions/cpsection/keyboard/view.py:319
msgid "Keyboard Layout(s)"
msgstr "Disposition(s) du clavier"
@@ -185,21 +198,21 @@ msgstr "Disposition(s) du clavier"
msgid "Language"
msgstr "Langue"
-#: ../extensions/cpsection/language/model.py:28
+#: ../extensions/cpsection/language/model.py:30
msgid "Could not access ~/.i18n. Create standard settings."
msgstr "Accès impossible à ~/.i18n. Création de paramètres par défaut."
-#: ../extensions/cpsection/language/model.py:124
+#: ../extensions/cpsection/language/model.py:131
#, python-format
msgid "Language for code=%s could not be determined."
msgstr "La langue associée au code = %s n'a pas pu être déterminée."
-#: ../extensions/cpsection/language/model.py:144
+#: ../extensions/cpsection/language/model.py:152
#, python-format
msgid "Sorry I do not speak '%s'."
msgstr "Désolé je ne parle pas '%s'."
-#: ../extensions/cpsection/language/view.py:56
+#: ../extensions/cpsection/language/view.py:57
msgid ""
"Add languages in the order you prefer. If a translation is not available, "
"the next in the list will be used."
@@ -207,50 +220,86 @@ 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/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Configuration du modem"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:94
+msgid "Username:"
+msgstr "Identifiant :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:106
+msgid "Password:"
+msgstr "Mot de passe :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:118
+msgid "Number:"
+msgstr "Numéro :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:130
+msgid "Access Point Name (APN):"
+msgstr "Nom du Point d'Accès (APN) :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:142
+msgid "Personal Identity Number (PIN):"
+msgstr "Numéro d'Identité Personnel (PIN) :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:154
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Clé de Déblocage Personnelle (PUK) :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:175
+msgid ""
+"You will need to provide the following information to set up a mobile "
+"broadband connection to a cellular (3G) network."
+msgstr ""
+"Fournir les informations ci-après pour configurer une connexion large bande "
+"à un réseau cellulaire (3G)."
+
#: ../extensions/cpsection/network/__init__.py:21
-#: ../extensions/cpsection/network/view.py:28
+#: ../extensions/cpsection/network/view.py:29
msgid "Network"
msgstr "Réseau"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:71
msgid "State is unknown."
msgstr "État inconnu."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:99
msgid "Error in specified radio argument use on/off."
msgstr "Argument 'radio' spécifié incorrect. Utiliser marche/arrêt."
-#: ../extensions/cpsection/network/model.py:137
+#: ../extensions/cpsection/network/model.py:140
msgid "Error in specified argument use 0/1."
msgstr "Argument spécifié incorrect. Utiliser 0/1."
-#: ../extensions/cpsection/network/view.py:59
+#: ../extensions/cpsection/network/view.py:61
msgid "Wireless"
msgstr "Réseau sans fil"
-#: ../extensions/cpsection/network/view.py:67
+#: ../extensions/cpsection/network/view.py:69
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:80
+#: ../extensions/cpsection/network/view.py:82
msgid "Radio"
msgstr "Radio"
-#: ../extensions/cpsection/network/view.py:96
+#: ../extensions/cpsection/network/view.py:98
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:105
+#: ../extensions/cpsection/network/view.py:107
msgid "Discard network history"
msgstr "Ignorer l'historique du réseau"
-#: ../extensions/cpsection/network/view.py:118
+#: ../extensions/cpsection/network/view.py:122
msgid "Collaboration"
msgstr "Collaboration"
-#: ../extensions/cpsection/network/view.py:126
+#: ../extensions/cpsection/network/view.py:130
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 "
@@ -260,7 +309,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:136
+#: ../extensions/cpsection/network/view.py:140
msgid "Server:"
msgstr "Serveur :"
@@ -268,23 +317,23 @@ msgstr "Serveur :"
msgid "Power"
msgstr "Alimentation"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:90
msgid "Error in automatic pm argument, use on/off."
-msgstr "Erreur dans l'argument gestion de l'alimentation automatique"
+msgstr "Erreur dans l'argument gestion de l'alimentation automatique."
-#: ../extensions/cpsection/power/model.py:81
+#: ../extensions/cpsection/power/model.py:120
msgid "Error in extreme pm argument, use on/off."
-msgstr "Erreur dans l'argument gestion de l'alimentation extrême"
+msgstr "Erreur dans l'argument gestion de l'alimentation extrême."
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:48
msgid "Power management"
msgstr "Gestion de l'alimentation"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:58
msgid "Automatic power management (increases battery life)"
msgstr "Gestion automatique de l'alimentation (prolonge la batterie)"
-#: ../extensions/cpsection/power/view.py:85
+#: ../extensions/cpsection/power/view.py:86
msgid ""
"Extreme power management (disableswireless radio, increases battery life)"
msgstr ""
@@ -295,7 +344,7 @@ msgstr ""
msgid "Software update"
msgstr "Mise à jour logicielle"
-#: ../extensions/cpsection/updater/view.py:62
+#: ../extensions/cpsection/updater/view.py:63
msgid ""
"Software updates correct errors, eliminate security vulnerabilities, and "
"provide new features."
@@ -303,109 +352,109 @@ msgstr ""
"Les mises à jour logicielles corrigent les erreurs, éliminent les failles de "
"sécurité et apportent de nouvelles fonctionnalités."
-#: ../extensions/cpsection/updater/view.py:122
+#: ../extensions/cpsection/updater/view.py:125
#, python-format
msgid "Checking %s..."
msgstr "Vérification de %s..."
-#: ../extensions/cpsection/updater/view.py:124
+#: ../extensions/cpsection/updater/view.py:127
#, python-format
msgid "Downloading %s..."
msgstr "Téléchargement de %s..."
-#: ../extensions/cpsection/updater/view.py:126
+#: ../extensions/cpsection/updater/view.py:129
#, python-format
msgid "Updating %s..."
msgstr "Mise à jour de %s..."
-#: ../extensions/cpsection/updater/view.py:135
+#: ../extensions/cpsection/updater/view.py:139
msgid "Your software is up-to-date"
msgstr "Vos logiciels sont à jour"
-#: ../extensions/cpsection/updater/view.py:137
+#: ../extensions/cpsection/updater/view.py:141
#, python-format
msgid "You can install %s update"
msgid_plural "You can install %s updates"
msgstr[0] "Vous pouvez installer %s mise à jour"
msgstr[1] "Vous pouvez installer %s mises à jour"
-#: ../extensions/cpsection/updater/view.py:155
+#: ../extensions/cpsection/updater/view.py:159
msgid "Checking for updates..."
msgstr "Vérification des mises à jour..."
-#: ../extensions/cpsection/updater/view.py:160
+#: ../extensions/cpsection/updater/view.py:164
msgid "Installing updates..."
msgstr "Installation des mises à jour..."
-#: ../extensions/cpsection/updater/view.py:165
+#: ../extensions/cpsection/updater/view.py:173
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
msgstr[0] "%s mise à jour a été installée"
msgstr[1] "%s mises à jour ont été installées"
-#: ../extensions/cpsection/updater/view.py:244
+#: ../extensions/cpsection/updater/view.py:255
msgid "Install selected"
msgstr "Installer les activités sélectionnées"
-#: ../extensions/cpsection/updater/view.py:265
+#: ../extensions/cpsection/updater/view.py:276
#, python-format
msgid "Download size: %s"
msgstr "Taille du téléchargement : %s"
-#: ../extensions/cpsection/updater/view.py:353
+#: ../extensions/cpsection/updater/view.py:364
#, python-format
-msgid "From version %(current)d to %(new)s (Size: %(size)s)"
-msgstr "De la version %(current)d à %(new)s (taille : %(size)s)"
+msgid "From version %(current)s to %(new)s (Size: %(size)s)"
+msgstr "De la version %(current)s à %(new)s (taille : %(size)s)"
#. TRANS: download size is 0
-#: ../extensions/cpsection/updater/view.py:373
+#: ../extensions/cpsection/updater/view.py:382
msgid "None"
msgstr "Zéro"
#. TRANS: download size of very small updates
-#: ../extensions/cpsection/updater/view.py:376
+#: ../extensions/cpsection/updater/view.py:385
msgid "1 KB"
msgstr "1 Ko"
#. TRANS: download size of small updates, e.g. '250 KB'
-#: ../extensions/cpsection/updater/view.py:379
+#: ../extensions/cpsection/updater/view.py:388
#, python-format
msgid "%.0f KB"
msgstr "%.0f Ko"
#. TRANS: download size of updates, e.g. '2.3 MB'
-#: ../extensions/cpsection/updater/view.py:382
+#: ../extensions/cpsection/updater/view.py:391
#, python-format
msgid "%.1f MB"
msgstr "%.1f Mo"
-#: ../extensions/deviceicon/battery.py:58
+#: ../extensions/deviceicon/battery.py:60
msgid "My Battery"
msgstr "Ma batterie"
-#: ../extensions/deviceicon/battery.py:137
+#: ../extensions/deviceicon/battery.py:141
msgid "Removed"
msgstr "Retiré"
-#: ../extensions/deviceicon/battery.py:140
+#: ../extensions/deviceicon/battery.py:144
msgid "Charging"
msgstr "En charge"
-#: ../extensions/deviceicon/battery.py:143
+#: ../extensions/deviceicon/battery.py:147
msgid "Very little power remaining"
msgstr "La batterie est pratiquement déchargée"
-#: ../extensions/deviceicon/battery.py:149
+#: ../extensions/deviceicon/battery.py:153
#, python-format
msgid "%(hour)d:%(min).2d remaining"
msgstr "%(hour)d:%(min).2d restantes"
-#: ../extensions/deviceicon/battery.py:152
+#: ../extensions/deviceicon/battery.py:156
msgid "Charged"
msgstr "Charge complète"
-#: ../extensions/deviceicon/network.py:44
+#: ../extensions/deviceicon/network.py:49
#, python-format
msgid "IP address: %s"
msgstr "Adresse IP : %s"
@@ -414,122 +463,332 @@ 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:110
+#: ../extensions/deviceicon/network.py:104
msgid "Disconnect..."
msgstr "Déconnexion..."
-#: ../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:264
+#: ../extensions/deviceicon/network.py:112
+#: ../extensions/deviceicon/network.py:290
+#: ../src/jarabe/desktop/networkviews.py:241
+#: ../src/jarabe/desktop/networkviews.py:546
+#: ../src/jarabe/desktop/networkviews.py:673
msgid "Connecting..."
msgstr "Connexion..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:124
-#: ../extensions/deviceicon/network.py:186
-#: ../src/jarabe/desktop/meshbox.py:270
+#: ../extensions/deviceicon/network.py:116
+#: ../extensions/deviceicon/network.py:182
+#: ../src/jarabe/desktop/networkviews.py:251
+#: ../src/jarabe/desktop/networkviews.py:552
+#: ../src/jarabe/desktop/networkviews.py:679
msgid "Connected"
msgstr "Connecté"
-#: ../extensions/deviceicon/network.py:146
+#: ../extensions/deviceicon/network.py:142
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:161
+#: ../extensions/deviceicon/network.py:157
msgid "Wired Network"
msgstr "Réseau filaire"
-#: ../extensions/deviceicon/network.py:189
+#: ../extensions/deviceicon/network.py:185
msgid "Speed"
msgstr "Vitesse"
-#: ../extensions/deviceicon/network.py:415
+#: ../extensions/deviceicon/network.py:211
+msgid "Wireless modem"
+msgstr "Modem sans fil"
+
+#: ../extensions/deviceicon/network.py:278
+msgid "Please wait..."
+msgstr "Patienter..."
+
+#: ../extensions/deviceicon/network.py:282
+#: ../src/jarabe/desktop/networkviews.py:134
+#: ../src/jarabe/desktop/networkviews.py:500
+#: ../src/jarabe/desktop/networkviews.py:630
+msgid "Connect"
+msgstr "Connecter"
+
+#: ../extensions/deviceicon/network.py:283
+msgid "Disconnected"
+msgstr "Déconnecté"
+
+#: ../extensions/deviceicon/network.py:289
+#: ../src/jarabe/controlpanel/toolbar.py:119
+#: ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/frame/activitiestray.py:587
+#: ../src/jarabe/frame/activitiestray.py:687
+#: ../src/jarabe/frame/activitiestray.py:715
+msgid "Cancel"
+msgstr "Annuler"
+
+#: ../extensions/deviceicon/network.py:297
+#: ../src/jarabe/desktop/networkviews.py:138
+#: ../src/jarabe/desktop/networkviews.py:504
+msgid "Disconnect"
+msgstr "Déconnecter"
+
+#: ../extensions/deviceicon/network.py:327
+msgid "Try connection again"
+msgstr "Essayez de vous reconnecter"
+
+#: ../extensions/deviceicon/network.py:330
+#, python-format
+msgid "Error: %s"
+msgstr "Erreur : %s"
+
+#: ../extensions/deviceicon/network.py:334
+#, python-format
+msgid "Suggestion: %s"
+msgstr "Suggestion : %s"
+
+#: ../extensions/deviceicon/network.py:340
+#: ../extensions/deviceicon/network.py:343
+#, python-format
+msgid "Connected for %s"
+msgstr "Connecté depuis %s"
+
+#: ../extensions/deviceicon/network.py:348
+#: ../extensions/deviceicon/network.py:349
+#, python-format
+msgid "%d KB"
+msgstr "%d Ko"
+
+#: ../extensions/deviceicon/network.py:354
+msgid "Check your Pin/Puk configuration."
+msgstr "Vérifiez votre configuration Pin/Puk."
+
+#: ../extensions/deviceicon/network.py:357
+msgid "Check your Access Point Name (APN) configuration"
+msgstr "Vérifiez la configuration de votre nom de point d'accès (APN)."
+
+#: ../extensions/deviceicon/network.py:361
+msgid "Check the Number configuration."
+msgstr "Vérifiez la configuration du numéro."
+
+#: ../extensions/deviceicon/network.py:363
+msgid "Check your configuration."
+msgstr "Vérifiez votre configuration."
+
+#: ../extensions/deviceicon/network.py:605
+msgid "Mesh Network"
+msgstr "Réseau maillé"
+
+#: ../extensions/deviceicon/network.py:648
#, python-format
-msgid "%s's network %s"
-msgstr "Réseau %s %s"
+msgid "Mesh Network %s"
+msgstr "Réseau maillé %s"
+
+#: ../extensions/deviceicon/network.py:771
+msgid "No GSM connection available."
+msgstr "Aucune connexion GSM disponible."
+
+#: ../extensions/deviceicon/network.py:772
+msgid "Create a connection in the control panel."
+msgstr "Créez une connexion dans le panneau de contrôle."
-#: ../extensions/deviceicon/speaker.py:59
+#: ../extensions/deviceicon/resources.py:48
+msgid "System resources"
+msgstr "Ressources système"
+
+#: ../extensions/deviceicon/resources.py:170
+#, python-format
+msgid "CPU in use: %d%%"
+msgstr "Processeur utilisé : %d%%"
+
+#: ../extensions/deviceicon/resources.py:172
+#, python-format
+msgid "Memory in use: %d%%"
+msgstr "Mémoire utilisée : %d%%"
+
+#: ../extensions/deviceicon/resources.py:202
+msgid "Cannot compute CPU and memory usage statistics!"
+msgstr ""
+"Calcul des statistiques d'utilisation du processeur et de la mémoire "
+"impossible !"
+
+#: ../extensions/deviceicon/speaker.py:60
msgid "My Speakers"
msgstr "Haut-parleurs"
-#: ../extensions/deviceicon/speaker.py:133
+#: ../extensions/deviceicon/speaker.py:136
msgid "Unmute"
msgstr "Activer le son"
-#: ../extensions/deviceicon/speaker.py:136
+#: ../extensions/deviceicon/speaker.py:139
msgid "Mute"
msgstr "Mettre en sourdine"
-#: ../extensions/globalkey/screenshot.py:56
+#: ../extensions/deviceicon/touchpad.py:37
+msgid "finger"
+msgstr "doigt"
+
+#: ../extensions/deviceicon/touchpad.py:38
+msgid "stylus"
+msgstr "stylet"
+
+#: ../extensions/deviceicon/touchpad.py:67
+msgid "My touchpad"
+msgstr "Mon pavé tactile"
+
+#: ../extensions/globalkey/screenshot.py:59
msgid "Mesh"
msgstr "Réseau maillé"
-#: ../extensions/globalkey/screenshot.py:58
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../extensions/globalkey/screenshot.py:61
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "Group"
msgstr "Groupe"
-#: ../extensions/globalkey/screenshot.py:60
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../extensions/globalkey/screenshot.py:63
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "Home"
msgstr "Accueil"
-#: ../extensions/globalkey/screenshot.py:66
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../extensions/globalkey/screenshot.py:69
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "Activity"
msgstr "Activité"
-#: ../extensions/globalkey/screenshot.py:69
+#: ../extensions/globalkey/screenshot.py:72
msgid "Screenshot"
msgstr "Capture d'écran"
-#: ../extensions/globalkey/screenshot.py:71
+#: ../extensions/globalkey/screenshot.py:74
#, python-format
msgid "Screenshot of \"%s\""
msgstr "Capture d'écran de \"%s\""
#: ../data/sugar.schemas.in.h:1
+msgid ""
+"\"disabled\" to ask nick on initialization; \"system\" to reuse UNIX account "
+"long name."
+msgstr ""
+"\"désactivé\" pour demander un pseudo lors de l'initialisation ; \"système\" "
+"pour réutiliser l'identifiant long du compte UNIX."
+
+#: ../data/sugar.schemas.in.h:2
+msgid "Additional directories which can contain updated translations."
+msgstr ""
+"Les dossiers supplémentaires peuvent comporter des traductions mises à jour."
+
+#: ../data/sugar.schemas.in.h:3
msgid "Backup URL"
msgstr "Sauvegarde de l'URL"
-#: ../data/sugar.schemas.in.h:2
+#: ../data/sugar.schemas.in.h:4
+msgid "Bundle IDs of protected activities"
+msgstr "Identifiant des bundles des activités protégées"
+
+#: ../data/sugar.schemas.in.h:5
msgid ""
"Color for the XO icon that is used throughout the desktop. The string is "
-"composed of the stroke color and fill color, format is that of rbg colors. "
+"composed of the stroke color and fill color, format is that of rgb colors. "
"Example: #AC32FF,#9A5200"
msgstr ""
-"Couleur du XO utilisée sur le Bureau. La chaîne indique la couleur du trait "
-"et du remplissage. Le format correspond aux couleurs RVB. Exemple : "
-"#AC32FF,#9A5200"
+"Couleur de l'icône du XO utilisée sur le Bureau. La chaîne indique la "
+"couleur du trait et du remplissage. Le format correspond aux couleurs RVB. "
+"Exemple : #AC32FF,#9A5200"
-#: ../data/sugar.schemas.in.h:3
+#: ../data/sugar.schemas.in.h:6
msgid "Corner Delay"
msgstr "Délai des coins"
-#: ../data/sugar.schemas.in.h:4
+#: ../data/sugar.schemas.in.h:7
+msgid "Default font face"
+msgstr "Police par défaut"
+
+#: ../data/sugar.schemas.in.h:8
+msgid "Default font size"
+msgstr "Corps de la police par défaut"
+
+#: ../data/sugar.schemas.in.h:9
+msgid "Default nick"
+msgstr "Pseudo par défaut"
+
+#: ../data/sugar.schemas.in.h:10
msgid "Delay for the activation of the frame using the corners."
msgstr "Délai d'activation du cadre à l'aide des coins."
-#: ../data/sugar.schemas.in.h:5
+#: ../data/sugar.schemas.in.h:11
msgid "Delay for the activation of the frame using the edges."
msgstr "Délai d'activation du cadre à l'aide des bords."
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:12
+msgid "Directory to search for translations"
+msgstr "Répertoire de recherche des traductions"
+
+#: ../data/sugar.schemas.in.h:13
msgid "Edge Delay"
msgstr "Délai des bords"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:14
msgid "Favorites Layout"
msgstr "Disposition favorite"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:15
msgid "Favorites resume mode"
-msgstr "Mode de reprise favori "
+msgstr "Mode de reprise favori"
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:16
+msgid "Font face that is used throughout the desktop."
+msgstr "Police utilisée sur le bureau."
+
+#: ../data/sugar.schemas.in.h:17
+msgid "Font size that is used throughout the desktop."
+msgstr "Corps de la police utilisée sur le bureau."
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network APN"
+msgstr "Code APN du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network PIN"
+msgstr "Code PIN du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network PUK"
+msgstr "Code PUK du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network access point name configuration"
+msgstr "Configuration du nom du point d'accès au réseau GSM"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network number"
+msgstr "Numéro du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network password"
+msgstr "Mot de passe du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network password configuration"
+msgstr "Configuration du mot de passe du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:25
+msgid "GSM network personal identification number configuration"
+msgstr "Configuration du numéro d'identification personnel du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:26
+msgid "GSM network personal unlock key configuration"
+msgstr "Configuration de la clé de déblocage personnelle du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:27
+msgid "GSM network telephone number configuration"
+msgstr "Configuration du numéro de téléphone du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:28
+msgid "GSM network username"
+msgstr "Nom d'utilisateur du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:29
+msgid "GSM network username configuration"
+msgstr "Configuration du nom d'utilisateur du réseau GSM"
+
+#: ../data/sugar.schemas.in.h:30
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
@@ -537,110 +796,139 @@ msgstr ""
"Si VRAI, Sugar permettra aux autres utilisateurs du serveur Jabber de nous "
"retrouver."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:31
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
+#: ../data/sugar.schemas.in.h:32
+msgid "If TRUE, Sugar will show a \"Restart\" option."
+msgstr "Si VRAI, Sugar affichera une option \"Redémarrer\"."
+
+#: ../data/sugar.schemas.in.h:33
+msgid ""
+"If TRUE, Sugar will show default Ad-hoc networks for channel 1,6 and 11. If "
+"Sugar sees no \"known\" network when it starts, it does autoconnect to an Ad-"
+"hoc network."
+msgstr ""
+"Si VRAI, Sugar affichera les réseaux ad-hoc des canaux 1,6 et 11. si Sugar "
+"ne voit aucun réseau \"connu\" au démarrage, il se connecte automatiquement à "
+"un réseau ad-hoc."
+
+#: ../data/sugar.schemas.in.h:34
msgid "Jabber Server"
msgstr "Serveur Jabber"
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:35
msgid "Keyboard layouts"
msgstr "Dispositions du clavier"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:36
msgid "Keyboard model"
msgstr "Modèle de clavier"
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:37
msgid "Keyboard options"
msgstr "Options du clavier"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:38
msgid "Layout of the favorites view."
msgstr "Disposition de la vue favorite."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:39
msgid ""
"List of keyboard layouts. Each entry should be in the form layout(variant)"
msgstr ""
"Liste des dispositions de claviers. Chaque ligne doit avoir la forme "
"disposition(variante)"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:40
msgid "List of keyboard options."
msgstr "Liste des options de clavier."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:41
msgid "Power Automatic"
msgstr "Alimentation automatique"
-#: ../data/sugar.schemas.in.h:19
+#: ../data/sugar.schemas.in.h:42
msgid "Power Automatic."
msgstr "Alimentation automatique."
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:43
msgid "Power Extreme"
msgstr "Alimentation extrême"
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:44
msgid "Power Extreme."
msgstr "Alimentation extrême."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:45
msgid "Publish to Gadget"
msgstr "Publication vers Gadget"
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:46
msgid "Setting for muting the sound device."
msgstr "Configuration de la mise en sourdine du périphérique audio."
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:47
msgid "Show Log out"
msgstr "Afficher Déconnexion"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:48
+msgid "Show Restart"
+msgstr "Afficher Redémarrer"
+
+#: ../data/sugar.schemas.in.h:49
+msgid "Show Sugar Ad-hoc networks"
+msgstr "Afficher les réseaux ad-hoc Sugar"
+
+#: ../data/sugar.schemas.in.h:50
msgid "Sound Muted"
msgstr "Audio désactivé"
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:51
msgid "The keyboard model to be used"
msgstr "Modèle de clavier à utiliser"
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:53
msgid "Timezone setting for the system."
msgstr "Configuration du fuseau horaire du système."
-#: ../data/sugar.schemas.in.h:29
+#: ../data/sugar.schemas.in.h:54
msgid "Url of the jabber server to use."
msgstr "URL du serveur Jabber à utiliser."
-#: ../data/sugar.schemas.in.h:30
+#: ../data/sugar.schemas.in.h:55
msgid "Url where the backup is saved to."
msgstr "URL d'enregistrement de la sauvegarde."
-#: ../data/sugar.schemas.in.h:31
+#: ../data/sugar.schemas.in.h:56
msgid "User Color"
msgstr "Couleurs de l'utilisateur"
-#: ../data/sugar.schemas.in.h:32
+#: ../data/sugar.schemas.in.h:57
msgid "User Name"
msgstr "Nom de l'utilisateur"
-#: ../data/sugar.schemas.in.h:33
+#: ../data/sugar.schemas.in.h:58
msgid "User name that is used throughout the desktop."
msgstr "Nom identifiant l'utilisateur sur le bureau."
-#: ../data/sugar.schemas.in.h:34
+#: ../data/sugar.schemas.in.h:59
+msgid ""
+"Users will not be allowed to erase these activities through the list view."
+msgstr ""
+"Les utilisateurs ne sont pas autorisés à supprimer ces activités depuis la "
+"vue liste."
+
+#: ../data/sugar.schemas.in.h:60
msgid "Volume Level"
msgstr "Niveau de volume"
-#: ../data/sugar.schemas.in.h:35
+#: ../data/sugar.schemas.in.h:61
msgid "Volume level for the sound device."
msgstr "Niveau de volume du périphérique audio."
-#: ../data/sugar.schemas.in.h:36
+#: ../data/sugar.schemas.in.h:62
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -671,7 +959,7 @@ msgstr "sugar-control-panel: %s"
# which must appear in the translated string (msgstr) as well.
#. TRANS: Translators, there's a empty line at the end of this string,
#. which must appear in the translated string (msgstr) as well.
-#: ../src/jarabe/controlpanel/cmd.py:37
+#: ../src/jarabe/controlpanel/cmd.py:38
msgid ""
"Usage: sugar-control-panel [ option ] key [ args ... ] \n"
" Control for the sugar environment. \n"
@@ -694,73 +982,53 @@ msgstr ""
" -s clé définir la valeur actuelle de cette clé \n"
" "
-#: ../src/jarabe/controlpanel/cmd.py:50
+#: ../src/jarabe/controlpanel/cmd.py:52
msgid "To apply your changes you have to restart sugar.\n"
msgstr "Redémarrer sugar pour que les changements prennent effet.\n"
-#: ../src/jarabe/controlpanel/gui.py:280
+#: ../src/jarabe/controlpanel/gui.py:297
+#: ../src/jarabe/journal/journaltoolbox.py:437
+#: ../src/jarabe/journal/volumestoolbar.py:158
msgid "Warning"
msgstr "Attention"
-#: ../src/jarabe/controlpanel/gui.py:281
-#: ../src/jarabe/controlpanel/sectionview.py:42
+#: ../src/jarabe/controlpanel/gui.py:298
+#: ../src/jarabe/controlpanel/sectionview.py:41
msgid "Changes require restart"
msgstr "Relancer pour valider"
-#: ../src/jarabe/controlpanel/gui.py:284
+#: ../src/jarabe/controlpanel/gui.py:301
msgid "Cancel changes"
msgstr "Abandonner"
-#: ../src/jarabe/controlpanel/gui.py:289 ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/controlpanel/gui.py:306 ../src/jarabe/desktop/homebox.py:72
msgid "Later"
msgstr "Plus tard"
-#: ../src/jarabe/controlpanel/gui.py:293
+#: ../src/jarabe/controlpanel/gui.py:310
msgid "Restart now"
msgstr "Maintenant"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:188
+#: ../src/jarabe/controlpanel/toolbar.py:63 ../src/jarabe/intro/window.py:212
msgid "Done"
msgstr "Accepter"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:68
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../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:332
+#: ../src/jarabe/controlpanel/toolbar.py:125
+#: ../src/jarabe/desktop/favoritesview.py:336
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
+#: ../src/jarabe/desktop/activitieslist.py:230
#, python-format
msgid "Version %s"
msgstr "Version %s"
-#: ../src/jarabe/desktop/activitieslist.py:355
+#: ../src/jarabe/desktop/activitieslist.py:354
msgid "Confirm erase"
msgstr "Confirmer la suppression"
# Conformer la suppression : faut-il supprimer %s définitivement ?
-#: ../src/jarabe/desktop/activitieslist.py:357
+#: ../src/jarabe/desktop/activitieslist.py:356
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
msgstr "Confirmer la suppression : faut-il supprimer %s définitivement ?"
@@ -769,388 +1037,446 @@ msgstr "Confirmer la suppression : faut-il supprimer %s définitivement ?"
# 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
+#: ../src/jarabe/desktop/activitieslist.py:360
+#: ../src/jarabe/frame/clipboardmenu.py:64
+#: ../src/jarabe/view/viewsource.py:221
msgid "Keep"
msgstr "Conserver"
-#: ../src/jarabe/desktop/activitieslist.py:364
-#: ../src/jarabe/desktop/activitieslist.py:407
-#: ../src/jarabe/journal/journaltoolbox.py:360
+#: ../src/jarabe/desktop/activitieslist.py:363
+#: ../src/jarabe/desktop/activitieslist.py:417
+#: ../src/jarabe/journal/journaltoolbox.py:391
#: ../src/jarabe/journal/palettes.py:112
msgid "Erase"
msgstr "Supprimer"
-#: ../src/jarabe/desktop/activitieslist.py:428
+#: ../src/jarabe/desktop/activitieslist.py:432
msgid "Remove favorite"
msgstr "Retirer le favori"
-#: ../src/jarabe/desktop/activitieslist.py:432
+#: ../src/jarabe/desktop/activitieslist.py:436
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
+#: ../src/jarabe/desktop/favoriteslayout.py:127
msgid "Freeform"
msgstr "Libre"
# 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:198
+#: ../src/jarabe/desktop/favoriteslayout.py:215
msgid "Ring"
msgstr "Concentrique"
# 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:337
+#: ../src/jarabe/desktop/favoriteslayout.py:402
msgid "Spiral"
msgstr "Spirale"
# 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:404
+#: ../src/jarabe/desktop/favoriteslayout.py:472
msgid "Box"
msgstr "Boîte"
# 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:445
+#: ../src/jarabe/desktop/favoriteslayout.py:515
msgid "Triangle"
msgstr "Triangle"
-#: ../src/jarabe/desktop/favoritesview.py:323
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "Registration Failed"
msgstr "Echec de l'enregistrement"
-#: ../src/jarabe/desktop/favoritesview.py:324
+#: ../src/jarabe/desktop/favoritesview.py:328
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:326
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Successful"
msgstr "Enregistrement réussi"
-#: ../src/jarabe/desktop/favoritesview.py:327
+#: ../src/jarabe/desktop/favoritesview.py:331
msgid "You are now registered with your school server."
-msgstr "Vous êtes maintenant enregistré sur le serveur de l'école"
+msgstr "Vous êtes maintenant enregistré sur le serveur de l'école."
-#: ../src/jarabe/desktop/favoritesview.py:671
+#: ../src/jarabe/desktop/favoritesview.py:627
msgid "Register"
msgstr "S'enregistrer"
-#: ../src/jarabe/desktop/homebox.py:63
+#: ../src/jarabe/desktop/favoritesview.py:629
+#: ../src/jarabe/desktop/favoritesview.py:646
+msgid "Register again"
+msgstr "S'enregistrer à nouveau"
+
+#: ../src/jarabe/desktop/homebox.py:65
msgid "Software Update"
msgstr "Mise à jour logicielle"
-#: ../src/jarabe/desktop/homebox.py:64
+#: ../src/jarabe/desktop/homebox.py:66
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:73
+#: ../src/jarabe/desktop/homebox.py:75
msgid "Check now"
msgstr "Vérifier maintenant"
-#: ../src/jarabe/desktop/homebox.py:192
+#: ../src/jarabe/desktop/homebox.py:193
msgid "List view"
msgstr "Écran liste"
-#: ../src/jarabe/desktop/homebox.py:193
+#: ../src/jarabe/desktop/homebox.py:194
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:255
+#: ../src/jarabe/desktop/homebox.py:257
msgid "Favorites view"
msgstr "Écran favoris"
-#: ../src/jarabe/desktop/homebox.py:256
+#: ../src/jarabe/desktop/homebox.py:258
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:131
+#: ../src/jarabe/desktop/keydialog.py:143
msgid "Key Type:"
msgstr "Type de clé :"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:163
msgid "Authentication Type:"
msgstr "Type d'authentification :"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "WPA & WPA2 Personal"
msgstr "WPA & WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:238
msgid "Wireless Security:"
msgstr "Sécurité sans fil :"
-#: ../src/jarabe/desktop/meshbox.py:136
-msgid "Connect"
-msgstr "Connecter"
-
-#: ../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:466
-#: ../src/jarabe/frame/activitiestray.py:761
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:64
+#: ../src/jarabe/desktop/meshbox.py:109
+#: ../src/jarabe/frame/activitiestray.py:622
+#: ../src/jarabe/journal/journaltoolbox.py:485
+#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:78
msgid "Resume"
msgstr "Reprendre"
-#: ../src/jarabe/desktop/meshbox.py:471
-#: ../src/jarabe/frame/activitiestray.py:235
+#: ../src/jarabe/desktop/meshbox.py:114
+#: ../src/jarabe/frame/activitiestray.py:173
msgid "Join"
msgstr "Rejoindre"
-#: ../src/jarabe/desktop/schoolserver.py:103
+#: ../src/jarabe/desktop/networkviews.py:497
+#, python-format
+msgid "Ad-hoc Network %d"
+msgstr "Réseau ad-hoc %d"
+
+#: ../src/jarabe/desktop/networkviews.py:628
+#, python-format
+msgid "Mesh Network %d"
+msgstr "Réseau maillé %d"
+
+#: ../src/jarabe/desktop/schoolserver.py:131
msgid "Cannot connect to the server."
msgstr "Impossible de se connecter au serveur."
-#: ../src/jarabe/desktop/schoolserver.py:108
+#: ../src/jarabe/desktop/schoolserver.py:136
msgid "The server could not complete the request."
msgstr "Le serveur n'a pas pu achever la requête."
-#: ../src/jarabe/frame/activitiestray.py:240
-#: ../src/jarabe/frame/activitiestray.py:698
+#: ../src/jarabe/frame/activitiestray.py:178
+#: ../src/jarabe/frame/activitiestray.py:559
msgid "Decline"
msgstr "Refuser"
-#: ../src/jarabe/frame/activitiestray.py:650
+#: ../src/jarabe/frame/activitiestray.py:509
#, python-format
msgid "%dB"
msgstr "%do"
-#: ../src/jarabe/frame/activitiestray.py:652
+#: ../src/jarabe/frame/activitiestray.py:511
#, python-format
msgid "%dKB"
msgstr "%dKo"
-#: ../src/jarabe/frame/activitiestray.py:654
+#: ../src/jarabe/frame/activitiestray.py:513
#, python-format
msgid "%dMB"
msgstr "%dMo"
-#: ../src/jarabe/frame/activitiestray.py:671
+#: ../src/jarabe/frame/activitiestray.py:530
#, python-format
msgid "%s of %s"
msgstr "%s sur %s"
-#: ../src/jarabe/frame/activitiestray.py:683
+#: ../src/jarabe/frame/activitiestray.py:544
#, python-format
msgid "Transfer from %r"
msgstr "Transfert depuis %r"
-#: ../src/jarabe/frame/activitiestray.py:693
+#: ../src/jarabe/frame/activitiestray.py:554
msgid "Accept"
msgstr "Accepter"
-#: ../src/jarabe/frame/activitiestray.py:716
-#: ../src/jarabe/frame/activitiestray.py:840
+#: ../src/jarabe/frame/activitiestray.py:577
+#: ../src/jarabe/frame/activitiestray.py:705
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:750
-#: ../src/jarabe/frame/activitiestray.py:875
+#: ../src/jarabe/frame/activitiestray.py:611
+#: ../src/jarabe/frame/activitiestray.py:740
msgid "Dismiss"
msgstr "Refuser"
-#: ../src/jarabe/frame/activitiestray.py:810
+#: ../src/jarabe/frame/activitiestray.py:675
#, python-format
msgid "Transfer to %r"
msgstr "Transfert vers %r"
-#: ../src/jarabe/frame/clipboardmenu.py:52 ../src/jarabe/view/palettes.py:218
+#: ../src/jarabe/frame/clipboardmenu.py:54 ../src/jarabe/view/palettes.py:220
msgid "Remove"
msgstr "Retirer"
-#: ../src/jarabe/frame/clipboardmenu.py:57
-#: ../src/jarabe/frame/clipboardmenu.py:80
+#: ../src/jarabe/frame/clipboardmenu.py:59
+#: ../src/jarabe/frame/clipboardmenu.py:82
msgid "Open"
msgstr "Ouvrir"
-#: ../src/jarabe/frame/clipboardmenu.py:85
+#: ../src/jarabe/frame/clipboardmenu.py:87
msgid "Open with"
msgstr "Ouvrir avec"
-#: ../src/jarabe/frame/clipboardobject.py:49
+#: ../src/jarabe/frame/clipboardobject.py:50
#, python-format
msgid "%s clipping"
msgstr "%s coupure"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "Neighborhood"
msgstr "Voisinage"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "F1"
msgstr "F1"
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "F2"
msgstr "F2"
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "F3"
msgstr "F3"
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "F4"
msgstr "F4"
-#: ../src/jarabe/intro/window.py:124
+#: ../src/jarabe/intro/window.py:97
+msgid "Name:"
+msgstr "Nom :"
+
+#: ../src/jarabe/intro/window.py:133
msgid "Click to change color:"
msgstr "Cliquer pour changer de couleur :"
-#: ../src/jarabe/intro/window.py:174 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:198 ../src/jarabe/journal/detailview.py:105
msgid "Back"
msgstr "Précédent"
-#: ../src/jarabe/intro/window.py:191
+#: ../src/jarabe/intro/window.py:215
msgid "Next"
msgstr "Suivant"
-#: ../src/jarabe/journal/expandedentry.py:164
-#: ../src/jarabe/journal/palettes.py:66
+#: ../src/jarabe/journal/expandedentry.py:154
+#: ../src/jarabe/journal/listmodel.py:144 ../src/jarabe/journal/palettes.py:59
msgid "Untitled"
msgstr "Sans titre"
-#: ../src/jarabe/journal/expandedentry.py:210
+#: ../src/jarabe/journal/expandedentry.py:243
msgid "No preview"
msgstr "Pas de prévisualisation"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:262
#, python-format
msgid "Kind: %s"
msgstr "Variante : %s"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:262
+#: ../src/jarabe/journal/listmodel.py:150
+#: ../src/jarabe/journal/listmodel.py:158
+#: ../src/jarabe/journal/listmodel.py:166
msgid "Unknown"
msgstr "Inconnu"
-#: ../src/jarabe/journal/expandedentry.py:230
+#: ../src/jarabe/journal/expandedentry.py:263
#, python-format
msgid "Date: %s"
msgstr "Date : %s"
-#: ../src/jarabe/journal/expandedentry.py:231
+#: ../src/jarabe/journal/expandedentry.py:264
#, python-format
msgid "Size: %s"
msgstr "Taille : %s"
-#: ../src/jarabe/journal/expandedentry.py:253 ../src/jarabe/journal/misc.py:92
+#: ../src/jarabe/journal/expandedentry.py:292
+#: ../src/jarabe/journal/misc.py:108
msgid "No date"
msgstr "Sans date"
-#: ../src/jarabe/journal/expandedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:299
msgid "Participants:"
msgstr "Participants :"
-#: ../src/jarabe/journal/expandedentry.py:283
+#: ../src/jarabe/journal/expandedentry.py:321
msgid "Description:"
msgstr "Description :"
-#: ../src/jarabe/journal/expandedentry.py:309
+#: ../src/jarabe/journal/expandedentry.py:346
msgid "Tags:"
msgstr "Étiquettes :"
-#: ../src/jarabe/journal/journalactivity.py:108
-#: ../src/jarabe/journal/volumestoolbar.py:47
+#: ../src/jarabe/journal/journalactivity.py:115
+#: ../src/jarabe/journal/journaltoolbox.py:456
+#: ../src/jarabe/journal/volumestoolbar.py:50
msgid "Journal"
msgstr "Journal"
-#: ../src/jarabe/journal/journaltoolbox.py:67
+#: ../src/jarabe/journal/journaltoolbox.py:69
msgid "Search"
msgstr "Rechercher"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:136
msgid "Anytime"
msgstr "N'importe quand"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:138
msgid "Today"
msgstr "Aujourd'hui"
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:140
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:132
+#: ../src/jarabe/journal/journaltoolbox.py:142
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:134
+#: ../src/jarabe/journal/journaltoolbox.py:144
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:136
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "Past year"
msgstr "Depuis une année"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:153
msgid "Anyone"
msgstr "Tout le monde"
-#: ../src/jarabe/journal/journaltoolbox.py:145
+#: ../src/jarabe/journal/journaltoolbox.py:155
msgid "My friends"
msgstr "Mes amis"
-#: ../src/jarabe/journal/journaltoolbox.py:146
+#: ../src/jarabe/journal/journaltoolbox.py:156
msgid "My class"
msgstr "Ma classe"
# TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:274
+#: ../src/jarabe/journal/journaltoolbox.py:298
msgid "Anything"
msgstr "Tout"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:350
+#: ../src/jarabe/journal/journaltoolbox.py:381
#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Copier"
+#: ../src/jarabe/journal/journaltoolbox.py:436
+#: ../src/jarabe/journal/volumestoolbar.py:157
+msgid "Entries without a file cannot be copied."
+msgstr "Impossible de copier les entrées sans fichier."
+
+#: ../src/jarabe/journal/journaltoolbox.py:445
+#: ../src/jarabe/journal/volumestoolbar.py:166
+#, python-format
+msgid "Error while copying the entry. %s"
+msgstr "Erreur lors de la copie de l'entrée. %s"
+
+#: ../src/jarabe/journal/journaltoolbox.py:446
+#: ../src/jarabe/journal/volumestoolbar.py:167
+msgid "Error"
+msgstr "Erreur"
+
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:431
-#: ../src/jarabe/journal/palettes.py:75
+#: ../src/jarabe/journal/journaltoolbox.py:488
+#: ../src/jarabe/journal/palettes.py:69
msgid "Start"
msgstr "Lancer"
-#: ../src/jarabe/journal/listview.py:361
+#: ../src/jarabe/journal/journaltoolbox.py:516
+msgid "Sort by date modified"
+msgstr "Trier sur la date de modification"
+
+#: ../src/jarabe/journal/journaltoolbox.py:517
+msgid "Sort by date created"
+msgstr "Trier sur la date de création"
+
+#: ../src/jarabe/journal/journaltoolbox.py:518
+msgid "Sort by size"
+msgstr "Trier sur la taille"
+
+#: ../src/jarabe/journal/journaltoolbox.py:527
+msgid "Sort view"
+msgstr "Trier l'affichage"
+
+#: ../src/jarabe/journal/listview.py:380
msgid "Your Journal is empty"
msgstr "Le journal est vide"
-#: ../src/jarabe/journal/listview.py:363
+#: ../src/jarabe/journal/listview.py:382
msgid "No matching entries"
msgstr "Aucune entrée correspondante"
-#: ../src/jarabe/journal/listview.py:374
+#: ../src/jarabe/journal/listview.py:393
msgid "Clear search"
msgstr "Effacer la recherche"
-#: ../src/jarabe/journal/modalalert.py:63
+#: ../src/jarabe/journal/misc.py:273
+#, python-format
+msgid "Older Version Of %s Activity"
+msgstr "Ancienne version de l'activité %s"
+
+#: ../src/jarabe/journal/misc.py:274
+#, python-format
+msgid "Do you want to downgrade to version %s"
+msgstr "Voulez-vous revenir à la version %s"
+
+#: ../src/jarabe/journal/modalalert.py:64
msgid "Your Journal is full"
msgstr "Votre journal est plein"
-#: ../src/jarabe/journal/modalalert.py:67
+#: ../src/jarabe/journal/modalalert.py:68
msgid "Please delete some old Journal entries to make space for new ones."
msgstr ""
"Effacer des entrées anciennes du Journal pour libérer de la place pour les "
"nouvelles entrées."
-#: ../src/jarabe/journal/modalalert.py:79
+#: ../src/jarabe/journal/modalalert.py:80
msgid "Show Journal"
msgstr "Montre le Journal"
@@ -1159,18 +1485,22 @@ msgid "Choose an object"
msgstr "Choisir un objet"
#: ../src/jarabe/journal/objectchooser.py:151
-#: ../src/jarabe/view/viewsource.py:308
+#: ../src/jarabe/view/viewsource.py:311
msgid "Close"
msgstr "Fermer"
-#: ../src/jarabe/journal/palettes.py:73
+#: ../src/jarabe/journal/palettes.py:67
msgid "Resume with"
msgstr "Reprendre avec"
-#: ../src/jarabe/journal/palettes.py:76
+#: ../src/jarabe/journal/palettes.py:70
msgid "Start with"
msgstr "Commencer avec"
+#: ../src/jarabe/journal/palettes.py:83 ../src/jarabe/journal/palettes.py:216
+msgid "No activity to start entry"
+msgstr "Acune activité pour démarrer l'entrée"
+
#: ../src/jarabe/journal/palettes.py:98
msgid "Send to"
msgstr "Envoyer à"
@@ -1179,99 +1509,306 @@ msgstr "Envoyer à"
msgid "View Details"
msgstr "Afficher les détails"
-#: ../src/jarabe/journal/palettes.py:185
+#: ../src/jarabe/journal/palettes.py:181
msgid "No friends present"
msgstr "Aucun ami présent"
-#: ../src/jarabe/journal/palettes.py:190
+#: ../src/jarabe/journal/palettes.py:186
msgid "No valid connection found"
msgstr "Aucune connexion valide trouvée"
-#: ../src/jarabe/journal/palettes.py:218
+#: ../src/jarabe/journal/palettes.py:214
msgid "No activity to resume entry"
msgstr "Aucune activité pour reprendre l'entrée"
-#: ../src/jarabe/journal/palettes.py:220
-msgid "No activity to start entry"
-msgstr "Acune activité pour démarrer l'entrée"
+#: ../src/jarabe/model/network.py:163
+msgid "The reason for the device state change is unknown."
+msgstr "La raison du changement d'état du périphérique est inconnue."
+
+#: ../src/jarabe/model/network.py:165
+msgid "The state change is normal."
+msgstr "Le changement d'état est normal."
+
+#: ../src/jarabe/model/network.py:167
+msgid "The device is now managed."
+msgstr "Le périphérique est géré."
+
+#: ../src/jarabe/model/network.py:169
+msgid "The device is no longer managed."
+msgstr "Le périphérique n'est plus géré."
+
+#: ../src/jarabe/model/network.py:171
+msgid "The device could not be readied for configuration."
+msgstr "Le périphérique n'est pas disponible pour être configuré."
+
+#: ../src/jarabe/model/network.py:173
+msgid ""
+"IP configuration could not be reserved (no available address, timeout, etc)."
+msgstr ""
+"Impossible de réserver la configuration IP (adresse indisponible, "
+"dépassement de délai, etc.)."
+
+#: ../src/jarabe/model/network.py:176
+msgid "The IP configuration is no longer valid."
+msgstr "La configuration IP n'est plus valide."
+
+#: ../src/jarabe/model/network.py:178
+msgid "Secrets were required, but not provided."
+msgstr "Secrets requis mais non fournis."
+
+#: ../src/jarabe/model/network.py:180
+msgid ""
+"The 802.1X supplicant disconnected from the access point or authentication "
+"server."
+msgstr ""
+"Déconnexion du supplicant 802.1X du point d'accès ou du serveur "
+"d'authentification."
+
+#: ../src/jarabe/model/network.py:183
+msgid "Configuration of the 802.1X supplicant failed."
+msgstr "Echec de la configuration du supplicant 802.1X."
+
+#: ../src/jarabe/model/network.py:185
+msgid "The 802.1X supplicant quit or failed unexpectedly."
+msgstr "Arrêt ou échec inattendu du supplicant 802.1X."
+
+#: ../src/jarabe/model/network.py:187
+msgid "The 802.1X supplicant took too long to authenticate."
+msgstr "Délai d'authentification trop long du supplicant 802.1X."
+
+#: ../src/jarabe/model/network.py:189
+msgid "The PPP service failed to start within the allowed time."
+msgstr "Echec du démarrage du service PPP dans le délai autorisé."
+
+#: ../src/jarabe/model/network.py:191
+msgid "The PPP service disconnected unexpectedly."
+msgstr "Déconnexion inattendue du service PPP."
+
+#: ../src/jarabe/model/network.py:193
+msgid "The PPP service quit or failed unexpectedly."
+msgstr "Arrêt ou échec inattendu du service PPP."
+
+#: ../src/jarabe/model/network.py:195
+msgid "The DHCP service failed to start within the allowed time."
+msgstr "Echec du démarrage du service DHCP dans le délai autorisé."
+
+#: ../src/jarabe/model/network.py:197
+msgid "The DHCP service reported an unexpected error."
+msgstr "Le service DHCP a signalé une erreur inattendue."
+
+#: ../src/jarabe/model/network.py:199
+msgid "The DHCP service quit or failed unexpectedly."
+msgstr "Arrêt ou échec inattendu du service DHCP."
+
+#: ../src/jarabe/model/network.py:201
+msgid "The shared connection service failed to start."
+msgstr "Echec du démarrage du service de connexion partagée."
+
+#: ../src/jarabe/model/network.py:203
+msgid "The shared connection service quit or failed unexpectedly."
+msgstr "Arrêt ou échec inattendu du service de connexion partagée."
+
+#: ../src/jarabe/model/network.py:206
+msgid "The AutoIP service failed to start."
+msgstr "Echec du démarrage du service AutoIP."
+
+#: ../src/jarabe/model/network.py:208
+msgid "The AutoIP service reported an unexpected error."
+msgstr "Le service AutoIP a signalé une erreur inattendue."
+
+#: ../src/jarabe/model/network.py:210
+msgid "The AutoIP service quit or failed unexpectedly."
+msgstr "Arrêt ou échec inattendu du service AutoIP."
+
+#: ../src/jarabe/model/network.py:212
+msgid "Dialing failed because the line was busy."
+msgstr "Echec de la numérotation car la ligne était occupée."
+
+#: ../src/jarabe/model/network.py:214
+msgid "Dialing failed because there was no dial tone."
+msgstr "Echec de la numérotation en l'absence de tonalité."
+
+#: ../src/jarabe/model/network.py:216
+msgid "Dialing failed because there was no carrier."
+msgstr "Echec de la numérotation résultant de l'absence de porteuse."
-#: ../src/jarabe/view/buddymenu.py:62
+#: ../src/jarabe/model/network.py:218
+msgid "Dialing timed out."
+msgstr "Délai de la numérotation dépassé."
+
+#: ../src/jarabe/model/network.py:220
+msgid "Dialing failed."
+msgstr "Echec de la numérotation."
+
+#: ../src/jarabe/model/network.py:222
+msgid "Modem initialization failed."
+msgstr "Echec de l'initialisation du modem."
+
+#: ../src/jarabe/model/network.py:224
+msgid "Failed to select the specified GSM APN"
+msgstr "Echec de la sélection de l'APN du GSM spécifié."
+
+#: ../src/jarabe/model/network.py:226
+msgid "Not searching for networks."
+msgstr "Pas de recherche de réseaux."
+
+#: ../src/jarabe/model/network.py:228
+msgid "Network registration was denied."
+msgstr "Enregistrement du réseau refusé."
+
+#: ../src/jarabe/model/network.py:230
+msgid "Network registration timed out."
+msgstr "Enregistrement du réseau expiré."
+
+#: ../src/jarabe/model/network.py:232
+msgid "Failed to register with the requested GSM network."
+msgstr "Echec de l'enregistrement avec le réseau GSM demandé."
+
+#: ../src/jarabe/model/network.py:234
+msgid "PIN check failed."
+msgstr "Echec de la vérification du code PIN."
+
+#: ../src/jarabe/model/network.py:236
+msgid "Necessary firmware for the device may be missing."
+msgstr "Le firmware requis pour le périphérique est peut-être absent."
+
+#: ../src/jarabe/model/network.py:238
+msgid "The device was removed."
+msgstr "Le périphérique a été retiré."
+
+#: ../src/jarabe/model/network.py:240
+msgid "NetworkManager went to sleep."
+msgstr "NetworkManager est en veille."
+
+#: ../src/jarabe/model/network.py:242
+msgid "The device's active connection was removed or disappeared."
+msgstr "La connexion active du périphérique a été supprimée ou a disparu."
+
+#: ../src/jarabe/model/network.py:245
+msgid "A user or client requested the disconnection."
+msgstr "Un utilisateur ou un client a demandé la déconnexion."
+
+#: ../src/jarabe/model/network.py:247
+msgid "The device's carrier/link changed."
+msgstr "La porteuse/le lien du périphérique a changé."
+
+#: ../src/jarabe/view/buddymenu.py:63
msgid "Remove friend"
msgstr "Retirer de mes amis"
-#: ../src/jarabe/view/buddymenu.py:65
+#: ../src/jarabe/view/buddymenu.py:66
msgid "Make friend"
msgstr "Ajouter à mes amis"
-#: ../src/jarabe/view/buddymenu.py:82
+#: ../src/jarabe/view/buddymenu.py:83
msgid "Shutdown"
msgstr "Éteindre"
-#: ../src/jarabe/view/buddymenu.py:90
+#: ../src/jarabe/view/buddymenu.py:91
+msgid "Restart"
+msgstr "Redémarrer"
+
+#: ../src/jarabe/view/buddymenu.py:97
msgid "Logout"
msgstr "Se déconnecter"
-#: ../src/jarabe/view/buddymenu.py:95
+#: ../src/jarabe/view/buddymenu.py:102
msgid "My Settings"
msgstr "Mes paramètres"
-#: ../src/jarabe/view/buddymenu.py:130
+#: ../src/jarabe/view/buddymenu.py:137
#, python-format
msgid "Invite to %s"
msgstr "Inviter à %s"
-#: ../src/jarabe/view/palettes.py:45
+#: ../src/jarabe/view/launcher.py:190
+#, python-format
+msgid "<b>%s</b> failed to start."
+msgstr "<b>%s</b> n'a pas pu être démarré."
+
+#: ../src/jarabe/view/palettes.py:46
msgid "Starting..."
msgstr "Démarrage..."
+#: ../src/jarabe/view/palettes.py:56
+msgid "Activity failed to start"
+msgstr "Echec du démarrage de l'activité"
+
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:71
+#: ../src/jarabe/view/palettes.py:85
msgid "View Source"
msgstr "Afficher la source"
-#: ../src/jarabe/view/palettes.py:82
+#: ../src/jarabe/view/palettes.py:96
msgid "Stop"
msgstr "Arrêter"
-#: ../src/jarabe/view/palettes.py:122
+#: ../src/jarabe/view/palettes.py:132
msgid "Start new"
msgstr "Commencer un nouveau"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:172
msgid "Show contents"
msgstr "Afficher les contenus"
-#: ../src/jarabe/view/palettes.py:193 ../src/jarabe/view/palettes.py:243
+#: ../src/jarabe/view/palettes.py:194 ../src/jarabe/view/palettes.py:245
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d Mo de libre"
-#: ../src/jarabe/view/viewsource.py:208
+#: ../src/jarabe/view/viewsource.py:211
msgid "Instance Source"
msgstr "Instancie source"
-#: ../src/jarabe/view/viewsource.py:233
+#: ../src/jarabe/view/viewsource.py:236
msgid "Source"
msgstr "Source"
-#: ../src/jarabe/view/viewsource.py:292
+#: ../src/jarabe/view/viewsource.py:295
msgid "Activity Bundle Source"
msgstr "Source du paquet activité"
-#: ../src/jarabe/view/viewsource.py:299
+#: ../src/jarabe/view/viewsource.py:302
#, python-format
msgid "View source: %r"
msgstr "Afficher le code source : %r"
+#: ../src/jarabe/util/emulator.py:40
+msgid "Sugar in a window"
+msgstr "Sugar dans une fenêtre"
+
+#~ msgid "APN:"
+#~ msgstr "APN :"
+
+#~ msgid "Create new wireless network"
+#~ msgstr "Créer un nouveau réseau sans fil"
+
+#, python-format
+#~ msgid "%s's network"
+#~ msgstr "Réseau %s"
+
+#, python-format
+#~ msgid "Data sent %d kb / received %d kb"
+#~ msgstr "Données envoyées %d ko / reçues %d ko"
+
+#~ msgid "Connection time "
+#~ msgstr "Durée de connexion"
+
+#~ msgid "Title"
+#~ msgstr "Titre"
+
+#~ msgid "Version"
+#~ msgstr "Version"
+
+#~ msgid "Date"
+#~ msgstr "Date"
+
#~ msgid "Cannot obtain data needed for registration."
#~ msgstr "Impossible d'obtenir les données nécessaires à l'enregistrement."
#~ msgid "Unmount"
#~ msgstr "Démonter"
-#~ msgid "Restart"
-#~ msgstr "Redémarrer"
-
#~ msgid ""
#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
#~ msgstr ""
@@ -1294,12 +1831,6 @@ msgstr "Afficher le code source : %r"
#~ msgid "Disconnecting..."
#~ msgstr "Déconnexion..."
-#~ msgid "Mesh Network"
-#~ msgstr "Réseau maillé"
-
-#~ msgid "Disconnected"
-#~ msgstr "Déconnecté"
-
#~ msgid "About my XO"
#~ msgstr "Mon XO"
diff --git a/po/it.po b/po/it.po
index d9a8548..a660a05 100644
--- a/po/it.po
+++ b/po/it.po
@@ -2,12 +2,20 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-02-11 00:32-0500\n"
-"PO-Revision-Date: 2010-03-17 17:21+0200\n"
+"POT-Creation-Date: 2010-12-23 01:15-0500\n"
+"PO-Revision-Date: 2011-02-09 17:49+0200\n"
"Last-Translator: Carlo Falciola <cfalciola@yahoo.it>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: it\n"
@@ -21,43 +29,39 @@ msgstr ""
msgid "About Me"
msgstr "Informazioni su"
-#: ../extensions/cpsection/aboutme/model.py:43
+#: ../extensions/cpsection/aboutme/model.py:48
msgid "You must enter a name."
msgstr "Devi inserire un nome."
-#: ../extensions/cpsection/aboutme/model.py:68
+#: ../extensions/cpsection/aboutme/model.py:75
#, python-format
msgid "stroke: color=%s hue=%s"
msgstr "linea: colore=%s tinta=%s"
-#: ../extensions/cpsection/aboutme/model.py:71
+#: ../extensions/cpsection/aboutme/model.py:78
#, python-format
msgid "stroke: %s"
msgstr "linea: %s"
-#: ../extensions/cpsection/aboutme/model.py:73
+#: ../extensions/cpsection/aboutme/model.py:80
#, python-format
msgid "fill: color=%s hue=%s"
msgstr "riempimento: colore=%s tinta=%s"
-#: ../extensions/cpsection/aboutme/model.py:75
+#: ../extensions/cpsection/aboutme/model.py:82
#, python-format
msgid "fill: %s"
msgstr "riempimento: %s"
-#: ../extensions/cpsection/aboutme/model.py:86
+#: ../extensions/cpsection/aboutme/model.py:94
msgid "Error in specified color modifiers."
-msgstr "Errore nella variazione dei colori richiesta"
+msgstr "Errore nella variazione dei colori richiesta."
-#: ../extensions/cpsection/aboutme/model.py:89
+#: ../extensions/cpsection/aboutme/model.py:97
msgid "Error in specified colors."
msgstr "Errore nella definizione dei colori."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:93
-msgid "Name:"
-msgstr "Nome:"
-
-#: ../extensions/cpsection/aboutme/view.py:128
+#: ../extensions/cpsection/aboutme/view.py:235
msgid "Click to change your color:"
msgstr "Seleziona per cambiare il tuo colore:"
@@ -65,43 +69,43 @@ msgstr "Seleziona per cambiare il tuo colore:"
msgid "About my Computer"
msgstr "Il mio Computer"
-#: ../extensions/cpsection/aboutcomputer/model.py:28
+#: ../extensions/cpsection/aboutcomputer/model.py:29
msgid "Not available"
msgstr "Non disponibile"
-#: ../extensions/cpsection/aboutcomputer/view.py:60
+#: ../extensions/cpsection/aboutcomputer/view.py:61
msgid "Identity"
msgstr "Identità"
-#: ../extensions/cpsection/aboutcomputer/view.py:69
+#: ../extensions/cpsection/aboutcomputer/view.py:70
msgid "Serial Number:"
msgstr "Numero di Serie:"
-#: ../extensions/cpsection/aboutcomputer/view.py:91
+#: ../extensions/cpsection/aboutcomputer/view.py:92
msgid "Software"
msgstr "Software"
-#: ../extensions/cpsection/aboutcomputer/view.py:100
+#: ../extensions/cpsection/aboutcomputer/view.py:101
msgid "Build:"
msgstr "Build:"
-#: ../extensions/cpsection/aboutcomputer/view.py:115
+#: ../extensions/cpsection/aboutcomputer/view.py:116
msgid "Sugar:"
msgstr "Sugar:"
-#: ../extensions/cpsection/aboutcomputer/view.py:131
+#: ../extensions/cpsection/aboutcomputer/view.py:132
msgid "Firmware:"
msgstr "Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:146
+#: ../extensions/cpsection/aboutcomputer/view.py:147
msgid "Wireless Firmware:"
msgstr "Wireless Firmware:"
-#: ../extensions/cpsection/aboutcomputer/view.py:169
+#: ../extensions/cpsection/aboutcomputer/view.py:170
msgid "Copyright and License"
msgstr "Copyright e Licenza"
-#: ../extensions/cpsection/aboutcomputer/view.py:184
+#: ../extensions/cpsection/aboutcomputer/view.py:188
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 "
@@ -113,7 +117,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:196
+#: ../extensions/cpsection/aboutcomputer/view.py:200
msgid "Full license:"
msgstr "Testo della Licenza:"
@@ -121,11 +125,11 @@ msgstr "Testo della Licenza:"
msgid "Date & Time"
msgstr "Data e Ora"
-#: ../extensions/cpsection/datetime/model.py:87
+#: ../extensions/cpsection/datetime/model.py:92
msgid "Error timezone does not exist."
msgstr "Errore, timezone non esistente."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:33
+#: ../extensions/cpsection/datetime/view.py:70 ../data/sugar.schemas.in.h:52
msgid "Timezone"
msgstr "Timezone"
@@ -133,50 +137,50 @@ msgstr "Timezone"
msgid "Frame"
msgstr "Cornice"
-#: ../extensions/cpsection/frame/model.py:38
-#: ../extensions/cpsection/frame/model.py:60
+#: ../extensions/cpsection/frame/model.py:41
+#: ../extensions/cpsection/frame/model.py:66
msgid "Value must be an integer."
msgstr "Valore deve essere un intero."
-#: ../extensions/cpsection/frame/view.py:26
+#: ../extensions/cpsection/frame/view.py:27
msgid "never"
msgstr "mai"
-#: ../extensions/cpsection/frame/view.py:27
+#: ../extensions/cpsection/frame/view.py:28
msgid "instantaneous"
msgstr "istantaneamente"
-#: ../extensions/cpsection/frame/view.py:28
+#: ../extensions/cpsection/frame/view.py:29
#, python-format
msgid "%s seconds"
msgstr "%s secondi"
-#: ../extensions/cpsection/frame/view.py:52
+#: ../extensions/cpsection/frame/view.py:54
msgid "Activation Delay"
msgstr "Ritardo attivazione"
-#: ../extensions/cpsection/frame/view.py:76
+#: ../extensions/cpsection/frame/view.py:78
msgid "Corner"
msgstr "Angolo"
-#: ../extensions/cpsection/frame/view.py:111
+#: ../extensions/cpsection/frame/view.py:113
msgid "Edge"
msgstr "Margine"
#: ../extensions/cpsection/keyboard/__init__.py:21
-#: ../extensions/cpsection/keyboard/view.py:31
+#: ../extensions/cpsection/keyboard/view.py:32
msgid "Keyboard"
msgstr "Tastiera"
-#: ../extensions/cpsection/keyboard/view.py:189
+#: ../extensions/cpsection/keyboard/view.py:190
msgid "Keyboard Model"
msgstr "Modello Tastiera"
-#: ../extensions/cpsection/keyboard/view.py:248
+#: ../extensions/cpsection/keyboard/view.py:250
msgid "Key(s) to change layout"
msgstr "Tasto/i per cambiare la disposizione"
-#: ../extensions/cpsection/keyboard/view.py:318
+#: ../extensions/cpsection/keyboard/view.py:319
msgid "Keyboard Layout(s)"
msgstr "Disposizione Tastiera"
@@ -185,21 +189,21 @@ msgstr "Disposizione Tastiera"
msgid "Language"
msgstr "Lingua"
-#: ../extensions/cpsection/language/model.py:28
+#: ../extensions/cpsection/language/model.py:30
msgid "Could not access ~/.i18n. Create standard settings."
msgstr "Impossibile accedere a ~/.i18n. Creazione configurazione standard."
-#: ../extensions/cpsection/language/model.py:124
+#: ../extensions/cpsection/language/model.py:131
#, python-format
msgid "Language for code=%s could not be determined."
msgstr "Linguaggio con codice=%s sconosciuto."
-#: ../extensions/cpsection/language/model.py:144
+#: ../extensions/cpsection/language/model.py:152
#, python-format
msgid "Sorry I do not speak '%s'."
msgstr "Spiacente, ma non parlo '%s'."
-#: ../extensions/cpsection/language/view.py:56
+#: ../extensions/cpsection/language/view.py:57
msgid ""
"Add languages in the order you prefer. If a translation is not available, "
"the next in the list will be used."
@@ -211,66 +215,82 @@ msgstr ""
msgid "Modem Configuration"
msgstr "Configurazione del modem"
-#: ../extensions/cpsection/modemconfiguration/view.py:90
+#: ../extensions/cpsection/modemconfiguration/view.py:94
msgid "Username:"
msgstr "Utente:"
-#: ../extensions/cpsection/modemconfiguration/view.py:101
+#: ../extensions/cpsection/modemconfiguration/view.py:106
msgid "Password:"
msgstr "Password:"
-#: ../extensions/cpsection/modemconfiguration/view.py:112
+#: ../extensions/cpsection/modemconfiguration/view.py:118
msgid "Number:"
msgstr "Numero:"
-#: ../extensions/cpsection/modemconfiguration/view.py:123
-msgid "APN:"
-msgstr "APN:"
+#: ../extensions/cpsection/modemconfiguration/view.py:130
+msgid "Access Point Name (APN):"
+msgstr "Nome Access Point (APN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:142
+msgid "Personal Identity Number (PIN):"
+msgstr "Personal Identity Number (PIN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:154
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Personal Unblocking Key (PUK):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:175
+msgid ""
+"You will need to provide the following information to set up a mobile "
+"broadband connection to a cellular (3G) network."
+msgstr ""
+"Dovresti fornire le informazioni richieste per configurare una connessione "
+"mobile in banda larga ad una rete cellulare (3G)."
#: ../extensions/cpsection/network/__init__.py:21
-#: ../extensions/cpsection/network/view.py:28
+#: ../extensions/cpsection/network/view.py:29
msgid "Network"
msgstr "Network"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:68
msgid "State is unknown."
msgstr "Stato sconosciuto."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:96
msgid "Error in specified radio argument use on/off."
msgstr "Errore nel campo specificato, utilizzare on/off."
-#: ../extensions/cpsection/network/model.py:137
+#: ../extensions/cpsection/network/model.py:133
msgid "Error in specified argument use 0/1."
msgstr "Errore nel campo specificato, utilizzare 0/1."
-#: ../extensions/cpsection/network/view.py:59
+#: ../extensions/cpsection/network/view.py:61
msgid "Wireless"
msgstr "Wireless"
-#: ../extensions/cpsection/network/view.py:67
+#: ../extensions/cpsection/network/view.py:69
msgid "Turn off the wireless radio to save battery life"
msgstr "Spegni il trasmettitore wireless per risparmiare carica della batteria"
-#: ../extensions/cpsection/network/view.py:80
+#: ../extensions/cpsection/network/view.py:82
msgid "Radio"
msgstr "Radio"
-#: ../extensions/cpsection/network/view.py:96
+#: ../extensions/cpsection/network/view.py:98
msgid "Discard network history if you have trouble connecting to the network"
msgstr ""
"Elimina la storia delle connessioni di rete effettuate nel caso di problemi "
"di connessione"
-#: ../extensions/cpsection/network/view.py:105
+#: ../extensions/cpsection/network/view.py:107
msgid "Discard network history"
msgstr "Elimina la storia delle connessioni di rete"
-#: ../extensions/cpsection/network/view.py:118
+#: ../extensions/cpsection/network/view.py:120
msgid "Collaboration"
msgstr "Collaborazione"
-#: ../extensions/cpsection/network/view.py:126
+#: ../extensions/cpsection/network/view.py:128
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 "
@@ -280,7 +300,7 @@ msgstr ""
"stesso server sono in grado di vedersi fra loro, anche se collegate a reti "
"differenti."
-#: ../extensions/cpsection/network/view.py:136
+#: ../extensions/cpsection/network/view.py:138
msgid "Server:"
msgstr "Server:"
@@ -288,26 +308,27 @@ msgstr "Server:"
msgid "Power"
msgstr "Energia"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:90
msgid "Error in automatic pm argument, use on/off."
msgstr ""
-"Errore nel campo Gestione Automatica Risparmio Energetico, utilizza on/off"
+"Errore nel campo Gestione Automatica Risparmio Energetico, utilizza on/off."
-#: ../extensions/cpsection/power/model.py:81
+#: ../extensions/cpsection/power/model.py:120
msgid "Error in extreme pm argument, use on/off."
-msgstr "Errore nel campo Gestione Estrema Risparmio Energetico, utilizza on/off"
+msgstr ""
+"Errore nel campo Gestione Estrema Risparmio Energetico, utilizza on/off."
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:48
msgid "Power management"
msgstr "Gestione Risparmio Energetico (power management)"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:58
msgid "Automatic power management (increases battery life)"
msgstr ""
"Gestione Automatica Risparmio Energetico (incrementa la durata delle "
"batterie)"
-#: ../extensions/cpsection/power/view.py:85
+#: ../extensions/cpsection/power/view.py:86
msgid ""
"Extreme power management (disableswireless radio, increases battery life)"
msgstr ""
@@ -324,7 +345,7 @@ msgid ""
"provide new features."
msgstr ""
"Gli aggiornamenti software correggono errori, eliminano vulnerabilità del "
-"codice ed offrono nuove funzioni. "
+"codice ed offrono nuove funzioni."
#: ../extensions/cpsection/updater/view.py:125
#, python-format
@@ -360,26 +381,26 @@ msgstr "Ricerca aggiornamenti..."
msgid "Installing updates..."
msgstr "Installazione aggiornamenti..."
-#: ../extensions/cpsection/updater/view.py:172
+#: ../extensions/cpsection/updater/view.py:173
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
msgstr[0] "%s aggiornamento è stato installato"
msgstr[1] "%s aggiornamenti sono stati installati"
-#: ../extensions/cpsection/updater/view.py:253
+#: ../extensions/cpsection/updater/view.py:255
msgid "Install selected"
msgstr "Installa selezionati"
-#: ../extensions/cpsection/updater/view.py:274
+#: ../extensions/cpsection/updater/view.py:276
#, python-format
msgid "Download size: %s"
msgstr "Dimensione dati da scaricare: %s"
-#: ../extensions/cpsection/updater/view.py:362
+#: ../extensions/cpsection/updater/view.py:364
#, python-format
-msgid "From version %(current)d to %(new)s (Size: %(size)s)"
-msgstr "Dalla versione %(current)d alla %(new)s (Dimensione: %(size)s)"
+msgid "From version %(current)s to %(new)s (Size: %(size)s)"
+msgstr "Dalla versione %(current)s alla %(new)s (Dimensione: %(size)s)"
#. TRANS: download size is 0
#: ../extensions/cpsection/updater/view.py:382
@@ -403,28 +424,28 @@ msgstr "%.0f KB"
msgid "%.1f MB"
msgstr "%.1f MB"
-#: ../extensions/deviceicon/battery.py:58
+#: ../extensions/deviceicon/battery.py:60
msgid "My Battery"
msgstr "La mia Batteria"
-#: ../extensions/deviceicon/battery.py:137
+#: ../extensions/deviceicon/battery.py:141
msgid "Removed"
msgstr "Rimosso"
-#: ../extensions/deviceicon/battery.py:140
+#: ../extensions/deviceicon/battery.py:144
msgid "Charging"
msgstr "Caricando"
-#: ../extensions/deviceicon/battery.py:143
+#: ../extensions/deviceicon/battery.py:147
msgid "Very little power remaining"
msgstr "Pochissima carica rimanente"
-#: ../extensions/deviceicon/battery.py:149
+#: ../extensions/deviceicon/battery.py:153
#, python-format
msgid "%(hour)d:%(min).2d remaining"
msgstr "%(hour)d:%(min).2d rimanenti"
-#: ../extensions/deviceicon/battery.py:152
+#: ../extensions/deviceicon/battery.py:156
msgid "Charged"
msgstr "Carica"
@@ -437,119 +458,174 @@ 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:111
+#: ../extensions/deviceicon/network.py:104
msgid "Disconnect..."
msgstr "Disconnessione..."
-#: ../extensions/deviceicon/network.py:116
-msgid "Create new wireless network"
-msgstr "Crea una nuova rete wireless"
-
-#: ../extensions/deviceicon/network.py:122
-#: ../extensions/deviceicon/network.py:284
-#: ../src/jarabe/desktop/meshbox.py:248 ../src/jarabe/desktop/meshbox.py:537
+#: ../extensions/deviceicon/network.py:112
+#: ../extensions/deviceicon/network.py:290
+#: ../src/jarabe/desktop/networkviews.py:239
+#: ../src/jarabe/desktop/networkviews.py:538
+#: ../src/jarabe/desktop/networkviews.py:667
msgid "Connecting..."
msgstr "Connessione..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:126
-#: ../extensions/deviceicon/network.py:198
-#: ../extensions/deviceicon/network.py:288
-#: ../src/jarabe/desktop/meshbox.py:254 ../src/jarabe/desktop/meshbox.py:543
+#: ../extensions/deviceicon/network.py:116
+#: ../extensions/deviceicon/network.py:182
+#: ../src/jarabe/desktop/networkviews.py:249
+#: ../src/jarabe/desktop/networkviews.py:544
+#: ../src/jarabe/desktop/networkviews.py:673
msgid "Connected"
msgstr "Connesso"
-#: ../extensions/deviceicon/network.py:158
+#: ../extensions/deviceicon/network.py:142
msgid "Channel"
msgstr "Canale"
-#: ../extensions/deviceicon/network.py:173
+#: ../extensions/deviceicon/network.py:157
msgid "Wired Network"
msgstr "Rete su cavo"
-#: ../extensions/deviceicon/network.py:201
+#: ../extensions/deviceicon/network.py:185
msgid "Speed"
msgstr "Velocità"
-#: ../extensions/deviceicon/network.py:228
+#: ../extensions/deviceicon/network.py:211
msgid "Wireless modem"
msgstr "Modem Wireless"
-#: ../extensions/deviceicon/network.py:276
+#: ../extensions/deviceicon/network.py:278
msgid "Please wait..."
msgstr "Attendi..."
-#: ../extensions/deviceicon/network.py:279
-#: ../src/jarabe/desktop/meshbox.py:164 ../src/jarabe/desktop/meshbox.py:494
+#: ../extensions/deviceicon/network.py:282
+#: ../src/jarabe/desktop/networkviews.py:149
+#: ../src/jarabe/desktop/networkviews.py:492
+#: ../src/jarabe/desktop/networkviews.py:624
msgid "Connect"
msgstr "Connetti"
-#: ../extensions/deviceicon/network.py:280
+#: ../extensions/deviceicon/network.py:283
msgid "Disconnected"
msgstr "Disconnesso"
-#: ../extensions/deviceicon/network.py:283
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:68
-#: ../src/jarabe/frame/activitiestray.py:700
-#: ../src/jarabe/frame/activitiestray.py:799
-#: ../src/jarabe/frame/activitiestray.py:827
+#: ../extensions/deviceicon/network.py:289
+#: ../src/jarabe/controlpanel/toolbar.py:119
+#: ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/frame/activitiestray.py:587
+#: ../src/jarabe/frame/activitiestray.py:687
+#: ../src/jarabe/frame/activitiestray.py:715
msgid "Cancel"
msgstr "Cancella"
-#: ../extensions/deviceicon/network.py:287
-#: ../src/jarabe/desktop/meshbox.py:168
+#: ../extensions/deviceicon/network.py:297
+#: ../src/jarabe/desktop/networkviews.py:153
+#: ../src/jarabe/desktop/networkviews.py:496
msgid "Disconnect"
msgstr "Disconnetti"
-#: ../extensions/deviceicon/network.py:530
+#: ../extensions/deviceicon/network.py:327
+msgid "Try connection again"
+msgstr "Prova nuovamente a connetterti"
+
+#: ../extensions/deviceicon/network.py:330
#, python-format
-msgid "%s's network"
-msgstr "rete di %s"
+msgid "Error: %s"
+msgstr "Errore: %s"
+
+#: ../extensions/deviceicon/network.py:334
+#, python-format
+msgid "Suggestion: %s"
+msgstr "Suggerimento: %s"
+
+#: ../extensions/deviceicon/network.py:340
+#: ../extensions/deviceicon/network.py:343
+#, python-format
+msgid "Connected for %s"
+msgstr "Connesso per %s"
+
+#: ../extensions/deviceicon/network.py:348
+#: ../extensions/deviceicon/network.py:349
+#, python-format
+msgid "%d KB"
+msgstr "%d KB"
+
+#: ../extensions/deviceicon/network.py:354
+msgid "Check your Pin/Puk configuration."
+msgstr "Verifica la configurazione di Pin/Puk ."
+
+#: ../extensions/deviceicon/network.py:357
+msgid "Check your Access Point Name (APN) configuration"
+msgstr "Verifica la configurazione dell'Access Point Name (APN)"
+
+#: ../extensions/deviceicon/network.py:361
+msgid "Check the Number configuration."
+msgstr "Verifica la configurazione del Numero"
+
+#: ../extensions/deviceicon/network.py:363
+msgid "Check your configuration."
+msgstr "Verifica la configurazione."
# A complete translation in italian: "rete a maglie" becames a tautology
-#: ../extensions/deviceicon/network.py:597
-#: ../extensions/deviceicon/network.py:656
+#: ../extensions/deviceicon/network.py:615
msgid "Mesh Network"
msgstr "Rete Mesh"
-#: ../extensions/deviceicon/network.py:857
+# A complete translation in italian: "rete a maglie" becames a tautology
+#: ../extensions/deviceicon/network.py:658
#, python-format
-msgid "Data sent %d kb / received %d kb"
-msgstr "Dati %d kb inviati / %d kb ricevuti"
+msgid "Mesh Network %s"
+msgstr "Rete Mesh %s"
+
+#: ../extensions/deviceicon/network.py:782
+msgid "No GSM connection available."
+msgstr "Connessione GSM non disponibile."
-#: ../extensions/deviceicon/network.py:868
-msgid "Connection time "
-msgstr "Tempo di connessione"
+#: ../extensions/deviceicon/network.py:783
+msgid "Create a connection in the control panel."
+msgstr "Definisci una connessione nel Pannello di Controllo."
-#: ../extensions/deviceicon/speaker.py:59
+#: ../extensions/deviceicon/speaker.py:60
msgid "My Speakers"
msgstr "I miei Altoparlanti"
-#: ../extensions/deviceicon/speaker.py:133
+#: ../extensions/deviceicon/speaker.py:136
msgid "Unmute"
msgstr "Attiva"
-#: ../extensions/deviceicon/speaker.py:136
+#: ../extensions/deviceicon/speaker.py:139
msgid "Mute"
msgstr "Silenzia"
+#: ../extensions/deviceicon/touchpad.py:37
+msgid "finger"
+msgstr "dito"
+
+#: ../extensions/deviceicon/touchpad.py:38
+msgid "stylus"
+msgstr "stilo"
+
+#: ../extensions/deviceicon/touchpad.py:67
+msgid "My touchpad"
+msgstr "Il mio Touchpad"
+
#: ../extensions/globalkey/screenshot.py:59
msgid "Mesh"
msgstr "Mesh"
#: ../extensions/globalkey/screenshot.py:61
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "Group"
msgstr "Gruppo"
#: ../extensions/globalkey/screenshot.py:63
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "Home"
msgstr "Casa"
#: ../extensions/globalkey/screenshot.py:69
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "Activity"
msgstr "Attività"
@@ -568,13 +644,21 @@ msgid ""
"long name."
msgstr ""
"se \"disabled\" viene richiesto il nome alla inizializzazione; se \"system\" "
-"verrà utilizzato il nome presente nell'account UNIX."
+"verrà utilizzato il nome presente nell'account UNIX."
#: ../data/sugar.schemas.in.h:2
+msgid "Additional directories which can contain updated translations."
+msgstr "Ulteriori directory contenenti traduzioni aggiornate."
+
+#: ../data/sugar.schemas.in.h:3
msgid "Backup URL"
msgstr "URL di salvataggio"
-#: ../data/sugar.schemas.in.h:3
+#: ../data/sugar.schemas.in.h:4
+msgid "Bundle IDs of protected activities"
+msgstr "ID dei pacchetti di attività protette"
+
+#: ../data/sugar.schemas.in.h:5
msgid ""
"Color for the XO icon that is used throughout the desktop. The string is "
"composed of the stroke color and fill color, format is that of rbg colors. "
@@ -584,52 +668,105 @@ msgstr ""
"composta dal colore della linea e dal colore del riempimento, il formato è "
"quello dei colori rbg. Esempio: #AC32FF,#9A5200"
-#: ../data/sugar.schemas.in.h:4
+#: ../data/sugar.schemas.in.h:6
msgid "Corner Delay"
msgstr "Ritardo dell'angolo"
-#: ../data/sugar.schemas.in.h:5
+#: ../data/sugar.schemas.in.h:7
msgid "Default font face"
msgstr "Tipo di carattere di default"
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:8
msgid "Default font size"
msgstr "Dimensione del carattere di default"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:9
msgid "Default nick"
msgstr "Nome di default"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:10
msgid "Delay for the activation of the frame using the corners."
msgstr ""
"Ritardo nella attivazione della cornice portando il puntatore in un angolo."
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:11
msgid "Delay for the activation of the frame using the edges."
-msgstr "Ritardo nella attivazione della cornice portando il puntatore sui bordi"
+msgstr ""
+"Ritardo nella attivazione della cornice portando il puntatore sui bordi."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:12
+msgid "Directory to search for translations"
+msgstr "Directory in cui cercare traduzioni"
+
+#: ../data/sugar.schemas.in.h:13
msgid "Edge Delay"
msgstr "Ritardo del bordo"
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:14
msgid "Favorites Layout"
msgstr "Disposizione delle attività preferite"
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:15
msgid "Favorites resume mode"
msgstr "Modalità di richiamo delle attività preferite"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:16
msgid "Font face that is used throughout the desktop."
msgstr "Tipo di carattere di default utilizzato in tutto il sistema."
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:17
msgid "Font size that is used throughout the desktop."
msgstr "Dimensione del carattere utilizzato in tutto il sistema."
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network APN"
+msgstr " APN rete GSM"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network PIN"
+msgstr "PIN rete GSM"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network PUK"
+msgstr "PUK rete GSM"
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network access point name configuration"
+msgstr "configurazione di access point name rete GSM"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network number"
+msgstr "numero di rete GSM"
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network password"
+msgstr "password di rete GSM"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network password configuration"
+msgstr "configurazione della password di rete GSM"
+
+#: ../data/sugar.schemas.in.h:25
+msgid "GSM network personal identification number configuration"
+msgstr "configurazione del personal identification number (PIN) della rete GSM"
+
+#: ../data/sugar.schemas.in.h:26
+msgid "GSM network personal unlock key configuration"
+msgstr "configurazione della chiave personale di sblocco (PUK) della rete GSM"
+
+#: ../data/sugar.schemas.in.h:27
+msgid "GSM network telephone number configuration"
+msgstr "configurazione del numero telefonico della rete GSM"
+
+#: ../data/sugar.schemas.in.h:28
+msgid "GSM network username"
+msgstr "username dela rete GSM"
+
+#: ../data/sugar.schemas.in.h:29
+msgid "GSM network username configuration"
+msgstr "configurazione dello username della rete GSM"
+
+#: ../data/sugar.schemas.in.h:30
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
@@ -637,110 +774,136 @@ msgstr ""
"Se TRUE, Sugar renderà il computer rintracciabile dagli altri utenti del "
"server Jabber."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:31
msgid "If TRUE, Sugar will show a \"Log out\" option."
-msgstr "Se TRUE, Sugar mostrerà una opzione di \"Disconnessione\"."
+msgstr "Se TRUE, Sugar mostrerà una opzione di \"Disconnessione\"."
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:32
+msgid "If TRUE, Sugar will show a \"Restart\" option."
+msgstr "Se TRUE, Sugar mostrerà una opzione di \"Riavvia\"."
+
+#: ../data/sugar.schemas.in.h:33
+msgid ""
+"If TRUE, Sugar will show default Ad-hoc networks for channel 1,6 and 11. If "
+"Sugar sees no \"known\" network when it starts, it does autoconnect to an Ad-"
+"hoc network."
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:34
msgid "Jabber Server"
msgstr "Jabber Server"
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:35
msgid "Keyboard layouts"
msgstr "Disposizioni tastiera"
-#: ../data/sugar.schemas.in.h:19
+#: ../data/sugar.schemas.in.h:36
msgid "Keyboard model"
msgstr "Modello tastiera"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:37
msgid "Keyboard options"
msgstr "Opzioni tastiera"
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:38
msgid "Layout of the favorites view."
msgstr "Disposizione della vista dei favoriti."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:39
msgid ""
"List of keyboard layouts. Each entry should be in the form layout(variant)"
msgstr ""
"Elenco di disposizioni della tastiera. Ogni voce deve essere nel formato "
-"layout(variant) "
+"layout(variant)"
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:40
msgid "List of keyboard options."
msgstr "Lista di opzioni per la tastiera."
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:41
msgid "Power Automatic"
msgstr "Economizzazione Automatica"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:42
msgid "Power Automatic."
msgstr "Economizzazione Automatica."
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:43
msgid "Power Extreme"
msgstr "Economizzazione Massima"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:44
msgid "Power Extreme."
msgstr "Economizzazione Massima."
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:45
msgid "Publish to Gadget"
msgstr "Pubblica su Gadget"
-#: ../data/sugar.schemas.in.h:29
+#: ../data/sugar.schemas.in.h:46
msgid "Setting for muting the sound device."
msgstr "Selezione per silenziare il riproduttore sonoro."
-#: ../data/sugar.schemas.in.h:30
+#: ../data/sugar.schemas.in.h:47
msgid "Show Log out"
msgstr "Mostra Disconnessione"
-#: ../data/sugar.schemas.in.h:31
+#: ../data/sugar.schemas.in.h:48
+msgid "Show Restart"
+msgstr "Mostra Riavvia"
+
+#: ../data/sugar.schemas.in.h:49
+msgid "Show Sugar Ad-hoc networks"
+msgstr "Mostra rete ad-hoc di Sugar"
+
+#: ../data/sugar.schemas.in.h:50
msgid "Sound Muted"
msgstr "Suono Silenziato"
-#: ../data/sugar.schemas.in.h:32
+#: ../data/sugar.schemas.in.h:51
msgid "The keyboard model to be used"
msgstr "Modello di tastiera da utilizzare"
-#: ../data/sugar.schemas.in.h:34
+#: ../data/sugar.schemas.in.h:53
msgid "Timezone setting for the system."
-msgstr "Selezione per il Timezone del sistema"
+msgstr "Selezione per il Timezone del sistema."
-#: ../data/sugar.schemas.in.h:35
+#: ../data/sugar.schemas.in.h:54
msgid "Url of the jabber server to use."
msgstr "Url del server jabber da utilizzare."
-#: ../data/sugar.schemas.in.h:36
+#: ../data/sugar.schemas.in.h:55
msgid "Url where the backup is saved to."
msgstr "Url su cui effettuare i salvataggi."
-#: ../data/sugar.schemas.in.h:37
+#: ../data/sugar.schemas.in.h:56
msgid "User Color"
-msgstr "Colore dell'Utente "
+msgstr "Colore dell'Utente"
-#: ../data/sugar.schemas.in.h:38
+#: ../data/sugar.schemas.in.h:57
msgid "User Name"
msgstr "Nome Utente"
-#: ../data/sugar.schemas.in.h:39
+#: ../data/sugar.schemas.in.h:58
msgid "User name that is used throughout the desktop."
msgstr "Nome Utente utilizzato in tutto il sistema."
-#: ../data/sugar.schemas.in.h:40
+#: ../data/sugar.schemas.in.h:59
+msgid ""
+"Users will not be allowed to erase these activities through the list view."
+msgstr ""
+"Gli utenti non saranno abilitati alla cancellazione di queste attività dalla "
+"Visualizzazione a lista"
+
+#: ../data/sugar.schemas.in.h:60
msgid "Volume Level"
msgstr "Livello Volume"
-#: ../data/sugar.schemas.in.h:41
+#: ../data/sugar.schemas.in.h:61
msgid "Volume level for the sound device."
msgstr "Livello del volume del riproduttore di suoni."
-#: ../data/sugar.schemas.in.h:42
+#: ../data/sugar.schemas.in.h:62
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -773,7 +936,7 @@ msgstr "sugar-control-panel: %s"
# which must appear in the translated string (msgstr) as well.
#. TRANS: Translators, there's a empty line at the end of this string,
#. which must appear in the translated string (msgstr) as well.
-#: ../src/jarabe/controlpanel/cmd.py:37
+#: ../src/jarabe/controlpanel/cmd.py:38
msgid ""
"Usage: sugar-control-panel [ option ] key [ args ... ] \n"
" Control for the sugar environment. \n"
@@ -796,50 +959,52 @@ msgstr ""
" -s key assegna il valore corrente alla \"key\" \n"
" "
-#: ../src/jarabe/controlpanel/cmd.py:50
+#: ../src/jarabe/controlpanel/cmd.py:52
msgid "To apply your changes you have to restart sugar.\n"
msgstr "Per applicare le modifiche è necessario riavviare sugar.\n"
-#: ../src/jarabe/controlpanel/gui.py:281
+#: ../src/jarabe/controlpanel/gui.py:285
+#: ../src/jarabe/journal/journaltoolbox.py:437
+#: ../src/jarabe/journal/volumestoolbar.py:158
msgid "Warning"
msgstr "Attenzione"
-#: ../src/jarabe/controlpanel/gui.py:282
-#: ../src/jarabe/controlpanel/sectionview.py:42
+#: ../src/jarabe/controlpanel/gui.py:286
+#: ../src/jarabe/controlpanel/sectionview.py:41
msgid "Changes require restart"
msgstr "Le modifiche rendono necessario un riavvio"
-#: ../src/jarabe/controlpanel/gui.py:285
+#: ../src/jarabe/controlpanel/gui.py:289
msgid "Cancel changes"
msgstr "Annulla modifiche"
-#: ../src/jarabe/controlpanel/gui.py:290 ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/controlpanel/gui.py:294 ../src/jarabe/desktop/homebox.py:72
msgid "Later"
msgstr "Dopo"
-#: ../src/jarabe/controlpanel/gui.py:294
+#: ../src/jarabe/controlpanel/gui.py:298
msgid "Restart now"
msgstr "Riavvia adesso"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:206
+#: ../src/jarabe/controlpanel/toolbar.py:63 ../src/jarabe/intro/window.py:211
msgid "Done"
msgstr "Fatto"
-#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:333
+#: ../src/jarabe/controlpanel/toolbar.py:125
+#: ../src/jarabe/desktop/favoritesview.py:336
msgid "Ok"
msgstr "Ok"
-#: ../src/jarabe/desktop/activitieslist.py:236
+#: ../src/jarabe/desktop/activitieslist.py:230
#, python-format
msgid "Version %s"
msgstr "Versione %s"
-#: ../src/jarabe/desktop/activitieslist.py:357
+#: ../src/jarabe/desktop/activitieslist.py:354
msgid "Confirm erase"
msgstr "Conferma cancellazione"
-#: ../src/jarabe/desktop/activitieslist.py:359
+#: ../src/jarabe/desktop/activitieslist.py:356
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
msgstr ""
@@ -849,255 +1014,264 @@ msgstr ""
# 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:363
-#: ../src/jarabe/frame/clipboardmenu.py:63
-#: ../src/jarabe/view/viewsource.py:218
+#: ../src/jarabe/desktop/activitieslist.py:360
+#: ../src/jarabe/frame/clipboardmenu.py:64
+#: ../src/jarabe/view/viewsource.py:221
msgid "Keep"
msgstr "Memorizza"
-#: ../src/jarabe/desktop/activitieslist.py:366
-#: ../src/jarabe/desktop/activitieslist.py:409
-#: ../src/jarabe/journal/journaltoolbox.py:360
-#: ../src/jarabe/journal/palettes.py:105
+#: ../src/jarabe/desktop/activitieslist.py:363
+#: ../src/jarabe/desktop/activitieslist.py:417
+#: ../src/jarabe/journal/journaltoolbox.py:391
+#: ../src/jarabe/journal/palettes.py:112
msgid "Erase"
msgstr "Elimina"
-#: ../src/jarabe/desktop/activitieslist.py:430
+#: ../src/jarabe/desktop/activitieslist.py:432
msgid "Remove favorite"
msgstr "Rimuovi preferito"
-#: ../src/jarabe/desktop/activitieslist.py:434
+#: ../src/jarabe/desktop/activitieslist.py:436
msgid "Make favorite"
msgstr "Definisci preferito"
# 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
+#: ../src/jarabe/desktop/favoriteslayout.py:127
msgid "Freeform"
msgstr "Formato libero"
# 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:198
+#: ../src/jarabe/desktop/favoriteslayout.py:215
msgid "Ring"
msgstr "Anello"
# 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:337
+#: ../src/jarabe/desktop/favoriteslayout.py:402
msgid "Spiral"
msgstr "Spirale"
# 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:404
+#: ../src/jarabe/desktop/favoriteslayout.py:472
msgid "Box"
msgstr "Scatola"
# 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:445
+#: ../src/jarabe/desktop/favoriteslayout.py:515
msgid "Triangle"
msgstr "Triangolo"
-#: ../src/jarabe/desktop/favoritesview.py:324
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "Registration Failed"
msgstr "Registrazione Fallita"
-#: ../src/jarabe/desktop/favoritesview.py:325
+#: ../src/jarabe/desktop/favoritesview.py:328
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:327
+#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Successful"
msgstr "Registrazione Effettuata"
-#: ../src/jarabe/desktop/favoritesview.py:328
+#: ../src/jarabe/desktop/favoritesview.py:331
msgid "You are now registered with your school server."
-msgstr "Ora sei registrato sul tuo server di scuola"
+msgstr "Ora sei registrato sul tuo server di scuola."
-#: ../src/jarabe/desktop/favoritesview.py:630
+#: ../src/jarabe/desktop/favoritesview.py:626
msgid "Register"
msgstr "Registra"
-#: ../src/jarabe/desktop/homebox.py:63
+#: ../src/jarabe/desktop/homebox.py:65
msgid "Software Update"
msgstr "Aggiornamento software"
-#: ../src/jarabe/desktop/homebox.py:64
+#: ../src/jarabe/desktop/homebox.py:66
msgid "Update your activities to ensure compatibility with your new software"
msgstr ""
"Aggiorna le tue attività perchè siano compatibili con il tuo sistema "
-"aggiornato."
+"aggiornato"
-#: ../src/jarabe/desktop/homebox.py:73
+#: ../src/jarabe/desktop/homebox.py:75
msgid "Check now"
msgstr "Verifica adesso"
-#: ../src/jarabe/desktop/homebox.py:192
+#: ../src/jarabe/desktop/homebox.py:193
msgid "List view"
msgstr "Vista Elenco"
-#: ../src/jarabe/desktop/homebox.py:193
+#: ../src/jarabe/desktop/homebox.py:194
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:255
+#: ../src/jarabe/desktop/homebox.py:257
msgid "Favorites view"
msgstr "Visualizza i Preferiti"
-#: ../src/jarabe/desktop/homebox.py:256
+#: ../src/jarabe/desktop/homebox.py:258
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:135
+#: ../src/jarabe/desktop/keydialog.py:143
msgid "Key Type:"
msgstr "Tipo Chiave:"
-#: ../src/jarabe/desktop/keydialog.py:155
+#: ../src/jarabe/desktop/keydialog.py:163
msgid "Authentication Type:"
msgstr "Tipo di Autenticazione:"
-#: ../src/jarabe/desktop/keydialog.py:220
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "WPA & WPA2 Personal"
msgstr "WPA & WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:229
+#: ../src/jarabe/desktop/keydialog.py:238
msgid "Wireless Security:"
msgstr "Wireless Security:"
-# A complete translation in italian: "rete a maglie" becames a tautology
-#: ../src/jarabe/desktop/meshbox.py:492
-#, python-format
-msgid "Mesh Network %d"
-msgstr "Rete Mesh %d"
-
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:629
-#: ../src/jarabe/frame/activitiestray.py:735
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:65 ../src/jarabe/view/palettes.py:67
+#: ../src/jarabe/desktop/meshbox.py:109
+#: ../src/jarabe/frame/activitiestray.py:622
+#: ../src/jarabe/journal/journaltoolbox.py:485
+#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:78
msgid "Resume"
msgstr "Riprendi"
-#: ../src/jarabe/desktop/meshbox.py:634
-#: ../src/jarabe/frame/activitiestray.py:233
+#: ../src/jarabe/desktop/meshbox.py:114
+#: ../src/jarabe/frame/activitiestray.py:173
msgid "Join"
msgstr "Associa"
-#: ../src/jarabe/desktop/schoolserver.py:103
+#: ../src/jarabe/desktop/networkviews.py:489
+#, python-format
+msgid "Ad-hoc Network %d"
+msgstr "Rete Ad-hoc %d"
+
+# A complete translation in italian: "rete a maglie" becames a tautology
+#: ../src/jarabe/desktop/networkviews.py:622
+#, python-format
+msgid "Mesh Network %d"
+msgstr "Rete Mesh %d"
+
+#: ../src/jarabe/desktop/schoolserver.py:131
msgid "Cannot connect to the server."
msgstr "Impossibile connettersi al server."
-#: ../src/jarabe/desktop/schoolserver.py:108
+#: ../src/jarabe/desktop/schoolserver.py:136
msgid "The server could not complete the request."
msgstr "Il server non può completare la richiesta."
-#: ../src/jarabe/frame/activitiestray.py:238
-#: ../src/jarabe/frame/activitiestray.py:672
+#: ../src/jarabe/frame/activitiestray.py:178
+#: ../src/jarabe/frame/activitiestray.py:559
msgid "Decline"
msgstr "Rinuncia"
-#: ../src/jarabe/frame/activitiestray.py:624
+#: ../src/jarabe/frame/activitiestray.py:509
#, python-format
msgid "%dB"
msgstr "%dB"
-#: ../src/jarabe/frame/activitiestray.py:626
+#: ../src/jarabe/frame/activitiestray.py:511
#, python-format
msgid "%dKB"
msgstr "%dKB"
-#: ../src/jarabe/frame/activitiestray.py:628
+#: ../src/jarabe/frame/activitiestray.py:513
#, python-format
msgid "%dMB"
msgstr "%dMB"
-#: ../src/jarabe/frame/activitiestray.py:645
+#: ../src/jarabe/frame/activitiestray.py:530
#, python-format
msgid "%s of %s"
msgstr "%s di %s"
-#: ../src/jarabe/frame/activitiestray.py:657
+#: ../src/jarabe/frame/activitiestray.py:544
#, python-format
msgid "Transfer from %r"
msgstr "Trasferisci da %r"
-#: ../src/jarabe/frame/activitiestray.py:667
+#: ../src/jarabe/frame/activitiestray.py:554
msgid "Accept"
msgstr "Accetta"
-#: ../src/jarabe/frame/activitiestray.py:690
-#: ../src/jarabe/frame/activitiestray.py:817
+#: ../src/jarabe/frame/activitiestray.py:577
+#: ../src/jarabe/frame/activitiestray.py:705
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:724
-#: ../src/jarabe/frame/activitiestray.py:852
+#: ../src/jarabe/frame/activitiestray.py:611
+#: ../src/jarabe/frame/activitiestray.py:740
msgid "Dismiss"
msgstr "Abbandona"
-#: ../src/jarabe/frame/activitiestray.py:787
+#: ../src/jarabe/frame/activitiestray.py:675
#, python-format
msgid "Transfer to %r"
msgstr "Trasferisci verso %r"
-#: ../src/jarabe/frame/clipboardmenu.py:53 ../src/jarabe/view/palettes.py:221
+#: ../src/jarabe/frame/clipboardmenu.py:54 ../src/jarabe/view/palettes.py:220
msgid "Remove"
msgstr "Rimuovi"
-#: ../src/jarabe/frame/clipboardmenu.py:58
-#: ../src/jarabe/frame/clipboardmenu.py:81
+#: ../src/jarabe/frame/clipboardmenu.py:59
+#: ../src/jarabe/frame/clipboardmenu.py:82
msgid "Open"
msgstr "Apri"
-#: ../src/jarabe/frame/clipboardmenu.py:86
+#: ../src/jarabe/frame/clipboardmenu.py:87
msgid "Open with"
msgstr "Apri con"
-#: ../src/jarabe/frame/clipboardobject.py:49
+#: ../src/jarabe/frame/clipboardobject.py:50
#, python-format
msgid "%s clipping"
msgstr "ritaglio %s"
# Letterale "Vicinato", sperimentale: I miei vicini
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "Neighborhood"
msgstr "I miei vicini"
-#: ../src/jarabe/frame/zoomtoolbar.py:37
+#: ../src/jarabe/frame/zoomtoolbar.py:38
msgid "F1"
msgstr "F1"
-#: ../src/jarabe/frame/zoomtoolbar.py:39
+#: ../src/jarabe/frame/zoomtoolbar.py:40
msgid "F2"
msgstr "F2"
-#: ../src/jarabe/frame/zoomtoolbar.py:41
+#: ../src/jarabe/frame/zoomtoolbar.py:42
msgid "F3"
msgstr "F3"
-#: ../src/jarabe/frame/zoomtoolbar.py:43
+#: ../src/jarabe/frame/zoomtoolbar.py:44
msgid "F4"
msgstr "F4"
-#: ../src/jarabe/intro/window.py:128
+#: ../src/jarabe/intro/window.py:96
+msgid "Name:"
+msgstr "Nome:"
+
+#: ../src/jarabe/intro/window.py:132
msgid "Click to change color:"
msgstr "Seleziona per cambiare colore:"
-#: ../src/jarabe/intro/window.py:192 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:197 ../src/jarabe/journal/detailview.py:105
msgid "Back"
msgstr "Indietro"
-#: ../src/jarabe/intro/window.py:209
+#: ../src/jarabe/intro/window.py:214
msgid "Next"
msgstr "Prossimo"
-#: ../src/jarabe/journal/expandedentry.py:152
-#: ../src/jarabe/journal/palettes.py:59
+#: ../src/jarabe/journal/expandedentry.py:154
+#: ../src/jarabe/journal/listmodel.py:144 ../src/jarabe/journal/palettes.py:59
msgid "Untitled"
msgstr "Senza titolo"
@@ -1111,6 +1285,9 @@ msgid "Kind: %s"
msgstr "Tipo: %s"
#: ../src/jarabe/journal/expandedentry.py:262
+#: ../src/jarabe/journal/listmodel.py:150
+#: ../src/jarabe/journal/listmodel.py:157
+#: ../src/jarabe/journal/listmodel.py:165
msgid "Unknown"
msgstr "Sconosciuto"
@@ -1124,114 +1301,158 @@ msgstr "Data: %s"
msgid "Size: %s"
msgstr "Dimensione: %s"
-#: ../src/jarabe/journal/expandedentry.py:286 ../src/jarabe/journal/misc.py:93
+#: ../src/jarabe/journal/expandedentry.py:292
+#: ../src/jarabe/journal/misc.py:108
msgid "No date"
msgstr "Nessuna data"
-#: ../src/jarabe/journal/expandedentry.py:293
+#: ../src/jarabe/journal/expandedentry.py:299
msgid "Participants:"
msgstr "Partecipanti:"
-#: ../src/jarabe/journal/expandedentry.py:316
+#: ../src/jarabe/journal/expandedentry.py:321
msgid "Description:"
msgstr "Descrizione:"
-#: ../src/jarabe/journal/expandedentry.py:341
+#: ../src/jarabe/journal/expandedentry.py:346
msgid "Tags:"
msgstr "Etichette:"
-#: ../src/jarabe/journal/journalactivity.py:108
-#: ../src/jarabe/journal/volumestoolbar.py:47
+#: ../src/jarabe/journal/journalactivity.py:115
+#: ../src/jarabe/journal/journaltoolbox.py:456
+#: ../src/jarabe/journal/volumestoolbar.py:50
msgid "Journal"
msgstr "Diario"
-#: ../src/jarabe/journal/journaltoolbox.py:67
+#: ../src/jarabe/journal/journaltoolbox.py:69
msgid "Search"
msgstr "Cerca"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:136
msgid "Anytime"
msgstr "Sempre"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:138
msgid "Today"
msgstr "Oggi"
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:140
msgid "Since yesterday"
msgstr "Da ieri"
# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
-#: ../src/jarabe/journal/journaltoolbox.py:132
+#: ../src/jarabe/journal/journaltoolbox.py:142
msgid "Past week"
msgstr "Settimana scorsa"
# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
-#: ../src/jarabe/journal/journaltoolbox.py:134
+#: ../src/jarabe/journal/journaltoolbox.py:144
msgid "Past month"
msgstr "Mese scorso"
# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
-#: ../src/jarabe/journal/journaltoolbox.py:136
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "Past year"
msgstr "Anno scorso"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:153
msgid "Anyone"
msgstr "Tutti"
-#: ../src/jarabe/journal/journaltoolbox.py:145
+#: ../src/jarabe/journal/journaltoolbox.py:155
msgid "My friends"
msgstr "I miei amici"
-#: ../src/jarabe/journal/journaltoolbox.py:146
+#: ../src/jarabe/journal/journaltoolbox.py:156
msgid "My class"
msgstr "La mia classe"
# TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:274
+#: ../src/jarabe/journal/journaltoolbox.py:298
msgid "Anything"
msgstr "Qualsiasi"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:350
-#: ../src/jarabe/journal/palettes.py:83
+#: ../src/jarabe/journal/journaltoolbox.py:381
+#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Copia"
+#: ../src/jarabe/journal/journaltoolbox.py:436
+#: ../src/jarabe/journal/volumestoolbar.py:157
+msgid "Entries without a file cannot be copied."
+msgstr "Elementi senza file non possono essere copiati"
+
+#: ../src/jarabe/journal/journaltoolbox.py:445
+#: ../src/jarabe/journal/volumestoolbar.py:166
+#, python-format
+msgid "Error while copying the entry. %s"
+msgstr "Errore durante la copia dell'elemento. %s"
+
+#: ../src/jarabe/journal/journaltoolbox.py:446
+#: ../src/jarabe/journal/volumestoolbar.py:167
+msgid "Error"
+msgstr "Errore"
+
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:431
-#: ../src/jarabe/journal/palettes.py:68
+#: ../src/jarabe/journal/journaltoolbox.py:488
+#: ../src/jarabe/journal/palettes.py:69
msgid "Start"
msgstr "Avvia"
-#: ../src/jarabe/journal/listview.py:373
+#: ../src/jarabe/journal/journaltoolbox.py:516
+msgid "Sort by date modified"
+msgstr ""
+
+#: ../src/jarabe/journal/journaltoolbox.py:517
+msgid "Sort by date created"
+msgstr ""
+
+#: ../src/jarabe/journal/journaltoolbox.py:518
+msgid "Sort by size"
+msgstr ""
+
+#: ../src/jarabe/journal/journaltoolbox.py:527
+msgid "Sort view"
+msgstr ""
+
+#: ../src/jarabe/journal/listview.py:380
msgid "Your Journal is empty"
msgstr "Il tuo Diario è vuoto"
-#: ../src/jarabe/journal/listview.py:375
+#: ../src/jarabe/journal/listview.py:382
msgid "No matching entries"
msgstr "Non ci sono registrazioni corrispondenti"
-#: ../src/jarabe/journal/listview.py:386
+#: ../src/jarabe/journal/listview.py:393
msgid "Clear search"
msgstr "Annulla ricerca"
-#: ../src/jarabe/journal/modalalert.py:63
+#: ../src/jarabe/journal/misc.py:273
+#, python-format
+msgid "Older Version Of %s Activity"
+msgstr ""
+
+#: ../src/jarabe/journal/misc.py:274
+#, python-format
+msgid "Do you want to downgrade to version %s "
+msgstr ""
+
+#: ../src/jarabe/journal/modalalert.py:64
msgid "Your Journal is full"
msgstr "Il tuo Diario è pieno"
-#: ../src/jarabe/journal/modalalert.py:67
+#: ../src/jarabe/journal/modalalert.py:68
msgid "Please delete some old Journal entries to make space for new ones."
msgstr ""
"Per favore cancella alcune registrazioni vecchie dal Diario per far spazio "
"alle nuove."
-#: ../src/jarabe/journal/modalalert.py:79
+#: ../src/jarabe/journal/modalalert.py:80
msgid "Show Journal"
msgstr "Apri il Diario"
@@ -1240,110 +1461,311 @@ msgid "Choose an object"
msgstr "Scegli un oggetto"
#: ../src/jarabe/journal/objectchooser.py:151
-#: ../src/jarabe/view/viewsource.py:308
+#: ../src/jarabe/view/viewsource.py:311
msgid "Close"
msgstr "Chiudi"
-#: ../src/jarabe/journal/palettes.py:66
+#: ../src/jarabe/journal/palettes.py:67
msgid "Resume with"
msgstr "Riprendi con"
-#: ../src/jarabe/journal/palettes.py:69
+#: ../src/jarabe/journal/palettes.py:70
msgid "Start with"
msgstr "Inizia con"
-#: ../src/jarabe/journal/palettes.py:91
+#: ../src/jarabe/journal/palettes.py:83 ../src/jarabe/journal/palettes.py:216
+msgid "No activity to start entry"
+msgstr "Attività per riprendere la sessione non presente"
+
+#: ../src/jarabe/journal/palettes.py:98
msgid "Send to"
msgstr "Invia a"
-#: ../src/jarabe/journal/palettes.py:100
+#: ../src/jarabe/journal/palettes.py:107
msgid "View Details"
msgstr "Visualizza Dettagli"
-#: ../src/jarabe/journal/palettes.py:178
+#: ../src/jarabe/journal/palettes.py:181
msgid "No friends present"
msgstr "Non ci sono amici presenti"
-#: ../src/jarabe/journal/palettes.py:183
+#: ../src/jarabe/journal/palettes.py:186
msgid "No valid connection found"
msgstr "Connessione non trovata"
-#: ../src/jarabe/journal/palettes.py:211
+#: ../src/jarabe/journal/palettes.py:214
msgid "No activity to resume entry"
msgstr "Sessione dell'Attività da riprendere non presente"
-#: ../src/jarabe/journal/palettes.py:213
-msgid "No activity to start entry"
-msgstr "Attività per riprendere la sessione non presente"
+#: ../src/jarabe/model/network.py:158
+msgid "The reason for the device state change is unknown."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:160
+msgid "The state change is normal."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:162
+msgid "The device is now managed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:164
+msgid "The device is no longer managed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:166
+msgid "The device could not be readied for configuration."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:168
+msgid ""
+"IP configuration could not be reserved (no available address, timeout, etc)."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:171
+msgid "The IP configuration is no longer valid."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:173
+msgid "Secrets were required, but not provided."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:175
+msgid ""
+"The 802.1X supplicant disconnected from the access point or authentication "
+"server."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:178
+msgid "Configuration of the 802.1X supplicant failed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:180
+msgid "The 802.1X supplicant quit or failed unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:182
+msgid "The 802.1X supplicant took too long to authenticate."
+msgstr ""
-#: ../src/jarabe/view/buddymenu.py:62
+#: ../src/jarabe/model/network.py:184
+msgid "The PPP service failed to start within the allowed time."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:186
+msgid "The PPP service disconnected unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:188
+msgid "The PPP service quit or failed unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:190
+msgid "The DHCP service failed to start within the allowed time."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:192
+msgid "The DHCP service reported an unexpected error."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:194
+msgid "The DHCP service quit or failed unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:196
+msgid "The shared connection service failed to start."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:198
+msgid "The shared connection service quit or failed unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:201
+msgid "The AutoIP service failed to start."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:203
+msgid "The AutoIP service reported an unexpected error."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:205
+msgid "The AutoIP service quit or failed unexpectedly."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:207
+msgid "Dialing failed because the line was busy."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:209
+msgid "Dialing failed because there was no dial tone."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:211
+msgid "Dialing failed because there was no carrier."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:213
+msgid "Dialing timed out."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:215
+msgid "Dialing failed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:217
+msgid "Modem initialization failed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:219
+msgid "Failed to select the specified GSM APN"
+msgstr ""
+
+#: ../src/jarabe/model/network.py:221
+msgid "Not searching for networks."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:223
+msgid "Network registration was denied."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:225
+msgid "Network registration timed out."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:227
+msgid "Failed to register with the requested GSM network."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:229
+msgid "PIN check failed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:231
+msgid "Necessary firmware for the device may be missing."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:233
+msgid "The device was removed."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:235
+msgid "NetworkManager went to sleep."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:237
+msgid "The device's active connection was removed or disappeared."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:240
+msgid "A user or client requested the disconnection."
+msgstr ""
+
+#: ../src/jarabe/model/network.py:242
+msgid "The device's carrier/link changed."
+msgstr ""
+
+#: ../src/jarabe/view/buddymenu.py:63
msgid "Remove friend"
msgstr "Rimuovi l'amico"
-#: ../src/jarabe/view/buddymenu.py:65
+#: ../src/jarabe/view/buddymenu.py:66
msgid "Make friend"
msgstr "Aggiungi agli amici"
-#: ../src/jarabe/view/buddymenu.py:82
+#: ../src/jarabe/view/buddymenu.py:83
msgid "Shutdown"
msgstr "Spegni"
-#: ../src/jarabe/view/buddymenu.py:90
+#: ../src/jarabe/view/buddymenu.py:91
+msgid "Restart"
+msgstr "Riavvia"
+
+#: ../src/jarabe/view/buddymenu.py:97
msgid "Logout"
msgstr "Disconnessione"
-#: ../src/jarabe/view/buddymenu.py:95
+#: ../src/jarabe/view/buddymenu.py:102
msgid "My Settings"
msgstr "Le mie Preferenze"
-#: ../src/jarabe/view/buddymenu.py:130
+#: ../src/jarabe/view/buddymenu.py:137
#, python-format
msgid "Invite to %s"
msgstr "Invito per %s"
-#: ../src/jarabe/view/palettes.py:45
+#: ../src/jarabe/view/launcher.py:190
+#, python-format
+msgid "<b>%s</b> failed to start."
+msgstr ""
+
+#: ../src/jarabe/view/palettes.py:46
msgid "Starting..."
msgstr "Inizio..."
+#: ../src/jarabe/view/palettes.py:56
+msgid "Activity failed to start"
+msgstr ""
+
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:74
+#: ../src/jarabe/view/palettes.py:85
msgid "View Source"
msgstr "Visualizza Sorgente"
-#: ../src/jarabe/view/palettes.py:85
+#: ../src/jarabe/view/palettes.py:96
msgid "Stop"
msgstr "Chiudi"
-#: ../src/jarabe/view/palettes.py:125
+#: ../src/jarabe/view/palettes.py:132
msgid "Start new"
msgstr "Inizia nuovo"
-#: ../src/jarabe/view/palettes.py:174
+#: ../src/jarabe/view/palettes.py:172
msgid "Show contents"
msgstr "Mostra i contenuti"
-#: ../src/jarabe/view/palettes.py:196 ../src/jarabe/view/palettes.py:246
+#: ../src/jarabe/view/palettes.py:194 ../src/jarabe/view/palettes.py:245
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB Liberi"
-#: ../src/jarabe/view/viewsource.py:208
+#: ../src/jarabe/view/viewsource.py:211
msgid "Instance Source"
msgstr "Sorgente Istanza"
-#: ../src/jarabe/view/viewsource.py:233
+#: ../src/jarabe/view/viewsource.py:236
msgid "Source"
msgstr "Sorgente"
-#: ../src/jarabe/view/viewsource.py:292
+#: ../src/jarabe/view/viewsource.py:295
msgid "Activity Bundle Source"
msgstr "Sorgente della Attività"
-#: ../src/jarabe/view/viewsource.py:299
+#: ../src/jarabe/view/viewsource.py:302
#, python-format
msgid "View source: %r"
msgstr "Vedi codice sorgente: %r"
+#: ../src/jarabe/util/emulator.py:40
+msgid "Sugar in a window"
+msgstr ""
+
+#~ msgid "APN:"
+#~ msgstr "APN:"
+
+#~ msgid "Create new wireless network"
+#~ msgstr "Crea una nuova rete wireless"
+
+#, python-format
+#~ msgid "%s's network"
+#~ msgstr "rete di %s"
+
+#, python-format
+#~ msgid "Data sent %d kb / received %d kb"
+#~ msgstr "Dati %d kb inviati / %d kb ricevuti"
+
+#~ msgid "Connection time "
+#~ msgstr "Tempo di connessione"
+
#~ msgid "Title"
#~ msgstr "Titolo"
@@ -1359,9 +1781,6 @@ msgstr "Vedi codice sorgente: %r"
#~ msgid "Unmount"
#~ msgstr "Rimuovi"
-#~ msgid "Restart"
-#~ msgstr "Riavvia"
-
#~ msgid ""
#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
#~ msgstr ""
diff --git a/po/nl.po b/po/nl.po
index b490159..bf437de 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -2,12 +2,16 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-08-26 00:31-0400\n"
-"PO-Revision-Date: 2009-09-05 07:25-0400\n"
+"POT-Creation-Date: 2010-06-08 00:31-0400\n"
+"PO-Revision-Date: 2010-08-07 21:55+0200\n"
"Last-Translator: Myckel Habets <myckel@sdf.lonestar.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: nl\n"
@@ -15,7 +19,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Pootle 1.2.1\n"
+"X-Generator: Pootle 2.0.3\n"
#: ../extensions/cpsection/aboutme/__init__.py:24
msgid "About Me"
@@ -53,7 +57,7 @@ msgstr "Fout in opgegeven kleurenmodificaties."
msgid "Error in specified colors."
msgstr "Fout in opgegeven kleuren."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:92
+#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:93
msgid "Name:"
msgstr "Naam:"
@@ -125,7 +129,7 @@ msgstr "Datum en Tijd"
msgid "Error timezone does not exist."
msgstr "Fout tijdzone bestaat niet."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:27
+#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:47
msgid "Timezone"
msgstr "Tijdzone"
@@ -161,24 +165,24 @@ msgstr "Hoek"
#: ../extensions/cpsection/frame/view.py:111
msgid "Edge"
-msgstr "Ribbe"
+msgstr "Rand"
#: ../extensions/cpsection/keyboard/__init__.py:21
#: ../extensions/cpsection/keyboard/view.py:31
msgid "Keyboard"
msgstr "Toetsenbord"
-#: ../extensions/cpsection/keyboard/view.py:187
+#: ../extensions/cpsection/keyboard/view.py:189
msgid "Keyboard Model"
msgstr "Toetsenbordmodel"
-#: ../extensions/cpsection/keyboard/view.py:243
+#: ../extensions/cpsection/keyboard/view.py:248
msgid "Key(s) to change layout"
-msgstr "Toets(en) om de layout te veranderen"
+msgstr "Toets(en) om de indeling te veranderen"
-#: ../extensions/cpsection/keyboard/view.py:311
+#: ../extensions/cpsection/keyboard/view.py:318
msgid "Keyboard Layout(s)"
-msgstr "Toetsenbordlayout(s)"
+msgstr "Toetsenbordindeling(en)"
#: ../extensions/cpsection/language/__init__.py:21
#: ../extensions/cpsection/language/view.py:33
@@ -197,7 +201,7 @@ msgstr "Taal voor code=%s kon niet bepaald worden."
#: ../extensions/cpsection/language/model.py:144
#, python-format
msgid "Sorry I do not speak '%s'."
-msgstr "Sorry, I spreek geen '%s'."
+msgstr "Sorry ik spreek geen '%s'."
#: ../extensions/cpsection/language/view.py:56
msgid ""
@@ -207,6 +211,42 @@ msgstr ""
"Voeg talen toe in de volgorde die je wenst. Als een vertaling niet "
"beschikbaar is, zal de volgende in de lijst gebruikt worden."
+#: ../extensions/cpsection/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Modem configuratie"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:91
+msgid "Username:"
+msgstr "Gebruikersnaam:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:102
+msgid "Password:"
+msgstr "Wachtwoord:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:113
+msgid "Number:"
+msgstr "Getal:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:124
+msgid "Access Point Name (APN):"
+msgstr "Access Point Naam (APN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:135
+msgid "Personal Identity Number (PIN):"
+msgstr "Persoonlijk Identiteitsnummer (PIN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:146
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Persoonlijke Ontsluitingssleutel (PUK):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:167
+msgid ""
+"You will need to provide the following information to set up a mobile "
+"broadband connection to a cellular (3G) network."
+msgstr ""
+"Je moet de volgende informatie opgeven om een mobiele breedbandverbinding "
+"via een mobiel (3G) netwerk op te zetten."
+
#: ../extensions/cpsection/network/__init__.py:21
#: ../extensions/cpsection/network/view.py:28
msgid "Network"
@@ -256,9 +296,9 @@ msgid ""
"server will be able to see each other, even when they aren't on the same "
"network."
msgstr ""
-"De server is het equivalent van in welke ruimte je je bevindt; mensen op "
+"De server is hetzelfde als de ruimte waar jij je in bevindt; mensen op "
"dezelfde server kunnen elkaar zien, ook als ze niet op hetzelfde netwerk "
-"zitten"
+"zitten."
#: ../extensions/cpsection/network/view.py:136
msgid "Server:"
@@ -268,11 +308,11 @@ msgstr "Server:"
msgid "Power"
msgstr "Energie"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:85
msgid "Error in automatic pm argument, use on/off."
msgstr "Fout in automatisch energiebeheer argument, gebruik on/off."
-#: ../extensions/cpsection/power/model.py:81
+#: ../extensions/cpsection/power/model.py:112
msgid "Error in extreme pm argument, use on/off."
msgstr "Fout in extreem energiebeheer argument, gebruik on/off."
@@ -295,7 +335,7 @@ msgstr ""
msgid "Software update"
msgstr "Software-update"
-#: ../extensions/cpsection/updater/view.py:62
+#: ../extensions/cpsection/updater/view.py:63
msgid ""
"Software updates correct errors, eliminate security vulnerabilities, and "
"provide new features."
@@ -303,79 +343,79 @@ msgstr ""
"Software-updates verbeteren fouten, verhelpen beveiligingsproblemen en "
"bieden nieuwe mogelijkheden."
-#: ../extensions/cpsection/updater/view.py:122
+#: ../extensions/cpsection/updater/view.py:125
#, python-format
msgid "Checking %s..."
msgstr "Controleren van %s..."
-#: ../extensions/cpsection/updater/view.py:124
+#: ../extensions/cpsection/updater/view.py:127
#, python-format
msgid "Downloading %s..."
msgstr "Downloaden van %s..."
-#: ../extensions/cpsection/updater/view.py:126
+#: ../extensions/cpsection/updater/view.py:129
#, python-format
msgid "Updating %s..."
msgstr "Updaten van %s..."
-#: ../extensions/cpsection/updater/view.py:135
+#: ../extensions/cpsection/updater/view.py:139
msgid "Your software is up-to-date"
msgstr "Je software is bijgewerkt"
-#: ../extensions/cpsection/updater/view.py:137
+#: ../extensions/cpsection/updater/view.py:141
#, python-format
msgid "You can install %s update"
msgid_plural "You can install %s updates"
msgstr[0] "Je kan %s update installeren"
msgstr[1] "Je kan %s updates installeren"
-#: ../extensions/cpsection/updater/view.py:155
+#: ../extensions/cpsection/updater/view.py:159
msgid "Checking for updates..."
msgstr "Controleren op updates..."
-#: ../extensions/cpsection/updater/view.py:160
+#: ../extensions/cpsection/updater/view.py:164
msgid "Installing updates..."
msgstr "Updates installeren..."
-#: ../extensions/cpsection/updater/view.py:165
+#: ../extensions/cpsection/updater/view.py:172
#, python-format
msgid "%s update was installed"
msgid_plural "%s updates were installed"
msgstr[0] "%s update is geïnstalleerd"
msgstr[1] "%s updates zijn geïnstalleerd"
-#: ../extensions/cpsection/updater/view.py:244
+#: ../extensions/cpsection/updater/view.py:253
msgid "Install selected"
msgstr "Installatie geselecteerd"
-#: ../extensions/cpsection/updater/view.py:265
+#: ../extensions/cpsection/updater/view.py:274
#, python-format
msgid "Download size: %s"
msgstr "Downloadgrootte: %s"
-#: ../extensions/cpsection/updater/view.py:353
+#: ../extensions/cpsection/updater/view.py:362
#, python-format
msgid "From version %(current)d to %(new)s (Size: %(size)s)"
msgstr "Van versie %(current)d naar %(new)s (Grootte: %(size)s)"
#. TRANS: download size is 0
-#: ../extensions/cpsection/updater/view.py:373
+#: ../extensions/cpsection/updater/view.py:382
msgid "None"
msgstr "Niets"
#. TRANS: download size of very small updates
-#: ../extensions/cpsection/updater/view.py:376
+#: ../extensions/cpsection/updater/view.py:385
msgid "1 KB"
msgstr "1 KB"
#. TRANS: download size of small updates, e.g. '250 KB'
-#: ../extensions/cpsection/updater/view.py:379
+#: ../extensions/cpsection/updater/view.py:388
#, python-format
msgid "%.0f KB"
msgstr "%.0f KB"
#. TRANS: download size of updates, e.g. '2.3 MB'
-#: ../extensions/cpsection/updater/view.py:382
+#: ../extensions/cpsection/updater/view.py:391
#, python-format
msgid "%.1f MB"
msgstr "%.1f MB"
@@ -405,47 +445,102 @@ msgstr "%(hour)d:%(min).2d over"
msgid "Charged"
msgstr "Opgeladen"
-#: ../extensions/deviceicon/network.py:44
+#: ../extensions/deviceicon/network.py:49
#, python-format
msgid "IP address: %s"
msgstr "IP adres: %s"
-#: ../extensions/deviceicon/network.py:110
+#: ../extensions/deviceicon/network.py:112
msgid "Disconnect..."
msgstr "Verbinding verbreken..."
-#: ../extensions/deviceicon/network.py:114
+#: ../extensions/deviceicon/network.py:117
msgid "Create new wireless network"
msgstr "Nieuw draadloos netwerk aanmaken"
-#: ../extensions/deviceicon/network.py:120
-#: ../src/jarabe/desktop/meshbox.py:261
+#: ../extensions/deviceicon/network.py:123
+#: ../extensions/deviceicon/network.py:285
+#: ../src/jarabe/desktop/meshbox.py:247 ../src/jarabe/desktop/meshbox.py:536
msgid "Connecting..."
msgstr "Verbinden..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:124
-#: ../extensions/deviceicon/network.py:186
-#: ../src/jarabe/desktop/meshbox.py:267
+#: ../extensions/deviceicon/network.py:127
+#: ../extensions/deviceicon/network.py:199
+#: ../extensions/deviceicon/network.py:289
+#: ../src/jarabe/desktop/meshbox.py:253 ../src/jarabe/desktop/meshbox.py:542
msgid "Connected"
msgstr "Verbonden"
-#: ../extensions/deviceicon/network.py:146
+#: ../extensions/deviceicon/network.py:159
msgid "Channel"
msgstr "Kanaal"
-#: ../extensions/deviceicon/network.py:161
+#: ../extensions/deviceicon/network.py:174
msgid "Wired Network"
msgstr "Bedraad netwerk"
-#: ../extensions/deviceicon/network.py:189
+#: ../extensions/deviceicon/network.py:202
msgid "Speed"
msgstr "Snelheid"
-#: ../extensions/deviceicon/network.py:415
+#: ../extensions/deviceicon/network.py:229
+msgid "Wireless modem"
+msgstr "Draadloze modem"
+
+#: ../extensions/deviceicon/network.py:277
+msgid "Please wait..."
+msgstr "Even geduld aub..."
+
+#: ../extensions/deviceicon/network.py:280
+#: ../src/jarabe/desktop/meshbox.py:163 ../src/jarabe/desktop/meshbox.py:493
+msgid "Connect"
+msgstr "Verbinden"
+
+#: ../extensions/deviceicon/network.py:281
+msgid "Disconnected"
+msgstr "Verbinding verbroken"
+
+#: ../extensions/deviceicon/network.py:284
+#: ../src/jarabe/controlpanel/toolbar.py:115
+#: ../src/jarabe/desktop/homebox.py:68
+#: ../src/jarabe/frame/activitiestray.py:708
+#: ../src/jarabe/frame/activitiestray.py:807
+#: ../src/jarabe/frame/activitiestray.py:835
+msgid "Cancel"
+msgstr "Annuleren"
+
+#: ../extensions/deviceicon/network.py:288
+#: ../src/jarabe/desktop/meshbox.py:167
+msgid "Disconnect"
+msgstr "Verbinding verbreken"
+
+#: ../extensions/deviceicon/network.py:292
+msgid "Sim requires Pin/Puk"
+msgstr "Sim vereist Pin/Puk"
+
+#: ../extensions/deviceicon/network.py:293
+msgid "Authentication Error"
+msgstr "Authenticatiefout"
+
+#: ../extensions/deviceicon/network.py:538
#, python-format
-msgid "%s's network %s"
-msgstr "%s's netwerk %s"
+msgid "%s's network"
+msgstr "%s's netwerk"
+
+#: ../extensions/deviceicon/network.py:605
+#: ../extensions/deviceicon/network.py:664
+msgid "Mesh Network"
+msgstr "Mesh netwerk"
+
+#: ../extensions/deviceicon/network.py:869
+#, python-format
+msgid "Data sent %d KB / received %d KB"
+msgstr "Gegevens verstuurd %d KB / ontvangen %d KB"
+
+#: ../extensions/deviceicon/network.py:880
+msgid "Connection time "
+msgstr "Tijd verbonden "
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
@@ -459,39 +554,51 @@ msgstr "Ontdempen"
msgid "Mute"
msgstr "Dempen"
-#: ../extensions/globalkey/screenshot.py:56
+#: ../extensions/globalkey/screenshot.py:59
msgid "Mesh"
msgstr "Mesh"
-#: ../extensions/globalkey/screenshot.py:58
+#: ../extensions/globalkey/screenshot.py:61
#: ../src/jarabe/frame/zoomtoolbar.py:39
msgid "Group"
msgstr "Groep"
-#: ../extensions/globalkey/screenshot.py:60
+#: ../extensions/globalkey/screenshot.py:63
#: ../src/jarabe/frame/zoomtoolbar.py:41
msgid "Home"
msgstr "Thuis"
-#: ../extensions/globalkey/screenshot.py:66
+#: ../extensions/globalkey/screenshot.py:69
#: ../src/jarabe/frame/zoomtoolbar.py:43
msgid "Activity"
msgstr "Activiteit"
-#: ../extensions/globalkey/screenshot.py:69
+#: ../extensions/globalkey/screenshot.py:72
msgid "Screenshot"
msgstr "Schermafdruk"
-#: ../extensions/globalkey/screenshot.py:71
+#: ../extensions/globalkey/screenshot.py:74
#, python-format
msgid "Screenshot of \"%s\""
msgstr "Schermafdruk van \"%s\""
#: ../data/sugar.schemas.in.h:1
-msgid "Backup URL"
-msgstr "Backup URL"
+msgid ""
+"\"disabled\" to ask nick on initialization; \"system\" to reuse UNIX account "
+"long name."
+msgstr ""
+"\"uitgezet\" om bijnaam te vragen bij initialisatie; \"systeem\" hergebruikt de "
+"UNIX gebruiker lange naam."
#: ../data/sugar.schemas.in.h:2
+msgid "Additional directories which can contain updated translations."
+msgstr "Additionele directories die bijgewerkte vertalingen kunnen bevatten."
+
+#: ../data/sugar.schemas.in.h:3
+msgid "Backup URL"
+msgstr "Back-up URL"
+
+#: ../data/sugar.schemas.in.h:4
msgid ""
"Color for the XO icon that is used throughout the desktop. The string is "
"composed of the stroke color and fill color, format is that of rbg colors. "
@@ -501,148 +608,220 @@ msgstr ""
"uit een lijnkleur en een vulkleur, formaat is gelijk aan rbg kleuren. "
"Bijvoorbeeld: #AC32FF,#9A5200"
-#: ../data/sugar.schemas.in.h:3
+#: ../data/sugar.schemas.in.h:5
msgid "Corner Delay"
msgstr "Hoekvertraging"
-#: ../data/sugar.schemas.in.h:4
+#: ../data/sugar.schemas.in.h:6
+msgid "Default font face"
+msgstr "Standaard lettertype"
+
+#: ../data/sugar.schemas.in.h:7
+msgid "Default font size"
+msgstr "Standaard lettergrootte"
+
+#: ../data/sugar.schemas.in.h:8
+msgid "Default nick"
+msgstr "Standaard bijnaam"
+
+#: ../data/sugar.schemas.in.h:9
msgid "Delay for the activation of the frame using the corners."
msgstr "Vertraging voor de activatie van het kader door de hoeken te gebruiken."
-#: ../data/sugar.schemas.in.h:5
+#: ../data/sugar.schemas.in.h:10
msgid "Delay for the activation of the frame using the edges."
msgstr "Vertraging voor de activatie van het kader door de randen te gebruiken."
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:11
+msgid "Directory to search for translations"
+msgstr "Directory om naar vertalingen te zoeken"
+
+#: ../data/sugar.schemas.in.h:12
msgid "Edge Delay"
msgstr "Randvertraging"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:13
msgid "Favorites Layout"
msgstr "Favorietenlayout"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:14
msgid "Favorites resume mode"
msgstr "Favorieten hervatmodus"
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:15
+msgid "Font face that is used throughout the desktop."
+msgstr "Lettertype dat overal gebruikt wordt op het bureaublad."
+
+#: ../data/sugar.schemas.in.h:16
+msgid "Font size that is used throughout the desktop."
+msgstr "Lettergrootte die overal gebruikt wordt op het bureaublad."
+
+#: ../data/sugar.schemas.in.h:17
+msgid "GSM network APN"
+msgstr "GSM netwerk APN"
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network PIN"
+msgstr "GSM netwerk PIN"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network PUK"
+msgstr "GSM netwerk PUK"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network access point name configuration"
+msgstr "GSM netwerk access point naam configuratie"
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network number"
+msgstr "GSM netwerknummer"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network password"
+msgstr "GSM netwerkwachtwoord"
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network password configuration"
+msgstr "GSM netwerkwachtwoord configuratie"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network personal identification number configuration"
+msgstr "GSM netwerk persoonlijk identificatienummer configuratie"
+
+#: ../data/sugar.schemas.in.h:25
+msgid "GSM network personal unlock key configuration"
+msgstr "GSM netwerk persoonlijk ontsluitsleutel configuratie"
+
+#: ../data/sugar.schemas.in.h:26
+msgid "GSM network telephone number configuration"
+msgstr "GSM netwerk telefoonnummer configuratie"
+
+#: ../data/sugar.schemas.in.h:27
+msgid "GSM network username"
+msgstr "GSM netwerkgebruikersnaam"
+
+#: ../data/sugar.schemas.in.h:28
+msgid "GSM network username configuration"
+msgstr "GSM netwerkgebruikersnaam configuratie"
+
+#: ../data/sugar.schemas.in.h:29
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
msgstr ""
-"Indien WAAR, Sugar zal ons opzoekbaar maken voor andere gebruikers op de "
+"Indien WAAR, dan zal Sugar ons opzoekbaar maken voor andere gebruikers op de "
"Jabber server."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:30
msgid "If TRUE, Sugar will show a \"Log out\" option."
msgstr "Indien WAAR, zal Sugar een \"Afmelden\" optie geven."
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:31
msgid "Jabber Server"
-msgstr "Jabber server"
+msgstr "Jabber-server"
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:32
msgid "Keyboard layouts"
-msgstr "Toetsenbordlayouts"
+msgstr "Toetsenbordindelingen"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:33
msgid "Keyboard model"
msgstr "Toetsenbordmodel"
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:34
msgid "Keyboard options"
msgstr "Toetsenbordopties"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:35
msgid "Layout of the favorites view."
-msgstr "Layout van de Favorieten weergave"
+msgstr "Layout van de favorieten weergave."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:36
msgid ""
"List of keyboard layouts. Each entry should be in the form layout(variant)"
msgstr ""
-"Lijst van toetsenbordlayouts. Elke ingang moet in de vorm layout(variant) "
-"zijn."
+"Lijst met toetsenbordindelingen. Elke invoer moet in de "
+"vormindeling(variant) zijn"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:37
msgid "List of keyboard options."
-msgstr "Lijst van toetsenbordopties."
+msgstr "Lijst met toetsenbordopties."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:38
msgid "Power Automatic"
msgstr "Energiebeheer: automatisch"
-#: ../data/sugar.schemas.in.h:19
+#: ../data/sugar.schemas.in.h:39
msgid "Power Automatic."
msgstr "Energiebeheer: automatisch."
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:40
msgid "Power Extreme"
msgstr "Energiebeheer: extreem"
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:41
msgid "Power Extreme."
msgstr "Energiebeheer: extreem."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:42
msgid "Publish to Gadget"
msgstr "Publiceren naar gadget"
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:43
msgid "Setting for muting the sound device."
msgstr "Instelling voor dempen van het audio-apparaat."
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:44
msgid "Show Log out"
msgstr "Afmelden weergeven"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:45
msgid "Sound Muted"
msgstr "Geluid gedempt"
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:46
msgid "The keyboard model to be used"
msgstr "Het toetsenbordmodel om te gebruiken"
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:48
msgid "Timezone setting for the system."
msgstr "Tijdzone-instelling voor het systeem."
-#: ../data/sugar.schemas.in.h:29
+#: ../data/sugar.schemas.in.h:49
msgid "Url of the jabber server to use."
-msgstr "Url van de te gebruiken jabber server."
+msgstr "Url van de te gebruiken jabber-server."
-#: ../data/sugar.schemas.in.h:30
+#: ../data/sugar.schemas.in.h:50
msgid "Url where the backup is saved to."
-msgstr "Url waar de backup opgeslagen wordt."
+msgstr "Url waar de back-up opgeslagen wordt."
-#: ../data/sugar.schemas.in.h:31
+#: ../data/sugar.schemas.in.h:51
msgid "User Color"
msgstr "Gebruikerskleur"
-#: ../data/sugar.schemas.in.h:32
+#: ../data/sugar.schemas.in.h:52
msgid "User Name"
msgstr "Gebruikersnaam"
-#: ../data/sugar.schemas.in.h:33
+#: ../data/sugar.schemas.in.h:53
msgid "User name that is used throughout the desktop."
msgstr "Gebruikersnaam die op de desktop gebruikt wordt."
-#: ../data/sugar.schemas.in.h:34
+#: ../data/sugar.schemas.in.h:54
msgid "Volume Level"
msgstr "Volumeniveau"
-#: ../data/sugar.schemas.in.h:35
+#: ../data/sugar.schemas.in.h:55
msgid "Volume level for the sound device."
-msgstr "Volumeniveau voor het audio-apparaat"
+msgstr "Volumeniveau voor het audio-apparaat."
-#: ../data/sugar.schemas.in.h:36
+#: ../data/sugar.schemas.in.h:56
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
msgstr ""
-"Indien in hervatmodus, klikken op een favoriet icoon zal ervoor zorgen dat "
-"de laatste ingang voor die activiteit hervat wordt."
+"Indien in hervatmodus, kun je door te klikken op een favorietenicoon ervoor "
+"zorgen dat de laatste invoer van die activiteit hervat wordt."
#: ../src/jarabe/controlpanel/cmd.py:28
#, python-format
@@ -650,7 +829,7 @@ msgid ""
"sugar-control-panel: WARNING, found more than one option with the same name: "
"%s module: %r"
msgstr ""
-"sugar-control-panel: WAARSCHUWING, meer dan een optie met dezelfde naam "
+"sugar-stuur-paneel: WAARSCHUWING, meer dan één optie met dezelfde naam "
"gevonden: %s module: %r"
#: ../src/jarabe/controlpanel/cmd.py:30
@@ -661,7 +840,7 @@ msgstr "sugar-control-panel: sleutel=%s is geen beschikbare optie"
#: ../src/jarabe/controlpanel/cmd.py:31
#, python-format
msgid "sugar-control-panel: %s"
-msgstr "sugar-control-panel: %s"
+msgstr "sugar-stuur-paneel: %s"
# TRANS: Translators, there's a empty line at the end of this string,
# which must appear in the translated string (msgstr) as well.
@@ -680,11 +859,11 @@ msgid ""
" -c key clear the current value for the key \n"
" "
msgstr ""
-"Gebruik: sugar-control-panel [ optie ] sleutel [ args ... ] \n"
+"Gebruik: sugar-stuur-paneel [ optie ] sleutel [ args ... ] \n"
" Configuratie voor de sugar omgeving. \n"
" Opties: \n"
" -h geef dit helpbericht weer en sluit af \n"
-" -l lijst van alle beschikbare opties \n"
+" -l lijst van alle beschikbare opties \n"
" -h sleutel geef informatie over deze sleutel \n"
" -g sleutel verkrijg de huidige waarde van de sleutel \n"
" -s sleutel zet de huidige waarde naar de sleutel \n"
@@ -695,68 +874,46 @@ msgstr ""
msgid "To apply your changes you have to restart sugar.\n"
msgstr "Om je veranderingen toe te passen moet je sugar herstarten.\n"
-#: ../src/jarabe/controlpanel/gui.py:280
+#: ../src/jarabe/controlpanel/gui.py:281
msgid "Warning"
msgstr "Waarschuwing"
-#: ../src/jarabe/controlpanel/gui.py:281
+#: ../src/jarabe/controlpanel/gui.py:282
#: ../src/jarabe/controlpanel/sectionview.py:42
msgid "Changes require restart"
msgstr "Verandering vereist een herstart"
-#: ../src/jarabe/controlpanel/gui.py:284
+#: ../src/jarabe/controlpanel/gui.py:285
msgid "Cancel changes"
msgstr "Veranderingen annuleren"
-#: ../src/jarabe/controlpanel/gui.py:289 ../src/jarabe/desktop/homebox.py:70
+#: ../src/jarabe/controlpanel/gui.py:290 ../src/jarabe/desktop/homebox.py:70
msgid "Later"
msgstr "Later"
-#: ../src/jarabe/controlpanel/gui.py:293
+#: ../src/jarabe/controlpanel/gui.py:294
msgid "Restart now"
msgstr "Herstart nu"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:188
+#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:206
msgid "Done"
msgstr "Klaar"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:68
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:822
-#: ../src/jarabe/frame/activitiestray.py:850
-msgid "Cancel"
-msgstr "Annuleren"
-
#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:332
+#: ../src/jarabe/desktop/favoritesview.py:333
msgid "Ok"
msgstr "Ok"
-#: ../src/jarabe/desktop/activitieslist.py:80
-#: ../src/jarabe/journal/listview.py:147
-msgid "Title"
-msgstr "Titel"
-
-#: ../src/jarabe/desktop/activitieslist.py:91
-msgid "Version"
-msgstr "Versie"
-
-#: ../src/jarabe/desktop/activitieslist.py:105
-#: ../src/jarabe/journal/listview.py:178
-msgid "Date"
-msgstr "Datum"
-
-#: ../src/jarabe/desktop/activitieslist.py:234
+#: ../src/jarabe/desktop/activitieslist.py:236
#, python-format
msgid "Version %s"
msgstr "Versie %s"
-#: ../src/jarabe/desktop/activitieslist.py:355
+#: ../src/jarabe/desktop/activitieslist.py:357
msgid "Confirm erase"
msgstr "Bevestig wissen"
-#: ../src/jarabe/desktop/activitieslist.py:357
+#: ../src/jarabe/desktop/activitieslist.py:359
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
msgstr "Bevestig wissen: Wilt u permanent %s wissen?"
@@ -766,24 +923,24 @@ msgstr "Bevestig wissen: Wilt u permanent %s wissen?"
# 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/desktop/activitieslist.py:363
+#: ../src/jarabe/frame/clipboardmenu.py:63
#: ../src/jarabe/view/viewsource.py:218
msgid "Keep"
msgstr "Bewaar"
-#: ../src/jarabe/desktop/activitieslist.py:364
-#: ../src/jarabe/desktop/activitieslist.py:406
-#: ../src/jarabe/journal/journaltoolbox.py:360
-#: ../src/jarabe/journal/palettes.py:112
+#: ../src/jarabe/desktop/activitieslist.py:366
+#: ../src/jarabe/desktop/activitieslist.py:409
+#: ../src/jarabe/journal/journaltoolbox.py:361
+#: ../src/jarabe/journal/palettes.py:106
msgid "Erase"
msgstr "Wissen"
-#: ../src/jarabe/desktop/activitieslist.py:427
+#: ../src/jarabe/desktop/activitieslist.py:430
msgid "Remove favorite"
msgstr "Verwijder favoriet"
-#: ../src/jarabe/desktop/activitieslist.py:431
+#: ../src/jarabe/desktop/activitieslist.py:434
msgid "Make favorite"
msgstr "Maak favoriet"
@@ -791,7 +948,7 @@ msgstr "Maak favoriet"
#. TRANS: label for the freeform layout in the favorites view
#: ../src/jarabe/desktop/favoriteslayout.py:116
msgid "Freeform"
-msgstr "VrijeVorm"
+msgstr "Vrijevorm"
# TRANS: label for the ring layout in the favorites view
#. TRANS: label for the ring layout in the favorites view
@@ -801,52 +958,52 @@ msgstr "Ring"
# 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:334
+#: ../src/jarabe/desktop/favoriteslayout.py:337
msgid "Spiral"
msgstr "Spiraal"
# 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:401
+#: ../src/jarabe/desktop/favoriteslayout.py:404
msgid "Box"
msgstr "Vierkant"
# 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:442
+#: ../src/jarabe/desktop/favoriteslayout.py:445
msgid "Triangle"
msgstr "Driehoek"
-#: ../src/jarabe/desktop/favoritesview.py:323
+#: ../src/jarabe/desktop/favoritesview.py:324
msgid "Registration Failed"
msgstr "Registratie mislukt"
-#: ../src/jarabe/desktop/favoritesview.py:324
+#: ../src/jarabe/desktop/favoritesview.py:325
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:326
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "Registration Successful"
msgstr "Registratie succesvol uitgevoerd"
-#: ../src/jarabe/desktop/favoritesview.py:327
+#: ../src/jarabe/desktop/favoritesview.py:328
msgid "You are now registered with your school server."
-msgstr "Je bent nu geregistreerd bij je schoolserver."
+msgstr "Je bent nu geregistreerd bij je school-server."
-#: ../src/jarabe/desktop/favoritesview.py:671
+#: ../src/jarabe/desktop/favoritesview.py:631
msgid "Register"
msgstr "Registreren"
#: ../src/jarabe/desktop/homebox.py:63
msgid "Software Update"
-msgstr "Software update"
+msgstr "Software Bijwerken"
#: ../src/jarabe/desktop/homebox.py:64
msgid "Update your activities to ensure compatibility with your new software"
msgstr ""
"Update je activiteiten om zeker ervan te zijn dat ze met je nieuwe software "
-"compatibel zijn."
+"compatibel zijn"
#: ../src/jarabe/desktop/homebox.py:73
msgid "Check now"
@@ -868,117 +1025,109 @@ msgstr "Favorietenweergave"
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:131
+#: ../src/jarabe/desktop/keydialog.py:135
msgid "Key Type:"
msgstr "Sleutel type:"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:155
msgid "Authentication Type:"
msgstr "Authenticatie type:"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:220
msgid "WPA & WPA2 Personal"
-msgstr "WPA en WPA 2 Persoonlijk"
+msgstr "WPA & WPA2 Persoonlijk"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "Wireless Security:"
msgstr "Draadloze netwerkbeveiliging:"
-#: ../src/jarabe/desktop/meshbox.py:136
-msgid "Connect"
-msgstr "Verbinden"
-
-#: ../src/jarabe/desktop/meshbox.py:140
-msgid "Disconnect"
-msgstr "Verbinding verbreken"
+#: ../src/jarabe/desktop/meshbox.py:491
+#, python-format
+msgid "Mesh Network %d"
+msgstr "Mesh Netwerk %d"
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:463
-#: ../src/jarabe/frame/activitiestray.py:761
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:64
+#: ../src/jarabe/desktop/meshbox.py:628
+#: ../src/jarabe/frame/activitiestray.py:743
+#: ../src/jarabe/journal/journaltoolbox.py:440
+#: ../src/jarabe/journal/palettes.py:66 ../src/jarabe/view/palettes.py:79
msgid "Resume"
msgstr "Hervatten"
-#: ../src/jarabe/desktop/meshbox.py:468
-#: ../src/jarabe/frame/activitiestray.py:235
+#: ../src/jarabe/desktop/meshbox.py:633
+#: ../src/jarabe/frame/activitiestray.py:241
msgid "Join"
msgstr "Bijvoegen"
-#: ../src/jarabe/desktop/schoolserver.py:34
-msgid "Cannot obtain data needed for registration."
-msgstr ""
-"Kan vereiste gegevens die nodig zijn voor de registratie niet verkrijgen."
-
-#: ../src/jarabe/desktop/schoolserver.py:51
+#: ../src/jarabe/desktop/schoolserver.py:104
msgid "Cannot connect to the server."
msgstr "Kan niet met de server verbinden."
-#: ../src/jarabe/desktop/schoolserver.py:56
+#: ../src/jarabe/desktop/schoolserver.py:109
msgid "The server could not complete the request."
msgstr "De server kon de aanvraag niet voltooien."
-#: ../src/jarabe/frame/activitiestray.py:240
-#: ../src/jarabe/frame/activitiestray.py:698
+#: ../src/jarabe/frame/activitiestray.py:246
+#: ../src/jarabe/frame/activitiestray.py:680
msgid "Decline"
msgstr "Weigeren"
-#: ../src/jarabe/frame/activitiestray.py:650
+#: ../src/jarabe/frame/activitiestray.py:632
#, python-format
msgid "%dB"
msgstr "%dB"
-#: ../src/jarabe/frame/activitiestray.py:652
+#: ../src/jarabe/frame/activitiestray.py:634
#, python-format
msgid "%dKB"
msgstr "%dKB"
-#: ../src/jarabe/frame/activitiestray.py:654
+#: ../src/jarabe/frame/activitiestray.py:636
#, python-format
msgid "%dMB"
msgstr "%dMB"
-#: ../src/jarabe/frame/activitiestray.py:671
+#: ../src/jarabe/frame/activitiestray.py:653
#, python-format
msgid "%s of %s"
msgstr "%s van %s"
-#: ../src/jarabe/frame/activitiestray.py:683
+#: ../src/jarabe/frame/activitiestray.py:665
#, python-format
msgid "Transfer from %r"
msgstr "Overdragen van %r"
-#: ../src/jarabe/frame/activitiestray.py:693
+#: ../src/jarabe/frame/activitiestray.py:675
msgid "Accept"
msgstr "Accepteren"
-#: ../src/jarabe/frame/activitiestray.py:716
-#: ../src/jarabe/frame/activitiestray.py:840
+#: ../src/jarabe/frame/activitiestray.py:698
+#: ../src/jarabe/frame/activitiestray.py:825
#, python-format
msgid "%s (%s)"
msgstr "%s (%s)"
-#: ../src/jarabe/frame/activitiestray.py:750
-#: ../src/jarabe/frame/activitiestray.py:875
+#: ../src/jarabe/frame/activitiestray.py:732
+#: ../src/jarabe/frame/activitiestray.py:860
msgid "Dismiss"
msgstr "Wegdoen"
-#: ../src/jarabe/frame/activitiestray.py:810
+#: ../src/jarabe/frame/activitiestray.py:795
#, python-format
msgid "Transfer to %r"
msgstr "Overdragen naar %r"
-#: ../src/jarabe/frame/clipboardmenu.py:52
+#: ../src/jarabe/frame/clipboardmenu.py:53 ../src/jarabe/view/palettes.py:233
msgid "Remove"
msgstr "Verwijderen"
-#: ../src/jarabe/frame/clipboardmenu.py:57
-#: ../src/jarabe/frame/clipboardmenu.py:80
+#: ../src/jarabe/frame/clipboardmenu.py:58
+#: ../src/jarabe/frame/clipboardmenu.py:81
msgid "Open"
msgstr "Openen"
-#: ../src/jarabe/frame/clipboardmenu.py:85
+#: ../src/jarabe/frame/clipboardmenu.py:86
msgid "Open with"
msgstr "Openen met"
@@ -1007,140 +1156,141 @@ msgstr "F3"
msgid "F4"
msgstr "F4"
-#: ../src/jarabe/intro/window.py:124
+#: ../src/jarabe/intro/window.py:128
msgid "Click to change color:"
msgstr "Klik om de kleur te veranderen:"
-#: ../src/jarabe/intro/window.py:174 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:192 ../src/jarabe/journal/detailview.py:103
msgid "Back"
msgstr "Terug"
-#: ../src/jarabe/intro/window.py:191
+#: ../src/jarabe/intro/window.py:209
msgid "Next"
msgstr "Volgende"
-#: ../src/jarabe/journal/expandedentry.py:164
-#: ../src/jarabe/journal/palettes.py:66
+#: ../src/jarabe/journal/expandedentry.py:151
+#: ../src/jarabe/journal/palettes.py:60
msgid "Untitled"
msgstr "Naamloos"
-#: ../src/jarabe/journal/expandedentry.py:210
+#: ../src/jarabe/journal/expandedentry.py:242
msgid "No preview"
msgstr "Geen voorbeeld"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:261
#, python-format
msgid "Kind: %s"
msgstr "Type: %s"
-#: ../src/jarabe/journal/expandedentry.py:229
+#: ../src/jarabe/journal/expandedentry.py:261
msgid "Unknown"
msgstr "Onbekend"
-#: ../src/jarabe/journal/expandedentry.py:230
+#: ../src/jarabe/journal/expandedentry.py:262
#, python-format
msgid "Date: %s"
msgstr "Datum: %s"
-#: ../src/jarabe/journal/expandedentry.py:231
+#: ../src/jarabe/journal/expandedentry.py:263
#, python-format
msgid "Size: %s"
msgstr "Grootte: %s"
-#: ../src/jarabe/journal/expandedentry.py:253 ../src/jarabe/journal/misc.py:92
+#: ../src/jarabe/journal/expandedentry.py:285 ../src/jarabe/journal/misc.py:93
msgid "No date"
msgstr "Geen datum"
-#: ../src/jarabe/journal/expandedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:292
msgid "Participants:"
msgstr "Deelnemers:"
-#: ../src/jarabe/journal/expandedentry.py:283
+#: ../src/jarabe/journal/expandedentry.py:315
msgid "Description:"
msgstr "Omschrijving:"
-#: ../src/jarabe/journal/expandedentry.py:309
+#: ../src/jarabe/journal/expandedentry.py:340
msgid "Tags:"
msgstr "Labels:"
#: ../src/jarabe/journal/journalactivity.py:108
+#: ../src/jarabe/journal/journaltoolbox.py:411
#: ../src/jarabe/journal/volumestoolbar.py:47
msgid "Journal"
msgstr "Dagboek"
-#: ../src/jarabe/journal/journaltoolbox.py:67
+#: ../src/jarabe/journal/journaltoolbox.py:68
msgid "Search"
msgstr "Zoeken"
-#: ../src/jarabe/journal/journaltoolbox.py:126
+#: ../src/jarabe/journal/journaltoolbox.py:127
msgid "Anytime"
msgstr "Ieder tijdstip"
-#: ../src/jarabe/journal/journaltoolbox.py:128
+#: ../src/jarabe/journal/journaltoolbox.py:129
msgid "Today"
msgstr "Vandaag"
-#: ../src/jarabe/journal/journaltoolbox.py:130
+#: ../src/jarabe/journal/journaltoolbox.py:131
msgid "Since yesterday"
msgstr "Sinds gisteren"
# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
-#: ../src/jarabe/journal/journaltoolbox.py:132
+#: ../src/jarabe/journal/journaltoolbox.py:133
msgid "Past week"
msgstr "Afgelopen week"
# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
-#: ../src/jarabe/journal/journaltoolbox.py:134
+#: ../src/jarabe/journal/journaltoolbox.py:135
msgid "Past month"
msgstr "Afgelopen maand"
# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
-#: ../src/jarabe/journal/journaltoolbox.py:136
+#: ../src/jarabe/journal/journaltoolbox.py:137
msgid "Past year"
msgstr "Afgelopen jaar"
-#: ../src/jarabe/journal/journaltoolbox.py:143
+#: ../src/jarabe/journal/journaltoolbox.py:144
msgid "Anyone"
msgstr "Iedereen"
-#: ../src/jarabe/journal/journaltoolbox.py:145
+#: ../src/jarabe/journal/journaltoolbox.py:146
msgid "My friends"
msgstr "Mijn vrienden"
-#: ../src/jarabe/journal/journaltoolbox.py:146
+#: ../src/jarabe/journal/journaltoolbox.py:147
msgid "My class"
msgstr "Mijn klas"
# TRANS: Item in a combo box that filters by entry type.
-#: ../src/jarabe/journal/journaltoolbox.py:274
+#: ../src/jarabe/journal/journaltoolbox.py:275
msgid "Anything"
msgstr "Alles"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:350
-#: ../src/jarabe/journal/palettes.py:90
+#: ../src/jarabe/journal/journaltoolbox.py:351
+#: ../src/jarabe/journal/palettes.py:84
msgid "Copy"
msgstr "Kopieer"
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:431
-#: ../src/jarabe/journal/palettes.py:75
+#: ../src/jarabe/journal/journaltoolbox.py:443
+#: ../src/jarabe/journal/palettes.py:69
msgid "Start"
msgstr "Start"
-#: ../src/jarabe/journal/listview.py:361
+#: ../src/jarabe/journal/listview.py:373
msgid "Your Journal is empty"
msgstr "Je dagboek is leeg"
-#: ../src/jarabe/journal/listview.py:363
+#: ../src/jarabe/journal/listview.py:375
msgid "No matching entries"
msgstr "Geen overeenkomende ingangen"
-#: ../src/jarabe/journal/listview.py:374
+#: ../src/jarabe/journal/listview.py:386
msgid "Clear search"
msgstr "Zoekopdracht wissen"
@@ -1161,41 +1311,41 @@ msgid "Choose an object"
msgstr "Kies een object"
#: ../src/jarabe/journal/objectchooser.py:151
-#: ../src/jarabe/view/viewsource.py:308
+#: ../src/jarabe/view/viewsource.py:310
msgid "Close"
msgstr "Sluiten"
-#: ../src/jarabe/journal/palettes.py:73
+#: ../src/jarabe/journal/palettes.py:67
msgid "Resume with"
msgstr "Hervatten met"
-#: ../src/jarabe/journal/palettes.py:76
+#: ../src/jarabe/journal/palettes.py:70
msgid "Start with"
msgstr "Starten met"
-#: ../src/jarabe/journal/palettes.py:98
+#: ../src/jarabe/journal/palettes.py:92
msgid "Send to"
msgstr "Verstuur naar"
-#: ../src/jarabe/journal/palettes.py:107
+#: ../src/jarabe/journal/palettes.py:101
msgid "View Details"
msgstr "Details weergeven"
-#: ../src/jarabe/journal/palettes.py:185
+#: ../src/jarabe/journal/palettes.py:179
msgid "No friends present"
msgstr "Geen vrienden aanwezig"
-#: ../src/jarabe/journal/palettes.py:190
+#: ../src/jarabe/journal/palettes.py:184
msgid "No valid connection found"
msgstr "Geen bruikbare verbinding gevonden"
-#: ../src/jarabe/journal/palettes.py:218
+#: ../src/jarabe/journal/palettes.py:212
msgid "No activity to resume entry"
-msgstr "Geen activiteit om ingang mee te hervatten"
+msgstr "Geen activiteit om invoer mee te hervatten"
-#: ../src/jarabe/journal/palettes.py:220
+#: ../src/jarabe/journal/palettes.py:214
msgid "No activity to start entry"
-msgstr "Geen activiteit om ingang mee te starten"
+msgstr "Geen activiteit om invoer mee te starten"
#: ../src/jarabe/view/buddymenu.py:62
msgid "Remove friend"
@@ -1222,36 +1372,41 @@ msgstr "Mijn instellingen"
msgid "Invite to %s"
msgstr "Nodig uit voor %s"
-#: ../src/jarabe/view/palettes.py:45
+#: ../src/jarabe/view/launcher.py:190
+#, python-format
+msgid "<b>%s</b> failed to start."
+msgstr "<b>%s</b> kon niet starten."
+
+#: ../src/jarabe/view/palettes.py:47
msgid "Starting..."
msgstr "Beginnen..."
+#: ../src/jarabe/view/palettes.py:57
+msgid "Activity failed to start"
+msgstr "Activiteit kon niet starten"
+
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:71
+#: ../src/jarabe/view/palettes.py:86
msgid "View Source"
msgstr "Bron weergeven"
-#: ../src/jarabe/view/palettes.py:82
+#: ../src/jarabe/view/palettes.py:97
msgid "Stop"
msgstr "Stop"
-#: ../src/jarabe/view/palettes.py:122
+#: ../src/jarabe/view/palettes.py:137
msgid "Start new"
msgstr "Begin nieuw"
-#: ../src/jarabe/view/palettes.py:171
+#: ../src/jarabe/view/palettes.py:186
msgid "Show contents"
msgstr "Inhoud weergeven"
-#: ../src/jarabe/view/palettes.py:193 ../src/jarabe/view/palettes.py:243
+#: ../src/jarabe/view/palettes.py:208 ../src/jarabe/view/palettes.py:258
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB vrij"
-#: ../src/jarabe/view/palettes.py:218
-msgid "Unmount"
-msgstr "Loskoppelen"
-
#: ../src/jarabe/view/viewsource.py:208
msgid "Instance Source"
msgstr "Werkkopie van bron maken"
@@ -1260,15 +1415,34 @@ msgstr "Werkkopie van bron maken"
msgid "Source"
msgstr "Bron"
-#: ../src/jarabe/view/viewsource.py:292
+#: ../src/jarabe/view/viewsource.py:294
msgid "Activity Bundle Source"
msgstr "Activiteitsbundel Bron"
-#: ../src/jarabe/view/viewsource.py:299
+#: ../src/jarabe/view/viewsource.py:301
#, python-format
msgid "View source: %r"
msgstr "Bron weergeven: %r"
+#~ msgid "APN:"
+#~ msgstr "APN:"
+
+#~ msgid "Title"
+#~ msgstr "Titel"
+
+#~ msgid "Version"
+#~ msgstr "Versie"
+
+#~ msgid "Date"
+#~ msgstr "Datum"
+
+#~ msgid "Cannot obtain data needed for registration."
+#~ msgstr ""
+#~ "Kan vereiste gegevens die nodig zijn voor de registratie niet verkrijgen."
+
+#~ msgid "Unmount"
+#~ msgstr "Loskoppelen"
+
#~ msgid "Restart"
#~ msgstr "Herstarten"
@@ -1290,12 +1464,6 @@ msgstr "Bron weergeven: %r"
#~ msgid "Disconnecting..."
#~ msgstr "Verbinding verbreken..."
-#~ msgid "Mesh Network"
-#~ msgstr "Mesh netwerk"
-
-#~ msgid "Disconnected"
-#~ msgstr "Verbinding verbroken"
-
#~ msgid "About my XO"
#~ msgstr "Over mijn XO"
diff --git a/src/jarabe/__init__.py b/src/jarabe/__init__.py
index 41b4b1c..ed2f639 100644
--- a/src/jarabe/__init__.py
+++ b/src/jarabe/__init__.py
@@ -23,4 +23,3 @@ refer to a command-line "shell" interface.
# 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/config.py.in b/src/jarabe/config.py.in
index 6c418e9..d22ee9a 100644
--- a/src/jarabe/config.py.in
+++ b/src/jarabe/config.py.in
@@ -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
-# pylint: disable-msg=C0301
+# pylint: disable=C0301
prefix = '@prefix@'
data_path = '@prefix@/share/sugar/data'
diff --git a/src/jarabe/controlpanel/__init__.py b/src/jarabe/controlpanel/__init__.py
index a9dd95a..85f6a24 100644
--- a/src/jarabe/controlpanel/__init__.py
+++ b/src/jarabe/controlpanel/__init__.py
@@ -13,4 +13,3 @@
# 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/controlpanel/cmd.py b/src/jarabe/controlpanel/cmd.py
index 7144b33..fe8f1a4 100644
--- a/src/jarabe/controlpanel/cmd.py
+++ b/src/jarabe/controlpanel/cmd.py
@@ -18,20 +18,21 @@ import sys
import getopt
import os
from gettext import gettext as _
-import traceback
import logging
from jarabe import config
+
_RESTART = 1
-_same_option_warning = _("sugar-control-panel: WARNING, found more than"
- " one option with the same name: %s module: %r")
-_no_option_error = _("sugar-control-panel: key=%s not an available option")
-_general_error = _("sugar-control-panel: %s")
+_same_option_warning = _('sugar-control-panel: WARNING, found more than one'
+ ' option with the same name: %s module: %r')
+_no_option_error = _('sugar-control-panel: key=%s not an available option')
+_general_error = _('sugar-control-panel: %s')
+
def cmd_help():
- '''Print the help to the screen'''
+ """Print the help to the screen"""
# TRANS: Translators, there's a empty line at the end of this string,
# which must appear in the translated string (msgstr) as well.
print _('Usage: sugar-control-panel [ option ] key [ args ... ] \n\
@@ -45,14 +46,16 @@ def cmd_help():
-c key clear the current value for the key \n\
')
+
def note_restart():
- '''Instructions how to restart sugar'''
+ """Instructions how to restart sugar"""
print _('To apply your changes you have to restart sugar.\n' +
'Hit ctrl+alt+erase on the keyboard to trigger a restart.')
+
def load_modules():
- '''Build a list of pointers to available modules and import them.
- '''
+ """Build a list of pointers to available modules and import them.
+ """
modules = []
path = os.path.join(config.ext_path, 'cpsection')
@@ -65,16 +68,16 @@ def load_modules():
module = __import__('.'.join(('cpsection', item, 'model')),
globals(), locals(), ['model'])
except Exception:
- logging.error('Exception while loading extension:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ logging.exception('Exception while loading extension:')
else:
modules.append(module)
return modules
+
def main():
try:
- options, args = getopt.getopt(sys.argv[1:], "h:s:g:c:l", [])
+ options, args = getopt.getopt(sys.argv[1:], 'h:s:g:c:l', [])
except getopt.GetoptError:
cmd_help()
sys.exit(2)
@@ -87,7 +90,7 @@ def main():
for option, key in options:
found = 0
- if option in ("-h"):
+ if option in ('-h'):
for module in modules:
method = getattr(module, 'set_' + key, None)
if method:
@@ -98,7 +101,7 @@ def main():
print _(_same_option_warning % (key, module))
if found == 0:
print _(_no_option_error % key)
- if option in ("-l"):
+ if option in ('-l'):
for module in modules:
methods = dir(module)
print '%s:' % module.__name__.split('.')[1]
@@ -106,9 +109,9 @@ def main():
if method.startswith('get_'):
print ' %s' % method[4:]
elif method.startswith('clear_'):
- print " %s (use the -c argument with this option)" \
+ print ' %s (use the -c argument with this option)' \
% method[6:]
- if option in ("-g"):
+ if option in ('-g'):
for module in modules:
method = getattr(module, 'print_' + key, None)
if method:
@@ -122,7 +125,7 @@ def main():
print _(_same_option_warning % (key, module))
if found == 0:
print _(_no_option_error % key)
- if option in ("-s"):
+ if option in ('-s'):
for module in modules:
method = getattr(module, 'set_' + key, None)
if method:
@@ -139,7 +142,7 @@ def main():
print _(_same_option_warning % (key, module))
if found == 0:
print _(_no_option_error % key)
- if option in ("-c"):
+ if option in ('-c'):
for module in modules:
method = getattr(module, 'clear_' + key, None)
if method:
diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
index 51d9820..2f55951 100644
--- a/src/jarabe/controlpanel/gui.py
+++ b/src/jarabe/controlpanel/gui.py
@@ -17,8 +17,6 @@
import os
import logging
from gettext import gettext as _
-import sys
-import traceback
import gobject
import gtk
@@ -32,8 +30,9 @@ from jarabe.controlpanel.toolbar import MainToolbar
from jarabe.controlpanel.toolbar import SectionToolbar
from jarabe import config
+
_logger = logging.getLogger('ControlPanel')
-_MAX_COLUMNS = 5
+
class ControlPanel(gtk.Window):
__gtype_name__ = 'SugarControlPanel'
@@ -41,6 +40,9 @@ class ControlPanel(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
+ self._max_columns = int(0.285 * (float(gtk.gdk.screen_width()) /
+ style.GRID_CELL_SIZE - 3))
+
self.set_border_width(style.LINE_WIDTH)
offset = style.GRID_CELL_SIZE
width = gtk.gdk.screen_width() - offset * 2
@@ -74,7 +76,7 @@ class ControlPanel(gtk.Window):
self.add(self._vbox)
self._vbox.show()
- self.connect("realize", self.__realize_cb)
+ self.connect('realize', self.__realize_cb)
self._options = self._get_options()
self._current_option = None
@@ -110,6 +112,7 @@ class ControlPanel(gtk.Window):
self._table = gtk.Table()
self._table.set_col_spacings(style.GRID_CELL_SIZE)
+ self._table.set_row_spacings(style.GRID_CELL_SIZE)
self._table.set_border_width(style.GRID_CELL_SIZE)
self._scrolledwindow = gtk.ScrolledWindow()
@@ -134,8 +137,17 @@ class ControlPanel(gtk.Window):
except ImportError:
del self._options['keyboard']
- row = 0
- column = 2
+ # If the screen width only supports two columns, start
+ # placing from the second row.
+ if self._max_columns == 2:
+ row = 1
+ column = 0
+ else:
+ # About Me and About my computer are hardcoded below to use the
+ # first two slots so we need to leave them free.
+ row = 0
+ column = 2
+
options = self._options.keys()
options.sort()
@@ -157,7 +169,7 @@ class ControlPanel(gtk.Window):
column, column + 1,
row, row + 1)
column += 1
- if column == _MAX_COLUMNS:
+ if column == self._max_columns:
column = 0
row += 1
@@ -214,11 +226,16 @@ class ControlPanel(gtk.Window):
globals(), locals(), ['model'])
model = ModelWrapper(mod)
- self._section_view = view_class(model,
- self._options[option]['alerts'])
+ try:
+ self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ self._section_view = view_class(model,
+ self._options[option]['alerts'])
+
+ self._set_canvas(self._section_view)
+ self._section_view.show()
+ finally:
+ self.get_window().set_cursor(None)
- self._set_canvas(self._section_view)
- self._section_view.show()
self._section_view.connect('notify::is-valid',
self.__valid_section_cb)
self._section_view.connect('request-close',
@@ -227,13 +244,13 @@ class ControlPanel(gtk.Window):
style.COLOR_WHITE.get_gdk_color())
def set_section_view_auto_close(self):
- '''Automatically close the control panel if there is "nothing to do"
- '''
+ """Automatically close the control panel if there is "nothing to do"
+ """
self._section_view.auto_close = True
def _get_options(self):
- '''Get the available option information from the extensions
- '''
+ """Get the available option information from the extensions
+ """
options = {}
path = os.path.join(config.ext_path, 'cpsection')
@@ -259,11 +276,9 @@ class ControlPanel(gtk.Window):
keywords.append(item)
options[item]['keywords'] = keywords
else:
- _logger.error('There is no CLASS constant specifieds ' \
- 'in the view file \'%s\'.' % item)
+ _logger.error('no CLASS attribute in %r', item)
except Exception:
- logging.error('Exception while loading extension:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ logging.exception('Exception while loading extension:')
return options
@@ -333,6 +348,7 @@ class ControlPanel(gtk.Window):
section_is_valid = section_view.props.is_valid
self._section_toolbar.accept_button.set_sensitive(section_is_valid)
+
class ModelWrapper(object):
def __init__(self, module):
self._module = module
@@ -360,18 +376,15 @@ class ModelWrapper(object):
except Exception, detail:
_logger.debug('Error undo option: %s', detail)
+
class _SectionIcon(gtk.EventBox):
- __gtype_name__ = "SugarSectionIcon"
+ __gtype_name__ = 'SugarSectionIcon'
__gproperties__ = {
- 'icon-name' : (str, None, None, None,
- gobject.PARAM_READWRITE),
- 'pixel-size' : (object, None, None,
- gobject.PARAM_READWRITE),
- 'xo-color' : (object, None, None,
- gobject.PARAM_READWRITE),
- 'title' : (str, None, None, None,
- gobject.PARAM_READWRITE)
+ 'icon-name': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'pixel-size': (object, None, None, gobject.PARAM_READWRITE),
+ 'xo-color': (object, None, None, gobject.PARAM_READWRITE),
+ 'title': (str, None, None, None, gobject.PARAM_READWRITE),
}
def __init__(self, **kwargs):
diff --git a/src/jarabe/controlpanel/inlinealert.py b/src/jarabe/controlpanel/inlinealert.py
index b1880da..f970af4 100644
--- a/src/jarabe/controlpanel/inlinealert.py
+++ b/src/jarabe/controlpanel/inlinealert.py
@@ -21,6 +21,7 @@ import pango
from sugar.graphics import style
from sugar.graphics.icon import Icon
+
class InlineAlert(gtk.HBox):
"""UI interface for Inline alerts
@@ -36,11 +37,9 @@ class InlineAlert(gtk.HBox):
__gtype_name__ = 'SugarInlineAlert'
__gproperties__ = {
- 'msg' : (str, None, None, None,
- gobject.PARAM_READWRITE),
- 'icon' : (object, None, None,
- gobject.PARAM_WRITABLE)
- }
+ 'msg': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'icon': (object, None, None, gobject.PARAM_WRITABLE),
+ }
def __init__(self, **kwargs):
@@ -80,4 +79,3 @@ class InlineAlert(gtk.HBox):
def do_get_property(self, pspec):
if pspec.name == 'msg':
return self._msg
-
diff --git a/src/jarabe/controlpanel/sectionview.py b/src/jarabe/controlpanel/sectionview.py
index 4de27a2..4b5751f 100644
--- a/src/jarabe/controlpanel/sectionview.py
+++ b/src/jarabe/controlpanel/sectionview.py
@@ -18,18 +18,17 @@ import gobject
import gtk
from gettext import gettext as _
+
class SectionView(gtk.VBox):
__gtype_name__ = 'SugarSectionView'
__gsignals__ = {
- 'request-close': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([]))
- }
+ 'request-close': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ }
__gproperties__ = {
- 'is_valid' : (bool, None, None, True,
- gobject.PARAM_READWRITE)
- }
+ 'is_valid': (bool, None, None, True, gobject.PARAM_READWRITE),
+ }
_APPLY_TIMEOUT = 1000
@@ -51,5 +50,5 @@ class SectionView(gtk.VBox):
return self._is_valid
def undo(self):
- '''Undo here the changes that have been made in this section.'''
+ """Undo here the changes that have been made in this section."""
pass
diff --git a/src/jarabe/controlpanel/toolbar.py b/src/jarabe/controlpanel/toolbar.py
index 320a8eb..fca34a0 100644
--- a/src/jarabe/controlpanel/toolbar.py
+++ b/src/jarabe/controlpanel/toolbar.py
@@ -25,6 +25,7 @@ from sugar.graphics.toolbutton import ToolButton
from sugar.graphics import iconentry
from sugar.graphics import style
+
class MainToolbar(gtk.Toolbar):
""" Main toolbar of the control panel
"""
@@ -36,8 +37,9 @@ class MainToolbar(gtk.Toolbar):
([])),
'search-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([str]))
+ ([str])),
}
+
def __init__(self):
gtk.Toolbar.__init__(self)
@@ -83,6 +85,7 @@ class MainToolbar(gtk.Toolbar):
def __stop_clicked_cb(self, button):
self.emit('stop-clicked')
+
class SectionToolbar(gtk.Toolbar):
""" Toolbar of the sections of the control panel
"""
@@ -94,8 +97,9 @@ class SectionToolbar(gtk.Toolbar):
([])),
'accept-clicked': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([]))
+ ([])),
}
+
def __init__(self):
gtk.Toolbar.__init__(self)
@@ -154,4 +158,3 @@ class SectionToolbar(gtk.Toolbar):
def __accept_button_clicked_cb(self, widget, data=None):
self.emit('accept-clicked')
-
diff --git a/src/jarabe/desktop/Makefile.am b/src/jarabe/desktop/Makefile.am
index 5b47455..25fb0b4 100644
--- a/src/jarabe/desktop/Makefile.am
+++ b/src/jarabe/desktop/Makefile.am
@@ -11,7 +11,7 @@ sugar_PYTHON = \
homewindow.py \
keydialog.py \
meshbox.py \
- myicon.py \
+ networkviews.py \
schoolserver.py \
snowflakelayout.py \
spreadlayout.py \
diff --git a/src/jarabe/desktop/__init__.py b/src/jarabe/desktop/__init__.py
index a9dd95a..85f6a24 100644
--- a/src/jarabe/desktop/__init__.py
+++ b/src/jarabe/desktop/__init__.py
@@ -13,4 +13,3 @@
# 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/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
index 87f2af0..0370ef3 100644
--- a/src/jarabe/desktop/activitieslist.py
+++ b/src/jarabe/desktop/activitieslist.py
@@ -30,19 +30,18 @@ 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
from jarabe.model import bundleregistry
from jarabe.view.palettes import ActivityPalette
-from jarabe.view import launcher
+from jarabe.journal import misc
+
class ActivitiesTreeView(gtk.TreeView):
__gtype_name__ = 'SugarActivitiesTreeView'
__gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
}
def __init__(self):
@@ -143,13 +142,7 @@ class ActivitiesTreeView(gtk.TreeView):
registry = bundleregistry.get_registry()
bundle = registry.get_bundle(row[ListModel.COLUMN_BUNDLE_ID])
- activity_id = activityfactory.create_activity_id()
-
- client = gconf.client_get_default()
- xo_color = XoColor(client.get_string('/desktop/sugar/user/color'))
-
- launcher.add_launcher(activity_id, bundle.get_icon(), xo_color)
- activityfactory.create(bundle, ActivityHandle(activity_id))
+ misc.launch(bundle)
def set_filter(self, query):
self._query = query.lower()
@@ -159,6 +152,7 @@ class ActivitiesTreeView(gtk.TreeView):
title = model[tree_iter][ListModel.COLUMN_TITLE]
return title is not None and title.lower().find(self._query) > -1
+
class ListModel(gtk.TreeModelSort):
__gtype_name__ = 'SugarListModel'
@@ -172,7 +166,7 @@ class ListModel(gtk.TreeModelSort):
COLUMN_DATE_TEXT = 7
def __init__(self):
- self._model = gtk.ListStore(str, bool, str, str, int, str, int, str)
+ self._model = gtk.ListStore(str, bool, str, str, str, str, int, str)
self._model_filter = self._model.filter_new()
gtk.TreeModelSort.__init__(self, self._model_filter)
@@ -243,6 +237,7 @@ class ListModel(gtk.TreeModelSort):
def refilter(self):
self._model_filter.refilter()
+
class CellRendererFavorite(CellRendererIcon):
__gtype_name__ = 'SugarCellRendererFavorite'
@@ -257,12 +252,13 @@ class CellRendererFavorite(CellRendererIcon):
self.props.prelit_stroke_color = style.COLOR_BUTTON_GREY.get_svg()
self.props.prelit_fill_color = style.COLOR_BUTTON_GREY.get_svg()
+
class CellRendererActivityIcon(CellRendererIcon):
__gtype_name__ = 'SugarCellRendererActivityIcon'
__gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
}
def __init__(self, tree_view):
@@ -295,6 +291,7 @@ class CellRendererActivityIcon(CellRendererIcon):
def __erase_activated_cb(self, palette, bundle_id):
self.emit('erase-activated', bundle_id)
+
class ActivitiesList(gtk.VBox):
__gtype_name__ = 'SugarActivitiesList'
@@ -376,14 +373,15 @@ class ActivitiesList(gtk.VBox):
if response_id == gtk.RESPONSE_OK:
registry = bundleregistry.get_registry()
bundle = registry.get_bundle(bundle_id)
- registry.uninstall(bundle)
+ registry.uninstall(bundle, delete_profile=True)
+
class ActivityListPalette(ActivityPalette):
__gtype_name__ = 'SugarActivityListPalette'
__gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
}
def __init__(self, activity_info):
@@ -406,13 +404,7 @@ class ActivityListPalette(ActivityPalette):
self._favorite_item.show()
if activity_info.is_user_activity():
- 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
+ self._add_erase_option(registry, activity_info)
registry = bundleregistry.get_registry()
self._activity_changed_sid = registry.connect('bundle_changed',
@@ -421,6 +413,16 @@ class ActivityListPalette(ActivityPalette):
self.connect('destroy', self.__destroy_cb)
+ def _add_erase_option(self, registry, activity_info):
+ 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) or \
+ registry.is_activity_protected(self._bundle_id):
+ menu_item.props.sensitive = False
+
def __destroy_cb(self, palette):
self.disconnect(self._activity_changed_sid)
@@ -433,7 +435,7 @@ class ActivityListPalette(ActivityPalette):
else:
label.set_text(_('Make favorite'))
client = gconf.client_get_default()
- xo_color = XoColor(client.get_string("/desktop/sugar/user/color"))
+ xo_color = XoColor(client.get_string('/desktop/sugar/user/color'))
self._favorite_icon.props.xo_color = xo_color
@@ -453,4 +455,3 @@ class ActivityListPalette(ActivityPalette):
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 85e1b59..360c147 100644
--- a/src/jarabe/desktop/favoriteslayout.py
+++ b/src/jarabe/desktop/favoriteslayout.py
@@ -1,4 +1,5 @@
# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2010 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
@@ -28,10 +29,18 @@ from sugar.graphics import style
from jarabe.model import bundleregistry
from jarabe.desktop.grid import Grid
+
_logger = logging.getLogger('FavoritesLayout')
_CELL_SIZE = 4
_BASE_SCALE = 1000
+_INTERMEDIATE_B = (style.STANDARD_ICON_SIZE + style.SMALL_ICON_SIZE) / 2
+_INTERMEDIATE_A = (style.STANDARD_ICON_SIZE + _INTERMEDIATE_B) / 2
+_INTERMEDIATE_C = (_INTERMEDIATE_B + style.SMALL_ICON_SIZE) / 2
+_ICON_SIZES = [style.MEDIUM_ICON_SIZE, style.STANDARD_ICON_SIZE,
+ _INTERMEDIATE_A, _INTERMEDIATE_B, _INTERMEDIATE_C,
+ style.SMALL_ICON_SIZE]
+
class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
"""Base class of the different layout types."""
@@ -81,7 +90,8 @@ class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
if icon not in self.box.get_children():
raise ValueError('Child not in box.')
- if not(hasattr(icon, 'get_bundle_id') and hasattr(icon, 'get_version')):
+ if not (hasattr(icon, 'get_bundle_id') and
+ hasattr(icon, 'get_version')):
logging.debug('Not an activity icon %r', icon)
return
@@ -101,6 +111,7 @@ class FavoritesLayout(gobject.GObject, hippo.CanvasLayout):
def allow_dnd(self):
return False
+
class RandomLayout(FavoritesLayout):
"""Lay out icons randomly; try to nudge them around to resolve overlaps."""
@@ -181,13 +192,19 @@ class RandomLayout(FavoritesLayout):
def allow_dnd(self):
return True
+
_MINIMUM_RADIUS = style.XLARGE_ICON_SIZE / 2 + style.DEFAULT_SPACING + \
style.STANDARD_ICON_SIZE * 2
_MAXIMUM_RADIUS = (gtk.gdk.screen_height() - style.GRID_CELL_SIZE) / 2 - \
style.STANDARD_ICON_SIZE - style.DEFAULT_SPACING
+_ICON_SPACING_FACTORS = [1.5, 1.4, 1.3, 1.2, 1.1, 1.0]
+_SPIRAL_SPACING_FACTORS = [1.5, 1.5, 1.5, 1.4, 1.3, 1.2]
+_MIMIMUM_RADIUS_ENCROACHMENT = 0.75
+_INITIAL_ANGLE = math.pi
+
class RingLayout(FavoritesLayout):
- """Lay out icons in a ring around the XO man."""
+ """Lay out icons in a ring or spiral around the XO man."""
__gtype_name__ = 'RingLayout'
icon_name = 'view-radial'
@@ -201,6 +218,7 @@ class RingLayout(FavoritesLayout):
def __init__(self):
FavoritesLayout.__init__(self)
self._locked_children = {}
+ self._spiral_mode = False
def append(self, icon, locked=False):
FavoritesLayout.append(self, icon, locked)
@@ -221,33 +239,78 @@ class RingLayout(FavoritesLayout):
self._locked_children[child] = (x, y)
def _calculate_radius_and_icon_size(self, children_count):
- # what's the radius required without downscaling?
- distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING
+ """ Adjust the ring or spiral radius and icon size as needed. """
+ self._spiral_mode = False
+ distance = style.MEDIUM_ICON_SIZE + style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.MEDIUM_ICON_SIZE)]
+ radius = max(children_count * distance / (2 * math.pi),
+ _MINIMUM_RADIUS)
+ if radius < _MAXIMUM_RADIUS:
+ return radius, style.MEDIUM_ICON_SIZE
+
+ distance = style.STANDARD_ICON_SIZE + style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(style.STANDARD_ICON_SIZE)]
+ radius = max(children_count * distance / (2 * math.pi),
+ _MINIMUM_RADIUS)
+ if radius < _MAXIMUM_RADIUS:
+ return radius, style.STANDARD_ICON_SIZE
+
+ self._spiral_mode = True
icon_size = style.STANDARD_ICON_SIZE
- # circumference is 2*pi*r; we want this to be at least
- # 'children_count * distance'
- radius = children_count * distance / (2 * math.pi)
- # limit computed radius to reasonable bounds.
- radius = max(radius, _MINIMUM_RADIUS)
- radius = min(radius, _MAXIMUM_RADIUS)
- # recompute icon size from limited radius
- if children_count > 0:
- icon_size = (2 * math.pi * radius / children_count) \
- - style.DEFAULT_SPACING
- # limit adjusted icon size.
- icon_size = max(icon_size, style.SMALL_ICON_SIZE)
- icon_size = min(icon_size, style.MEDIUM_ICON_SIZE)
+ angle_, radius = self._calculate_angle_and_radius(children_count,
+ icon_size)
+ while radius > _MAXIMUM_RADIUS:
+ i = _ICON_SIZES.index(icon_size)
+ if i < len(_ICON_SIZES) - 1:
+ icon_size = _ICON_SIZES[i + 1]
+ angle_, radius = self._calculate_angle_and_radius(
+ children_count, icon_size)
+ else:
+ break
return radius, icon_size
- def _calculate_position(self, radius, icon_size, index, children_count,
- sin=math.sin, cos=math.cos):
+ def _calculate_position(self, radius, icon_size, icon_index,
+ children_count, sin=math.sin, cos=math.cos):
+ """ Calculate an icon position on a circle or a spiral. """
width, height = self.box.get_allocation()
- angle = index * (2 * math.pi / children_count) - math.pi / 2
- x = radius * cos(angle) + (width - icon_size) / 2
- y = radius * sin(angle) + (height - icon_size -
- (style.GRID_CELL_SIZE/2) ) / 2
+ if self._spiral_mode:
+ min_width_, box_width = self.box.get_width_request()
+ min_height_, box_height = self.box.get_height_request(box_width)
+ angle, radius = self._calculate_angle_and_radius(icon_index,
+ icon_size)
+ x, y = self._convert_from_polar_to_cartesian(angle, radius,
+ icon_size,
+ width, height)
+ else:
+ angle = icon_index * (2 * math.pi / children_count) - math.pi / 2
+ x = radius * cos(angle) + (width - icon_size) / 2
+ y = radius * sin(angle) + (height - icon_size - \
+ (style.GRID_CELL_SIZE / 2)) / 2
+ return x, y
+
+ def _convert_from_polar_to_cartesian(self, angle, radius, icon_size, width,
+ height):
+ """ Convert angle, radius to x, y """
+ x = int(math.sin(angle) * radius)
+ y = int(math.cos(angle) * radius)
+ x = - x + (width - icon_size) / 2
+ y = y + (height - icon_size - (style.GRID_CELL_SIZE / 2)) / 2
return x, y
+ def _calculate_angle_and_radius(self, icon_count, icon_size):
+ """ Based on icon_count and icon_size, calculate radius and angle. """
+ spiral_spacing = _SPIRAL_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
+ icon_spacing = icon_size + style.DEFAULT_SPACING * \
+ _ICON_SPACING_FACTORS[_ICON_SIZES.index(icon_size)]
+ angle = _INITIAL_ANGLE
+ radius = _MINIMUM_RADIUS - (icon_size * _MIMIMUM_RADIUS_ENCROACHMENT)
+ for i_ in range(icon_count):
+ circumference = radius * 2 * math.pi
+ n = circumference / icon_spacing
+ angle += (2 * math.pi / n)
+ radius += (float(icon_spacing) * spiral_spacing / n)
+ return angle, radius
+
def _get_children_in_ring(self):
children_in_ring = [child for child in self.box.get_layout_children() \
if child not in self._locked_children]
@@ -294,6 +357,7 @@ class RingLayout(FavoritesLayout):
else:
return 0
+
_SUNFLOWER_CONSTANT = style.STANDARD_ICON_SIZE * .75
"""Chose a constant such that STANDARD_ICON_SIZE icons are nicely spaced."""
@@ -319,6 +383,7 @@ This is the golden angle: http://en.wikipedia.org/wiki/Golden_angle
Calculation: math.radians(360) / ( _GOLDEN_RATIO * _GOLDEN_RATIO )
"""
+
class SunflowerLayout(RingLayout):
"""Spiral layout based on Fibonacci ratio in phyllotaxis.
@@ -346,7 +411,8 @@ class SunflowerLayout(RingLayout):
return None, style.STANDARD_ICON_SIZE
def adjust_index(self, i):
- """Skip floret indices which end up outside the desired bounding box."""
+ """Skip floret indices which end up outside the desired bounding box.
+ """
for idx in self.skipped_indices:
if i < idx:
break
@@ -377,7 +443,7 @@ class SunflowerLayout(RingLayout):
# removed to make room for the "active activity" icon.
x = r * cos(phi) + (width - icon_size) / 2
y = r * sin(phi) + (height - icon_size - \
- (style.GRID_CELL_SIZE / 2) ) / 2
+ (style.GRID_CELL_SIZE / 2)) / 2
# skip allocations outside the allocation box.
# give up once we can't fit
@@ -385,10 +451,12 @@ class SunflowerLayout(RingLayout):
if y < 0 or y > (height - icon_size) or \
x < 0 or x > (width - icon_size):
self.skipped_indices.append(index)
- continue # try again
+ # try again
+ continue
return x, y
+
class BoxLayout(RingLayout):
"""Lay out icons in a square around the XO man."""
@@ -421,14 +489,16 @@ class BoxLayout(RingLayout):
return (90 - d) / 45.
if d < 225:
return -1
- return cos_d(360 - d) # mirror around 180
+ # mirror around 180
+ return cos_d(360 - d)
cos = lambda r: cos_d(math.degrees(r))
sin = lambda r: cos_d(math.degrees(r) - 90)
- return RingLayout._calculate_position\
- (self, radius, icon_size, index, children_count,
- sin=sin, cos=cos)
+ return RingLayout._calculate_position(self, radius, icon_size, index,
+ children_count, sin=sin,
+ cos=cos)
+
class TriangleLayout(RingLayout):
"""Lay out icons in a triangle around the XO man."""
@@ -467,7 +537,8 @@ class TriangleLayout(RingLayout):
return (d + 90) / 120.
if d <= 90:
return (90 - d) / 60.
- return -cos_d(180 - d) # mirror around 90
+ # mirror around 90
+ return -cos_d(180 - d)
sqrt_3 = math.sqrt(3)
@@ -478,11 +549,12 @@ class TriangleLayout(RingLayout):
return ((d + 90) / 120.) * sqrt_3 - 1
if d <= 90:
return sqrt_3 - 1
- return sin_d(180 - d) # mirror around 90
+ # mirror around 90
+ return sin_d(180 - d)
cos = lambda r: cos_d(math.degrees(r))
sin = lambda r: sin_d(math.degrees(r))
- return RingLayout._calculate_position\
- (self, radius, icon_size, index, children_count,
- sin=sin, cos=cos)
+ return RingLayout._calculate_position(self, radius, icon_size, index,
+ children_count, sin=sin,
+ cos=cos)
diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py
index aca945a..b4a4e75 100644
--- a/src/jarabe/desktop/favoritesview.py
+++ b/src/jarabe/desktop/favoritesview.py
@@ -30,25 +30,23 @@ from sugar.graphics.menuitem import MenuItem
from sugar.graphics.alert import Alert
from sugar.graphics.xocolor import XoColor
from sugar.activity import activityfactory
-from sugar.activity.activityhandle import ActivityHandle
-from sugar.presence import presenceservice
from sugar import dispatch
from sugar.datastore import datastore
from jarabe.view.palettes import JournalPalette
from jarabe.view.palettes import CurrentActivityPalette, ActivityPalette
+from jarabe.view.buddyicon import BuddyIcon
from jarabe.view.buddymenu import BuddyMenu
-from jarabe.view import launcher
-from jarabe.model.buddy import BuddyModel
+from jarabe.model.buddy import get_owner_instance
from jarabe.model import shell
from jarabe.model import bundleregistry
from jarabe.journal import misc
from jarabe.desktop import schoolserver
from jarabe.desktop.schoolserver import RegisterError
-from jarabe.desktop.myicon import MyIcon
from jarabe.desktop import favoriteslayout
+
_logger = logging.getLogger('FavoritesView')
_ICON_DND_TARGET = ('activity-icon', gtk.TARGET_SAME_WIDGET, 0)
@@ -62,6 +60,9 @@ LAYOUT_MAP = {favoriteslayout.RingLayout.key: favoriteslayout.RingLayout,
`FavoritesLayout` which implement the layouts. Additional information
about the layout can be accessed with fields of the class."""
+_favorites_settings = None
+
+
class FavoritesView(hippo.Canvas):
__gtype_name__ = 'SugarFavoritesView'
@@ -82,7 +83,7 @@ class FavoritesView(hippo.Canvas):
self._box.props.background_color = style.COLOR_WHITE.get_int()
self.set_root(self._box)
- self._my_icon = _MyIcon(style.XLARGE_ICON_SIZE)
+ self._my_icon = OwnerIcon(style.XLARGE_ICON_SIZE)
self._my_icon.connect('register-activate', self.__register_activate_cb)
self._box.append(self._my_icon)
@@ -172,7 +173,8 @@ class FavoritesView(hippo.Canvas):
height = allocation.height
min_w_, my_icon_width = self._my_icon.get_width_request()
- min_h_, my_icon_height = self._my_icon.get_height_request(my_icon_width)
+ min_h_, my_icon_height = self._my_icon.get_height_request(
+ my_icon_width)
x = (width - my_icon_width) / 2
y = (height - my_icon_height - style.GRID_CELL_SIZE) / 2
self._layout.move_icon(self._my_icon, x, y, locked=True)
@@ -190,7 +192,8 @@ class FavoritesView(hippo.Canvas):
# TODO: Dnd methods. This should be merged somehow inside hippo-canvas.
def __button_press_event_cb(self, widget, event):
if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS:
- self._last_clicked_icon = self._get_icon_at_coords(event.x, event.y)
+ self._last_clicked_icon = self._get_icon_at_coords(event.x,
+ event.y)
if self._last_clicked_icon is not None:
self._pressed_button = event.button
self._press_start_x = event.x
@@ -203,9 +206,9 @@ class FavoritesView(hippo.Canvas):
icon_x, icon_y = icon.get_context().translate_to_widget(icon)
icon_width, icon_height = icon.get_allocation()
- if (x >= icon_x ) and (x <= icon_x + icon_width) and \
- (y >= icon_y ) and (y <= icon_y + icon_height) and \
- isinstance(icon, ActivityIcon):
+ if (x >= icon_x) and (x <= icon_x + icon_width) and \
+ (y >= icon_y) and (y <= icon_y + icon_height) and \
+ isinstance(icon, ActivityIcon):
return icon
return None
@@ -274,7 +277,7 @@ class FavoritesView(hippo.Canvas):
def _set_layout(self, layout):
if layout not in LAYOUT_MAP:
- logging.warn('Unknown favorites layout: %r' % layout)
+ logging.warn('Unknown favorites layout: %r', layout)
layout = favoriteslayout.RingLayout.key
assert layout in LAYOUT_MAP
@@ -327,7 +330,7 @@ class FavoritesView(hippo.Canvas):
alert.props.title = _('Registration Successful')
alert.props.msg = _('You are now registered ' \
'with your school server.')
- self._my_icon.remove_register_menu()
+ self._my_icon.set_registered()
ok_icon = Icon(icon_name='dialog-ok')
alert.add_button(gtk.RESPONSE_OK, _('Ok'), ok_icon)
@@ -387,7 +390,7 @@ class ActivityIcon(CanvasIcon):
break
def _get_last_activity_async(self, bundle_id, properties):
- query = {'activity': bundle_id}
+ query = {'activity': bundle_id}
datastore.find(query, sorting=['+timestamp'],
limit=self._MAX_RESUME_ENTRIES,
properties=properties,
@@ -395,8 +398,8 @@ class ActivityIcon(CanvasIcon):
error_handler=self.__get_last_activity_error_handler_cb)
def __get_last_activity_reply_handler_cb(self, entries, total_count):
- # If there's a problem with the DS index, we may get entries not related
- # to this activity.
+ # If there's a problem with the DS index, we may get entries not
+ # related to this activity.
checked_entries = []
for entry in entries:
if entry['activity'] == self.bundle_id:
@@ -484,16 +487,7 @@ class ActivityIcon(CanvasIcon):
if self._resume_mode and self._journal_entries:
self._resume(self._journal_entries[0])
else:
- client = gconf.client_get_default()
- xo_color = XoColor(client.get_string('/desktop/sugar/user/color'))
-
- activity_id = activityfactory.create_activity_id()
- launcher.add_launcher(activity_id,
- self._activity_info.get_icon(),
- xo_color)
-
- handle = ActivityHandle(activity_id)
- activityfactory.create(self._activity_info, handle)
+ misc.launch(self._activity_info)
def get_bundle_id(self):
return self._activity_info.get_bundle_id()
@@ -516,6 +510,7 @@ class ActivityIcon(CanvasIcon):
self._resume_mode = resume_mode
self._update()
+
class FavoritePalette(ActivityPalette):
__gtype_name__ = 'SugarFavoritePalette'
@@ -564,6 +559,7 @@ class FavoritePalette(ActivityPalette):
if entry is not None:
self.emit('entry-activate', entry)
+
class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
def __init__(self):
CanvasIcon.__init__(self, cache=True)
@@ -602,17 +598,18 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
self._home_activity = home_activity
self._update()
-class _MyIcon(MyIcon):
- __gtype_name__ = 'SugarFavoritesMyIcon'
+
+class OwnerIcon(BuddyIcon):
+ __gtype_name__ = 'SugarFavoritesOwnerIcon'
__gsignals__ = {
- 'register-activate' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([]))
+ 'register-activate': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([])),
}
- def __init__(self, scale):
- MyIcon.__init__(self, scale)
- self._power_manager = None
+ def __init__(self, size):
+ BuddyIcon.__init__(self, buddy=get_owner_instance(), size=size)
+
self._palette_enabled = False
self._register_menu = None
@@ -621,17 +618,20 @@ class _MyIcon(MyIcon):
self._palette_enabled = True
return
- presence_service = presenceservice.get_instance()
- owner = BuddyModel(buddy=presence_service.get_owner())
- palette = BuddyMenu(owner)
+ palette = BuddyMenu(get_owner_instance())
client = gconf.client_get_default()
backup_url = client.get_string('/desktop/sugar/backup_url')
+
if not backup_url:
self._register_menu = MenuItem(_('Register'), 'media-record')
- self._register_menu.connect('activate', self.__register_activate_cb)
- palette.menu.append(self._register_menu)
- self._register_menu.show()
+ else:
+ self._register_menu = MenuItem(_('Register again'),
+ 'media-record')
+
+ self._register_menu.connect('activate', self.__register_activate_cb)
+ palette.menu.append(self._register_menu)
+ self._register_menu.show()
return palette
@@ -641,12 +641,17 @@ class _MyIcon(MyIcon):
def __register_activate_cb(self, menuitem):
self.emit('register-activate')
- def remove_register_menu(self):
+ def set_registered(self):
self.palette.menu.remove(self._register_menu)
+ self._register_menu = MenuItem(_('Register again'), 'media-record')
+ self._register_menu.connect('activate', self.__register_activate_cb)
+ self.palette.menu.append(self._register_menu)
+ self._register_menu.show()
+
class FavoritesSetting(object):
- _FAVORITES_KEY = "/desktop/sugar/desktop/favorites_layout"
+ _FAVORITES_KEY = '/desktop/sugar/desktop/favorites_layout'
def __init__(self):
client = gconf.client_get_default()
@@ -672,7 +677,6 @@ class FavoritesSetting(object):
layout = property(get_layout, set_layout)
-_favorites_settings = None
def get_settings():
global _favorites_settings
diff --git a/src/jarabe/desktop/friendview.py b/src/jarabe/desktop/friendview.py
index 4c5f1c8..8dab35f 100644
--- a/src/jarabe/desktop/friendview.py
+++ b/src/jarabe/desktop/friendview.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -18,17 +19,15 @@ import hippo
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
-from sugar.presence import presenceservice
from jarabe.view.buddyicon import BuddyIcon
from jarabe.model import bundleregistry
+
class FriendView(hippo.CanvasBox):
def __init__(self, buddy, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
- self._pservice = presenceservice.get_instance()
-
self._buddy = buddy
self._buddy_icon = BuddyIcon(buddy)
self._buddy_icon.props.size = style.LARGE_ICON_SIZE
@@ -37,14 +36,12 @@ class FriendView(hippo.CanvasBox):
self._activity_icon = CanvasIcon(size=style.LARGE_ICON_SIZE)
self._activity_icon_visible = False
- if self._buddy.is_present():
- self._buddy_appeared_cb(buddy)
+ self._update_activity()
- self._buddy.connect('current-activity-changed',
- self._buddy_activity_changed_cb)
- self._buddy.connect('appeared', self._buddy_appeared_cb)
- self._buddy.connect('disappeared', self._buddy_disappeared_cb)
- self._buddy.connect('color-changed', self._buddy_color_changed_cb)
+ self._buddy.connect('notify::current-activity',
+ self.__buddy_notify_current_activity_cb)
+ self._buddy.connect('notify::present', self.__buddy_notify_present_cb)
+ self._buddy.connect('notify::color', self.__buddy_notify_color_cb)
def _get_new_icon_name(self, ps_activity):
registry = bundleregistry.get_registry()
@@ -58,30 +55,30 @@ class FriendView(hippo.CanvasBox):
self.remove(self._activity_icon)
self._activity_icon_visible = False
- def _buddy_activity_changed_cb(self, buddy, ps_activity=None):
- if not ps_activity:
+ def __buddy_notify_current_activity_cb(self, buddy, pspec):
+ self._update_activity()
+
+ def _update_activity(self):
+ if not self._buddy.props.present or \
+ not self._buddy.props.current_activity:
self._remove_activity_icon()
return
# FIXME: use some sort of "unknown activity" icon rather
# than hiding the icon?
- name = self._get_new_icon_name(ps_activity)
+ name = self._get_new_icon_name(self._buddy.current_activity)
if name:
self._activity_icon.props.file_name = name
- self._activity_icon.props.xo_color = buddy.get_color()
+ self._activity_icon.props.xo_color = self._buddy.props.color
if not self._activity_icon_visible:
self.append(self._activity_icon, hippo.PACK_EXPAND)
self._activity_icon_visible = True
else:
self._remove_activity_icon()
- def _buddy_appeared_cb(self, buddy):
- home_activity = self._buddy.get_current_activity()
- self._buddy_activity_changed_cb(buddy, home_activity)
-
- def _buddy_disappeared_cb(self, buddy):
- self._buddy_activity_changed_cb(buddy, None)
+ def __buddy_notify_present_cb(self, buddy, pspec):
+ self._update_activity()
- def _buddy_color_changed_cb(self, buddy, color):
+ def __buddy_notify_color_cb(self, buddy, pspec):
# TODO: shouldn't this change self._buddy_icon instead?
- self._activity_icon.props.xo_color = buddy.get_color()
+ self._activity_icon.props.xo_color = buddy.props.color
diff --git a/src/jarabe/desktop/grid.py b/src/jarabe/desktop/grid.py
index f3412c9..eab4033 100644
--- a/src/jarabe/desktop/grid.py
+++ b/src/jarabe/desktop/grid.py
@@ -22,17 +22,19 @@ import gtk
from sugar import _sugarext
+
_PLACE_TRIALS = 20
_MAX_WEIGHT = 255
_REFRESH_RATE = 200
_MAX_COLLISIONS_PER_REFRESH = 20
+
class Grid(_sugarext.Grid):
__gsignals__ = {
- 'child-changed' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
+ 'child-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
}
+
def __init__(self, width, height):
gobject.GObject.__init__(self)
@@ -185,7 +187,8 @@ class Grid(_sugarext.Grid):
for c in self._children:
intersection = child_rect.intersect(self._child_rects[c])
if c != child and intersection.width > 0:
- if c not in self._locked_children and c not in self._collisions:
+ if (c not in self._locked_children and
+ c not in self._collisions):
collision_found = True
self._collisions.append(c)
diff --git a/src/jarabe/desktop/groupbox.py b/src/jarabe/desktop/groupbox.py
index 76c2981..ed8f8ae 100644
--- a/src/jarabe/desktop/groupbox.py
+++ b/src/jarabe/desktop/groupbox.py
@@ -23,18 +23,19 @@ import gconf
from sugar.graphics import style
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.xocolor import XoColor
-from sugar.presence import presenceservice
from jarabe.view.buddymenu import BuddyMenu
-from jarabe.model.buddy import BuddyModel
+from jarabe.model.buddy import get_owner_instance
from jarabe.model import friends
from jarabe.desktop.friendview import FriendView
from jarabe.desktop.spreadlayout import SpreadLayout
+
class GroupBox(hippo.Canvas):
__gtype_name__ = 'SugarGroupBox'
+
def __init__(self):
- logging.debug("STARTUP: Loading the group view")
+ logging.debug('STARTUP: Loading the group view')
gobject.GObject.__init__(self)
@@ -48,15 +49,13 @@ class GroupBox(hippo.Canvas):
self._box.set_layout(self._layout)
client = gconf.client_get_default()
- color = XoColor(client.get_string("/desktop/sugar/user/color"))
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
self._owner_icon = CanvasIcon(icon_name='computer-xo', cache=True,
xo_color=color)
self._owner_icon.props.size = style.LARGE_ICON_SIZE
- presence_service = presenceservice.get_instance()
- owner = BuddyModel(buddy=presence_service.get_owner())
- self._owner_icon.set_palette(BuddyMenu(owner))
+ self._owner_icon.set_palette(BuddyMenu(get_owner_instance()))
self._layout.add(self._owner_icon)
friends_model = friends.get_model()
diff --git a/src/jarabe/desktop/homebox.py b/src/jarabe/desktop/homebox.py
index 85279ff..661326e 100644
--- a/src/jarabe/desktop/homebox.py
+++ b/src/jarabe/desktop/homebox.py
@@ -30,16 +30,18 @@ from sugar.graphics.icon import Icon
from jarabe.desktop import favoritesview
from jarabe.desktop.activitieslist import ActivitiesList
+
_FAVORITES_VIEW = 0
_LIST_VIEW = 1
_AUTOSEARCH_TIMEOUT = 1000
+
class HomeBox(gtk.VBox):
__gtype_name__ = 'SugarHomeBox'
def __init__(self):
- logging.debug("STARTUP: Loading the home view")
+ logging.debug('STARTUP: Loading the home view')
gobject.GObject.__init__(self)
@@ -57,7 +59,7 @@ class HomeBox(gtk.VBox):
def show_software_updates_alert(self):
alert = Alert()
updater_icon = Icon(icon_name='module-updater',
- pixel_size = style.STANDARD_ICON_SIZE)
+ pixel_size=style.STANDARD_ICON_SIZE)
alert.props.icon = updater_icon
updater_icon.show()
alert.props.title = _('Software Update')
@@ -125,7 +127,7 @@ class HomeBox(gtk.VBox):
else:
raise ValueError('Invalid view: %r' % view)
- _REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
+ _REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
def resume(self):
pass
@@ -144,16 +146,15 @@ class HomeBox(gtk.VBox):
def set_resume_mode(self, resume_mode):
self._favorites_view.set_resume_mode(resume_mode)
+
class HomeToolbar(gtk.Toolbar):
__gtype_name__ = 'SugarHomeToolbar'
__gsignals__ = {
- 'query-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
+ 'query-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([str])),
- 'view-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
+ 'view-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
@@ -239,13 +240,14 @@ class HomeToolbar(gtk.Toolbar):
if self._autosearch_timer:
gobject.source_remove(self._autosearch_timer)
self._autosearch_timer = gobject.timeout_add(_AUTOSEARCH_TIMEOUT,
- self.__autosearch_timer_cb)
+ self.__autosearch_timer_cb)
def __autosearch_timer_cb(self):
self._autosearch_timer = None
self.search_entry.activate()
return False
+
class FavoritesButton(RadioToolButton):
__gtype_name__ = 'SugarFavoritesButton'
@@ -295,4 +297,3 @@ class FavoritesButton(RadioToolButton):
def _update_icon(self):
self.props.named_icon = favoritesview.LAYOUT_MAP[self._layout]\
.icon_name
-
diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py
index d830ed0..07deff7 100644
--- a/src/jarabe/desktop/homewindow.py
+++ b/src/jarabe/desktop/homewindow.py
@@ -16,6 +16,7 @@
import logging
+import gobject
import gtk
from sugar.graphics import style
@@ -28,11 +29,15 @@ from jarabe.desktop.transitionbox import TransitionBox
from jarabe.model.shell import ShellModel
from jarabe.model import shell
-_HOME_PAGE = 0
-_GROUP_PAGE = 1
-_MESH_PAGE = 2
+
+_HOME_PAGE = 0
+_GROUP_PAGE = 1
+_MESH_PAGE = 2
_TRANSITION_PAGE = 3
+_instance = None
+
+
class HomeWindow(gtk.Window):
def __init__(self):
logging.debug('STARTUP: Loading the desktop window')
@@ -45,8 +50,10 @@ class HomeWindow(gtk.Window):
self._active = False
self._fully_obscured = True
- self.set_default_size(gtk.gdk.screen_width(),
- gtk.gdk.screen_height())
+ screen = self.get_screen()
+ screen.connect('size-changed', self.__screen_size_change_cb)
+ self.set_default_size(screen.get_width(),
+ screen.get_height())
self.realize()
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP)
@@ -73,13 +80,16 @@ class HomeWindow(gtk.Window):
self.__zoom_level_changed_cb)
def _deactivate_view(self, level):
- group = palettegroup.get_group("default")
+ group = palettegroup.get_group('default')
group.popdown()
if level == ShellModel.ZOOM_HOME:
self._home_box.suspend()
elif level == ShellModel.ZOOM_MESH:
self._mesh_box.suspend()
+ def __screen_size_change_cb(self, screen):
+ self.resize(screen.get_width(), screen.get_height())
+
def _activate_view(self, level):
if level == ShellModel.ZOOM_HOME:
self._home_box.resume()
@@ -178,11 +188,22 @@ class HomeWindow(gtk.Window):
def get_home_box(self):
return self._home_box
-_instance = None
+ def busy_during_delayed_action(self, action):
+ """Use busy cursor during execution of action, scheduled via idle_add.
+ """
+ def action_wrapper(old_cursor):
+ try:
+ action()
+ finally:
+ self.get_window().set_cursor(old_cursor)
+
+ old_cursor = self.get_window().get_cursor()
+ self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ gobject.idle_add(action_wrapper, old_cursor)
+
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 1e6d17a..6241b9b 100644
--- a/src/jarabe/desktop/keydialog.py
+++ b/src/jarabe/desktop/keydialog.py
@@ -24,8 +24,14 @@ import dbus
from jarabe.model import network
from jarabe.model.network import Secrets
+
IW_AUTH_ALG_OPEN_SYSTEM = 'open'
-IW_AUTH_ALG_SHARED_KEY = 'shared'
+IW_AUTH_ALG_SHARED_KEY = 'shared'
+
+WEP_PASSPHRASE = 1
+WEP_HEX = 2
+WEP_ASCII = 3
+
def string_is_hex(key):
is_hex = True
@@ -34,6 +40,7 @@ def string_is_hex(key):
is_hex = False
return is_hex
+
def string_is_ascii(string):
try:
string.encode('ascii')
@@ -41,12 +48,14 @@ def string_is_ascii(string):
except UnicodeEncodeError:
return False
+
def string_to_hex(passphrase):
key = ''
for c in passphrase:
key += '%02x' % ord(c)
return key
+
def hash_passphrase(passphrase):
# passphrase must have a length of 64
if len(passphrase) > 64:
@@ -57,16 +66,18 @@ def hash_passphrase(passphrase):
passphrase = hashlib.md5(passphrase).digest()
return string_to_hex(passphrase)[:26]
+
class CanceledKeyRequestError(dbus.DBusException):
def __init__(self):
dbus.DBusException.__init__(self)
self._dbus_error_name = network.NM_SETTINGS_IFACE + '.CanceledError'
+
class KeyDialog(gtk.Dialog):
def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, settings,
response):
gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL)
- self.set_title("Wireless Key Required")
+ self.set_title('Wireless Key Required')
self._settings = settings
self._response = response
@@ -108,9 +119,6 @@ class KeyDialog(gtk.Dialog):
def get_response_object(self):
return self._response
-WEP_PASSPHRASE = 1
-WEP_HEX = 2
-WEP_ASCII = 3
class WEPKeyDialog(KeyDialog):
def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, settings,
@@ -120,9 +128,9 @@ class WEPKeyDialog(KeyDialog):
# WEP key type
self.key_store = gtk.ListStore(str, int)
- self.key_store.append(["Passphrase (128-bit)", WEP_PASSPHRASE])
- self.key_store.append(["Hex (40/128-bit)", WEP_HEX])
- self.key_store.append(["ASCII (40/128-bit)", WEP_ASCII])
+ self.key_store.append(['Passphrase (128-bit)', WEP_PASSPHRASE])
+ self.key_store.append(['Hex (40/128-bit)', WEP_HEX])
+ self.key_store.append(['ASCII (40/128-bit)', WEP_ASCII])
self.key_combo = gtk.ComboBox(self.key_store)
cell = gtk.CellRendererText()
@@ -132,7 +140,7 @@ class WEPKeyDialog(KeyDialog):
self.key_combo.connect('changed', self._key_combo_changed_cb)
hbox = gtk.HBox()
- hbox.pack_start(gtk.Label(_("Key Type:")))
+ hbox.pack_start(gtk.Label(_('Key Type:')))
hbox.pack_start(self.key_combo)
hbox.show_all()
self.vbox.pack_start(hbox)
@@ -142,8 +150,8 @@ class WEPKeyDialog(KeyDialog):
# WEP authentication mode
self.auth_store = gtk.ListStore(str, str)
- self.auth_store.append(["Open System", IW_AUTH_ALG_OPEN_SYSTEM])
- self.auth_store.append(["Shared Key", IW_AUTH_ALG_SHARED_KEY])
+ self.auth_store.append(['Open System', IW_AUTH_ALG_OPEN_SYSTEM])
+ self.auth_store.append(['Shared Key', IW_AUTH_ALG_SHARED_KEY])
self.auth_combo = gtk.ComboBox(self.auth_store)
cell = gtk.CellRendererText()
@@ -152,7 +160,7 @@ class WEPKeyDialog(KeyDialog):
self.auth_combo.set_active(0)
hbox = gtk.HBox()
- hbox.pack_start(gtk.Label(_("Authentication Type:")))
+ hbox.pack_start(gtk.Label(_('Authentication Type:')))
hbox.pack_start(self.auth_combo)
hbox.show_all()
@@ -179,8 +187,8 @@ class WEPKeyDialog(KeyDialog):
def print_security(self):
(key, auth_alg) = self._get_security()
- print "Key: %s" % key
- print "Auth: %d" % auth_alg
+ print 'Key: %s' % key
+ print 'Auth: %d' % auth_alg
def create_security(self):
(key, auth_alg) = self._get_security()
@@ -209,6 +217,7 @@ class WEPKeyDialog(KeyDialog):
self.set_response_sensitive(gtk.RESPONSE_OK, valid)
+
class WPAKeyDialog(KeyDialog):
def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, settings,
response):
@@ -217,7 +226,7 @@ class WPAKeyDialog(KeyDialog):
self.add_key_entry()
self.store = gtk.ListStore(str)
- self.store.append([_("WPA & WPA2 Personal")])
+ self.store.append([_('WPA & WPA2 Personal')])
self.combo = gtk.ComboBox(self.store)
cell = gtk.CellRendererText()
@@ -226,7 +235,7 @@ class WPAKeyDialog(KeyDialog):
self.combo.set_active(0)
self.hbox = gtk.HBox()
- self.hbox.pack_start(gtk.Label(_("Wireless Security:")))
+ self.hbox.pack_start(gtk.Label(_('Wireless Security:')))
self.hbox.pack_start(self.combo)
self.hbox.show_all()
@@ -246,21 +255,21 @@ class WPAKeyDialog(KeyDialog):
from subprocess import Popen, PIPE
p = Popen(['wpa_passphrase', ssid, key], stdout=PIPE)
for line in p.stdout:
- if line.strip().startswith("psk="):
+ if line.strip().startswith('psk='):
real_key = line.strip()[4:]
if p.wait() != 0:
- raise RuntimeError("Error hashing passphrase")
+ raise RuntimeError('Error hashing passphrase')
if real_key and len(real_key) != 64:
real_key = None
if not real_key:
- raise RuntimeError("Invalid key")
+ raise RuntimeError('Invalid key')
return real_key
def print_security(self):
key = self._get_security()
- print "Key: %s" % key
+ print 'Key: %s' % key
def create_security(self):
secrets = Secrets(self._settings)
@@ -281,6 +290,7 @@ class WPAKeyDialog(KeyDialog):
self.set_response_sensitive(gtk.RESPONSE_OK, valid)
return False
+
def create(ssid, flags, wpa_flags, rsn_flags, dev_caps, settings, response):
if wpa_flags == network.NM_802_11_AP_SEC_NONE and \
rsn_flags == network.NM_802_11_AP_SEC_NONE:
@@ -290,13 +300,15 @@ def create(ssid, flags, wpa_flags, rsn_flags, dev_caps, settings, response):
key_dialog = WPAKeyDialog(ssid, flags, wpa_flags, rsn_flags,
dev_caps, settings, response)
- key_dialog.connect("response", _key_dialog_response_cb)
- key_dialog.connect("destroy", _key_dialog_destroy_cb)
+ key_dialog.connect('response', _key_dialog_response_cb)
+ key_dialog.connect('destroy', _key_dialog_destroy_cb)
key_dialog.show_all()
+
def _key_dialog_destroy_cb(key_dialog, data=None):
_key_dialog_response_cb(key_dialog, gtk.RESPONSE_CANCEL)
+
def _key_dialog_response_cb(key_dialog, response_id):
response = key_dialog.get_response_object()
secrets = None
@@ -308,10 +320,9 @@ def _key_dialog_response_cb(key_dialog, response_id):
response.set_error(CanceledKeyRequestError())
elif response_id == gtk.RESPONSE_OK:
if not secrets:
- raise RuntimeError("Invalid security arguments.")
+ raise RuntimeError('Invalid security arguments.')
response.set_secrets(secrets)
else:
- raise RuntimeError("Unhandled key dialog response %d" % response_id)
+ raise RuntimeError('Unhandled key dialog response %d' % response_id)
key_dialog.destroy()
-
diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
index a04922b..ad4b873 100644
--- a/src/jarabe/desktop/meshbox.py
+++ b/src/jarabe/desktop/meshbox.py
@@ -1,6 +1,7 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
-# Copyright (C) 2009 One Laptop per Child
+# Copyright (C) 2009-2010 One Laptop per Child
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -18,41 +19,34 @@
from gettext import gettext as _
import logging
-import hashlib
import dbus
import hippo
+import glib
import gobject
import gtk
+import gconf
from sugar.graphics.icon import CanvasIcon, Icon
-from sugar.graphics.xocolor import XoColor
-from sugar.graphics import xocolor
from sugar.graphics import style
-from sugar.graphics.icon import get_icon_state
from sugar.graphics import palette
from sugar.graphics import iconentry
from sugar.graphics.menuitem import MenuItem
-from sugar.activity.activityhandle import ActivityHandle
-from sugar.activity import activityfactory
-from sugar.util import unique_id
-from sugar import profile
from jarabe.model import neighborhood
+from jarabe.model.buddy import get_owner_instance
from jarabe.view.buddyicon import BuddyIcon
-from jarabe.view.pulsingicon import CanvasPulsingIcon
-from jarabe.view import launcher
from jarabe.desktop.snowflakelayout import SnowflakeLayout
from jarabe.desktop.spreadlayout import SpreadLayout
-from jarabe.desktop import keydialog
-from jarabe.model import bundleregistry
+from jarabe.desktop.networkviews import WirelessNetworkView
+from jarabe.desktop.networkviews import OlpcMeshView
+from jarabe.desktop.networkviews import SugarAdhocView
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
from jarabe.model.network import AccessPoint
from jarabe.model.olpcmesh import OlpcMeshManager
+from jarabe.model.adhoc import get_adhoc_manager_instance
+from jarabe.journal import misc
+
_NM_SERVICE = 'org.freedesktop.NetworkManager'
_NM_IFACE = 'org.freedesktop.NetworkManager'
@@ -66,522 +60,7 @@ _NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
_AP_ICON_NAME = 'network-wireless'
_OLPC_MESH_ICON_NAME = 'network-mesh'
-class WirelessNetworkView(CanvasPulsingIcon):
- def __init__(self, initial_ap):
- CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE,
- cache=True)
- self._bus = dbus.SystemBus()
- self._access_points = {initial_ap.model.object_path: initial_ap}
- self._active_ap = None
- self._device = initial_ap.device
- self._palette_icon = None
- self._disconnect_item = None
- self._connect_item = None
- self._greyed_out = False
- self._name = initial_ap.name
- self._mode = initial_ap.mode
- self._strength = initial_ap.strength
- self._flags = initial_ap.flags
- self._wpa_flags = initial_ap.wpa_flags
- self._rsn_flags = initial_ap.rsn_flags
- self._device_caps = 0
- self._device_state = None
- self._connection = None
- self._color = None
-
- if self._mode == network.NM_802_11_MODE_ADHOC \
- and self._name_encodes_colors():
- encoded_color = self._name.split("#", 1)
- if len(encoded_color) == 2:
- self._color = xocolor.XoColor('#' + encoded_color[1])
- else:
- 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.connect('button-release-event', self.__button_release_event_cb)
-
- pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
- style.COLOR_TRANSPARENT.get_svg()))
- self.props.pulse_color = pulse_color
-
- self._palette = self._create_palette()
- self.set_palette(self._palette)
- self._palette_icon.props.xo_color = self._color
-
- if network.find_connection_by_ssid(self._name) is not None:
- self.props.badge_name = "emblem-favorite"
- self._palette_icon.props.badge_name = "emblem-favorite"
- elif initial_ap.flags == network.NM_802_11_AP_FLAGS_PRIVACY:
- self.props.badge_name = "emblem-locked"
- self._palette_icon.props.badge_name = "emblem-locked"
- else:
- self.props.badge_name = None
- self._palette_icon.props.badge_name = None
-
- interface_props = dbus.Interface(self._device,
- 'org.freedesktop.DBus.Properties')
- interface_props.Get(_NM_DEVICE_IFACE, 'State',
- reply_handler=self.__get_device_state_reply_cb,
- error_handler=self.__get_device_state_error_cb)
- interface_props.Get(_NM_WIRELESS_IFACE, 'WirelessCapabilities',
- reply_handler=self.__get_device_caps_reply_cb,
- error_handler=self.__get_device_caps_error_cb)
- interface_props.Get(_NM_WIRELESS_IFACE, 'ActiveAccessPoint',
- reply_handler=self.__get_active_ap_reply_cb,
- error_handler=self.__get_active_ap_error_cb)
-
- self._bus.add_signal_receiver(self.__device_state_changed_cb,
- signal_name='StateChanged',
- path=self._device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
- self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
- signal_name='PropertiesChanged',
- path=self._device.object_path,
- dbus_interface=_NM_WIRELESS_IFACE)
-
- def _name_encodes_colors(self):
- """Match #XXXXXX,#YYYYYY at the end of the network name"""
- return self._name[-7] == '#' and self._name[-8] == ',' \
- and self._name[-15] == '#'
-
- def _create_palette(self):
- icon_name = get_icon_state(_AP_ICON_NAME, self._strength)
- self._palette_icon = Icon(icon_name=icon_name,
- icon_size=style.STANDARD_ICON_SIZE,
- badge_name=self.props.badge_name)
-
- p = palette.Palette(primary_text=self._name,
- icon=self._palette_icon)
-
- self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
- self._connect_item.connect('activate', self.__connect_activate_cb)
- p.menu.append(self._connect_item)
-
- self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject')
- self._disconnect_item.connect('activate',
- self._disconnect_activate_cb)
- p.menu.append(self._disconnect_item)
-
- return p
-
- def __device_state_changed_cb(self, new_state, old_state, reason):
- self._device_state = new_state
- self._update_state()
-
- def __update_active_ap(self, ap_path):
- if ap_path in self._access_points:
- # save reference to active AP, so that we always display the
- # strength of that one
- self._active_ap = self._access_points[ap_path]
- self.update_strength()
- self._update_state()
- elif self._active_ap is not None:
- # revert to showing state of strongest AP again
- self._active_ap = None
- self.update_strength()
- self._update_state()
-
- def __wireless_properties_changed_cb(self, properties):
- if 'ActiveAccessPoint' in properties:
- self.__update_active_ap(properties['ActiveAccessPoint'])
-
- def __get_active_ap_reply_cb(self, ap_path):
- self.__update_active_ap(ap_path)
-
- def __get_active_ap_error_cb(self, err):
- logging.error('Error getting the active access point: %s', err)
-
- def __get_device_caps_reply_cb(self, caps):
- self._device_caps = caps
-
- def __get_device_caps_error_cb(self, err):
- logging.error('Error getting the wireless device properties: %s', err)
-
- def __get_device_state_reply_cb(self, state):
- self._device_state = state
- self._update()
-
- def __get_device_state_error_cb(self, err):
- logging.error('Error getting the device state: %s', err)
-
- def _update(self):
- self._update_state()
- self._update_color()
-
- def _update_state(self):
- if self._active_ap is not None:
- state = self._device_state
- else:
- state = network.DEVICE_STATE_UNKNOWN
-
- if state == network.DEVICE_STATE_ACTIVATED:
- connection = network.find_connection_by_ssid(self._name)
- if connection:
- if self._mode == network.NM_802_11_MODE_INFRA:
- connection.set_connected()
-
- icon_name = '%s-connected' % _AP_ICON_NAME
- else:
- icon_name = _AP_ICON_NAME
-
- icon_name = get_icon_state(icon_name, self._strength)
- if icon_name:
- self.props.icon_name = icon_name
- icon = self._palette.props.icon
- icon.props.icon_name = icon_name
-
- if state == network.DEVICE_STATE_PREPARE or \
- state == network.DEVICE_STATE_CONFIG or \
- state == network.DEVICE_STATE_NEED_AUTH or \
- state == network.DEVICE_STATE_IP_CONFIG:
- if self._disconnect_item:
- self._disconnect_item.show()
- self._connect_item.hide()
- self._palette.props.secondary_text = _('Connecting...')
- self.props.pulsing = True
- elif state == network.DEVICE_STATE_ACTIVATED:
- if self._disconnect_item:
- self._disconnect_item.show()
- self._connect_item.hide()
- self._palette.props.secondary_text = _('Connected')
- self.props.pulsing = False
- else:
- if self._disconnect_item:
- self._disconnect_item.hide()
- self._connect_item.show()
- self._palette.props.secondary_text = None
- self.props.pulsing = False
-
- def _update_color(self):
- if self._greyed_out:
- self.props.pulsing = False
- self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
- else:
- self.props.base_color = self._color
-
- def _disconnect_activate_cb(self, item):
- pass
-
- def _add_ciphers_from_flags(self, flags, pairwise):
- ciphers = []
- if pairwise:
- if flags & network.NM_802_11_AP_SEC_PAIR_TKIP:
- ciphers.append("tkip")
- if flags & network.NM_802_11_AP_SEC_PAIR_CCMP:
- ciphers.append("ccmp")
- else:
- if flags & network.NM_802_11_AP_SEC_GROUP_WEP40:
- ciphers.append("wep40")
- if flags & network.NM_802_11_AP_SEC_GROUP_WEP104:
- ciphers.append("wep104")
- if flags & network.NM_802_11_AP_SEC_GROUP_TKIP:
- ciphers.append("tkip")
- if flags & network.NM_802_11_AP_SEC_GROUP_CCMP:
- ciphers.append("ccmp")
- return ciphers
-
- def _get_security(self):
- if not (self._flags & network.NM_802_11_AP_FLAGS_PRIVACY) and \
- (self._wpa_flags == network.NM_802_11_AP_SEC_NONE) and \
- (self._rsn_flags == network.NM_802_11_AP_SEC_NONE):
- # No security
- return None
-
- if (self._flags & network.NM_802_11_AP_FLAGS_PRIVACY) and \
- (self._wpa_flags == network.NM_802_11_AP_SEC_NONE) and \
- (self._rsn_flags == network.NM_802_11_AP_SEC_NONE):
- # Static WEP, Dynamic WEP, or LEAP
- wireless_security = WirelessSecurity()
- wireless_security.key_mgmt = 'none'
- return wireless_security
-
- if (self._mode != network.NM_802_11_MODE_INFRA):
- # Stuff after this point requires infrastructure
- logging.error('The infrastructure mode is not supoorted'
- ' by your wireless device.')
- return None
-
- if (self._rsn_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK) and \
- (self._device_caps & network.NM_802_11_DEVICE_CAP_RSN):
- # WPA2 PSK first
- pairwise = self._add_ciphers_from_flags(self._rsn_flags, True)
- group = self._add_ciphers_from_flags(self._rsn_flags, False)
- wireless_security = WirelessSecurity()
- wireless_security.key_mgmt = 'wpa-psk'
- wireless_security.proto = 'rsn'
- wireless_security.pairwise = pairwise
- wireless_security.group = group
- return wireless_security
-
- if (self._wpa_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK) and \
- (self._device_caps & network.NM_802_11_DEVICE_CAP_WPA):
- # WPA PSK
- pairwise = self._add_ciphers_from_flags(self._wpa_flags, True)
- group = self._add_ciphers_from_flags(self._wpa_flags, False)
- wireless_security = WirelessSecurity()
- wireless_security.key_mgmt = 'wpa-psk'
- wireless_security.proto = 'wpa'
- wireless_security.pairwise = pairwise
- wireless_security.group = group
- return wireless_security
-
- def __connect_activate_cb(self, icon):
- self._connect()
-
- def __button_release_event_cb(self, icon, event):
- self._connect()
-
- def _connect(self):
- connection = network.find_connection_by_ssid(self._name)
- if connection is None:
- settings = Settings()
- settings.connection.id = 'Auto ' + self._name
- uuid = settings.connection.uuid = unique_id()
- settings.connection.type = '802-11-wireless'
- settings.wireless.ssid = self._name
-
- if self._mode == network.NM_802_11_MODE_INFRA:
- 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 = 'link-local'
-
- wireless_security = self._get_security()
- settings.wireless_security = wireless_security
-
- if wireless_security is not None:
- settings.wireless.security = '802-11-wireless-security'
-
- connection = network.add_connection(uuid, settings)
-
- obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
- netmgr = dbus.Interface(obj, _NM_IFACE)
-
- netmgr.ActivateConnection(network.SETTINGS_SERVICE, connection.path,
- self._device.object_path,
- "/",
- reply_handler=self.__activate_reply_cb,
- error_handler=self.__activate_error_cb)
-
- def __activate_reply_cb(self, connection):
- logging.debug('Connection activated: %s', connection)
-
- def __activate_error_cb(self, err):
- logging.error('Failed to activate connection: %s', err)
-
- def set_filter(self, query):
- self._greyed_out = self._name.lower().find(query) == -1
- self._update_state()
- self._update_color()
-
- def create_keydialog(self, settings, response):
- keydialog.create(self._name, self._flags, self._wpa_flags,
- self._rsn_flags, self._device_caps, settings, response)
-
- def update_strength(self):
- if self._active_ap is not None:
- # display strength of AP that we are connected to
- new_strength = self._active_ap.strength
- else:
- # display the strength of the strongest AP that makes up this
- # network, also considering that there may be no APs
- new_strength = max([0] + [ap.strength for ap in
- self._access_points.values()])
-
- if new_strength != self._strength:
- self._strength = new_strength
- self._update_state()
-
- def add_ap(self, ap):
- self._access_points[ap.model.object_path] = ap
- self.update_strength()
-
- def remove_ap(self, ap):
- path = ap.model.object_path
- if path not in self._access_points:
- return
- del self._access_points[path]
- if self._active_ap == ap:
- self._active_ap = None
- self.update_strength()
-
- def num_aps(self):
- return len(self._access_points)
-
- def find_ap(self, ap_path):
- if ap_path not in self._access_points:
- return None
- return self._access_points[ap_path]
-
- def is_olpc_mesh(self):
- return self._mode == network.NM_802_11_MODE_ADHOC \
- and self.name == "olpc-mesh"
-
- def remove_all_aps(self):
- for ap in self._access_points.values():
- ap.disconnect()
- self._access_points = {}
- self._active_ap = None
- self.update_strength()
-
- def disconnect(self):
- self._bus.remove_signal_receiver(self.__device_state_changed_cb,
- signal_name='StateChanged',
- path=self._device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
- self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
- signal_name='PropertiesChanged',
- path=self._device.object_path,
- dbus_interface=_NM_WIRELESS_IFACE)
-
-
-class OlpcMeshView(CanvasPulsingIcon):
- def __init__(self, mesh_mgr, channel):
- CanvasPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME,
- size=style.STANDARD_ICON_SIZE, cache=True)
- self._bus = dbus.SystemBus()
- self._channel = channel
- self._mesh_mgr = mesh_mgr
- self._disconnect_item = None
- self._connect_item = None
- self._greyed_out = False
- self._name = ''
- self._device_state = None
- self._connection = None
- self._active = False
- device = mesh_mgr.mesh_device
-
- self.connect('button-release-event', self.__button_release_event_cb)
-
- interface_props = dbus.Interface(device,
- 'org.freedesktop.DBus.Properties')
- interface_props.Get(_NM_DEVICE_IFACE, 'State',
- reply_handler=self.__get_device_state_reply_cb,
- error_handler=self.__get_device_state_error_cb)
- interface_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
- reply_handler=self.__get_active_channel_reply_cb,
- error_handler=self.__get_active_channel_error_cb)
-
- self._bus.add_signal_receiver(self.__device_state_changed_cb,
- signal_name='StateChanged',
- path=device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
- self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
- signal_name='PropertiesChanged',
- path=device.object_path,
- dbus_interface=_NM_OLPC_MESH_IFACE)
-
- pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
- style.COLOR_TRANSPARENT.get_svg()))
- self.props.pulse_color = pulse_color
- self.props.base_color = profile.get_color()
- self._palette = self._create_palette()
- self.set_palette(self._palette)
-
- def _create_palette(self):
- _palette = palette.Palette(_("Mesh Network %d") % self._channel)
-
- self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
- self._connect_item.connect('activate', self.__connect_activate_cb)
- _palette.menu.append(self._connect_item)
-
- return _palette
-
- def __get_device_state_reply_cb(self, state):
- self._device_state = state
- self._update()
-
- def __get_device_state_error_cb(self, err):
- logging.error('Error getting the device state: %s', err)
-
- def __device_state_changed_cb(self, new_state, old_state, reason):
- self._device_state = new_state
- self._update()
-
- def __get_active_channel_reply_cb(self, channel):
- self._active = (channel == self._channel)
- self._update()
-
- def __get_active_channel_error_cb(self, err):
- logging.error('Error getting the active channel: %s', err)
-
- def __wireless_properties_changed_cb(self, properties):
- if 'ActiveChannel' in properties:
- channel = properties['ActiveChannel']
- self._active = (channel == self._channel)
- self._update()
-
- def _update(self):
- if self._active:
- state = self._device_state
- else:
- state = network.DEVICE_STATE_UNKNOWN
-
- if state in [network.DEVICE_STATE_PREPARE,
- network.DEVICE_STATE_CONFIG,
- network.DEVICE_STATE_NEED_AUTH,
- network.DEVICE_STATE_IP_CONFIG]:
- if self._disconnect_item:
- self._disconnect_item.show()
- self._connect_item.hide()
- self._palette.props.secondary_text = _('Connecting...')
- self.props.pulsing = True
- elif state == network.DEVICE_STATE_ACTIVATED:
- if self._disconnect_item:
- self._disconnect_item.show()
- self._connect_item.hide()
- self._palette.props.secondary_text = _('Connected')
- self.props.pulsing = False
- else:
- if self._disconnect_item:
- self._disconnect_item.hide()
- self._connect_item.show()
- self._palette.props.secondary_text = None
- self.props.pulsing = False
-
- def _update_color(self):
- if self._greyed_out:
- self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
- else:
- self.props.base_color = profile.get_color()
-
- def __connect_activate_cb(self, icon):
- self._connect()
-
- def __button_release_event_cb(self, icon, event):
- self._connect()
-
- def _connect(self):
- self._mesh_mgr.user_activate_channel(self._channel)
-
- def __activate_reply_cb(self, connection):
- logging.debug('Connection activated: %s', connection)
-
- def __activate_error_cb(self, err):
- logging.error('Failed to activate connection: %s', err)
-
- def set_filter(self, query):
- self._greyed_out = (query != '')
- self._update_color()
-
- def disconnect(self):
- self._bus.remove_signal_receiver(self.__device_state_changed_cb,
- signal_name='StateChanged',
- path=self._device.object_path,
- dbus_interface=_NM_DEVICE_IFACE)
- self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
- signal_name='PropertiesChanged',
- path=self._device.object_path,
- dbus_interface=_NM_OLPC_MESH_IFACE)
+_AUTOSEARCH_TIMEOUT = 1000
class ActivityView(hippo.CanvasBox):
@@ -589,6 +68,9 @@ class ActivityView(hippo.CanvasBox):
hippo.CanvasBox.__init__(self)
self._model = model
+ self._model.connect('current-buddy-added', self.__buddy_added_cb)
+ self._model.connect('current-buddy-removed', self.__buddy_removed_cb)
+
self._icons = {}
self._palette = None
@@ -598,31 +80,30 @@ class ActivityView(hippo.CanvasBox):
self._icon = self._create_icon()
self._layout.add(self._icon, center=True)
- self._update_palette()
+ self._palette = self._create_palette()
+ self._icon.set_palette(self._palette)
- activity = self._model.activity
- activity.connect('notify::name', self._name_changed_cb)
- activity.connect('notify::color', self._color_changed_cb)
- activity.connect('notify::private', self._private_changed_cb)
- activity.connect('joined', self._joined_changed_cb)
- #FIXME: 'joined' signal not working, see #5032
+ for buddy in self._model.props.current_buddies:
+ self._add_buddy(buddy)
def _create_icon(self):
- icon = CanvasIcon(file_name=self._model.get_icon_name(),
+ icon = CanvasIcon(file_name=self._model.bundle.get_icon(),
xo_color=self._model.get_color(), cache=True,
size=style.STANDARD_ICON_SIZE)
icon.connect('activated', self._clicked_cb)
return icon
def _create_palette(self):
- p_icon = Icon(file=self._model.get_icon_name(),
+ p_text = glib.markup_escape_text(self._model.bundle.get_name())
+ p_icon = Icon(file=self._model.bundle.get_icon(),
xo_color=self._model.get_color())
p_icon.props.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
- p = palette.Palette(None, primary_text=self._model.activity.props.name,
+ p = palette.Palette(None,
+ primary_text=p_text,
icon=p_icon)
- private = self._model.activity.props.private
- joined = self._model.activity.props.joined
+ private = self._model.props.private
+ joined = get_owner_instance() in self._model.props.buddies
if joined:
item = MenuItem(_('Resume'), 'activity-start')
@@ -637,42 +118,30 @@ class ActivityView(hippo.CanvasBox):
return p
- def _update_palette(self):
- self._palette = self._create_palette()
- self._icon.set_palette(self._palette)
-
def has_buddy_icon(self, key):
- return self._icons.has_key(key)
+ return key in self._icons
+
+ def __buddy_added_cb(self, activity, buddy):
+ self._add_buddy(buddy)
- def add_buddy_icon(self, key, icon):
- self._icons[key] = icon
+ def _add_buddy(self, buddy):
+ icon = BuddyIcon(buddy, style.STANDARD_ICON_SIZE)
+ self._icons[buddy.props.key] = icon
self._layout.add(icon)
- def remove_buddy_icon(self, key):
- icon = self._icons[key]
- del self._icons[key]
+ def __buddy_removed_cb(self, activity, buddy):
+ icon = self._icons[buddy.props.key]
+ del self._icons[buddy.props.key]
icon.destroy()
def _clicked_cb(self, item):
- shell_model = shell.get_model()
- activity = shell_model.get_activity_by_id(self._model.get_id())
- if activity:
- activity.get_window().activate(gtk.get_current_event_time())
- return
-
- bundle_id = self._model.get_bundle_id()
- bundle = bundleregistry.get_registry().get_bundle(bundle_id)
-
- launcher.add_launcher(self._model.get_id(),
- bundle.get_icon(),
- self._model.get_color())
-
- handle = ActivityHandle(self._model.get_id())
- activityfactory.create(bundle, handle)
+ bundle = self._model.get_bundle()
+ misc.launch(bundle, activity_id=self._model.activity_id,
+ color=self._model.get_color())
def set_filter(self, query):
- text_to_check = self._model.activity.props.name.lower() + \
- self._model.activity.props.type.lower()
+ text_to_check = self._model.bundle.get_name().lower() + \
+ self._model.bundle.get_bundle_id().lower()
if text_to_check.find(query) == -1:
self._icon.props.stroke_color = '#D5D5D5'
self._icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
@@ -683,31 +152,13 @@ class ActivityView(hippo.CanvasBox):
if hasattr(icon, 'set_filter'):
icon.set_filter(query)
- def _name_changed_cb(self, activity, pspec):
- self._update_palette()
-
- def _color_changed_cb(self, activity, pspec):
- self._layout.remove(self._icon)
- self._icon = self._create_icon()
- self._layout.add(self._icon, center=True)
- self._icon.set_palette(self._palette)
-
- def _private_changed_cb(self, activity, pspec):
- self._update_palette()
-
- def _joined_changed_cb(self, widget, event):
- logging.debug('ActivityView._joined_changed_cb')
-
-_AUTOSEARCH_TIMEOUT = 1000
-
class MeshToolbar(gtk.Toolbar):
__gtype_name__ = 'MeshToolbar'
__gsignals__ = {
- 'query-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str]))
+ 'query-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
}
def __init__(self):
@@ -770,15 +221,23 @@ class MeshToolbar(gtk.Toolbar):
return False
-class DeviceObserver(object):
- def __init__(self, box, device):
- self._box = box
+class DeviceObserver(gobject.GObject):
+ __gsignals__ = {
+ 'access-point-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'access-point-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ }
+
+ def __init__(self, device):
+ gobject.GObject.__init__(self)
self._bus = dbus.SystemBus()
- self._device = device
+ self.device = device
- wireless = dbus.Interface(self._device, _NM_WIRELESS_IFACE)
- wireless.GetAccessPoints(reply_handler=self._get_access_points_reply_cb,
- error_handler=self._get_access_points_error_cb)
+ wireless = dbus.Interface(device, _NM_WIRELESS_IFACE)
+ wireless.GetAccessPoints(
+ reply_handler=self._get_access_points_reply_cb,
+ error_handler=self._get_access_points_error_cb)
self._bus.add_signal_receiver(self.__access_point_added_cb,
signal_name='AccessPointAdded',
@@ -792,35 +251,42 @@ class DeviceObserver(object):
def _get_access_points_reply_cb(self, access_points_o):
for ap_o in access_points_o:
ap = self._bus.get_object(_NM_SERVICE, ap_o)
- self._box.add_access_point(self._device, ap)
+ self.emit('access-point-added', ap)
def _get_access_points_error_cb(self, err):
logging.error('Failed to get access points: %s', err)
def __access_point_added_cb(self, access_point_o):
ap = self._bus.get_object(_NM_SERVICE, access_point_o)
- self._box.add_access_point(self._device, ap)
+ self.emit('access-point-added', ap)
def __access_point_removed_cb(self, access_point_o):
- self._box.remove_access_point(access_point_o)
+ self.emit('access-point-removed', access_point_o)
def disconnect(self):
self._bus.remove_signal_receiver(self.__access_point_added_cb,
signal_name='AccessPointAdded',
- path=self._device.object_path,
+ path=self.device.object_path,
dbus_interface=_NM_WIRELESS_IFACE)
self._bus.remove_signal_receiver(self.__access_point_removed_cb,
signal_name='AccessPointRemoved',
- path=self._device.object_path,
+ path=self.device.object_path,
dbus_interface=_NM_WIRELESS_IFACE)
class NetworkManagerObserver(object):
+
+ _SHOW_ADHOC_GCONF_KEY = '/desktop/sugar/network/adhoc'
+
def __init__(self, box):
self._box = box
self._bus = None
self._devices = {}
self._netmgr = None
+ self._olpc_mesh_device_o = None
+
+ client = gconf.client_get_default()
+ self._have_adhoc_networks = client.get_bool(self._SHOW_ADHOC_GCONF_KEY)
def listen(self):
try:
@@ -840,6 +306,9 @@ class NetworkManagerObserver(object):
self._bus.add_signal_receiver(self.__device_removed_cb,
signal_name='DeviceRemoved',
dbus_interface=_NM_IFACE)
+ self._bus.add_signal_receiver(self.__properties_changed_cb,
+ signal_name='PropertiesChanged',
+ dbus_interface=_NM_IFACE)
settings = network.get_settings()
if settings is not None:
@@ -849,13 +318,12 @@ class NetworkManagerObserver(object):
# FIXME It would be better to do all of this async, but I cannot think
# of a good way to. NM could really use some love here.
- netmgr_props = dbus.Interface(
- self._netmgr, 'org.freedesktop.DBus.Properties')
+ netmgr_props = dbus.Interface(self._netmgr, dbus.PROPERTIES_IFACE)
active_connections_o = netmgr_props.Get(_NM_IFACE, 'ActiveConnections')
for conn_o in active_connections_o:
obj = self._bus.get_object(_NM_IFACE, conn_o)
- props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
state = props.Get(_NM_ACTIVE_CONN_IFACE, 'State')
if state == network.NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
ap_o = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
@@ -867,8 +335,8 @@ class NetworkManagerObserver(object):
settings = kwargs['connection'].get_settings()
net.create_keydialog(settings, kwargs['response'])
if not found:
- logging.error('Could not determine AP for'
- ' specific object %s' % conn_o)
+ logging.error('Could not determine AP for specific object'
+ ' %s', conn_o)
def __get_devices_reply_cb(self, devices_o):
for dev_o in devices_o:
@@ -879,12 +347,19 @@ class NetworkManagerObserver(object):
def _check_device(self, device_o):
device = self._bus.get_object(_NM_SERVICE, device_o)
- props = dbus.Interface(device, 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(device, dbus.PROPERTIES_IFACE)
device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
if device_type == network.DEVICE_TYPE_802_11_WIRELESS:
- self._devices[device_o] = DeviceObserver(self._box, device)
+ self._devices[device_o] = DeviceObserver(device)
+ self._devices[device_o].connect('access-point-added',
+ self.__ap_added_cb)
+ self._devices[device_o].connect('access-point-removed',
+ self.__ap_removed_cb)
+ if self._have_adhoc_networks:
+ self._box.add_adhoc_networks(device)
elif device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
+ self._olpc_mesh_device_o = device_o
self._box.enable_olpc_mesh(device)
def _get_device_path_error_cb(self, err):
@@ -898,23 +373,41 @@ class NetworkManagerObserver(object):
observer = self._devices[device_o]
observer.disconnect()
del self._devices[device_o]
+ if self._have_adhoc_networks:
+ self._box.remove_adhoc_networks()
return
-
- device = self._bus.get_object(_NM_SERVICE, device_o)
- props = dbus.Interface(device, 'org.freedesktop.DBus.Properties')
- device_type = props.Get(_NM_DEVICE_IFACE, 'DeviceType')
- if device_type == network.DEVICE_TYPE_802_11_OLPC_MESH:
- self._box.disable_olpc_mesh(device)
+
+ if self._olpc_mesh_device_o == device_o:
+ self._box.disable_olpc_mesh(device_o)
+
+ def __ap_added_cb(self, device_observer, access_point):
+ self._box.add_access_point(device_observer.device, access_point)
+
+ def __ap_removed_cb(self, device_observer, access_point_o):
+ self._box.remove_access_point(access_point_o)
+
+ def __properties_changed_cb(self, properties):
+ if 'WirelessHardwareEnabled' in properties:
+ if properties['WirelessHardwareEnabled']:
+ if not self._have_adhoc_networks:
+ self._box.remove_adhoc_networks()
+ elif properties['WirelessHardwareEnabled']:
+ for device in self._devices:
+ if self._have_adhoc_networks:
+ self._box.add_adhoc_networks(device)
+
class MeshBox(gtk.VBox):
__gtype_name__ = 'SugarMeshBox'
def __init__(self):
- logging.debug("STARTUP: Loading the mesh view")
+ logging.debug('STARTUP: Loading the mesh view')
gobject.GObject.__init__(self)
self.wireless_networks = {}
+ self._adhoc_manager = None
+ self._adhoc_networks = []
self._model = neighborhood.get_model()
self._buddies = {}
@@ -942,11 +435,10 @@ class MeshBox(gtk.VBox):
self._layout_box.set_layout(self._layout)
for buddy_model in self._model.get_buddies():
- self._add_alone_buddy(buddy_model)
+ self._add_buddy(buddy_model)
self._model.connect('buddy-added', self._buddy_added_cb)
self._model.connect('buddy-removed', self._buddy_removed_cb)
- self._model.connect('buddy-moved', self._buddy_moved_cb)
for activity_model in self._model.get_activities():
self._add_activity(activity_model)
@@ -970,24 +462,22 @@ class MeshBox(gtk.VBox):
gtk.VBox.do_size_allocate(self, allocation)
def _buddy_added_cb(self, model, buddy_model):
- self._add_alone_buddy(buddy_model)
+ self._add_buddy(buddy_model)
def _buddy_removed_cb(self, model, buddy_model):
self._remove_buddy(buddy_model)
- def _buddy_moved_cb(self, model, buddy_model, activity_model):
- # Owner doesn't move from the center
- if buddy_model.is_owner():
- return
- self._move_buddy(buddy_model, activity_model)
-
def _activity_added_cb(self, model, activity_model):
self._add_activity(activity_model)
def _activity_removed_cb(self, model, activity_model):
self._remove_activity(activity_model)
- def _add_alone_buddy(self, buddy_model):
+ def _add_buddy(self, buddy_model):
+ buddy_model.connect('notify::current-activity',
+ self.__buddy_notify_current_activity_cb)
+ if buddy_model.props.current_activity is not None:
+ return
icon = BuddyIcon(buddy_model)
if buddy_model.is_owner():
self._owner_icon = icon
@@ -996,36 +486,23 @@ class MeshBox(gtk.VBox):
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
- self._buddies[buddy_model.get_buddy().object_path()] = icon
+ self._buddies[buddy_model.props.key] = icon
- def _remove_alone_buddy(self, buddy_model):
- icon = self._buddies[buddy_model.get_buddy().object_path()]
+ def _remove_buddy(self, buddy_model):
+ logging.debug('MeshBox._remove_buddy')
+ icon = self._buddies[buddy_model.props.key]
self._layout.remove(icon)
- del self._buddies[buddy_model.get_buddy().object_path()]
+ del self._buddies[buddy_model.props.key]
icon.destroy()
- def _remove_buddy(self, buddy_model):
- object_path = buddy_model.get_buddy().object_path()
- if self._buddies.has_key(object_path):
- self._remove_alone_buddy(buddy_model)
- else:
- for activity in self._activities.values():
- if activity.has_buddy_icon(object_path):
- activity.remove_buddy_icon(object_path)
-
- def _move_buddy(self, buddy_model, activity_model):
- self._remove_buddy(buddy_model)
-
- if activity_model == None:
- self._add_alone_buddy(buddy_model)
- elif activity_model.get_id() in self._activities:
- activity = self._activities[activity_model.get_id()]
-
- icon = BuddyIcon(buddy_model, style.STANDARD_ICON_SIZE)
- activity.add_buddy_icon(buddy_model.get_buddy().object_path(), icon)
-
- if hasattr(icon, 'set_filter'):
- icon.set_filter(self._query)
+ def __buddy_notify_current_activity_cb(self, buddy_model, pspec):
+ logging.debug('MeshBox.__buddy_notify_current_activity_cb %s',
+ buddy_model.props.current_activity)
+ if buddy_model.props.current_activity is None:
+ if not buddy_model.props.key in self._buddies:
+ self._add_buddy(buddy_model)
+ elif buddy_model.props.key in self._buddies:
+ self._remove_buddy(buddy_model)
def _add_activity(self, activity_model):
icon = ActivityView(activity_model)
@@ -1034,58 +511,70 @@ class MeshBox(gtk.VBox):
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
- self._activities[activity_model.get_id()] = icon
+ self._activities[activity_model.activity_id] = icon
def _remove_activity(self, activity_model):
- icon = self._activities[activity_model.get_id()]
+ icon = self._activities[activity_model.activity_id]
self._layout.remove(icon)
- del self._activities[activity_model.get_id()]
+ del self._activities[activity_model.activity_id]
icon.destroy()
# add AP to its corresponding network icon on the desktop,
# creating one if it doesn't already exist
def _add_ap_to_network(self, ap):
- hash = ap.network_hash()
- if hash in self.wireless_networks:
- self.wireless_networks[hash].add_ap(ap)
+ hash_value = ap.network_hash()
+ if hash_value in self.wireless_networks:
+ self.wireless_networks[hash_value].add_ap(ap)
else:
# this is a new network
icon = WirelessNetworkView(ap)
- self.wireless_networks[hash] = icon
+ self.wireless_networks[hash_value] = icon
self._layout.add(icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
- def _remove_net_if_empty(self, net, hash):
+ def _remove_net_if_empty(self, net, hash_value):
# remove a network if it has no APs left
if net.num_aps() == 0:
net.disconnect()
self._layout.remove(net)
- del self.wireless_networks[hash]
+ del self.wireless_networks[hash_value]
- def _ap_props_changed_cb(self, ap, old_hash):
+ def _ap_props_changed_cb(self, ap, old_hash_value):
# if we have mesh hardware, ignore OLPC mesh networks that appear as
# normal wifi networks
if len(self._mesh) > 0 and ap.mode == network.NM_802_11_MODE_ADHOC \
- and ap.name == "olpc-mesh":
- logging.debug("ignoring OLPC mesh IBSS")
+ and ap.name == 'olpc-mesh':
+ logging.debug('ignoring OLPC mesh IBSS')
ap.disconnect()
return
- if old_hash is None: # new AP finished initializing
+ if self._adhoc_manager is not None and \
+ network.is_sugar_adhoc_network(ap.name) and \
+ ap.mode == network.NM_802_11_MODE_ADHOC:
+ if old_hash_value is None:
+ # new Ad-hoc network finished initializing
+ self._adhoc_manager.add_access_point(ap)
+ # we are called as well in other cases but we do not need to
+ # act here as we don't display signal strength for Ad-hoc networks
+ return
+
+ if old_hash_value is None:
+ # new AP finished initializing
self._add_ap_to_network(ap)
return
- hash = ap.network_hash()
- if old_hash == hash:
+ hash_value = ap.network_hash()
+ if old_hash_value == hash_value:
# no change in network identity, so just update signal strengths
- self.wireless_networks[hash].update_strength()
+ self.wireless_networks[hash_value].update_strength()
return
# properties change includes a change of the identity of the network
# that it is on. so create this as a new network.
- self.wireless_networks[old_hash].remove_ap(ap)
- self._remove_net_if_empty(self.wireless_networks[old_hash], old_hash)
+ self.wireless_networks[old_hash_value].remove_ap(ap)
+ self._remove_net_if_empty(self.wireless_networks[old_hash_value],
+ old_hash_value)
self._add_ap_to_network(ap)
def add_access_point(self, device, ap_o):
@@ -1094,6 +583,11 @@ class MeshBox(gtk.VBox):
ap.initialize()
def remove_access_point(self, ap_o):
+ if self._adhoc_manager is not None:
+ if self._adhoc_manager.is_sugar_adhoc_access_point(ap_o):
+ self._adhoc_manager.remove_access_point(ap_o)
+ return
+
# we don't keep an index of ap object path to network, but since
# we'll only ever have a handful of networks, just try them all...
for net in self.wireless_networks.values():
@@ -1108,7 +602,26 @@ class MeshBox(gtk.VBox):
# it's not an error if the AP isn't found, since we might have ignored
# it (e.g. olpc-mesh adhoc network)
- logging.debug('Can not remove access point %s' % ap_o)
+ logging.debug('Can not remove access point %s', ap_o)
+
+ def add_adhoc_networks(self, device):
+ if self._adhoc_manager is None:
+ self._adhoc_manager = get_adhoc_manager_instance()
+ self._adhoc_manager.start_listening(device)
+ self._add_adhoc_network_icon(1)
+ self._add_adhoc_network_icon(6)
+ self._add_adhoc_network_icon(11)
+ self._adhoc_manager.autoconnect()
+
+ def remove_adhoc_networks(self):
+ for icon in self._adhoc_networks:
+ self._layout.remove(icon)
+ self._adhoc_networks = []
+
+ def _add_adhoc_network_icon(self, channel):
+ icon = SugarAdhocView(channel)
+ self._layout.add(icon)
+ self._adhoc_networks.append(icon)
def _add_olpc_mesh_icon(self, mesh_mgr, channel):
icon = OlpcMeshView(mesh_mgr, channel)
@@ -1123,15 +636,15 @@ class MeshBox(gtk.VBox):
# the OLPC mesh can be recognised as a "normal" wifi network. remove
# any such normal networks if they have been created
- for hash, net in self.wireless_networks.iteritems():
+ for hash_value, net in self.wireless_networks.iteritems():
if not net.is_olpc_mesh():
continue
- logging.debug("removing OLPC mesh IBSS")
+ logging.debug('removing OLPC mesh IBSS')
net.remove_all_aps()
net.disconnect()
self._layout.remove(net)
- del self.wireless_networks[hash]
+ del self.wireless_networks[hash_value]
def disable_olpc_mesh(self, mesh_device):
for icon in self._mesh:
diff --git a/src/jarabe/desktop/networkviews.py b/src/jarabe/desktop/networkviews.py
new file mode 100644
index 0000000..99d46b6
--- /dev/null
+++ b/src/jarabe/desktop/networkviews.py
@@ -0,0 +1,721 @@
+# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
+# Copyright (C) 2009-2010 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
+
+from gettext import gettext as _
+import logging
+import hashlib
+
+import dbus
+import glib
+
+from sugar.graphics.icon import Icon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics import xocolor
+from sugar.graphics import style
+from sugar.graphics.icon import get_icon_state
+from sugar.graphics import palette
+from sugar.graphics.menuitem import MenuItem
+from sugar.util import unique_id
+from sugar import profile
+
+from jarabe.view.pulsingicon import CanvasPulsingIcon
+from jarabe.desktop import keydialog
+from jarabe.model import network
+from jarabe.model.network import Settings
+from jarabe.model.network import IP4Config
+from jarabe.model.network import WirelessSecurity
+from jarabe.model.adhoc import get_adhoc_manager_instance
+
+
+_NM_SERVICE = 'org.freedesktop.NetworkManager'
+_NM_IFACE = 'org.freedesktop.NetworkManager'
+_NM_PATH = '/org/freedesktop/NetworkManager'
+_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
+_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
+_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
+_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
+_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
+
+_AP_ICON_NAME = 'network-wireless'
+_OLPC_MESH_ICON_NAME = 'network-mesh'
+
+
+class WirelessNetworkView(CanvasPulsingIcon):
+ def __init__(self, initial_ap):
+ CanvasPulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE,
+ cache=True)
+ self._bus = dbus.SystemBus()
+ self._access_points = {initial_ap.model.object_path: initial_ap}
+ self._active_ap = None
+ self._device = initial_ap.device
+ self._palette_icon = None
+ self._disconnect_item = None
+ self._connect_item = None
+ self._greyed_out = False
+ self._name = initial_ap.name
+ self._mode = initial_ap.mode
+ self._strength = initial_ap.strength
+ self._flags = initial_ap.flags
+ self._wpa_flags = initial_ap.wpa_flags
+ self._rsn_flags = initial_ap.rsn_flags
+ self._device_caps = 0
+ self._device_state = None
+ self._color = None
+
+ if self._mode == network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._name):
+ self._color = profile.get_color()
+ else:
+ 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.connect('button-release-event', self.__button_release_event_cb)
+
+ pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.pulse_color = pulse_color
+
+ self._palette = self._create_palette()
+ self.set_palette(self._palette)
+ self._palette_icon.props.xo_color = self._color
+ self._update_badge()
+
+ interface_props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
+ interface_props.Get(_NM_DEVICE_IFACE, 'State',
+ reply_handler=self.__get_device_state_reply_cb,
+ error_handler=self.__get_device_state_error_cb)
+ interface_props.Get(_NM_WIRELESS_IFACE, 'WirelessCapabilities',
+ reply_handler=self.__get_device_caps_reply_cb,
+ error_handler=self.__get_device_caps_error_cb)
+ interface_props.Get(_NM_WIRELESS_IFACE, 'ActiveAccessPoint',
+ reply_handler=self.__get_active_ap_reply_cb,
+ error_handler=self.__get_active_ap_error_cb)
+
+ self._bus.add_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+ self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_WIRELESS_IFACE)
+
+ def _create_palette(self):
+ icon_name = get_icon_state(_AP_ICON_NAME, self._strength)
+ self._palette_icon = Icon(icon_name=icon_name,
+ icon_size=style.STANDARD_ICON_SIZE,
+ badge_name=self.props.badge_name)
+
+ p = palette.Palette(primary_text=glib.markup_escape_text(self._name),
+ icon=self._palette_icon)
+
+ self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
+ self._connect_item.connect('activate', self.__connect_activate_cb)
+ p.menu.append(self._connect_item)
+
+ self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject')
+ self._disconnect_item.connect('activate',
+ self._disconnect_activate_cb)
+ p.menu.append(self._disconnect_item)
+
+ return p
+
+ def __device_state_changed_cb(self, new_state, old_state, reason):
+ self._device_state = new_state
+ self._update_state()
+ self._update_icon()
+ self._update_badge()
+
+ def __update_active_ap(self, ap_path):
+ if ap_path in self._access_points:
+ # save reference to active AP, so that we always display the
+ # strength of that one
+ self._active_ap = self._access_points[ap_path]
+ self.update_strength()
+ elif self._active_ap is not None:
+ # revert to showing state of strongest AP again
+ self._active_ap = None
+ self.update_strength()
+
+ def __wireless_properties_changed_cb(self, properties):
+ if 'ActiveAccessPoint' in properties:
+ self.__update_active_ap(properties['ActiveAccessPoint'])
+
+ def __get_active_ap_reply_cb(self, ap_path):
+ self.__update_active_ap(ap_path)
+
+ def __get_active_ap_error_cb(self, err):
+ logging.error('Error getting the active access point: %s', err)
+
+ def __get_device_caps_reply_cb(self, caps):
+ self._device_caps = caps
+
+ def __get_device_caps_error_cb(self, err):
+ logging.error('Error getting the wireless device properties: %s', err)
+
+ def __get_device_state_reply_cb(self, state):
+ self._device_state = state
+ self._update_state()
+ self._update_color()
+ self._update_badge()
+
+ def __get_device_state_error_cb(self, err):
+ logging.error('Error getting the device state: %s', err)
+
+ def _update_icon(self):
+ if self._mode == network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._name):
+ channel = max([1] + [ap.channel for ap in
+ self._access_points.values()])
+ if self._device_state == network.DEVICE_STATE_ACTIVATED and \
+ self._active_ap is not None:
+ icon_name = 'network-adhoc-%s-connected' % channel
+ else:
+ icon_name = 'network-adhoc-%s' % channel
+ self.props.icon_name = icon_name
+ icon = self._palette.props.icon
+ icon.props.icon_name = icon_name
+ else:
+ if self._device_state == network.DEVICE_STATE_ACTIVATED and \
+ self._active_ap is not None:
+ icon_name = '%s-connected' % _AP_ICON_NAME
+ else:
+ icon_name = _AP_ICON_NAME
+
+ icon_name = get_icon_state(icon_name, self._strength)
+ if icon_name:
+ self.props.icon_name = icon_name
+ icon = self._palette.props.icon
+ icon.props.icon_name = icon_name
+
+ def _update_badge(self):
+ if self._mode != network.NM_802_11_MODE_ADHOC:
+ if network.find_connection_by_ssid(self._name) is not None:
+ self.props.badge_name = 'emblem-favorite'
+ self._palette_icon.props.badge_name = 'emblem-favorite'
+ elif self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:
+ self.props.badge_name = 'emblem-locked'
+ self._palette_icon.props.badge_name = 'emblem-locked'
+ else:
+ self.props.badge_name = None
+ self._palette_icon.props.badge_name = None
+ else:
+ self.props.badge_name = None
+ self._palette_icon.props.badge_name = None
+
+ def _update_state(self):
+ if self._active_ap is not None:
+ state = self._device_state
+ else:
+ state = network.DEVICE_STATE_UNKNOWN
+
+ if state == network.DEVICE_STATE_PREPARE or \
+ state == network.DEVICE_STATE_CONFIG or \
+ state == network.DEVICE_STATE_NEED_AUTH or \
+ state == network.DEVICE_STATE_IP_CONFIG:
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connecting...')
+ self.props.pulsing = True
+ elif state == network.DEVICE_STATE_ACTIVATED:
+ connection = network.find_connection_by_ssid(self._name)
+ if connection is not None:
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection.set_connected()
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connected')
+ self.props.pulsing = False
+ else:
+ if self._disconnect_item:
+ self._disconnect_item.hide()
+ self._connect_item.show()
+ self._palette.props.secondary_text = None
+ self.props.pulsing = False
+
+ def _update_color(self):
+ if self._greyed_out:
+ self.props.pulsing = False
+ self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
+ else:
+ self.props.base_color = self._color
+
+ def _disconnect_activate_cb(self, item):
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection = network.find_connection_by_ssid(self._name)
+ if connection:
+ connection.disable_autoconnect()
+
+ ap_paths = self._access_points.keys()
+ network.disconnect_access_points(ap_paths)
+
+ def _add_ciphers_from_flags(self, flags, pairwise):
+ ciphers = []
+ if pairwise:
+ if flags & network.NM_802_11_AP_SEC_PAIR_TKIP:
+ ciphers.append('tkip')
+ if flags & network.NM_802_11_AP_SEC_PAIR_CCMP:
+ ciphers.append('ccmp')
+ else:
+ if flags & network.NM_802_11_AP_SEC_GROUP_WEP40:
+ ciphers.append('wep40')
+ if flags & network.NM_802_11_AP_SEC_GROUP_WEP104:
+ ciphers.append('wep104')
+ if flags & network.NM_802_11_AP_SEC_GROUP_TKIP:
+ ciphers.append('tkip')
+ if flags & network.NM_802_11_AP_SEC_GROUP_CCMP:
+ ciphers.append('ccmp')
+ return ciphers
+
+ def _get_security(self):
+ if not (self._flags & network.NM_802_11_AP_FLAGS_PRIVACY) and \
+ (self._wpa_flags == network.NM_802_11_AP_SEC_NONE) and \
+ (self._rsn_flags == network.NM_802_11_AP_SEC_NONE):
+ # No security
+ return None
+
+ if (self._flags & network.NM_802_11_AP_FLAGS_PRIVACY) and \
+ (self._wpa_flags == network.NM_802_11_AP_SEC_NONE) and \
+ (self._rsn_flags == network.NM_802_11_AP_SEC_NONE):
+ # Static WEP, Dynamic WEP, or LEAP
+ wireless_security = WirelessSecurity()
+ wireless_security.key_mgmt = 'none'
+ return wireless_security
+
+ if (self._mode != network.NM_802_11_MODE_INFRA):
+ # Stuff after this point requires infrastructure
+ logging.error('The infrastructure mode is not supoorted'
+ ' by your wireless device.')
+ return None
+
+ if (self._rsn_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK) and \
+ (self._device_caps & network.NM_802_11_DEVICE_CAP_RSN):
+ # WPA2 PSK first
+ pairwise = self._add_ciphers_from_flags(self._rsn_flags, True)
+ group = self._add_ciphers_from_flags(self._rsn_flags, False)
+ wireless_security = WirelessSecurity()
+ wireless_security.key_mgmt = 'wpa-psk'
+ wireless_security.proto = 'rsn'
+ wireless_security.pairwise = pairwise
+ wireless_security.group = group
+ return wireless_security
+
+ if (self._wpa_flags & network.NM_802_11_AP_SEC_KEY_MGMT_PSK) and \
+ (self._device_caps & network.NM_802_11_DEVICE_CAP_WPA):
+ # WPA PSK
+ pairwise = self._add_ciphers_from_flags(self._wpa_flags, True)
+ group = self._add_ciphers_from_flags(self._wpa_flags, False)
+ wireless_security = WirelessSecurity()
+ wireless_security.key_mgmt = 'wpa-psk'
+ wireless_security.proto = 'wpa'
+ wireless_security.pairwise = pairwise
+ wireless_security.group = group
+ return wireless_security
+
+ def __connect_activate_cb(self, icon):
+ self._connect()
+
+ def __button_release_event_cb(self, icon, event):
+ self._connect()
+
+ def _connect(self):
+ connection = network.find_connection_by_ssid(self._name)
+ if connection is None:
+ settings = Settings()
+ settings.connection.id = 'Auto ' + self._name
+ uuid = settings.connection.uuid = unique_id()
+ settings.connection.type = '802-11-wireless'
+ settings.wireless.ssid = self._name
+
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ 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 = 'link-local'
+
+ wireless_security = self._get_security()
+ settings.wireless_security = wireless_security
+
+ if wireless_security is not None:
+ settings.wireless.security = '802-11-wireless-security'
+
+ connection = network.add_connection(uuid, settings)
+
+ obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
+ netmgr = dbus.Interface(obj, _NM_IFACE)
+
+ netmgr.ActivateConnection(network.SETTINGS_SERVICE, connection.path,
+ self._device.object_path,
+ '/',
+ reply_handler=self.__activate_reply_cb,
+ error_handler=self.__activate_error_cb)
+
+ def __activate_reply_cb(self, connection):
+ logging.debug('Connection activated: %s', connection)
+
+ def __activate_error_cb(self, err):
+ logging.error('Failed to activate connection: %s', err)
+
+ def set_filter(self, query):
+ self._greyed_out = self._name.lower().find(query) == -1
+ self._update_icon()
+ self._update_color()
+
+ def create_keydialog(self, settings, response):
+ keydialog.create(self._name, self._flags, self._wpa_flags,
+ self._rsn_flags, self._device_caps, settings,
+ response)
+
+ def update_strength(self):
+ if self._active_ap is not None:
+ # display strength of AP that we are connected to
+ new_strength = self._active_ap.strength
+ else:
+ # display the strength of the strongest AP that makes up this
+ # network, also considering that there may be no APs
+ new_strength = max([0] + [ap.strength for ap in
+ self._access_points.values()])
+
+ if new_strength != self._strength:
+ self._strength = new_strength
+ self._update_icon()
+
+ def add_ap(self, ap):
+ self._access_points[ap.model.object_path] = ap
+ self.update_strength()
+
+ def remove_ap(self, ap):
+ path = ap.model.object_path
+ if path not in self._access_points:
+ return
+ del self._access_points[path]
+ if self._active_ap == ap:
+ self._active_ap = None
+ self.update_strength()
+
+ def num_aps(self):
+ return len(self._access_points)
+
+ def find_ap(self, ap_path):
+ if ap_path not in self._access_points:
+ return None
+ return self._access_points[ap_path]
+
+ def is_olpc_mesh(self):
+ return self._mode == network.NM_802_11_MODE_ADHOC \
+ and self.name == 'olpc-mesh'
+
+ def remove_all_aps(self):
+ for ap in self._access_points.values():
+ ap.disconnect()
+ self._access_points = {}
+ self._active_ap = None
+ self.update_strength()
+
+ def disconnect(self):
+ self._bus.remove_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+ self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_WIRELESS_IFACE)
+
+
+class SugarAdhocView(CanvasPulsingIcon):
+ """To mimic the mesh behavior on devices where mesh hardware is
+ not available we support the creation of an Ad-hoc network on
+ three channels 1, 6, 11. This is the class for an icon
+ representing a channel in the neighborhood view.
+
+ """
+
+ _ICON_NAME = 'network-adhoc-'
+ _NAME = 'Ad-hoc Network '
+
+ def __init__(self, channel):
+ CanvasPulsingIcon.__init__(self,
+ icon_name=self._ICON_NAME + str(channel),
+ size=style.STANDARD_ICON_SIZE, cache=True)
+ self._bus = dbus.SystemBus()
+ self._channel = channel
+ self._disconnect_item = None
+ self._connect_item = None
+ self._palette_icon = None
+ self._greyed_out = False
+
+ get_adhoc_manager_instance().connect('members-changed',
+ self.__members_changed_cb)
+ get_adhoc_manager_instance().connect('state-changed',
+ self.__state_changed_cb)
+
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.pulse_color = pulse_color
+ self._state_color = XoColor('%s,%s' % \
+ (profile.get_color().get_stroke_color(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.base_color = self._state_color
+ self._palette = self._create_palette()
+ self.set_palette(self._palette)
+ self._palette_icon.props.xo_color = self._state_color
+
+ def _create_palette(self):
+ self._palette_icon = Icon( \
+ icon_name=self._ICON_NAME + str(self._channel),
+ icon_size=style.STANDARD_ICON_SIZE)
+
+ palette_ = palette.Palette(_('Ad-hoc Network %d') % self._channel,
+ icon=self._palette_icon)
+
+ self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
+ self._connect_item.connect('activate', self.__connect_activate_cb)
+ palette_.menu.append(self._connect_item)
+
+ self._disconnect_item = MenuItem(_('Disconnect'), 'media-eject')
+ self._disconnect_item.connect('activate',
+ self.__disconnect_activate_cb)
+ palette_.menu.append(self._disconnect_item)
+
+ return palette_
+
+ def __button_release_event_cb(self, icon, event):
+ get_adhoc_manager_instance().activate_channel(self._channel)
+
+ def __connect_activate_cb(self, icon):
+ get_adhoc_manager_instance().activate_channel(self._channel)
+
+ def __disconnect_activate_cb(self, icon):
+ get_adhoc_manager_instance().deactivate_active_channel()
+
+ def __state_changed_cb(self, adhoc_manager, channel, device_state):
+ if self._channel == channel:
+ state = device_state
+ else:
+ state = network.DEVICE_STATE_UNKNOWN
+
+ if state == network.DEVICE_STATE_ACTIVATED:
+ icon_name = '%s-connected' % (self._ICON_NAME + str(self._channel))
+ else:
+ icon_name = self._ICON_NAME + str(self._channel)
+
+ if icon_name is not None:
+ self.props.icon_name = icon_name
+ icon = self._palette.props.icon
+ icon.props.icon_name = icon_name
+
+ if state in [network.DEVICE_STATE_PREPARE,
+ network.DEVICE_STATE_CONFIG,
+ network.DEVICE_STATE_NEED_AUTH,
+ network.DEVICE_STATE_IP_CONFIG]:
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connecting...')
+ self.props.pulsing = True
+ elif state == network.DEVICE_STATE_ACTIVATED:
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connected')
+ self.props.pulsing = False
+ else:
+ if self._disconnect_item:
+ self._disconnect_item.hide()
+ self._connect_item.show()
+ self._palette.props.secondary_text = None
+ self.props.pulsing = False
+
+ def _update_color(self):
+ if self._greyed_out:
+ self.props.pulsing = False
+ self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
+ else:
+ self.props.base_color = self._state_color
+
+ def __members_changed_cb(self, adhoc_manager, channel, has_members):
+ if channel == self._channel:
+ if has_members == True:
+ self._state_color = profile.get_color()
+ else:
+ color = '%s,%s' % (profile.get_color().get_stroke_color(),
+ style.COLOR_TRANSPARENT.get_svg())
+ self._state_color = XoColor(color)
+
+ if not self._greyed_out:
+ self.props.base_color = self._state_color
+ self._palette_icon.props.xo_color = self._state_color
+
+ def set_filter(self, query):
+ name = self._NAME + str(self._channel)
+ self._greyed_out = name.lower().find(query) == -1
+ self._update_color()
+
+
+class OlpcMeshView(CanvasPulsingIcon):
+ def __init__(self, mesh_mgr, channel):
+ CanvasPulsingIcon.__init__(self, icon_name=_OLPC_MESH_ICON_NAME,
+ size=style.STANDARD_ICON_SIZE, cache=True)
+ self._bus = dbus.SystemBus()
+ self._channel = channel
+ self._mesh_mgr = mesh_mgr
+ self._disconnect_item = None
+ self._connect_item = None
+ self._greyed_out = False
+ self._name = ''
+ self._device_state = None
+ self._active = False
+ device = mesh_mgr.mesh_device
+
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ interface_props = dbus.Interface(device, dbus.PROPERTIES_IFACE)
+ interface_props.Get(_NM_DEVICE_IFACE, 'State',
+ reply_handler=self.__get_device_state_reply_cb,
+ error_handler=self.__get_device_state_error_cb)
+ interface_props.Get(_NM_OLPC_MESH_IFACE, 'ActiveChannel',
+ reply_handler=self.__get_active_channel_reply_cb,
+ error_handler=self.__get_active_channel_error_cb)
+
+ self._bus.add_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+ self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=device.object_path,
+ dbus_interface=_NM_OLPC_MESH_IFACE)
+
+ pulse_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self.props.pulse_color = pulse_color
+ self.props.base_color = profile.get_color()
+ self._palette = self._create_palette()
+ self.set_palette(self._palette)
+
+ def _create_palette(self):
+ _palette = palette.Palette(_('Mesh Network %d') % self._channel)
+
+ self._connect_item = MenuItem(_('Connect'), 'dialog-ok')
+ self._connect_item.connect('activate', self.__connect_activate_cb)
+ _palette.menu.append(self._connect_item)
+
+ return _palette
+
+ def __get_device_state_reply_cb(self, state):
+ self._device_state = state
+ self._update()
+
+ def __get_device_state_error_cb(self, err):
+ logging.error('Error getting the device state: %s', err)
+
+ def __device_state_changed_cb(self, new_state, old_state, reason):
+ self._device_state = new_state
+ self._update()
+
+ def __get_active_channel_reply_cb(self, channel):
+ self._active = (channel == self._channel)
+ self._update()
+
+ def __get_active_channel_error_cb(self, err):
+ logging.error('Error getting the active channel: %s', err)
+
+ def __wireless_properties_changed_cb(self, properties):
+ if 'ActiveChannel' in properties:
+ channel = properties['ActiveChannel']
+ self._active = (channel == self._channel)
+ self._update()
+
+ def _update(self):
+ if self._active:
+ state = self._device_state
+ else:
+ state = network.DEVICE_STATE_UNKNOWN
+
+ if state in [network.DEVICE_STATE_PREPARE,
+ network.DEVICE_STATE_CONFIG,
+ network.DEVICE_STATE_NEED_AUTH,
+ network.DEVICE_STATE_IP_CONFIG]:
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connecting...')
+ self.props.pulsing = True
+ elif state == network.DEVICE_STATE_ACTIVATED:
+ if self._disconnect_item:
+ self._disconnect_item.show()
+ self._connect_item.hide()
+ self._palette.props.secondary_text = _('Connected')
+ self.props.pulsing = False
+ else:
+ if self._disconnect_item:
+ self._disconnect_item.hide()
+ self._connect_item.show()
+ self._palette.props.secondary_text = None
+ self.props.pulsing = False
+
+ def _update_color(self):
+ if self._greyed_out:
+ self.props.base_color = XoColor('#D5D5D5,#D5D5D5')
+ else:
+ self.props.base_color = profile.get_color()
+
+ def __connect_activate_cb(self, icon):
+ self._connect()
+
+ def __button_release_event_cb(self, icon, event):
+ self._connect()
+
+ def _connect(self):
+ self._mesh_mgr.user_activate_channel(self._channel)
+
+ def __activate_reply_cb(self, connection):
+ logging.debug('Connection activated: %s', connection)
+
+ def __activate_error_cb(self, err):
+ logging.error('Failed to activate connection: %s', err)
+
+ def set_filter(self, query):
+ self._greyed_out = (query != '')
+ self._update_color()
+
+ def disconnect(self):
+ device_object_path = self._mesh_mgr.mesh_device.object_path
+
+ self._bus.remove_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=device_object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+ self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=device_object_path,
+ dbus_interface=_NM_OLPC_MESH_IFACE)
diff --git a/src/jarabe/desktop/schoolserver.py b/src/jarabe/desktop/schoolserver.py
index fc9ddeb..aea2357 100644
--- a/src/jarabe/desktop/schoolserver.py
+++ b/src/jarabe/desktop/schoolserver.py
@@ -16,8 +16,9 @@
import logging
from gettext import gettext as _
-from xmlrpclib import ServerProxy, Error
+import xmlrpclib
import socket
+import httplib
import os
from string import ascii_uppercase
import random
@@ -29,15 +30,17 @@ import gconf
from sugar import env
from sugar.profile import get_profile
-REGISTER_URL = 'http://schoolserver:8080/'
+_REGISTER_URL = 'http://schoolserver:8080/'
+_REGISTER_TIMEOUT = 8
-def generate_serial_number():
+
+def _generate_serial_number():
""" Generates a serial number based on 3 random uppercase letters
and the last 8 digits of the current unix seconds. """
serial_part1 = []
- for y_ in range(3) :
+ for y_ in range(3):
serial_part1.append(random.choice(ascii_uppercase))
serial_part1 = ''.join(serial_part1)
@@ -46,7 +49,8 @@ def generate_serial_number():
return serial
-def store_identifiers(serial_number, uuid, backup_url):
+
+def _store_identifiers(serial_number, uuid_, backup_url):
""" Stores the serial number, uuid and backup_url
in the identifier folder inside the profile directory
so that these identifiers can be used for backup. """
@@ -64,7 +68,7 @@ def store_identifiers(serial_number, uuid, backup_url):
if os.path.exists(os.path.join(identifier_path, 'uuid')):
os.remove(os.path.join(identifier_path, 'uuid'))
uuid_file = open(os.path.join(identifier_path, 'uuid'), 'w')
- uuid_file.write(uuid)
+ uuid_file.write(uuid_)
uuid_file.close()
if os.path.exists(os.path.join(identifier_path, 'backup_url')):
@@ -73,33 +77,56 @@ def store_identifiers(serial_number, uuid, backup_url):
backup_url_file.write(backup_url)
backup_url_file.close()
+
class RegisterError(Exception):
pass
-def register_laptop(url=REGISTER_URL):
+
+class _TimeoutHTTP(httplib.HTTP):
+
+ def __init__(self, host='', port=None, strict=None, timeout=None):
+ if port == 0:
+ port = None
+ # FIXME: Depending on undocumented internals that can break between
+ # Python releases. Please have a look at SL #2350
+ self._setup(self._connection_class(host,
+ port, strict, timeout=_REGISTER_TIMEOUT))
+
+
+class _TimeoutTransport(xmlrpclib.Transport):
+
+ def make_connection(self, host):
+ host, extra_headers, x509_ = self.get_host_info(host)
+ return _TimeoutHTTP(host, timeout=_REGISTER_TIMEOUT)
+
+
+def register_laptop(url=_REGISTER_URL):
profile = get_profile()
client = gconf.client_get_default()
- if have_ofw_tree():
- sn = read_ofw('mfg-data/SN')
- uuid_ = read_ofw('mfg-data/U#')
+ if _have_ofw_tree():
+ sn = _read_ofw('mfg-data/SN')
+ uuid_ = _read_ofw('mfg-data/U#')
sn = sn or 'SHF00000000'
uuid_ = uuid_ or '00000000-0000-0000-0000-000000000000'
else:
- sn = generate_serial_number()
+ sn = _generate_serial_number()
uuid_ = str(uuid.uuid1())
- setting_name = '/desktop/sugar/collaboration/jabber_server'
- jabber_server = client.get_string(setting_name)
- store_identifiers(sn, uuid_, jabber_server)
+
+ setting_name = '/desktop/sugar/collaboration/jabber_server'
+ jabber_server = client.get_string(setting_name)
+ _store_identifiers(sn, uuid_, jabber_server)
+
+ if jabber_server:
url = 'http://' + jabber_server + ':8080/'
nick = client.get_string('/desktop/sugar/user/nick')
- server = ServerProxy(url)
+ server = xmlrpclib.ServerProxy(url, _TimeoutTransport())
try:
data = server.register(sn, nick, uuid_, profile.pubkey)
- except (Error, socket.error):
+ except (xmlrpclib.Error, TypeError, socket.error):
logging.exception('Registration: cannot connect to server')
raise RegisterError(_('Cannot connect to the server.'))
@@ -114,10 +141,12 @@ def register_laptop(url=REGISTER_URL):
return True
-def have_ofw_tree():
+
+def _have_ofw_tree():
return os.path.exists('/ofw')
-def read_ofw(path):
+
+def _read_ofw(path):
path = os.path.join('/ofw', path)
if not os.path.exists(path):
return None
diff --git a/src/jarabe/desktop/snowflakelayout.py b/src/jarabe/desktop/snowflakelayout.py
index 5782cff..e4963ba 100644
--- a/src/jarabe/desktop/snowflakelayout.py
+++ b/src/jarabe/desktop/snowflakelayout.py
@@ -21,11 +21,14 @@ import hippo
from sugar.graphics import style
+
_BASE_DISTANCE = style.zoom(25)
_CHILDREN_FACTOR = style.zoom(3)
+
class SnowflakeLayout(gobject.GObject, hippo.CanvasLayout):
__gtype_name__ = 'SugarSnowflakeLayout'
+
def __init__(self):
gobject.GObject.__init__(self)
self._nflakes = 0
diff --git a/src/jarabe/desktop/spreadlayout.py b/src/jarabe/desktop/spreadlayout.py
index ffc5bc7..9200361 100644
--- a/src/jarabe/desktop/spreadlayout.py
+++ b/src/jarabe/desktop/spreadlayout.py
@@ -22,10 +22,13 @@ from sugar.graphics import style
from jarabe.desktop.grid import Grid
+
_CELL_SIZE = 4.0
+
class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
__gtype_name__ = 'SugarSpreadLayout'
+
def __init__(self):
gobject.GObject.__init__(self)
self._box = None
@@ -80,4 +83,3 @@ class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
def _grid_child_changed_cb(self, grid, child):
child.emit_request_changed()
-
diff --git a/src/jarabe/desktop/transitionbox.py b/src/jarabe/desktop/transitionbox.py
index af17cfb..4042044 100644
--- a/src/jarabe/desktop/transitionbox.py
+++ b/src/jarabe/desktop/transitionbox.py
@@ -20,7 +20,9 @@ import gobject
from sugar.graphics import style
from sugar.graphics import animator
-from jarabe.desktop.myicon import MyIcon
+from jarabe.model.buddy import get_owner_instance
+from jarabe.view.buddyicon import BuddyIcon
+
class _Animation(animator.Animation):
def __init__(self, icon, start_size, end_size):
@@ -34,8 +36,10 @@ class _Animation(animator.Animation):
d = (self.end_size - self.start_size) * current
self._icon.props.size = self.start_size + d
+
class _Layout(gobject.GObject, hippo.CanvasLayout):
__gtype_name__ = 'SugarTransitionBoxLayout'
+
def __init__(self):
gobject.GObject.__init__(self)
self._box = None
@@ -59,12 +63,12 @@ class _Layout(gobject.GObject, hippo.CanvasLayout):
y + (height - child_height) / 2,
child_width, child_height, origin_changed)
+
class TransitionBox(hippo.Canvas):
__gtype_name__ = 'SugarTransitionBox'
__gsignals__ = {
- 'completed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([]))
+ 'completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self):
@@ -77,7 +81,8 @@ class TransitionBox(hippo.Canvas):
self._layout = _Layout()
self._box.set_layout(self._layout)
- self._my_icon = MyIcon(style.XLARGE_ICON_SIZE)
+ self._my_icon = BuddyIcon(buddy=get_owner_instance(),
+ size=style.XLARGE_ICON_SIZE)
self._box.append(self._my_icon)
self._animator = animator.Animator(0.3)
diff --git a/src/jarabe/frame/__init__.py b/src/jarabe/frame/__init__.py
index d7aec3d..b3e4b80 100644
--- a/src/jarabe/frame/__init__.py
+++ b/src/jarabe/frame/__init__.py
@@ -16,8 +16,10 @@
from jarabe.frame.frame import Frame
+
_view = None
+
def get_view():
global _view
if not _view:
diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py
index b5762ee..6e08fc0 100644
--- a/src/jarabe/frame/activitiestray.py
+++ b/src/jarabe/frame/activitiestray.py
@@ -1,5 +1,6 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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,20 +34,16 @@ from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.icon import Icon, get_icon_file_name
from sugar.graphics.palette import Palette, WidgetInvoker
from sugar.graphics.menuitem import MenuItem
-from sugar.activity.activityhandle import ActivityHandle
-from sugar.activity import activityfactory
from sugar.datastore import datastore
from sugar import mime
from sugar import env
from jarabe.model import shell
-from jarabe.model import neighborhood
-from jarabe.model import owner
+from jarabe.model import invites
from jarabe.model import bundleregistry
from jarabe.model import filetransfer
from jarabe.view.palettes import JournalPalette, CurrentActivityPalette
from jarabe.view.pulsingicon import PulsingIcon
-from jarabe.view import launcher
from jarabe.frame.frameinvoker import FrameWidgetInvoker
from jarabe.frame.notification import NotificationIcon
import jarabe.frame
@@ -59,6 +56,7 @@ class ActivityButton(RadioToolButton):
self.set_palette_invoker(FrameWidgetInvoker(self))
self._home_activity = home_activity
+ self._notify_launch_hid = None
self._icon = PulsingIcon()
self._icon.props.base_color = home_activity.get_icon_color()
@@ -72,13 +70,12 @@ class ActivityButton(RadioToolButton):
self.set_icon_widget(self._icon)
self._icon.show()
- if home_activity.props.launching:
+ if home_activity.props.launch_status == shell.Activity.LAUNCHING:
self._icon.props.pulsing = True
- self._notify_launching_hid = home_activity.connect( \
- 'notify::launching', self.__notify_launching_cb)
- else:
- self._notify_launching_hid = None
- self._notif_icon = None
+ self._notify_launch_hid = home_activity.connect( \
+ 'notify::launch-status', self.__notify_launch_status_cb)
+ elif home_activity.props.launch_status == shell.Activity.LAUNCH_FAILED:
+ self._on_failed_launch()
def create_palette(self):
if self._home_activity.is_journal():
@@ -88,26 +85,64 @@ class ActivityButton(RadioToolButton):
palette.set_group_id('frame')
self.set_palette(palette)
- def __notify_launching_cb(self, home_activity, pspec):
- if not home_activity.props.launching:
+ def _on_failed_launch(self):
+ # TODO http://bugs.sugarlabs.org/ticket/2007
+ pass
+
+ def __notify_launch_status_cb(self, home_activity, pspec):
+ home_activity.disconnect(self._notify_launch_hid)
+ self._notify_launch_hid = None
+ if home_activity.props.launch_status == shell.Activity.LAUNCH_FAILED:
+ self._on_failed_launch()
+ else:
self._icon.props.pulsing = False
- home_activity.disconnect(self._notify_launching_hid)
-class BaseInviteButton(ToolButton):
+
+class InviteButton(ToolButton):
+ """Invite to shared activity"""
def __init__(self, invite):
ToolButton.__init__(self)
+
self._invite = invite
+ self.connect('clicked', self.__clicked_cb)
+ self.connect('destroy', self.__destroy_cb)
+
+ bundle_registry = bundleregistry.get_registry()
+ bundle = bundle_registry.get_bundle(invite.get_bundle_id())
+
self._icon = Icon()
+ self._icon.props.xo_color = invite.get_color()
+ if bundle is not None:
+ self._icon.props.file = bundle.get_icon()
+ else:
+ self._icon.props.icon_name = 'image-missing'
self.set_icon_widget(self._icon)
self._icon.show()
- self.connect('clicked', self.__clicked_cb)
- self.connect('destroy', self.__destroy_cb)
+ palette = InvitePalette(invite)
+ palette.props.invoker = FrameWidgetInvoker(self)
+ palette.set_group_id('frame')
+ self.set_palette(palette)
+
self._notif_icon = NotificationIcon()
self._notif_icon.connect('button-release-event',
self.__button_release_event_cb)
+ self._notif_icon.props.xo_color = invite.get_color()
+ if bundle is not None:
+ self._notif_icon.props.icon_filename = bundle.get_icon()
+ else:
+ self._notif_icon.props.icon_name = 'image-missing'
+
+ palette = InvitePalette(invite)
+ palette.props.invoker = WidgetInvoker(self._notif_icon)
+ palette.set_group_id('frame')
+ self._notif_icon.palette = palette
+
+ frame = jarabe.frame.get_view()
+ frame.add_notification(self._notif_icon, gtk.CORNER_TOP_LEFT)
+
def __button_release_event_cb(self, icon, event):
self.emit('clicked')
@@ -118,118 +153,23 @@ class BaseInviteButton(ToolButton):
self._notif_icon = None
self._launch()
- def _launch(self):
- """Launch the target of the invite"""
- raise NotImplementedError
-
def __destroy_cb(self, button):
frame = jarabe.frame.get_view()
frame.remove_notification(self._notif_icon)
-class ActivityInviteButton(BaseInviteButton):
- """Invite to shared activity"""
- def __init__(self, invite):
- BaseInviteButton.__init__(self, invite)
- mesh = neighborhood.get_model()
- activity_model = mesh.get_activity(invite.get_activity_id())
- self._activity_model = activity_model
- self._bundle_id = activity_model.get_bundle_id()
-
- self._icon.props.xo_color = activity_model.get_color()
- if activity_model.get_icon_name():
- self._icon.props.file = activity_model.get_icon_name()
- else:
- self._icon.props.icon_name = 'image-missing'
-
- palette = ActivityInvitePalette(invite)
- palette.props.invoker = FrameWidgetInvoker(self)
- palette.set_group_id('frame')
- self.set_palette(palette)
-
- self._notif_icon.props.xo_color = activity_model.get_color()
- if activity_model.get_icon_name():
- icon_name = activity_model.get_icon_name()
- self._notif_icon.props.icon_filename = icon_name
- else:
- self._notif_icon.props.icon_name = 'image-missing'
-
- palette = ActivityInvitePalette(invite)
- palette.props.invoker = WidgetInvoker(self._notif_icon)
- palette.set_group_id('frame')
- self._notif_icon.palette = palette
-
- frame = jarabe.frame.get_view()
- frame.add_notification(self._notif_icon,
- gtk.CORNER_TOP_LEFT)
-
def _launch(self):
"""Join the activity in the invite."""
+ self._invite.join()
- shell_model = shell.get_model()
- activity = shell_model.get_activity_by_id(self._activity_model.get_id())
- if activity:
- activity.get_window().activate(gtk.get_current_event_time())
- return
-
- registry = bundleregistry.get_registry()
- bundle = registry.get_bundle(self._bundle_id)
-
- launcher.add_launcher(self._activity_model.get_id(),
- bundle.get_icon(),
- self._activity_model.get_color())
- handle = ActivityHandle(self._activity_model.get_id())
- activityfactory.create(bundle, handle)
+class InvitePalette(Palette):
+ """Palette for frame or notification icon for invites."""
-class PrivateInviteButton(BaseInviteButton):
- """Invite to a private one to one channel"""
def __init__(self, invite):
- BaseInviteButton.__init__(self, invite)
- self._private_channel = invite.get_private_channel()
- self._bundle_id = invite.get_bundle_id()
-
- client = gconf.client_get_default()
- color = XoColor(client.get_string('/desktop/sugar/user/color'))
-
- self._icon.props.xo_color = color
- registry = bundleregistry.get_registry()
- self._bundle = registry.get_bundle(self._bundle_id)
-
- if self._bundle:
- self._icon.props.file = self._bundle.get_icon()
- else:
- self._icon.props.icon_name = 'image-missing'
-
- palette = PrivateInvitePalette(invite)
- palette.props.invoker = FrameWidgetInvoker(self)
- palette.set_group_id('frame')
- self.set_palette(palette)
-
- self._notif_icon.props.xo_color = color
-
- if self._bundle:
- self._notif_icon.props.icon_filename = self._bundle.get_icon()
- else:
- self._notif_icon.props.icon_name = 'image-missing'
-
- palette = PrivateInvitePalette(invite)
- palette.props.invoker = WidgetInvoker(self._notif_icon)
- palette.set_group_id('frame')
- self._notif_icon.palette = palette
-
- frame = jarabe.frame.get_view()
- frame.add_notification(self._notif_icon,
- gtk.CORNER_TOP_LEFT)
-
- def _launch(self):
- """Start the activity with private channel."""
- activityfactory.create_with_uri(self._bundle, self._private_channel)
-
-class BaseInvitePalette(Palette):
- """Palette for frame or notification icon for invites."""
- def __init__(self):
Palette.__init__(self, '')
+ self._invite = invite
+
menu_item = MenuItem(_('Join'), icon_name='dialog-ok')
menu_item.connect('activate', self.__join_activate_cb)
self.menu.append(menu_item)
@@ -240,72 +180,22 @@ class BaseInvitePalette(Palette):
self.menu.append(menu_item)
menu_item.show()
- def __join_activate_cb(self, menu_item):
- self._join()
-
- def __decline_activate_cb(self, menu_item):
- self._decline()
-
- def _join(self):
- raise NotImplementedError
-
- def _decline(self):
- raise NotImplementedError
-
-
-class ActivityInvitePalette(BaseInvitePalette):
- """Palette for shared activity invites."""
-
- def __init__(self, invite):
- BaseInvitePalette.__init__(self)
-
- mesh = neighborhood.get_model()
- activity_model = mesh.get_activity(invite.get_activity_id())
- self._activity_model = activity_model
- self._bundle_id = activity_model.get_bundle_id()
+ bundle_id = invite.get_bundle_id()
registry = bundleregistry.get_registry()
- self._bundle = registry.get_bundle(self._bundle_id)
+ self._bundle = registry.get_bundle(bundle_id)
if self._bundle:
self.set_primary_text(self._bundle.get_name())
else:
- self.set_primary_text(self._bundle_id)
+ self.set_primary_text(bundle_id)
- def _join(self):
- handle = ActivityHandle(self._activity_model.get_id())
- activityfactory.create(self._bundle, handle)
+ def __join_activate_cb(self, menu_item):
+ self._invite.join()
- def _decline(self):
- invites = owner.get_model().get_invites()
+ def __decline_activate_cb(self, menu_item):
+ invites_model = invites.get_instance()
activity_id = self._activity_model.get_id()
- invites.remove_activity(activity_id)
-
-
-class PrivateInvitePalette(BaseInvitePalette):
- """Palette for private channel invites."""
-
- def __init__(self, invite):
- BaseInvitePalette.__init__(self)
-
- self._private_channel = invite.get_private_channel()
- self._bundle_id = invite.get_bundle_id()
-
- registry = bundleregistry.get_registry()
- self._bundle = registry.get_bundle(self._bundle_id)
- if self._bundle:
- self.set_primary_text(self._bundle.get_name())
- else:
- self.set_primary_text(self._bundle_id)
-
- def _join(self):
- activityfactory.create_with_uri(self._bundle, self._private_channel)
-
- invites = owner.get_model().get_invites()
- invites.remove_private_channel(self._private_channel)
-
- def _decline(self):
- invites = owner.get_model().get_invites()
- invites.remove_private_channel(self._private_channel)
+ invites_model.remove_activity(activity_id)
class ActivitiesTray(HTray):
@@ -318,13 +208,14 @@ class ActivitiesTray(HTray):
self._home_model = shell.get_model()
self._home_model.connect('activity-added', self.__activity_added_cb)
- self._home_model.connect('activity-removed', self.__activity_removed_cb)
+ self._home_model.connect('activity-removed',
+ self.__activity_removed_cb)
self._home_model.connect('active-activity-changed',
self.__activity_changed_cb)
self._home_model.connect('tabbing-activity-changed',
self.__tabbing_activity_changed_cb)
- self._invites = owner.get_model().get_invites()
+ self._invites = invites.get_instance()
for invite in self._invites:
self._add_invite(invite)
self._invites.connect('invite-added', self.__invite_added_cb)
@@ -388,32 +279,22 @@ class ActivitiesTray(HTray):
window.activate(gtk.get_current_event_time())
def __invite_clicked_cb(self, icon, invite):
- if hasattr(invite, 'get_activity_id'):
- self._invites.remove_invite(invite)
- else:
- self._invites.remove_private_invite(invite)
+ self._invites.remove_invite(invite)
- def __invite_added_cb(self, invites, invite):
+ def __invite_added_cb(self, invites_model, invite):
self._add_invite(invite)
- def __invite_removed_cb(self, invites, invite):
+ def __invite_removed_cb(self, invites_model, invite):
self._remove_invite(invite)
def _add_invite(self, invite):
- """Add an invite (SugarInvite or PrivateInvite)"""
- item = None
- if hasattr(invite, 'get_activity_id'):
- mesh = neighborhood.get_model()
- activity_model = mesh.get_activity(invite.get_activity_id())
- if activity_model is not None:
- item = ActivityInviteButton(invite)
- else:
- item = PrivateInviteButton(invite)
- if item is not None:
- item.connect('clicked', self.__invite_clicked_cb, invite)
- self.add_item(item)
- item.show()
- self._invite_to_item[invite] = item
+ """Add an invite"""
+ item = InviteButton(invite)
+ item.connect('clicked', self.__invite_clicked_cb, invite)
+ self.add_item(item)
+ item.show()
+
+ self._invite_to_item[invite] = item
def _remove_invite(self, invite):
self.remove_item(self._invite_to_item[invite])
@@ -432,6 +313,7 @@ class ActivitiesTray(HTray):
self.add_item(button)
button.show()
+
class BaseTransferButton(ToolButton):
"""Button with a notification attached
"""
@@ -468,6 +350,7 @@ class BaseTransferButton(ToolButton):
filetransfer.FT_REASON_LOCAL_STOPPED:
self.remove()
+
class IncomingTransferButton(BaseTransferButton):
"""UI element representing an ongoing incoming file transfer
"""
@@ -490,7 +373,7 @@ class IncomingTransferButton(BaseTransferButton):
self.notif_icon.props.icon_name = icon_name
break
- icon_color = XoColor(file_transfer.buddy.props.color)
+ icon_color = file_transfer.buddy.props.color
self.props.icon_widget.props.xo_color = icon_color
self.notif_icon.props.xo_color = icon_color
@@ -515,18 +398,18 @@ class IncomingTransferButton(BaseTransferButton):
self._ds_object.metadata['buddies'] = ''
self._ds_object.metadata['preview'] = ''
self._ds_object.metadata['icon-color'] = \
- file_transfer.buddy.props.color
+ file_transfer.buddy.props.color.to_string()
self._ds_object.metadata['mime_type'] = file_transfer.mime_type
elif file_transfer.props.state == filetransfer.FT_STATE_COMPLETED:
logging.debug('__notify_state_cb COMPLETED')
self._ds_object.metadata['progress'] = '100'
self._ds_object.file_path = file_transfer.destination_path
- datastore.write(self._ds_jobject, transfer_ownership=True,
+ datastore.write(self._ds_object, transfer_ownership=True,
reply_handler=self.__reply_handler_cb,
error_handler=self.__error_handler_cb)
elif file_transfer.props.state == filetransfer.FT_STATE_CANCELLED:
logging.debug('__notify_state_cb CANCELLED')
- object_id = self._jobject.object_id
+ object_id = self._ds_object.object_id
if object_id is not None:
self._ds_object.destroy()
datastore.delete(object_id)
@@ -536,17 +419,19 @@ class IncomingTransferButton(BaseTransferButton):
progress = file_transfer.props.transferred_bytes / \
file_transfer.file_size
self._ds_object.metadata['progress'] = str(progress * 100)
- datastore.write(self._ds_object.object_id, update_mtime=False)
+ datastore.write(self._ds_object, update_mtime=False)
def __reply_handler_cb(self):
- logging.debug('__reply_handler_cb %r', self._object_id)
+ logging.debug('__reply_handler_cb %r', self._ds_object.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._ds_object.object_id,
+ error)
def __dismiss_clicked_cb(self, palette):
self.remove()
+
class OutgoingTransferButton(BaseTransferButton):
"""UI element representing an ongoing outgoing file transfer
"""
@@ -564,7 +449,7 @@ class OutgoingTransferButton(BaseTransferButton):
break
client = gconf.client_get_default()
- icon_color = XoColor(client.get_string("/desktop/sugar/user/color"))
+ icon_color = XoColor(client.get_string('/desktop/sugar/user/color'))
self.props.icon_widget.props.xo_color = icon_color
self.notif_icon.props.xo_color = icon_color
@@ -582,14 +467,14 @@ class OutgoingTransferButton(BaseTransferButton):
def __dismiss_clicked_cb(self, palette):
self.remove()
+
class BaseTransferPalette(Palette):
"""Base palette class for frame or notification icon for file transfers
"""
- __gtype_name__ = "SugarBaseTransferPalette"
+ __gtype_name__ = 'SugarBaseTransferPalette'
__gsignals__ = {
- 'dismiss-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
+ 'dismiss-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self, file_transfer):
@@ -644,10 +529,12 @@ class BaseTransferPalette(Palette):
total = self._format_size(self.file_transfer.file_size)
self.progress_label.props.label = _('%s of %s') % (transferred, total)
+
class IncomingTransferPalette(BaseTransferPalette):
"""Palette for frame or notification icon for incoming file transfers
"""
- __gtype_name__ = "SugarIncomingTransferPalette"
+ __gtype_name__ = 'SugarIncomingTransferPalette'
+
def __init__(self, file_transfer):
BaseTransferPalette.__init__(self, file_transfer)
@@ -770,10 +657,11 @@ class IncomingTransferPalette(BaseTransferPalette):
def __dismiss_activate_cb(self, menu_item):
self.emit('dismiss-clicked')
+
class OutgoingTransferPalette(BaseTransferPalette):
"""Palette for frame or notification icon for outgoing file transfers
"""
- __gtype_name__ = "SugarOutgoingTransferPalette"
+ __gtype_name__ = 'SugarOutgoingTransferPalette'
def __init__(self, file_transfer):
BaseTransferPalette.__init__(self, file_transfer)
diff --git a/src/jarabe/frame/clipboard.py b/src/jarabe/frame/clipboard.py
index 3b9f745..be2b902 100644
--- a/src/jarabe/frame/clipboard.py
+++ b/src/jarabe/frame/clipboard.py
@@ -26,6 +26,10 @@ from sugar import mime
from jarabe.frame.clipboardobject import ClipboardObject, Format
+
+_instance = None
+
+
class Clipboard(gobject.GObject):
__gsignals__ = {
@@ -34,7 +38,7 @@ class Clipboard(gobject.GObject):
'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([int])),
'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([object]))
+ ([object])),
}
def __init__(self):
@@ -69,7 +73,7 @@ class Clipboard(gobject.GObject):
+ ' with path at ' + new_uri)
else:
cb_object.add_format(Format(format_type, data, on_disk))
- logging.debug('Added in-memory format of type ' + format_type + '.')
+ logging.debug('Added in-memory format of type %s.', format_type)
self.emit('object-state-changed', cb_object)
@@ -82,9 +86,9 @@ class Clipboard(gobject.GObject):
def set_object_percent(self, object_id, percent):
cb_object = self._objects[object_id]
if percent < 0 or percent > 100:
- raise ValueError("invalid percentage")
+ raise ValueError('invalid percentage')
if cb_object.get_percent() > percent:
- raise ValueError("invalid percentage; less than current percent")
+ raise ValueError('invalid percentage; less than current percent')
if cb_object.get_percent() == percent:
# ignore setting same percentage
return
@@ -126,21 +130,21 @@ class Clipboard(gobject.GObject):
def _copy_file(self, original_uri):
uri = urlparse.urlparse(original_uri)
- path_, file_name = os.path.split(uri.path)
+ path = uri.path # pylint: disable=E1101
+ directory_, file_name = os.path.split(path)
root, ext = os.path.splitext(file_name)
if not ext or ext == '.':
- mime_type = mime.get_for_file(uri.path)
+ mime_type = mime.get_for_file(path)
ext = '.' + mime.get_primary_extension(mime_type)
f_, new_file_path = tempfile.mkstemp(ext, root)
del f_
- shutil.copyfile(uri.path, new_file_path)
+ shutil.copyfile(path, new_file_path)
os.chmod(new_file_path, 0644)
return 'file://' + new_file_path
-_instance = None
def get_instance():
global _instance
diff --git a/src/jarabe/frame/clipboardicon.py b/src/jarabe/frame/clipboardicon.py
index 279db08..aa72d8a 100644
--- a/src/jarabe/frame/clipboardicon.py
+++ b/src/jarabe/frame/clipboardicon.py
@@ -31,6 +31,7 @@ from jarabe.frame.frameinvoker import FrameWidgetInvoker
from jarabe.frame.notification import NotificationIcon
import jarabe.frame
+
class ClipboardIcon(RadioToolButton):
__gtype_name__ = 'SugarClipboardIcon'
@@ -71,7 +72,8 @@ class ClipboardIcon(RadioToolButton):
def _drag_data_get_cb(self, widget, context, selection, target_type,
event_time):
- logging.debug('_drag_data_get_cb: requested target ' + selection.target)
+ logging.debug('_drag_data_get_cb: requested target %s',
+ selection.target)
data = self._cb_object.get_formats()[selection.target].get_data()
selection.set(selection.target, 8, data)
@@ -79,8 +81,8 @@ class ClipboardIcon(RadioToolButton):
logging.debug('ClipboardIcon._put_in_clipboard')
if self._cb_object.get_percent() < 100:
- raise ValueError('Object is not complete,' \
- ' cannot be put into the clipboard.')
+ raise ValueError('Object is not complete, cannot be put into the'
+ ' clipboard.')
targets = self._get_targets()
if targets:
diff --git a/src/jarabe/frame/clipboardmenu.py b/src/jarabe/frame/clipboardmenu.py
index b998110..d11538d 100644
--- a/src/jarabe/frame/clipboardmenu.py
+++ b/src/jarabe/frame/clipboardmenu.py
@@ -35,6 +35,7 @@ from jarabe.frame import clipboard
from jarabe.journal import misc
from jarabe.model import bundleregistry
+
class ClipboardMenu(Palette):
def __init__(self, cb_object):
@@ -212,7 +213,8 @@ class ClipboardMenu(Palette):
if most_significant_mime_type == 'text/uri-list':
uris = mime.split_uri_list(format_.get_data())
if len(uris) == 1 and uris[0].startswith('file://'):
- file_path = urlparse.urlparse(uris[0]).path
+ parsed_url = urlparse.urlparse(uris[0])
+ file_path = parsed_url.path # pylint: disable=E1101
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
@@ -221,7 +223,8 @@ class ClipboardMenu(Palette):
mime_type = 'text/uri-list'
else:
if format_.is_on_disk():
- file_path = urlparse.urlparse(format_.get_data()).path
+ parsed_url = urlparse.urlparse(format_.get_data())
+ file_path = parsed_url.path # pylint: disable=E1101
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
diff --git a/src/jarabe/frame/clipboardobject.py b/src/jarabe/frame/clipboardobject.py
index e9403f9..407af2f 100644
--- a/src/jarabe/frame/clipboardobject.py
+++ b/src/jarabe/frame/clipboardobject.py
@@ -24,6 +24,7 @@ from gettext import gettext as _
from sugar import mime
from sugar.bundle.activitybundle import ActivityBundle
+
class ClipboardObject(object):
def __init__(self, object_path, name):
@@ -105,15 +106,18 @@ class ClipboardObject(object):
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)
+ scheme = uri.scheme # pylint: disable=E1101
+ if scheme == 'file':
+ path = uri.path # pylint: disable=E1101
+ if os.path.exists(path):
+ format_ = mime.get_for_file(path)
else:
- format_ = mime.get_from_file_name(uri.path)
+ format_ = mime.get_from_file_name(path)
logging.debug('Chose %r!', format_)
return format_
+
class Format(object):
def __init__(self, mime_type, data, on_disk):
@@ -126,8 +130,9 @@ class Format(object):
def destroy(self):
if self._on_disk:
uri = urlparse.urlparse(self._data)
- if os.path.isfile(uri.path):
- os.remove(uri.path)
+ path = uri.path # pylint: disable=E1101
+ if os.path.isfile(path):
+ os.remove(path)
def get_type(self):
return self._type
diff --git a/src/jarabe/frame/clipboardpanelwindow.py b/src/jarabe/frame/clipboardpanelwindow.py
index ac324f4..f5d537c 100644
--- a/src/jarabe/frame/clipboardpanelwindow.py
+++ b/src/jarabe/frame/clipboardpanelwindow.py
@@ -25,6 +25,7 @@ from jarabe.frame.clipboardtray import ClipboardTray
from jarabe.frame import clipboard
+
class ClipboardPanelWindow(FrameWindow):
def __init__(self, frame, orientation):
FrameWindow.__init__(self, orientation)
@@ -35,7 +36,7 @@ class ClipboardPanelWindow(FrameWindow):
# NOTE: we need to keep a reference to gtk.Clipboard in order to keep
# listening to it.
self._clipboard = gtk.Clipboard()
- self._clipboard.connect("owner-change", self._owner_change_cb)
+ self._clipboard.connect('owner-change', self._owner_change_cb)
self._clipboard_tray = ClipboardTray()
canvas_widget = hippo.CanvasWidget(widget=self._clipboard_tray)
@@ -43,14 +44,14 @@ class ClipboardPanelWindow(FrameWindow):
# Receiving dnd drops
self.drag_dest_set(0, [], 0)
- self.connect("drag_motion", self._clipboard_tray.drag_motion_cb)
- self.connect("drag_leave", self._clipboard_tray.drag_leave_cb)
- self.connect("drag_drop", self._clipboard_tray.drag_drop_cb)
- self.connect("drag_data_received",
+ self.connect('drag_motion', self._clipboard_tray.drag_motion_cb)
+ self.connect('drag_leave', self._clipboard_tray.drag_leave_cb)
+ self.connect('drag_drop', self._clipboard_tray.drag_drop_cb)
+ self.connect('drag_data_received',
self._clipboard_tray.drag_data_received_cb)
def _owner_change_cb(self, x_clipboard, event):
- logging.debug("owner_change_cb")
+ logging.debug('owner_change_cb')
if self._clipboard_tray.owns_clipboard():
return
@@ -100,4 +101,3 @@ class ClipboardPanelWindow(FrameWindow):
selection.type,
selection.data,
on_disk=False)
-
diff --git a/src/jarabe/frame/clipboardtray.py b/src/jarabe/frame/clipboardtray.py
index 8beb6a8..f49b799 100644
--- a/src/jarabe/frame/clipboardtray.py
+++ b/src/jarabe/frame/clipboardtray.py
@@ -27,6 +27,7 @@ from sugar.graphics import style
from jarabe.frame import clipboard
from jarabe.frame.clipboardicon import ClipboardIcon
+
class _ContextMap(object):
"""Maps a drag context to the clipboard object involved in the dragging."""
def __init__(self):
@@ -40,8 +41,8 @@ class _ContextMap(object):
def get_object_id(self, context):
"""Retrieves the object_id associated with context.
- Will release the association when this function was called as many times
- as the number of data_types that this clipboard object contains.
+ Will release the association when this function was called as many
+ times as the number of data_types that this clipboard object contains.
"""
[object_id, data_types_left] = self._context_map[context]
@@ -56,6 +57,7 @@ class _ContextMap(object):
def has_context(self, context):
return context in self._context_map
+
class ClipboardTray(tray.VTray):
MAX_ITEMS = gtk.gdk.screen_height() / style.GRID_CELL_SIZE - 2
@@ -154,7 +156,7 @@ class ClipboardTray(tray.VTray):
if 'XdndDirectSave0' in context.targets:
window = context.source_window
prop_type, format_, filename = \
- window.property_get('XdndDirectSave0','text/plain')
+ window.property_get('XdndDirectSave0', 'text/plain')
# FIXME query the clipboard service for a filename?
base_dir = tempfile.gettempdir()
@@ -192,12 +194,13 @@ class ClipboardTray(tray.VTray):
if selection.data == 'S':
window = context.source_window
- prop_type, format_, dest = \
- window.property_get('XdndDirectSave0', 'text/plain')
+ prop_type, format_, dest = window.property_get(
+ 'XdndDirectSave0', 'text/plain')
clipboardservice = clipboard.get_instance()
- clipboardservice.add_object_format( \
- object_id, 'XdndDirectSave0', dest, on_disk=True)
+ clipboardservice.add_object_format(object_id,
+ 'XdndDirectSave0',
+ dest, on_disk=True)
else:
self._add_selection(object_id, selection)
@@ -213,4 +216,3 @@ class ClipboardTray(tray.VTray):
return True
else:
return False
-
diff --git a/src/jarabe/frame/devicestray.py b/src/jarabe/frame/devicestray.py
index 72affe3..c5db639 100644
--- a/src/jarabe/frame/devicestray.py
+++ b/src/jarabe/frame/devicestray.py
@@ -15,14 +15,13 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
-import sys
-import traceback
import logging
from sugar.graphics import tray
from jarabe import config
+
class DevicesTray(tray.HTray):
def __init__(self):
tray.HTray.__init__(self, align=tray.ALIGN_TO_END)
@@ -35,14 +34,14 @@ class DevicesTray(tray.HTray):
locals(), [module_name])
mod.setup(self)
except Exception:
- logging.error('Exception while loading extension:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ logging.exception('Exception while loading extension:')
def add_device(self, view):
index = 0
- relative_index = getattr(view, "FRAME_POSITION_RELATIVE", -1)
+ relative_index = getattr(view, 'FRAME_POSITION_RELATIVE', -1)
for item in self.get_children():
- current_relative_index = getattr(item, "FRAME_POSITION_RELATIVE", 0)
+ current_relative_index = getattr(item, 'FRAME_POSITION_RELATIVE',
+ 0)
if current_relative_index >= relative_index:
index += 1
else:
diff --git a/src/jarabe/frame/eventarea.py b/src/jarabe/frame/eventarea.py
index 166aaf5..1b5bf86 100644
--- a/src/jarabe/frame/eventarea.py
+++ b/src/jarabe/frame/eventarea.py
@@ -19,14 +19,14 @@ import gobject
import wnck
import gconf
+
_MAX_DELAY = 1000
+
class EventArea(gobject.GObject):
__gsignals__ = {
- 'enter': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
- 'leave': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([]))
+ 'enter': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'leave': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self):
@@ -37,10 +37,11 @@ class EventArea(gobject.GObject):
self._sids = {}
client = gconf.client_get_default()
self._edge_delay = client.get_int('/desktop/sugar/frame/edge_delay')
- self._corner_delay = client.get_int('/desktop/sugar/frame/corner_delay')
+ self._corner_delay = client.get_int('/desktop/sugar/frame'
+ '/corner_delay')
right = gtk.gdk.screen_width() - 1
- bottom = gtk.gdk.screen_height() -1
+ bottom = gtk.gdk.screen_height() - 1
width = gtk.gdk.screen_width() - 2
height = gtk.gdk.screen_height() - 2
@@ -94,6 +95,7 @@ class EventArea(gobject.GObject):
invisible.connect('drag_leave', self._drag_leave_cb)
invisible.realize()
+ # pylint: disable=E1101
invisible.window.set_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.ENTER_NOTIFY_MASK |
gtk.gdk.LEAVE_NOTIFY_MASK)
diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py
index 55f866f..079eeeb 100644
--- a/src/jarabe/frame/frame.py
+++ b/src/jarabe/frame/frame.py
@@ -35,6 +35,7 @@ from jarabe.frame.clipboardpanelwindow import ClipboardPanelWindow
from jarabe.frame.notification import NotificationIcon, NotificationWindow
from jarabe.model import notifications
+
TOP_RIGHT = 0
TOP_LEFT = 1
BOTTOM_RIGHT = 2
@@ -43,6 +44,7 @@ BOTTOM_LEFT = 3
_FRAME_HIDING_DELAY = 500
_NOTIFICATION_DURATION = 5000
+
class _Animation(animator.Animation):
def __init__(self, frame, end):
start = frame.current_position
@@ -52,6 +54,7 @@ class _Animation(animator.Animation):
def next_frame(self, current):
self._frame.move(current)
+
class _MouseListener(object):
def __init__(self, frame):
self._frame = frame
@@ -79,6 +82,7 @@ class _MouseListener(object):
self._hide_sid = gobject.timeout_add(
_FRAME_HIDING_DELAY, self._hide_frame_timeout_cb)
+
class _KeyListener(object):
def __init__(self, frame):
self._frame = frame
@@ -90,13 +94,14 @@ class _KeyListener(object):
else:
self._frame.show(Frame.MODE_KEYBOARD)
+
class Frame(object):
- MODE_MOUSE = 0
+ MODE_MOUSE = 0
MODE_KEYBOARD = 1
MODE_NON_INTERACTIVE = 2
def __init__(self):
- logging.debug("STARTUP: Loading the frame")
+ logging.debug('STARTUP: Loading the frame')
self.mode = None
self._palette_group = palettegroup.get_group('frame')
@@ -173,12 +178,12 @@ class Frame(object):
def _create_top_panel(self):
panel = self._create_panel(gtk.POS_TOP)
- # TODO: setting box_width and hippo.PACK_EXPAND looks like a hack to me.
- # Why hippo isn't respecting the request size of these controls?
+ # TODO: setting box_width and hippo.PACK_EXPAND looks like a hack to
+ # me. Why hippo isn't respecting the request size of these controls?
zoom_toolbar = ZoomToolbar()
panel.append(hippo.CanvasWidget(widget=zoom_toolbar,
- box_width=4*style.GRID_CELL_SIZE))
+ box_width=4 * style.GRID_CELL_SIZE))
zoom_toolbar.show()
activities_tray = ActivitiesTray()
@@ -193,7 +198,8 @@ class Frame(object):
# TODO: same issue as in _create_top_panel()
devices_tray = DevicesTray()
- panel.append(hippo.CanvasWidget(widget=devices_tray), hippo.PACK_EXPAND)
+ panel.append(hippo.CanvasWidget(widget=devices_tray),
+ hippo.PACK_EXPAND)
devices_tray.show()
return panel
@@ -322,7 +328,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')
icon = NotificationIcon()
hints = kwargs['hints']
@@ -348,4 +354,3 @@ class Frame(object):
# Do nothing for now. Our notification UI is so simple, there's no
# point yet.
pass
-
diff --git a/src/jarabe/frame/frameinvoker.py b/src/jarabe/frame/frameinvoker.py
index e4a13e1..a4abfa8 100644
--- a/src/jarabe/frame/frameinvoker.py
+++ b/src/jarabe/frame/frameinvoker.py
@@ -19,6 +19,7 @@ import gtk
from sugar.graphics import style
from sugar.graphics.palette import WidgetInvoker
+
def _get_screen_area():
frame_thickness = style.GRID_CELL_SIZE
@@ -28,6 +29,7 @@ def _get_screen_area():
return gtk.gdk.Rectangle(x, y, width, height)
+
class FrameWidgetInvoker(WidgetInvoker):
def __init__(self, widget):
WidgetInvoker.__init__(self, widget, widget.child)
diff --git a/src/jarabe/frame/framewindow.py b/src/jarabe/frame/framewindow.py
index a7d8fe7..c77e76c 100644
--- a/src/jarabe/frame/framewindow.py
+++ b/src/jarabe/frame/framewindow.py
@@ -19,6 +19,7 @@ import hippo
from sugar.graphics import style
+
class FrameWindow(gtk.Window):
__gtype_name__ = 'SugarFrameWindow'
diff --git a/src/jarabe/frame/friendstray.py b/src/jarabe/frame/friendstray.py
index b5437e5..31a9809 100644
--- a/src/jarabe/frame/friendstray.py
+++ b/src/jarabe/frame/friendstray.py
@@ -14,13 +14,16 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from sugar.presence import presenceservice
+import logging
+
from sugar.graphics.tray import VTray, TrayIcon
from jarabe.view.buddymenu import BuddyMenu
from jarabe.frame.frameinvoker import FrameWidgetInvoker
from jarabe.model import shell
-from jarabe.model.buddy import BuddyModel
+from jarabe.model.buddy import get_owner_instance
+from jarabe.model import neighborhood
+
class FriendIcon(TrayIcon):
def __init__(self, buddy):
@@ -32,46 +35,32 @@ class FriendIcon(TrayIcon):
self.palette.props.icon_visible = False
self.palette.set_group_id('frame')
+
class FriendsTray(VTray):
def __init__(self):
VTray.__init__(self)
- self._activity_ps = None
- self._joined_hid = -1
- self._left_hid = -1
+ self._shared_activity = None
self._buddies = {}
- self._pservice = presenceservice.get_instance()
- self._pservice.connect('activity-appeared',
- self.__activity_appeared_cb)
-
- self._owner = self._pservice.get_owner()
-
- # Add initial activities the PS knows about
- self._pservice.get_activities_async( \
- reply_handler=self._get_activities_cb)
-
shell.get_model().connect('active-activity-changed',
- self._active_activity_changed_cb)
+ self.__active_activity_changed_cb)
- def _get_activities_cb(self, activities_list):
- for act in activities_list:
- self.__activity_appeared_cb(self._pservice, act)
+ neighborhood.get_model().connect('activity-added',
+ self.__neighborhood_activity_added_cb)
def add_buddy(self, buddy):
- if self._buddies.has_key(buddy.props.key):
+ if buddy.props.key in self._buddies:
return
- model = BuddyModel(buddy=buddy)
-
- icon = FriendIcon(model)
+ icon = FriendIcon(buddy)
self.add_item(icon)
icon.show()
self._buddies[buddy.props.key] = icon
def remove_buddy(self, buddy):
- if not self._buddies.has_key(buddy.props.key):
+ if buddy.props.key not in self._buddies:
return
self.remove_item(self._buddies[buddy.props.key])
@@ -83,39 +72,23 @@ class FriendsTray(VTray):
item.destroy()
self._buddies = {}
- def __activity_appeared_cb(self, pservice, activity_ps):
- activity = shell.get_model().get_active_activity()
- if activity and activity_ps.props.id == activity.get_activity_id():
- self._set_activity_ps(activity_ps, True)
-
- def _set_activity_ps(self, activity_ps, shared_activity):
- if self._activity_ps == activity_ps:
- return
+ def __neighborhood_activity_added_cb(self, neighborhood_model,
+ shared_activity):
+ logging.debug('FriendsTray.__neighborhood_activity_added_cb')
+ self.clear()
- if self._joined_hid > 0:
- self._activity_ps.disconnect(self._joined_hid)
- self._joined_hid = -1
- if self._left_hid > 0:
- self._activity_ps.disconnect(self._left_hid)
- self._left_hid = -1
+ # always display ourselves
+ self.add_buddy(get_owner_instance())
- self._activity_ps = activity_ps
+ self._set_current_activity(shared_activity.activity_id)
+ def __active_activity_changed_cb(self, home_model, home_activity):
+ logging.debug('FriendsTray.__active_activity_changed_cb')
self.clear()
# always display ourselves
- self.add_buddy(self._owner)
-
- if shared_activity is True:
- for buddy in activity_ps.get_joined_buddies():
- self.add_buddy(buddy)
+ self.add_buddy(get_owner_instance())
- self._joined_hid = activity_ps.connect(
- 'buddy-joined', self.__buddy_joined_cb)
- self._left_hid = activity_ps.connect(
- 'buddy-left', self.__buddy_left_cb)
-
- def _active_activity_changed_cb(self, home_model, home_activity):
if home_activity is None:
return
@@ -123,19 +96,25 @@ class FriendsTray(VTray):
if activity_id is None:
return
- # check if activity is shared
- activity = None
- for act in self._pservice.get_activities():
- if activity_id == act.props.id:
- activity = act
- break
- if activity:
- self._set_activity_ps(activity, True)
- else:
- self._set_activity_ps(home_activity, False)
-
- def __buddy_joined_cb(self, activity, buddy):
+ self._set_current_activity(activity_id)
+
+ def _set_current_activity(self, activity_id):
+ logging.debug('FriendsTray._set_current_activity')
+ neighborhood_model = neighborhood.get_model()
+ self._shared_activity = neighborhood_model.get_activity(activity_id)
+ if self._shared_activity is None:
+ return
+
+ for buddy in self._shared_activity.get_buddies():
+ self.add_buddy(buddy)
+
+ self._shared_activity.connect('buddy-added', self.__buddy_added_cb)
+ self._shared_activity.connect('buddy-removed', self.__buddy_removed_cb)
+
+ def __buddy_added_cb(self, activity, buddy):
+ logging.debug('FriendsTray.__buddy_added_cb')
self.add_buddy(buddy)
- def __buddy_left_cb(self, activity, buddy):
+ def __buddy_removed_cb(self, activity, buddy):
+ logging.debug('FriendsTray.__buddy_removed_cb')
self.remove_buddy(buddy)
diff --git a/src/jarabe/frame/notification.py b/src/jarabe/frame/notification.py
index 83dc27e..3471e2c 100644
--- a/src/jarabe/frame/notification.py
+++ b/src/jarabe/frame/notification.py
@@ -22,13 +22,14 @@ from sugar.graphics.xocolor import XoColor
from jarabe.view.pulsingicon import PulsingIcon
+
class NotificationIcon(gtk.EventBox):
__gtype_name__ = 'SugarNotificationIcon'
__gproperties__ = {
- 'xo-color' : (object, None, None, gobject.PARAM_READWRITE),
- 'icon-name' : (str, None, None, None, gobject.PARAM_READWRITE),
- 'icon-filename' : (str, None, None, None, gobject.PARAM_READWRITE)
+ 'xo-color': (object, None, None, gobject.PARAM_READWRITE),
+ 'icon-name': (str, None, None, None, gobject.PARAM_READWRITE),
+ 'icon-filename': (str, None, None, None, gobject.PARAM_READWRITE),
}
_PULSE_TIMEOUT = 3
@@ -45,7 +46,8 @@ class NotificationIcon(gtk.EventBox):
self.add(self._icon)
self._icon.show()
- gobject.timeout_add_seconds(self._PULSE_TIMEOUT, self.__stop_pulsing_cb)
+ gobject.timeout_add_seconds(self._PULSE_TIMEOUT,
+ self.__stop_pulsing_cb)
self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
@@ -80,6 +82,7 @@ class NotificationIcon(gtk.EventBox):
palette = property(_get_palette, _set_palette)
+
class NotificationWindow(gtk.Window):
__gtype_name__ = 'SugarNotificationWindow'
@@ -97,4 +100,3 @@ class NotificationWindow(gtk.Window):
color = gtk.gdk.color_parse(style.COLOR_TOOLBAR_GREY.get_html())
self.modify_bg(gtk.STATE_NORMAL, color)
-
diff --git a/src/jarabe/frame/zoomtoolbar.py b/src/jarabe/frame/zoomtoolbar.py
index 2ed3c54..6c10c61 100644
--- a/src/jarabe/frame/zoomtoolbar.py
+++ b/src/jarabe/frame/zoomtoolbar.py
@@ -26,6 +26,7 @@ from sugar.graphics.radiotoolbutton import RadioToolButton
from jarabe.frame.frameinvoker import FrameWidgetInvoker
from jarabe.model import shell
+
class ZoomToolbar(gtk.Toolbar):
def __init__(self):
gtk.Toolbar.__init__(self)
@@ -86,4 +87,3 @@ class ZoomToolbar(gtk.Toolbar):
self._activity_button.props.active = True
else:
raise ValueError('Invalid zoom level: %r' % (new_level))
-
diff --git a/src/jarabe/intro/Makefile.am b/src/jarabe/intro/Makefile.am
index a9fb96b..2ea7cea 100644
--- a/src/jarabe/intro/Makefile.am
+++ b/src/jarabe/intro/Makefile.am
@@ -1,7 +1,3 @@
-imagedir = $(pythondir)/jarabe/intro
-image_DATA = default-picture.png
-
-EXTRA_DIST = $(conf_DATA) $(image_DATA)
sugardir = $(pythondir)/jarabe/intro
sugar_PYTHON = \
__init__.py \
diff --git a/src/jarabe/intro/__init__.py b/src/jarabe/intro/__init__.py
index ca4f64d..d2932f1 100644
--- a/src/jarabe/intro/__init__.py
+++ b/src/jarabe/intro/__init__.py
@@ -8,6 +8,7 @@ from sugar.profile import get_profile
from jarabe.intro.window import IntroWindow
from jarabe.intro.window import create_profile
+
def check_profile():
profile = get_profile()
diff --git a/src/jarabe/intro/colorpicker.py b/src/jarabe/intro/colorpicker.py
index a939857..997199b 100644
--- a/src/jarabe/intro/colorpicker.py
+++ b/src/jarabe/intro/colorpicker.py
@@ -20,6 +20,7 @@ from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.graphics.xocolor import XoColor
+
class ColorPicker(hippo.CanvasBox, hippo.CanvasItem):
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
diff --git a/src/jarabe/intro/default-picture.png b/src/jarabe/intro/default-picture.png
deleted file mode 100644
index e26b9b0..0000000
--- a/src/jarabe/intro/default-picture.png
+++ /dev/null
Binary files differ
diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py
index 35c0cda..df19fbf 100644
--- a/src/jarabe/intro/window.py
+++ b/src/jarabe/intro/window.py
@@ -32,38 +32,34 @@ from sugar.graphics.xocolor import XoColor
from jarabe.intro import colorpicker
+
_BACKGROUND_COLOR = style.COLOR_WHITE
-def create_profile(name, color=None, pixbuf=None):
- if not pixbuf:
- path = os.path.join(os.path.dirname(__file__), 'default-picture.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file(path)
+def create_profile(name, color=None):
if not color:
color = XoColor()
- icon_path = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
- pixbuf.save(icon_path, "jpeg", {"quality":"85"})
-
client = gconf.client_get_default()
- client.set_string("/desktop/sugar/user/nick", name)
- client.set_string("/desktop/sugar/user/color", color.to_string())
+ client.set_string('/desktop/sugar/user/nick', name)
+ client.set_string('/desktop/sugar/user/color', color.to_string())
+ client.suggest_sync()
# Generate keypair
import commands
- keypath = os.path.join(env.get_profile_path(), "owner.key")
+ keypath = os.path.join(env.get_profile_path(), 'owner.key')
if not os.path.isfile(keypath):
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.")
+ logging.error('Keypair exists, skip generation.')
+
class _Page(hippo.CanvasBox):
__gproperties__ = {
- 'valid' : (bool, None, None, False,
- gobject.PARAM_READABLE)
+ 'valid': (bool, None, None, False, gobject.PARAM_READABLE),
}
def __init__(self, **kwargs):
@@ -81,6 +77,7 @@ class _Page(hippo.CanvasBox):
def activate(self):
pass
+
class _NamePage(_Page):
def __init__(self, intro):
_Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER,
@@ -90,7 +87,7 @@ class _NamePage(_Page):
self._intro = intro
- label = hippo.CanvasText(text=_("Name:"))
+ label = hippo.CanvasText(text=_('Name:'))
self.append(label)
self._entry = CanvasEntry(box_width=style.zoom(300))
@@ -118,6 +115,7 @@ class _NamePage(_Page):
def activate(self):
self._entry.props.widget.grab_focus()
+
class _ColorPage(_Page):
def __init__(self, **kwargs):
_Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER,
@@ -125,7 +123,7 @@ class _ColorPage(_Page):
spacing=style.DEFAULT_SPACING,
yalign=hippo.ALIGNMENT_CENTER, **kwargs)
- self._label = hippo.CanvasText(text=_("Click to change color:"),
+ self._label = hippo.CanvasText(text=_('Click to change color:'),
xalign=hippo.ALIGNMENT_CENTER)
self.append(self._label)
@@ -138,10 +136,11 @@ class _ColorPage(_Page):
def get_color(self):
return self._cp.get_color()
+
class _IntroBox(hippo.CanvasBox):
__gsignals__ = {
'done': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
}
PAGE_NAME = 0
@@ -166,13 +165,9 @@ class _IntroBox(hippo.CanvasBox):
self._page = self.PAGE_COLOR
if default_nick == 'system':
pwd_entry = pwd.getpwuid(os.getuid())
- if pwd_entry.pw_gecos:
- nick = pwd_entry.pw_gecos.split(',')[0]
- self._name_page.set_name(nick)
- else:
- self._name_page.set_name(pwd_entry.pw_name)
- else:
- self._name_page.set_name(default_nick)
+ default_nick = (pwd_entry.pw_gecos.split(',')[0] or
+ pwd_entry.pw_name)
+ self._name_page.set_name(default_nick)
self._setup_page()
@@ -255,6 +250,7 @@ class _IntroBox(hippo.CanvasBox):
self.emit('done', name, color)
+
class IntroWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
@@ -282,16 +278,16 @@ class IntroWindow(gtk.Window):
return False
def __key_press_cb(self, widget, event):
- if gtk.gdk.keyval_name(event.keyval) == "Return":
+ if gtk.gdk.keyval_name(event.keyval) == 'Return':
self._intro_box.next()
return True
- elif gtk.gdk.keyval_name(event.keyval) == "Escape":
+ elif gtk.gdk.keyval_name(event.keyval) == 'Escape':
self._intro_box.back()
return True
return False
-if __name__ == "__main__":
+if __name__ == '__main__':
w = IntroWindow()
w.show()
w.connect('destroy', gtk.main_quit)
diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am
index f4bf273..ba29062 100644
--- a/src/jarabe/journal/Makefile.am
+++ b/src/jarabe/journal/Makefile.am
@@ -6,6 +6,7 @@ sugar_PYTHON = \
journalactivity.py \
journalentrybundle.py \
journaltoolbox.py \
+ journalwindow.py \
keepicon.py \
listmodel.py \
listview.py \
diff --git a/src/jarabe/journal/detailview.py b/src/jarabe/journal/detailview.py
index b4a2339..aa8c039 100644
--- a/src/jarabe/journal/detailview.py
+++ b/src/jarabe/journal/detailview.py
@@ -27,11 +27,12 @@ from sugar.graphics.icon import CanvasIcon
from jarabe.journal.expandedentry import ExpandedEntry
from jarabe.journal import model
+
class DetailView(gtk.VBox):
__gtype_name__ = 'DetailView'
__gsignals__ = {
- 'go-back-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
+ 'go-back-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self, **kwargs):
@@ -84,6 +85,7 @@ class DetailView(gtk.VBox):
metadata = gobject.property(
type=object, getter=get_metadata, setter=set_metadata)
+
class BackBar(hippo.CanvasBox):
def __init__(self):
hippo.CanvasBox.__init__(self,
diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py
index c8e40c1..fe2f320 100644
--- a/src/jarabe/journal/expandedentry.py
+++ b/src/jarabe/journal/expandedentry.py
@@ -36,6 +36,7 @@ from jarabe.journal.palettes import ObjectPalette, BuddyPalette
from jarabe.journal import misc
from jarabe.journal import model
+
class Separator(hippo.CanvasBox, hippo.CanvasItem):
def __init__(self, orientation):
hippo.CanvasBox.__init__(self,
@@ -46,6 +47,7 @@ class Separator(hippo.CanvasBox, hippo.CanvasItem):
else:
self.props.box_height = style.LINE_WIDTH
+
class BuddyList(hippo.CanvasBox):
def __init__(self, buddies):
hippo.CanvasBox.__init__(self, xalign=hippo.ALIGNMENT_START,
@@ -61,6 +63,7 @@ class BuddyList(hippo.CanvasBox):
hbox.append(icon)
self.append(hbox)
+
class ExpandedEntry(hippo.CanvasBox):
def __init__(self):
hippo.CanvasBox.__init__(self)
@@ -209,9 +212,7 @@ class ExpandedEntry(hippo.CanvasBox):
height = style.zoom(240)
box = hippo.CanvasBox()
- if self._metadata.has_key('preview') and \
- len(self._metadata['preview']) > 4:
-
+ if len(self._metadata.get('preview', '')) > 4:
if self._metadata['preview'][1:4] == 'PNG':
preview_data = self._metadata['preview']
else:
@@ -260,8 +261,9 @@ class ExpandedEntry(hippo.CanvasBox):
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'])),)]
+ _('Size: %s') % (format_size(int(self._metadata.get('filesize',
+ model.get_file_size(self._metadata['uid']))))),
+ ]
for line in lines:
text = hippo.CanvasText(text=line,
@@ -279,10 +281,15 @@ class ExpandedEntry(hippo.CanvasBox):
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')
+ try:
+ timestamp = float(self._metadata['timestamp'])
+ except (ValueError, TypeError):
+ logging.warning('Invalid timestamp for %r: %r',
+ self._metadata['uid'],
+ self._metadata['timestamp'])
+ else:
+ return time.strftime('%x', time.localtime(timestamp))
+ return _('No date')
def _create_buddy_list(self):
@@ -300,8 +307,7 @@ class ExpandedEntry(hippo.CanvasBox):
vbox.append(text)
- if self._metadata.has_key('buddies') and \
- self._metadata['buddies']:
+ if self._metadata.get('buddies'):
buddies = simplejson.loads(self._metadata['buddies']).values()
vbox.append(BuddyList(buddies))
return vbox
diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py
index 0559560..a33038a 100644
--- a/src/jarabe/journal/journalactivity.py
+++ b/src/jarabe/journal/journalactivity.py
@@ -17,8 +17,6 @@
import logging
from gettext import gettext as _
-import sys
-import traceback
import uuid
import gtk
@@ -27,6 +25,8 @@ import statvfs
import os
from sugar.graphics.window import Window
+from sugar.graphics.alert import ErrorAlert
+
from sugar.bundle.bundle import ZipExtractException, RegistrationException
from sugar import env
from sugar.activity import activityfactory
@@ -42,6 +42,8 @@ from jarabe.journal.journalentrybundle import JournalEntryBundle
from jarabe.journal.objectchooser import ObjectChooser
from jarabe.journal.modalalert import ModalAlert
from jarabe.journal import model
+from jarabe.journal.journalwindow import JournalWindow
+
J_DBUS_SERVICE = 'org.laptop.Journal'
J_DBUS_INTERFACE = 'org.laptop.Journal'
@@ -50,6 +52,9 @@ J_DBUS_PATH = '/org/laptop/Journal'
_SPACE_TRESHOLD = 52428800
_BUNDLE_ID = 'org.laptop.JournalActivity'
+_journal = None
+
+
class JournalActivityDBusService(dbus.service.Object):
def __init__(self, parent):
self._parent = parent
@@ -79,7 +84,8 @@ class JournalActivityDBusService(dbus.service.Object):
chooser.destroy()
del chooser
- @dbus.service.method(J_DBUS_INTERFACE, in_signature='is', out_signature='s')
+ @dbus.service.method(J_DBUS_INTERFACE, in_signature='is',
+ out_signature='s')
def ChooseObject(self, parent_xid, what_filter=''):
chooser_id = uuid.uuid4().hex
if parent_xid > 0:
@@ -92,18 +98,19 @@ class JournalActivityDBusService(dbus.service.Object):
return chooser_id
- @dbus.service.signal(J_DBUS_INTERFACE, signature="ss")
+ @dbus.service.signal(J_DBUS_INTERFACE, signature='ss')
def ObjectChooserResponse(self, chooser_id, object_id):
pass
- @dbus.service.signal(J_DBUS_INTERFACE, signature="s")
+ @dbus.service.signal(J_DBUS_INTERFACE, signature='s')
def ObjectChooserCancelled(self, chooser_id):
pass
-class JournalActivity(Window):
+
+class JournalActivity(JournalWindow):
def __init__(self):
- logging.debug("STARTUP: Loading the journal")
- Window.__init__(self)
+ logging.debug('STARTUP: Loading the journal')
+ JournalWindow.__init__(self)
self.set_title(_('Journal'))
@@ -138,6 +145,15 @@ class JournalActivity(Window):
self._critical_space_alert = None
self._check_available_space()
+ def __volume_error_cb(self, gobject, message, severity):
+ alert = ErrorAlert(title=severity, msg=message)
+ alert.connect('response', self.__alert_response_cb)
+ self.add_alert(alert)
+ alert.show()
+
+ def __alert_response_cb(self, alert, response_id):
+ self.remove_alert(alert)
+
def __realize_cb(self, window):
wm.set_bundle_id(window.window, _BUNDLE_ID)
activity_id = activityfactory.create_activity_id()
@@ -161,6 +177,7 @@ class JournalActivity(Window):
self._volumes_toolbar = VolumesToolbar()
self._volumes_toolbar.connect('volume-changed',
self.__volume_changed_cb)
+ self._volumes_toolbar.connect('volume-error', self.__volume_error_cb)
self._main_view.pack_start(self._volumes_toolbar, expand=False)
search_toolbar = self._main_toolbox.search_toolbar
@@ -171,7 +188,8 @@ class JournalActivity(Window):
self._secondary_view = gtk.VBox()
self._detail_toolbox = DetailToolbox()
- entry_toolbar = self._detail_toolbox.entry_toolbar
+ self._detail_toolbox.entry_toolbar.connect('volume-error',
+ self.__volume_error_cb)
self._detail_view = DetailView()
self._detail_view.connect('go-back-clicked', self.__go_back_clicked_cb)
@@ -210,8 +228,7 @@ class JournalActivity(Window):
try:
self._detail_toolbox.entry_toolbar.set_metadata(metadata)
except Exception:
- logging.error('Exception while displaying entry:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ logging.exception('Exception while displaying entry:')
self.set_toolbar_box(self._detail_toolbox)
self._detail_toolbox.show()
@@ -219,8 +236,7 @@ class JournalActivity(Window):
try:
self._detail_view.props.metadata = metadata
except Exception:
- logging.error('Exception while displaying entry:\n' + \
- ''.join(traceback.format_exception(*sys.exc_info())))
+ logging.exception('Exception while displaying entry:')
self.set_canvas(self._secondary_view)
self._secondary_view.show()
@@ -314,11 +330,11 @@ class JournalActivity(Window):
self._list_view.set_is_visible(visible)
def _check_available_space(self):
- ''' Check available space on device
+ """Check available space on device
If the available space is below 50MB an alert will be
shown which encourages to delete old journal entries.
- '''
+ """
if self._critical_space_alert:
return
@@ -345,7 +361,6 @@ class JournalActivity(Window):
self.show_main_view()
self.search_grab_focus()
-_journal = None
def get_journal():
global _journal
@@ -354,6 +369,6 @@ def get_journal():
_journal.show()
return _journal
+
def start():
get_journal()
-
diff --git a/src/jarabe/journal/journalentrybundle.py b/src/jarabe/journal/journalentrybundle.py
index 41777c7..c220c09 100644
--- a/src/jarabe/journal/journalentrybundle.py
+++ b/src/jarabe/journal/journalentrybundle.py
@@ -25,6 +25,7 @@ from sugar.bundle.bundle import Bundle, MalformedBundleException
from jarabe.journal import model
+
class JournalEntryBundle(Bundle):
"""A Journal entry bundle
@@ -41,7 +42,7 @@ class JournalEntryBundle(Bundle):
Bundle.__init__(self, path)
def install(self, uid=''):
- if os.environ.has_key('SUGAR_ACTIVITY_ROOT'):
+ if 'SUGAR_ACTIVITY_ROOT' in os.environ:
install_dir = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],
'data')
else:
@@ -91,4 +92,3 @@ class JournalEntryBundle(Bundle):
def is_installed(self):
# These bundles can be reinstalled as many times as desired.
return False
-
diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py
index 61671bc..cdf998d 100644
--- a/src/jarabe/journal/journaltoolbox.py
+++ b/src/jarabe/journal/journaltoolbox.py
@@ -43,6 +43,7 @@ from jarabe.model import bundleregistry
from jarabe.journal import misc
from jarabe.journal import model
+
_AUTOSEARCH_TIMEOUT = 1000
_ACTION_ANYTIME = 0
@@ -68,13 +69,13 @@ class MainToolbox(Toolbox):
self.add_toolbar(_('Search'), self.search_toolbar)
self.search_toolbar.show()
+
class SearchToolbar(gtk.Toolbar):
__gtype_name__ = 'SearchToolbar'
__gsignals__ = {
- 'query-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
+ 'query-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
@@ -109,6 +110,14 @@ class SearchToolbar(gtk.Toolbar):
self.insert(tool_item, -1)
tool_item.show()
+ self._sorting_button = SortingButton()
+ self._sorting_button.connect('clicked',
+ self.__sorting_button_clicked_cb)
+ self.insert(self._sorting_button, -1)
+ self._sorting_button.connect('sort-property-changed',
+ self.__sort_changed_cb)
+ self._sorting_button.show()
+
# TODO: enable it when the DS supports saving the buddies.
#self._with_search_combo = self._get_with_search_combo()
#tool_item = ToolComboBox(self._with_search_combo)
@@ -191,6 +200,14 @@ class SearchToolbar(gtk.Toolbar):
if text:
query['query'] = text
+ property_, order = self._sorting_button.get_current_sort()
+
+ if order == gtk.SORT_ASCENDING:
+ sign = '+'
+ else:
+ sign = '-'
+ query['order_by'] = [sign + property_]
+
return query
def _get_date_range(self):
@@ -213,6 +230,12 @@ class SearchToolbar(gtk.Toolbar):
def _combo_changed_cb(self, combo):
self._update_if_needed()
+ def __sort_changed_cb(self, button):
+ self._update_if_needed()
+
+ def __sorting_button_clicked_cb(self, button):
+ self._sorting_button.palette.popup(immediate=True, state=1)
+
def _update_if_needed(self):
new_query = self._build_query()
if self._query != new_query:
@@ -320,6 +343,7 @@ class SearchToolbar(gtk.Toolbar):
self._when_search_combo.set_active(0)
self._favorite_button.props.active = False
+
class DetailToolbox(Toolbox):
def __init__(self):
Toolbox.__init__(self)
@@ -328,7 +352,13 @@ class DetailToolbox(Toolbox):
self.add_toolbar('', self.entry_toolbar)
self.entry_toolbar.show()
+
class EntryToolbar(gtk.Toolbar):
+ __gsignals__ = {
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str, str])),
+ }
+
def __init__(self):
gtk.Toolbar.__init__(self)
@@ -398,7 +428,22 @@ class EntryToolbar(gtk.Toolbar):
misc.resume(self._metadata, service_name)
def _copy_menu_item_activate_cb(self, menu_item, mount_point):
- model.copy(self._metadata, mount_point)
+ file_path = model.get_file(self._metadata['uid'])
+
+ if not file_path or not os.path.exists(file_path):
+ logging.warn('Entries without a file cannot be copied.')
+ self.emit('volume-error',
+ _('Entries without a file cannot be copied.'),
+ _('Warning'))
+ return
+
+ try:
+ model.copy(self._metadata, mount_point)
+ except IOError, e:
+ logging.exception('Error while copying the entry. %s', e.strerror)
+ self.emit('volume-error',
+ _('Error while copying the entry. %s') % e.strerror,
+ _('Error'))
def _refresh_copy_palette(self):
palette = self._copy.get_palette()
@@ -456,3 +501,48 @@ class EntryToolbar(gtk.Toolbar):
activity_info.get_bundle_id())
palette.menu.append(menu_item)
menu_item.show()
+
+
+class SortingButton(ToolButton):
+ __gtype_name__ = 'JournalSortingButton'
+
+ __gsignals__ = {
+ 'sort-property-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ }
+
+ _SORT_OPTIONS = [
+ ('timestamp', 'view-lastedit', _('Sort by date modified')),
+ ('creation_time', 'view-created', _('Sort by date created')),
+ ('filesize', 'view-size', _('Sort by size')),
+ ]
+
+ def __init__(self):
+ ToolButton.__init__(self)
+
+ self._property = 'timestamp'
+ self._order = gtk.SORT_ASCENDING
+
+ self.props.tooltip = _('Sort view')
+ self.props.icon_name = 'view-lastedit'
+
+ for property_, icon, label in self._SORT_OPTIONS:
+ button = MenuItem(icon_name=icon, text_label=label)
+ button.connect('activate',
+ self.__sort_type_changed_cb,
+ property_,
+ icon)
+ button.show()
+ self.props.palette.menu.insert(button, -1)
+
+ def __sort_type_changed_cb(self, widget, property_, icon_name):
+ self._property = property_
+ #FIXME: Implement sorting order
+ self._order = gtk.SORT_ASCENDING
+ self.emit('sort-property-changed')
+
+ self.props.icon_name = icon_name
+
+ def get_current_sort(self):
+ return (self._property, self._order)
diff --git a/src/jarabe/desktop/myicon.py b/src/jarabe/journal/journalwindow.py
index 4a4ad95..31bc790 100644
--- a/src/jarabe/desktop/myicon.py
+++ b/src/jarabe/journal/journalwindow.py
@@ -1,4 +1,5 @@
-# Copyright (C) 2006-2007 Red Hat, Inc.
+#Copyright (C) 2010 Software for Education, Entertainment and Training
+#Activities
#
# 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
@@ -14,15 +15,19 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import gconf
+from sugar.graphics.window import Window
-from sugar.graphics.icon import CanvasIcon
-from sugar.graphics.xocolor import XoColor
+_journal_window = None
-class MyIcon(CanvasIcon):
- def __init__(self, size):
- client = gconf.client_get_default()
- color = XoColor(client.get_string("/desktop/sugar/user/color"))
- CanvasIcon.__init__(self, size=size,
- icon_name='computer-xo',
- xo_color=color)
+
+class JournalWindow(Window):
+
+ def __init__(self):
+
+ global _journal_window
+ Window.__init__(self)
+ _journal_window = self
+
+
+def get_journal_window():
+ return _journal_window
diff --git a/src/jarabe/journal/keepicon.py b/src/jarabe/journal/keepicon.py
index 2c692c6..1253afc 100644
--- a/src/jarabe/journal/keepicon.py
+++ b/src/jarabe/journal/keepicon.py
@@ -22,6 +22,7 @@ from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.graphics.xocolor import XoColor
+
class KeepIcon(CanvasIcon):
def __init__(self, keep):
CanvasIcon.__init__(self, icon_name='emblem-favorite',
diff --git a/src/jarabe/journal/listmodel.py b/src/jarabe/journal/listmodel.py
index 07f8544..3902eba 100644
--- a/src/jarabe/journal/listmodel.py
+++ b/src/jarabe/journal/listmodel.py
@@ -19,6 +19,7 @@ import logging
import simplejson
import gobject
import gtk
+from gettext import gettext as _
from sugar.graphics.xocolor import XoColor
from sugar.graphics import style
@@ -27,20 +28,18 @@ 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,
- ([])),
+ 'ready': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
COLUMN_UID = 0
@@ -48,22 +47,28 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource):
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}
+ COLUMN_TIMESTAMP = 5
+ COLUMN_CREATION_TIME = 6
+ COLUMN_FILESIZE = 7
+ COLUMN_PROGRESS = 8
+ COLUMN_BUDDY_1 = 9
+ COLUMN_BUDDY_2 = 10
+ COLUMN_BUDDY_3 = 11
+
+ _COLUMN_TYPES = {
+ COLUMN_UID: str,
+ COLUMN_FAVORITE: bool,
+ COLUMN_ICON: str,
+ COLUMN_ICON_COLOR: object,
+ COLUMN_TITLE: str,
+ COLUMN_TIMESTAMP: str,
+ COLUMN_CREATION_TIME: str,
+ COLUMN_FILESIZE: str,
+ COLUMN_PROGRESS: int,
+ COLUMN_BUDDY_1: object,
+ COLUMN_BUDDY_3: object,
+ COLUMN_BUDDY_2: object,
+ }
_PAGE_SIZE = 10
@@ -135,25 +140,64 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource):
xo_color = misc.get_icon_color(metadata)
self._cached_row.append(xo_color)
- title = gobject.markup_escape_text(metadata.get('title', None))
- self._cached_row.append('<b>%s</b>' % title)
+ title = gobject.markup_escape_text(metadata.get('title',
+ _('Untitled')))
+ self._cached_row.append('<b>%s</b>' % (title, ))
- timestamp = int(metadata.get('timestamp', 0))
- self._cached_row.append(util.timestamp_to_elapsed_string(timestamp))
+ try:
+ timestamp = float(metadata.get('timestamp', 0))
+ except (TypeError, ValueError):
+ timestamp_content = _('Unknown')
+ else:
+ timestamp_content = util.timestamp_to_elapsed_string(timestamp)
+ self._cached_row.append(timestamp_content)
- self._cached_row.append(int(metadata.get('progress', 100)))
+ try:
+ creation_time = float(metadata.get('creation_time'))
+ except (TypeError, ValueError):
+ self._cached_row.append(_('Unknown'))
+ else:
+ self._cached_row.append(
+ util.timestamp_to_elapsed_string(float(creation_time)))
- if metadata.get('buddies', ''):
- buddies = simplejson.loads(metadata['buddies']).values()
+ try:
+ size = int(metadata.get('filesize'))
+ except (TypeError, ValueError):
+ self._cached_row.append(_('Unknown'))
else:
+ self._cached_row.append(util.format_size(size))
+
+ try:
+ progress = int(float(metadata.get('progress', 100)))
+ except (TypeError, ValueError):
+ progress = 100
+ self._cached_row.append(progress)
+
+ buddies = []
+ if metadata.get('buddies'):
+ try:
+ buddies = simplejson.loads(metadata['buddies']).values()
+ except simplejson.decoder.JSONDecodeError, exception:
+ logging.warning('Cannot decode buddies for %r: %s',
+ metadata['uid'], exception)
+
+ if not isinstance(buddies, list):
+ logging.warning('Content of buddies for %r is not a list: %r',
+ metadata['uid'], buddies)
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)
+ try:
+ nick, color = buddies.pop(0)
+ except (AttributeError, ValueError), exception:
+ logging.warning('Malformed buddies for %r: %s',
+ metadata['uid'], exception)
+ else:
+ self._cached_row.append((nick, XoColor(color)))
+ continue
+
+ self._cached_row.append(None)
return self._cached_row[column]
@@ -198,4 +242,3 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource):
return True
return False
-
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 9e19f70..0aee1b7 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -34,11 +34,13 @@ from jarabe.journal.palettes import ObjectPalette, BuddyPalette
from jarabe.journal import model
from jarabe.journal import misc
+
UPDATE_INTERVAL = 300
MESSAGE_EMPTY_JOURNAL = 0
MESSAGE_NO_MATCH = 1
+
class TreeView(gtk.TreeView):
__gtype_name__ = 'JournalTreeView'
@@ -47,8 +49,8 @@ class TreeView(gtk.TreeView):
self.set_headers_visible(False)
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.
+ # 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
@@ -58,13 +60,12 @@ class TreeView(gtk.TreeView):
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,
- gobject.TYPE_NONE,
- ([]))
+ 'clear-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self):
@@ -81,7 +82,8 @@ class BaseListView(gtk.Bin):
self.connect('destroy', self.__destroy_cb)
self._scrolled_window = gtk.ScrolledWindow()
- self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self._scrolled_window.set_policy(gtk.POLICY_NEVER,
+ gtk.POLICY_AUTOMATIC)
self.add(self._scrolled_window)
self._scrolled_window.show()
@@ -97,7 +99,7 @@ class BaseListView(gtk.Bin):
self.cell_title = None
self.cell_icon = None
self._title_column = None
- self.date_column = None
+ self.sort_column = None
self._add_columns()
self.tree_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
@@ -141,7 +143,8 @@ class BaseListView(gtk.Bin):
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, 'file-name',
+ ListModel.COLUMN_ICON)
column.add_attribute(self.cell_icon, 'xo-color',
ListModel.COLUMN_ICON_COLOR)
self.tree_view.append_column(column)
@@ -163,7 +166,8 @@ class BaseListView(gtk.Bin):
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,
+ 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)
@@ -190,15 +194,16 @@ class BaseListView(gtk.Bin):
date = util.timestamp_to_elapsed_string(timestamp)
date_width = self._get_width_for_string(date)
- self.date_column = gtk.TreeViewColumn()
- 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.pack_start(cell_text)
- self.date_column.add_attribute(cell_text, 'text', ListModel.COLUMN_DATE)
- self.tree_view.append_column(self.date_column)
+ self.sort_column = gtk.TreeViewColumn()
+ self.sort_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
+ self.sort_column.props.fixed_width = date_width
+ self.sort_column.set_alignment(1)
+ self.sort_column.props.resizable = True
+ self.sort_column.props.clickable = True
+ self.sort_column.pack_start(cell_text)
+ self.sort_column.add_attribute(cell_text, 'text',
+ ListModel.COLUMN_TIMESTAMP)
+ self.tree_view.append_column(self.sort_column)
def _get_width_for_string(self, text):
# Add some extra margin
@@ -252,11 +257,16 @@ class BaseListView(gtk.Bin):
def update_with_query(self, query_dict):
logging.debug('ListView.update_with_query')
+ if 'order_by' not in query_dict:
+ query_dict['order_by'] = ['+timestamp']
+ if query_dict['order_by'] != self._query.get('order_by'):
+ property_ = query_dict['order_by'][0][1:]
+ cell_text = self.sort_column.get_cell_renderers()[0]
+ self.sort_column.set_attributes(cell_text,
+ text=getattr(ListModel, 'COLUMN_' + property_.upper(),
+ ListModel.COLUMN_TIMESTAMP))
self._query = query_dict
- if 'order_by' not in self._query:
- self._query['order_by'] = ['+timestamp']
-
self.refresh()
def refresh(self):
@@ -316,12 +326,9 @@ class BaseListView(gtk.Bin):
def _is_query_empty(self):
# FIXME: This is a hack, we shouldn't have to update this every time
# a new search term is added.
- if self._query.get('query', '') or self._query.get('mime_type', '') or \
- self._query.get('keep', '') or self._query.get('mtime', '') or \
- self._query.get('activity', ''):
- return False
- else:
- return True
+ return not (self._query.get('query') or self._query.get('mime_type') or
+ self._query.get('keep') or self._query.get('mtime') or
+ self._query.get('activity'))
def __model_progress_cb(self, tree_model):
if self._progress_bar is None:
@@ -365,8 +372,8 @@ class BaseListView(gtk.Bin):
icon = CanvasIcon(size=style.LARGE_ICON_SIZE,
icon_name='activity-journal',
- stroke_color = style.COLOR_BUTTON_GREY.get_svg(),
- fill_color = style.COLOR_TRANSPARENT.get_svg())
+ stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
+ fill_color=style.COLOR_TRANSPARENT.get_svg())
box.append(icon)
if message == MESSAGE_EMPTY_JOURNAL:
@@ -379,7 +386,7 @@ class BaseListView(gtk.Bin):
text = hippo.CanvasText(text=text,
xalign=hippo.ALIGNMENT_CENTER,
font_desc=style.FONT_BOLD.get_pango_desc(),
- color = style.COLOR_BUTTON_GREY.get_int())
+ color=style.COLOR_BUTTON_GREY.get_int())
box.append(text)
if message == MESSAGE_NO_MATCH:
@@ -415,7 +422,7 @@ class BaseListView(gtk.Bin):
while True:
x, y, width, height = self.tree_view.get_cell_area(path,
- self.date_column)
+ self.sort_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:
@@ -455,13 +462,13 @@ class BaseListView(gtk.Bin):
self.update_dates()
return True
+
class ListView(BaseListView):
__gtype_name__ = 'JournalListView'
__gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
+ 'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
@@ -533,6 +540,7 @@ class ListView(BaseListView):
def __editing_canceled_cb(self, cell):
self.cell_title.props.editable = False
+
class CellRendererFavorite(CellRendererIcon):
__gtype_name__ = 'JournalCellRendererFavorite'
@@ -547,6 +555,7 @@ class CellRendererFavorite(CellRendererIcon):
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'
@@ -563,12 +572,12 @@ class CellRendererDetail(CellRendererIcon):
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,
+ 'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([str])),
}
@@ -605,6 +614,7 @@ class CellRendererActivityIcon(CellRendererIcon):
show_palette = gobject.property(type=bool, default=True,
setter=set_show_palette)
+
class CellRendererBuddy(CellRendererIcon):
__gtype_name__ = 'JournalCellRendererBuddy'
@@ -638,4 +648,3 @@ class CellRendererBuddy(CellRendererIcon):
self.props.xo_color = xo_color
buddy = gobject.property(type=object, setter=set_buddy)
-
diff --git a/src/jarabe/journal/misc.py b/src/jarabe/journal/misc.py
index 24ad216..1431d5f 100644
--- a/src/jarabe/journal/misc.py
+++ b/src/jarabe/journal/misc.py
@@ -27,8 +27,10 @@ from sugar.activity import activityfactory
from sugar.activity.activityhandle import ActivityHandle
from sugar.graphics.icon import get_icon_file_name
from sugar.graphics.xocolor import XoColor
+from sugar.graphics.alert import ConfirmationAlert
from sugar import mime
from sugar.bundle.activitybundle import ActivityBundle
+from sugar.bundle.bundle import AlreadyInstalledException
from sugar.bundle.contentbundle import ContentBundle
from sugar import util
@@ -36,6 +38,8 @@ from jarabe.view import launcher
from jarabe.model import bundleregistry, shell
from jarabe.journal.journalentrybundle import JournalEntryBundle
from jarabe.journal import model
+from jarabe.journal import journalwindow
+
def _get_icon_for_mime(mime_type):
generic_types = mime.get_all_generic_types()
@@ -52,6 +56,7 @@ def _get_icon_for_mime(mime_type):
if file_name is not None:
return file_name
+
def get_icon_name(metadata):
file_name = None
@@ -81,35 +86,46 @@ def get_icon_name(metadata):
return file_name
+
def get_date(metadata):
""" Convert from a string in iso format to a more human-like format. """
- if metadata.has_key('timestamp'):
- timestamp = float(metadata['timestamp'])
- return util.timestamp_to_elapsed_string(timestamp)
- elif metadata.has_key('mtime'):
- ti = time.strptime(metadata['mtime'], "%Y-%m-%dT%H:%M:%S")
- return util.timestamp_to_elapsed_string(time.mktime(ti))
- else:
- return _('No date')
+ if 'timestamp' in metadata:
+ try:
+ timestamp = float(metadata['timestamp'])
+ except (TypeError, ValueError):
+ logging.warning('Invalid timestamp: %r', metadata['timestamp'])
+ else:
+ return util.timestamp_to_elapsed_string(timestamp)
+
+ if 'mtime' in metadata:
+ try:
+ ti = time.strptime(metadata['mtime'], '%Y-%m-%dT%H:%M:%S')
+ except (TypeError, ValueError):
+ logging.warning('Invalid mtime: %r', metadata['mtime'])
+ else:
+ return util.timestamp_to_elapsed_string(time.mktime(ti))
+
+ return _('No date')
+
def get_bundle(metadata):
try:
if is_activity_bundle(metadata):
- file_path = util.TempFilePath(model.get_file(metadata['uid']))
+ file_path = model.get_file(metadata['uid'])
if not os.path.exists(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']))
+ file_path = model.get_file(metadata['uid'])
if not os.path.exists(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']))
+ file_path = model.get_file(metadata['uid'])
if not os.path.exists(file_path):
logging.warning('Invalid path: %r', file_path)
return None
@@ -120,6 +136,7 @@ def get_bundle(metadata):
logging.exception('Incorrect bundle')
return None
+
def _get_activities_for_mime(mime_type):
registry = bundleregistry.get_registry()
result = registry.get_activities_for_type(mime_type)
@@ -130,6 +147,7 @@ def _get_activities_for_mime(mime_type):
result.append(activity)
return result
+
def get_activities(metadata):
activities = []
@@ -148,6 +166,7 @@ def get_activities(metadata):
return activities
+
def resume(metadata, bundle_id=None):
registry = bundleregistry.get_registry()
@@ -159,19 +178,16 @@ def resume(metadata, bundle_id=None):
bundle = ActivityBundle(file_path)
if not registry.is_installed(bundle):
logging.debug('Installing activity bundle')
- registry.install(bundle)
+ try:
+ registry.install(bundle)
+ except AlreadyInstalledException:
+ _downgrade_option_alert(bundle)
+ return
else:
logging.debug('Upgrading activity bundle')
registry.upgrade(bundle)
- logging.debug('activityfactory.creating bundle with id %r',
- bundle.get_bundle_id())
- installed_bundle = registry.get_bundle(bundle.get_bundle_id())
- if installed_bundle:
- activityfactory.create(installed_bundle)
- else:
- logging.error('Bundle %r is not installed.',
- bundle.get_bundle_id())
+ _launch_bundle(bundle)
elif is_content_bundle(metadata) and bundle_id is None:
@@ -192,17 +208,10 @@ def resume(metadata, bundle_id=None):
logging.debug('activityfactory.creating with uri %s', uri)
activity_bundle = registry.get_bundle(activities[0].get_bundle_id())
- activityfactory.create_with_uri(activity_bundle, bundle.get_start_uri())
+ launch(activity_bundle, uri=uri)
else:
activity_id = metadata.get('activity_id', '')
- if activity_id:
- shell_model = shell.get_model()
- activity = shell_model.get_activity_by_id(activity_id)
- if activity:
- activity.get_window().activate(gtk.get_current_event_time())
- return
-
if bundle_id is None:
activities = get_activities(metadata)
if not activities:
@@ -213,36 +222,91 @@ def resume(metadata, bundle_id=None):
bundle = registry.get_bundle(bundle_id)
-
if metadata.get('mountpoint', '/') == '/':
object_id = metadata['uid']
else:
object_id = model.copy(metadata, '/')
- if activity_id:
- launcher.add_launcher(activity_id, bundle.get_icon(),
- get_icon_color(metadata))
- handle = ActivityHandle(object_id=object_id,
- activity_id=activity_id)
- activityfactory.create(bundle, handle)
- else:
- activityfactory.create_with_object_id(bundle, object_id)
+ launch(bundle, activity_id=activity_id, object_id=object_id,
+ color=get_icon_color(metadata))
+
+
+def _launch_bundle(bundle):
+ registry = bundleregistry.get_registry()
+ logging.debug('activityfactory.creating bundle with id %r',
+ bundle.get_bundle_id())
+ installed_bundle = registry.get_bundle(bundle.get_bundle_id())
+ if installed_bundle:
+ launch(installed_bundle)
+ else:
+ logging.error('Bundle %r is not installed.',
+ bundle.get_bundle_id())
+
+
+def launch(bundle, activity_id=None, object_id=None, uri=None, color=None,
+ invited=False):
+ if activity_id is None or not activity_id:
+ activity_id = activityfactory.create_activity_id()
+
+ logging.debug('launch bundle_id=%s activity_id=%s object_id=%s uri=%s',
+ bundle.get_bundle_id(), activity_id, object_id, uri)
+
+ shell_model = shell.get_model()
+ activity = shell_model.get_activity_by_id(activity_id)
+ if activity is not None:
+ logging.debug('re-launch %r', activity.get_window())
+ activity.get_window().activate(gtk.get_current_event_time())
+ return
+
+ if color is None:
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ launcher.add_launcher(activity_id, bundle.get_icon(), color)
+ activity_handle = ActivityHandle(activity_id=activity_id,
+ object_id=object_id, uri=uri, invited=invited)
+ activityfactory.create(bundle, activity_handle)
+
+
+def _downgrade_option_alert(bundle):
+ alert = ConfirmationAlert()
+ alert.props.title = _('Older Version Of %s Activity') % (bundle.get_name())
+ alert.props.msg = _('Do you want to downgrade to version %s') % \
+ bundle.get_activity_version()
+ alert.connect('response', _downgrade_alert_response_cb, bundle)
+ journalwindow.get_journal_window().add_alert(alert)
+ alert.show()
+
+
+def _downgrade_alert_response_cb(alert, response_id, bundle):
+ if response_id is gtk.RESPONSE_OK:
+ journalwindow.get_journal_window().remove_alert(alert)
+ registry = bundleregistry.get_registry()
+ registry.install(bundle, force_downgrade=True)
+ _launch_bundle(bundle)
+ elif response_id is gtk.RESPONSE_CANCEL:
+ journalwindow.get_journal_window().remove_alert(alert)
+
def is_activity_bundle(metadata):
mime_type = metadata.get('mime_type', '')
return mime_type == ActivityBundle.MIME_TYPE or \
mime_type == ActivityBundle.DEPRECATED_MIME_TYPE
+
def is_content_bundle(metadata):
return metadata.get('mime_type', '') == ContentBundle.MIME_TYPE
+
def is_journal_bundle(metadata):
return metadata.get('mime_type', '') == JournalEntryBundle.MIME_TYPE
+
def is_bundle(metadata):
return is_activity_bundle(metadata) or is_content_bundle(metadata) or \
is_journal_bundle(metadata)
+
def get_icon_color(metadata):
if metadata is None or not 'icon-color' in metadata:
client = gconf.client_get_default()
diff --git a/src/jarabe/journal/modalalert.py b/src/jarabe/journal/modalalert.py
index c7c6a0a..6880941 100644
--- a/src/jarabe/journal/modalalert.py
+++ b/src/jarabe/journal/modalalert.py
@@ -22,6 +22,7 @@ from sugar.graphics.icon import Icon
from sugar.graphics import style
from sugar.graphics.xocolor import XoColor
+
class ModalAlert(gtk.Window):
__gtype_name__ = 'SugarModalAlert'
@@ -84,14 +85,12 @@ class ModalAlert(gtk.Window):
self.add(self._main_view)
self._main_view.show()
- self.connect("realize", self.__realize_cb)
+ self.connect('realize', self.__realize_cb)
def __realize_cb(self, widget):
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.window.set_accept_focus(True)
def __show_journal_cb(self, button):
- '''The opener will listen on the destroy signal
- '''
+ """The opener will listen on the destroy signal"""
self.destroy()
-
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index ffc62e0..320e577 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2008, One Laptop Per Child
+# Copyright (C) 2007-2010, 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
@@ -16,11 +16,13 @@
import logging
import os
+import errno
from datetime import datetime
import time
import shutil
-from stat import S_IFMT, S_IFDIR, S_IFREG
+from stat import S_IFLNK, S_IFMT, S_IFDIR, S_IFREG
import re
+from operator import itemgetter
import gobject
import dbus
@@ -31,18 +33,25 @@ from sugar import dispatch
from sugar import mime
from sugar import util
+
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
# Properties the journal cares about.
-PROPERTIES = ['uid', 'title', 'mtime', 'timestamp', 'keep', 'buddies',
- 'icon-color', 'mime_type', 'progress', 'activity', 'mountpoint',
- 'activity_id', 'bundle_id']
+PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id',
+ 'creation_time', 'filesize', 'icon-color', 'keep', 'mime_type',
+ 'mountpoint', 'mtime', 'progress', 'timestamp', 'title', 'uid']
MIN_PAGES_TO_CACHE = 3
MAX_PAGES_TO_CACHE = 5
+_datastore = None
+created = dispatch.Signal()
+updated = dispatch.Signal()
+deleted = dispatch.Signal()
+
+
class _Cache(object):
__gtype_name__ = 'model_Cache'
@@ -73,7 +82,7 @@ class BaseResultSet(object):
"""
def __init__(self, query, page_size):
- self._total_count = -1
+ self._total_count = -1
self._position = -1
self._query = query
self._page_size = page_size
@@ -108,8 +117,6 @@ class BaseResultSet(object):
self._position = position
def read(self):
- logging.debug('ResultSet.read position: %r', self._position)
-
if self._position == -1:
self.seek(0)
@@ -142,7 +149,8 @@ class BaseResultSet(object):
self._cache.append_all(entries)
self._offset = offset
- elif remaining_forward_entries <= 0 and remaining_backwards_entries > 0:
+ 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',
@@ -184,11 +192,10 @@ class BaseResultSet(object):
objects_excess = len(self._cache) - cache_limit
if objects_excess > 0:
del self._cache[-objects_excess:]
- else:
- logging.debug('cache hit and no need to grow the cache')
return self._cache[self._position - self._offset]
+
class DatastoreResultSet(BaseResultSet):
"""Encapsulates the result of a query on the datastore
"""
@@ -216,6 +223,7 @@ class DatastoreResultSet(BaseResultSet):
return entries, total_count
+
class InplaceResultSet(BaseResultSet):
"""Encapsulates the result of a query on a mount point
"""
@@ -223,7 +231,9 @@ class InplaceResultSet(BaseResultSet):
BaseResultSet.__init__(self, query, page_size)
self._mount_point = mount_point
self._file_list = None
- self._pending_directories = 0
+ self._pending_directories = []
+ self._visited_directories = []
+ self._pending_files = []
self._stopped = False
query_text = query.get('query', '')
@@ -246,15 +256,27 @@ class InplaceResultSet(BaseResultSet):
self._mime_types = query.get('mime_type', [])
+ self._sort = query.get('order_by', ['+timestamp'])[0]
+
def setup(self):
self._file_list = []
- self._recurse_dir(self._mount_point)
+ self._pending_directories = [self._mount_point]
+ self._visited_directories = []
+ self._pending_files = []
+ gobject.idle_add(self._scan)
def stop(self):
self._stopped = True
def setup_ready(self):
- self._file_list.sort(lambda a, b: b[2] - a[2])
+ if self._sort[1:] == 'filesize':
+ keygetter = itemgetter(3)
+ else:
+ # timestamp
+ keygetter = itemgetter(2)
+ self._file_list.sort(lambda a, b: cmp(b, a),
+ key=keygetter,
+ reverse=(self._sort[0] == '-'))
self.ready.send(self)
def find(self, query):
@@ -267,13 +289,13 @@ class InplaceResultSet(BaseResultSet):
t = time.time()
offset = int(query.get('offset', 0))
- limit = int(query.get('limit', len(self._file_list)))
+ limit = int(query.get('limit', len(self._file_list)))
total_count = len(self._file_list)
files = self._file_list[offset:offset + limit]
entries = []
- for file_path, stat, mtime_ in files:
+ for file_path, stat, mtime_, size_ in files:
metadata = _get_file_metadata(file_path, stat)
metadata['mountpoint'] = self._mount_point
entries.append(metadata)
@@ -282,75 +304,115 @@ class InplaceResultSet(BaseResultSet):
return entries, total_count
- def _recurse_dir(self, dir_path):
- self._pending_directories += 1
- gobject.idle_add(self._idle_recurse_dir, dir_path)
+ def _scan(self):
+ if self._stopped:
+ return False
- def _idle_recurse_dir(self, dir_path):
- try:
- self._real_recurse_dir(dir_path)
- finally:
- self._pending_directories -= 1
- if self._pending_directories == 0:
- self.setup_ready()
+ self.progress.send(self)
- def _real_recurse_dir(self, dir_path):
- if self._stopped:
- return
+ if self._pending_files:
+ self._scan_a_file()
+ return True
+
+ if self._pending_directories:
+ self._scan_a_directory()
+ return True
+
+ self.setup_ready()
+ self._visited_directories = []
+ return False
+
+ def _scan_a_file(self):
+ full_path = self._pending_files.pop(0)
try:
- dirs = os.listdir(dir_path)
- except Exception:
- logging.exception('Error reading directory %r', dir_path)
- dirs = []
+ stat = os.lstat(full_path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ logging.exception(
+ 'Error reading metadata of file %r', full_path)
+ return
+
+ if S_IFMT(stat.st_mode) == S_IFLNK:
+ try:
+ link = os.readlink(full_path)
+ except OSError, e:
+ logging.exception(
+ 'Error reading target of link %r', full_path)
+ return
+
+ if not os.path.abspath(link).startswith(self._mount_point):
+ return
- for entry in dirs:
- if entry.startswith('.'):
- continue
- full_path = dir_path + '/' + entry
try:
stat = os.stat(full_path)
- if S_IFMT(stat.st_mode) == S_IFDIR:
- self._recurse_dir(full_path)
- elif S_IFMT(stat.st_mode) == S_IFREG:
- add_to_list = True
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ logging.exception(
+ 'Error reading metadata of linked file %r', full_path)
+ return
+
+ if S_IFMT(stat.st_mode) == S_IFDIR:
+ id_tuple = stat.st_ino, stat.st_dev
+ if not id_tuple in self._visited_directories:
+ self._visited_directories.append(id_tuple)
+ self._pending_directories.append(full_path)
+ return
+
+ if S_IFMT(stat.st_mode) != S_IFREG:
+ return
- if self._regex is not None and \
- not self._regex.match(full_path):
- add_to_list = False
+ if self._regex is not None and \
+ not self._regex.match(full_path):
+ return
- if None not in [self._date_start, self._date_end] and \
- (stat.st_mtime < self._date_start or
- stat.st_mtime > self._date_end):
- add_to_list = False
+ if self._date_start is not None and stat.st_mtime < self._date_start:
+ return
- if self._mime_types:
- mime_type = gio.content_type_guess(filename=full_path)
- if mime_type not in self._mime_types:
- add_to_list = False
+ if self._date_end is not None and stat.st_mtime > self._date_end:
+ return
- if add_to_list:
- file_info = (full_path, stat, int(stat.st_mtime))
- self._file_list.append(file_info)
+ if self._mime_types:
+ mime_type = gio.content_type_guess(filename=full_path)
+ if mime_type not in self._mime_types:
+ return
- self.progress.send(self)
+ file_info = (full_path, stat, int(stat.st_mtime), stat.st_size)
+ self._file_list.append(file_info)
+
+ return
+
+ def _scan_a_directory(self):
+ dir_path = self._pending_directories.pop(0)
+
+ try:
+ entries = os.listdir(dir_path)
+ except OSError, e:
+ if e.errno != errno.EACCES:
+ logging.exception('Error reading directory %r', dir_path)
+ return
+
+ for entry in entries:
+ if entry.startswith('.'):
+ continue
+ self._pending_files.append(dir_path + '/' + entry)
+ return
- except Exception:
- logging.exception('Error reading file %r', full_path)
def _get_file_metadata(path, stat):
client = gconf.client_get_default()
return {'uid': path,
'title': os.path.basename(path),
'timestamp': stat.st_mtime,
+ 'filesize': stat.st_size,
'mime_type': gio.content_type_guess(filename=path),
'activity': '',
'activity_id': '',
'icon-color': client.get_string('/desktop/sugar/user/color'),
'description': path}
-_datastore = None
+
def _get_datastore():
global _datastore
if _datastore is None:
@@ -364,15 +426,19 @@ def _get_datastore():
return _datastore
+
def _datastore_created_cb(object_id):
created.send(None, object_id=object_id)
+
def _datastore_updated_cb(object_id):
updated.send(None, object_id=object_id)
+
def _datastore_deleted_cb(object_id):
deleted.send(None, object_id=object_id)
+
def find(query_, page_size):
"""Returns a ResultSet
"""
@@ -387,6 +453,7 @@ def find(query_, page_size):
else:
return InplaceResultSet(query, page_size, mount_points[0])
+
def _get_mount_point(path):
dir_path = os.path.dirname(path)
while True:
@@ -395,6 +462,7 @@ def _get_mount_point(path):
else:
dir_path = dir_path.rsplit(os.sep, 1)[0]
+
def get(object_id):
"""Returns the metadata for an object
"""
@@ -407,6 +475,7 @@ def get(object_id):
metadata['mountpoint'] = '/'
return metadata
+
def get_file(object_id):
"""Returns the file for an object
"""
@@ -421,6 +490,7 @@ def get_file(object_id):
else:
return None
+
def get_file_size(object_id):
"""Return the file size for an object
"""
@@ -436,12 +506,14 @@ def get_file_size(object_id):
return 0
+
def get_unique_values(key):
"""Returns a list with the different values a property has taken
"""
empty_dict = dbus.Dictionary({}, signature='ss')
return _get_datastore().get_uniquevaluesfor(key, empty_dict)
+
def delete(object_id):
"""Removes an object from persistent storage
"""
@@ -451,6 +523,7 @@ def delete(object_id):
else:
_get_datastore().delete(object_id)
+
def copy(metadata, mount_point):
"""Copies an object to another mount point
"""
@@ -462,6 +535,7 @@ def copy(metadata, mount_point):
return write(metadata, file_path, transfer_ownership=False)
+
def write(metadata, file_path='', update_mtime=True, transfer_ownership=True):
"""Creates or updates an entry for that id
"""
@@ -496,6 +570,7 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True):
return object_id
+
def _get_file_name(title, mime_type):
file_name = title
@@ -520,11 +595,12 @@ def _get_file_name(title, mime_type):
return file_name
+
def _get_unique_file_name(mount_point, file_name):
if os.path.exists(os.path.join(mount_point, file_name)):
i = 1
+ name, extension = os.path.splitext(file_name)
while len(file_name) <= 255:
- name, extension = os.path.splitext(file_name)
file_name = name + '_' + str(i) + extension
if not os.path.exists(os.path.join(mount_point, file_name)):
break
@@ -532,10 +608,7 @@ def _get_unique_file_name(mount_point, file_name):
return file_name
+
def is_editable(metadata):
mountpoint = metadata.get('mountpoint', '/')
return mountpoint == '/'
-
-created = dispatch.Signal()
-updated = dispatch.Signal()
-deleted = dispatch.Signal()
diff --git a/src/jarabe/journal/objectchooser.py b/src/jarabe/journal/objectchooser.py
index 49af3e6..ecb8ecf 100644
--- a/src/jarabe/journal/objectchooser.py
+++ b/src/jarabe/journal/objectchooser.py
@@ -29,14 +29,13 @@ from jarabe.journal.listmodel import ListModel
from jarabe.journal.journaltoolbox import SearchToolbar
from jarabe.journal.volumestoolbar import VolumesToolbar
+
class ObjectChooser(gtk.Window):
__gtype_name__ = 'ObjectChooser'
__gsignals__ = {
- 'response': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([int]))
+ 'response': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([int])),
}
def __init__(self, parent=None, what_filter=''):
@@ -136,6 +135,7 @@ class ObjectChooser(gtk.Window):
visible = event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED
self._list_view.set_is_visible(visible)
+
class TitleBox(VolumesToolbar):
__gtype_name__ = 'TitleBox'
@@ -162,6 +162,7 @@ class TitleBox(VolumesToolbar):
self.insert(tool_item, -1)
tool_item.show()
+
class ChooserListView(BaseListView):
__gtype_name__ = 'ChooserListView'
@@ -187,7 +188,7 @@ class ChooserListView(BaseListView):
if event.window != tree_view.get_bin_window():
return False
- pos = tree_view.get_path_at_pos(event.x, event.y)
+ pos = tree_view.get_path_at_pos(int(event.x), int(event.y))
if pos is None:
return False
@@ -196,4 +197,3 @@ class ChooserListView(BaseListView):
self.emit('entry-activated', uid)
return False
-
diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py
index 0e7702d..9ae1afb 100644
--- a/src/jarabe/journal/palettes.py
+++ b/src/jarabe/journal/palettes.py
@@ -28,20 +28,19 @@ from sugar.graphics.icon import Icon
from sugar.graphics.xocolor import XoColor
from sugar import mime
-from jarabe.model import bundleregistry
from jarabe.model import friends
from jarabe.model import filetransfer
from jarabe.model import mimeregistry
from jarabe.journal import misc
from jarabe.journal import model
+
class ObjectPalette(Palette):
__gtype_name__ = 'ObjectPalette'
__gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
+ 'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([str])),
}
@@ -54,7 +53,7 @@ class ObjectPalette(Palette):
activity_icon.props.file = misc.get_icon_name(metadata)
activity_icon.props.xo_color = misc.get_icon_color(metadata)
- if metadata.has_key('title'):
+ if 'title' in metadata:
title = gobject.markup_escape_text(metadata['title'])
else:
title = _('Untitled')
@@ -62,22 +61,29 @@ class ObjectPalette(Palette):
Palette.__init__(self, primary_text=title,
icon=activity_icon)
- if metadata.get('activity_id', ''):
- resume_label = _('Resume')
- resume_with_label = _('Resume with')
- else:
- resume_label = _('Start')
- resume_with_label = _('Start with')
- menu_item = MenuItem(resume_label, 'activity-start')
- menu_item.connect('activate', self.__start_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
+ if misc.get_activities(metadata) or misc.is_bundle(metadata):
+ if metadata.get('activity_id', ''):
+ resume_label = _('Resume')
+ resume_with_label = _('Resume with')
+ else:
+ resume_label = _('Start')
+ resume_with_label = _('Start with')
+ menu_item = MenuItem(resume_label, 'activity-start')
+ menu_item.connect('activate', self.__start_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
- menu_item = MenuItem(resume_with_label, 'activity-start')
- self.menu.append(menu_item)
- menu_item.show()
- start_with_menu = StartWithMenu(self._metadata)
- menu_item.set_submenu(start_with_menu)
+ menu_item = MenuItem(resume_with_label, 'activity-start')
+ self.menu.append(menu_item)
+ menu_item.show()
+ start_with_menu = StartWithMenu(self._metadata)
+ menu_item.set_submenu(start_with_menu)
+
+ else:
+ menu_item = MenuItem(_('No activity to start entry'))
+ menu_item.set_sensitive(False)
+ self.menu.append(menu_item)
+ menu_item.show()
client = gconf.client_get_default()
color = XoColor(client.get_string('/desktop/sugar/user/color'))
@@ -128,11 +134,6 @@ class ObjectPalette(Palette):
self._temp_file_path = None
def __erase_activate_cb(self, menu_item):
- registry = bundleregistry.get_registry()
-
- bundle = misc.get_bundle(self._metadata)
- if bundle is not None and registry.is_installed(bundle):
- registry.uninstall(bundle)
model.delete(self._metadata['uid'])
def __detail_activate_cb(self, menu_item):
@@ -152,12 +153,13 @@ class ObjectPalette(Palette):
filetransfer.start_transfer(buddy, file_name, title, description,
mime_type)
+
class FriendsMenu(gtk.Menu):
__gtype_name__ = 'JournalFriendsMenu'
__gsignals__ = {
- 'friend-selected' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([object])),
+ 'friend-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py
index 74b974c..2d842f1 100644
--- a/src/jarabe/journal/volumestoolbar.py
+++ b/src/jarabe/journal/volumestoolbar.py
@@ -15,6 +15,8 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
+import os
+import statvfs
from gettext import gettext as _
import gobject
@@ -25,17 +27,20 @@ import gconf
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.palette import Palette
from sugar.graphics.xocolor import XoColor
+from sugar import env
from jarabe.journal import model
from jarabe.view.palettes import VolumePalette
+
class VolumesToolbar(gtk.Toolbar):
__gtype_name__ = 'VolumesToolbar'
__gsignals__ = {
- 'volume-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str]))
+ 'volume-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str, str])),
}
def __init__(self):
@@ -44,7 +49,6 @@ class VolumesToolbar(gtk.Toolbar):
self._mount_removed_hid = None
button = JournalButton()
- button.set_palette(Palette(_('Journal')))
button.connect('toggled', self._button_toggled_cb)
self.insert(button, 0)
button.show()
@@ -61,10 +65,10 @@ class VolumesToolbar(gtk.Toolbar):
def _set_up_volumes(self):
volume_monitor = gio.volume_monitor_get()
- self._mount_added_hid = \
- volume_monitor.connect('mount-added', self.__mount_added_cb)
- self._mount_removed_hid = \
- volume_monitor.connect('mount-removed', self.__mount_removed_cb)
+ self._mount_added_hid = volume_monitor.connect('mount-added',
+ self.__mount_added_cb)
+ self._mount_removed_hid = volume_monitor.connect('mount-removed',
+ self.__mount_removed_cb)
for mount in volume_monitor.get_mounts():
self._add_button(mount)
@@ -81,6 +85,7 @@ class VolumesToolbar(gtk.Toolbar):
button = VolumeButton(mount)
button.props.group = self._volume_buttons[0]
button.connect('toggled', self._button_toggled_cb)
+ button.connect('volume-error', self.__volume_error_cb)
position = self.get_item_index(self._volume_buttons[-1]) + 1
self.insert(button, position)
button.show()
@@ -90,6 +95,9 @@ class VolumesToolbar(gtk.Toolbar):
if len(self.get_children()) > 1:
self.show()
+ def __volume_error_cb(self, button, strerror, severity):
+ self.emit('volume-error', strerror, severity)
+
def _button_toggled_cb(self, button):
if button.props.active:
self.emit('volume-changed', button.mount_point)
@@ -122,7 +130,13 @@ class VolumesToolbar(gtk.Toolbar):
button = self._get_button_for_mount(mount)
button.props.active = True
+
class BaseButton(RadioToolButton):
+ __gsignals__ = {
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str, str])),
+ }
+
def __init__(self, mount_point):
RadioToolButton.__init__(self)
@@ -133,11 +147,26 @@ class BaseButton(RadioToolButton):
gtk.gdk.ACTION_COPY)
self.connect('drag-data-received', self._drag_data_received_cb)
- def _drag_data_received_cb(self, widget, drag_context, x, y, selection_data,
- info, timestamp):
+ def _drag_data_received_cb(self, widget, drag_context, x, y,
+ selection_data, info, timestamp):
object_id = selection_data.data
metadata = model.get(object_id)
- model.copy(metadata, self.mount_point)
+ file_path = model.get_file(metadata['uid'])
+ if not file_path or not os.path.exists(file_path):
+ logging.warn('Entries without a file cannot be copied.')
+ self.emit('volume-error',
+ _('Entries without a file cannot be copied.'),
+ _('Warning'))
+ return
+
+ try:
+ model.copy(metadata, self.mount_point)
+ except IOError, e:
+ logging.exception('Error while copying the entry. %s', e.strerror)
+ self.emit('volume-error',
+ _('Error while copying the entry. %s') % e.strerror,
+ _('Error'))
+
class VolumeButton(BaseButton):
def __init__(self, mount):
@@ -169,6 +198,7 @@ class VolumeButton(BaseButton):
#palette.set_group_id('frame')
return palette
+
class JournalButton(BaseButton):
def __init__(self):
BaseButton.__init__(self, mount_point='/')
@@ -179,3 +209,36 @@ class JournalButton(BaseButton):
color = XoColor(client.get_string('/desktop/sugar/user/color'))
self.props.xo_color = color
+ def create_palette(self):
+ palette = JournalButtonPalette(self)
+ return palette
+
+
+class JournalButtonPalette(Palette):
+
+ def __init__(self, mount):
+ Palette.__init__(self, _('Journal'))
+ vbox = gtk.VBox()
+ self.set_content(vbox)
+ vbox.show()
+
+ self._progress_bar = gtk.ProgressBar()
+ vbox.add(self._progress_bar)
+ self._progress_bar.show()
+
+ self._free_space_label = gtk.Label()
+ self._free_space_label.set_alignment(0.5, 0.5)
+ vbox.add(self._free_space_label)
+ self._free_space_label.show()
+
+ self.connect('popup', self.__popup_cb)
+
+ def __popup_cb(self, palette):
+ stat = os.statvfs(env.get_profile_path())
+ free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]
+ total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS]
+
+ fraction = (total_space - free_space) / float(total_space)
+ self._progress_bar.props.fraction = fraction
+ self._free_space_label.props.label = _('%(free_space)d MB Free') % \
+ {'free_space': free_space / (1024 * 1024)}
diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
index e9f0700..92e8712 100644
--- a/src/jarabe/model/Makefile.am
+++ b/src/jarabe/model/Makefile.am
@@ -1,5 +1,6 @@
sugardir = $(pythondir)/jarabe/model
sugar_PYTHON = \
+ adhoc.py \
__init__.py \
buddy.py \
bundleregistry.py \
@@ -7,12 +8,12 @@ sugar_PYTHON = \
friends.py \
invites.py \
olpcmesh.py \
- owner.py \
- mimeregistry.py \
+ mimeregistry.py \
neighborhood.py \
network.py \
notifications.py \
shell.py \
screen.py \
session.py \
- sound.py
+ sound.py \
+ telepathyclient.py
diff --git a/src/jarabe/model/__init__.py b/src/jarabe/model/__init__.py
index a9dd95a..85f6a24 100644
--- a/src/jarabe/model/__init__.py
+++ b/src/jarabe/model/__init__.py
@@ -13,4 +13,3 @@
# 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/model/adhoc.py b/src/jarabe/model/adhoc.py
new file mode 100644
index 0000000..8842a5c
--- /dev/null
+++ b/src/jarabe/model/adhoc.py
@@ -0,0 +1,294 @@
+# Copyright (C) 2010 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
+
+import dbus
+import gobject
+
+from jarabe.model import network
+from jarabe.model.network import Settings
+from sugar.util import unique_id
+from jarabe.model.network import IP4Config
+
+
+_NM_SERVICE = 'org.freedesktop.NetworkManager'
+_NM_IFACE = 'org.freedesktop.NetworkManager'
+_NM_PATH = '/org/freedesktop/NetworkManager'
+_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
+_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
+_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
+_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
+
+_adhoc_manager_instance = None
+
+
+def get_adhoc_manager_instance():
+ global _adhoc_manager_instance
+ if _adhoc_manager_instance is None:
+ _adhoc_manager_instance = AdHocManager()
+ return _adhoc_manager_instance
+
+
+class AdHocManager(gobject.GObject):
+ """To mimic the mesh behavior on devices where mesh hardware is
+ not available we support the creation of an Ad-hoc network on
+ three channels 1, 6, 11. If Sugar sees no "known" network when it
+ starts, it does autoconnect to an Ad-hoc network.
+
+ """
+
+ __gsignals__ = {
+ 'members-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
+ 'state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
+ }
+
+ _AUTOCONNECT_TIMEOUT = 30
+ _CHANNEL_1 = 1
+ _CHANNEL_6 = 6
+ _CHANNEL_11 = 11
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._bus = dbus.SystemBus()
+ self._device = None
+ self._idle_source = 0
+ self._listening_called = 0
+ self._device_state = network.DEVICE_STATE_UNKNOWN
+
+ self._current_channel = None
+ self._networks = {self._CHANNEL_1: None,
+ self._CHANNEL_6: None,
+ self._CHANNEL_11: None}
+
+ def start_listening(self, device):
+ self._listening_called += 1
+ if self._listening_called > 1:
+ raise RuntimeError('The start listening method can' \
+ ' only be called once.')
+
+ self._device = device
+ props = dbus.Interface(device, dbus.PROPERTIES_IFACE)
+ self._device_state = props.Get(_NM_DEVICE_IFACE, 'State')
+
+ self._bus.add_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+
+ self._bus.add_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_WIRELESS_IFACE)
+
+ def stop_listening(self):
+ self._bus.remove_signal_receiver(self.__device_state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+ self._bus.remove_signal_receiver(self.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_WIRELESS_IFACE)
+
+ def __device_state_changed_cb(self, new_state, old_state, reason):
+ self._device_state = new_state
+ self._update_state()
+
+ def __wireless_properties_changed_cb(self, properties):
+ if 'ActiveAccessPoint' in properties and \
+ properties['ActiveAccessPoint'] != '/':
+ active_ap = self._bus.get_object(_NM_SERVICE,
+ properties['ActiveAccessPoint'])
+ props = dbus.Interface(active_ap, dbus.PROPERTIES_IFACE)
+ props.GetAll(_NM_ACCESSPOINT_IFACE, byte_arrays=True,
+ reply_handler=self.__get_all_ap_props_reply_cb,
+ error_handler=self.__get_all_ap_props_error_cb)
+
+ def __get_all_ap_props_reply_cb(self, properties):
+ if properties['Mode'] == network.NM_802_11_MODE_ADHOC and \
+ 'Frequency' in properties:
+ frequency = properties['Frequency']
+ self._current_channel = network.frequency_to_channel(frequency)
+ else:
+ self._current_channel = None
+ self._update_state()
+
+ def __get_all_ap_props_error_cb(self, err):
+ logging.error('Error getting the access point properties: %s', err)
+
+ def _update_state(self):
+ self.emit('state-changed', self._current_channel, self._device_state)
+
+ def _have_configured_connections(self):
+ return len(network.get_settings().connections) > 0
+
+ def autoconnect(self):
+ """Autoconnect to an Ad-hoc network"""
+ if self._device_state != network.DEVICE_STATE_DISCONNECTED:
+ return
+ elif self._have_configured_connections():
+ self._autoconnect_adhoc_timer()
+ else:
+ self._autoconnect_adhoc()
+
+ def _autoconnect_adhoc_timer(self):
+ """Start a timer which basically looks for 30 seconds of inactivity
+ on the device, then does autoconnect to an Ad-hoc network.
+
+ """
+ if self._idle_source != 0:
+ gobject.source_remove(self._idle_source)
+ self._idle_source = gobject.timeout_add_seconds( \
+ self._AUTOCONNECT_TIMEOUT, self.__idle_check_cb)
+
+ def __idle_check_cb(self):
+ if self._device_state == network.DEVICE_STATE_DISCONNECTED:
+ logging.debug('Connect to Ad-hoc network due to inactivity.')
+ self._autoconnect_adhoc()
+ return False
+
+ def _autoconnect_adhoc(self):
+ """First we try if there is an Ad-hoc network that is used by other
+ learners in the area, if not we default to channel 1.
+
+ """
+ if self._networks[self._CHANNEL_1] is not None:
+ self._connect(self._CHANNEL_1)
+ elif self._networks[self._CHANNEL_6] is not None:
+ self._connect(self._CHANNEL_6)
+ elif self._networks[self._CHANNEL_11] is not None:
+ self._connect(self._CHANNEL_11)
+ else:
+ self._connect(self._CHANNEL_1)
+
+ def activate_channel(self, channel):
+ """Activate a sugar Ad-hoc network.
+
+ Keyword arguments:
+ channel -- Channel to connect to (should be 1, 6, 11)
+
+ """
+ self._connect(channel)
+
+ def _connect(self, channel):
+ name = 'Ad-hoc Network %d' % channel
+ connection = network.find_connection_by_ssid(name)
+ if connection is None:
+ settings = Settings()
+ settings.connection.id = name
+ settings.connection.uuid = unique_id()
+ settings.connection.type = '802-11-wireless'
+ settings.wireless.ssid = dbus.ByteArray(name)
+ settings.wireless.band = 'bg'
+ settings.wireless.channel = channel
+ settings.wireless.mode = 'adhoc'
+ settings.ip4_config = IP4Config()
+ settings.ip4_config.method = 'link-local'
+
+ connection = network.add_connection(name, settings)
+
+ obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
+ netmgr = dbus.Interface(obj, _NM_IFACE)
+
+ netmgr.ActivateConnection(network.SETTINGS_SERVICE,
+ connection.path,
+ self._device.object_path,
+ '/',
+ reply_handler=self.__activate_reply_cb,
+ error_handler=self.__activate_error_cb)
+
+ def deactivate_active_channel(self):
+ """Deactivate the current active channel."""
+ obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
+ netmgr = dbus.Interface(obj, _NM_IFACE)
+
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
+ netmgr_props.Get(_NM_IFACE, 'ActiveConnections', \
+ reply_handler=self.__get_active_connections_reply_cb,
+ error_handler=self.__get_active_connections_error_cb)
+
+ def __get_active_connections_reply_cb(self, active_connections_o):
+ for connection_o in active_connections_o:
+ obj = self._bus.get_object(_NM_IFACE, connection_o)
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ state = props.Get(_NM_ACTIVE_CONN_IFACE, 'State')
+ if state == network.NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+ access_point_o = props.Get(_NM_ACTIVE_CONN_IFACE,
+ 'SpecificObject')
+ if access_point_o != '/':
+ obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
+ netmgr = dbus.Interface(obj, _NM_IFACE)
+ netmgr.DeactivateConnection(connection_o)
+
+ def __get_active_connections_error_cb(self, err):
+ logging.error('Error getting the active connections: %s', err)
+
+ def __activate_reply_cb(self, connection):
+ logging.debug('Ad-hoc network created: %s', connection)
+
+ def __activate_error_cb(self, err):
+ logging.error('Failed to create Ad-hoc network: %s', err)
+
+ def add_access_point(self, access_point):
+ """Add an access point to a network and notify the view to idicate
+ the member change.
+
+ Keyword arguments:
+ access_point -- Access Point
+
+ """
+ if access_point.name.endswith(' 1'):
+ self._networks[self._CHANNEL_1] = access_point
+ self.emit('members-changed', self._CHANNEL_1, True)
+ elif access_point.name.endswith(' 6'):
+ self._networks[self._CHANNEL_6] = access_point
+ self.emit('members-changed', self._CHANNEL_6, True)
+ elif access_point.name.endswith('11'):
+ self._networks[self._CHANNEL_11] = access_point
+ self.emit('members-changed', self._CHANNEL_11, True)
+
+ def is_sugar_adhoc_access_point(self, ap_object_path):
+ """Checks whether an access point is part of a sugar Ad-hoc network.
+
+ Keyword arguments:
+ ap_object_path -- Access Point object path
+
+ Return: Boolean
+
+ """
+ for access_point in self._networks.values():
+ if access_point is not None:
+ if access_point.model.object_path == ap_object_path:
+ return True
+ return False
+
+ def remove_access_point(self, ap_object_path):
+ """Remove an access point from a sugar Ad-hoc network.
+
+ Keyword arguments:
+ ap_object_path -- Access Point object path
+
+ """
+ for channel in self._networks:
+ if self._networks[channel] is not None:
+ if self._networks[channel].model.object_path == ap_object_path:
+ self.emit('members-changed', channel, False)
+ self._networks[channel] = None
+ break
diff --git a/src/jarabe/model/buddy.py b/src/jarabe/model/buddy.py
index 5978bae..c580e68 100644
--- a/src/jarabe/model/buddy.py
+++ b/src/jarabe/model/buddy.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -14,169 +15,220 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from sugar.presence import presenceservice
-from sugar.graphics.xocolor import XoColor
+import logging
+
import gobject
+import gconf
+import dbus
+from telepathy.client import Connection
+from telepathy.interfaces import CONNECTION
+
+from sugar.graphics.xocolor import XoColor
+from sugar.profile import get_profile
+
+from jarabe.util.telepathy import connection_watcher
+
-_NOT_PRESENT_COLOR = "#d5d5d5,#FFFFFF"
-
-class BuddyModel(gobject.GObject):
- __gsignals__ = {
- 'appeared': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
- 'disappeared': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([])),
- 'nick-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'color-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- '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]))
- }
-
- def __init__(self, key=None, buddy=None, nick=None):
- if (key and buddy) or (not key and not buddy):
- raise RuntimeError("Must specify only _one_ of key or buddy.")
-
- gobject.GObject.__init__(self)
+CONNECTION_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
+_owner_instance = None
+
+
+class BaseBuddyModel(gobject.GObject):
+ __gtype_name__ = 'SugarBaseBuddyModel'
+
+ def __init__(self, **kwargs):
+ self._key = None
+ self._nick = None
self._color = None
self._tags = None
- self._ba_handler = None
- self._pc_handler = None
- self._dis_handler = None
- self._bic_handler = None
- self._cac_handler = None
-
- self._pservice = presenceservice.get_instance()
-
- self._buddy = None
-
- if not buddy:
- self._key = key
- # connect to the PS's buddy-appeared signal and
- # wait for the buddy to appear
- self._ba_handler = self._pservice.connect('buddy-appeared',
- self._buddy_appeared_cb)
- # Set color to 'inactive'/'disconnected'
- self._set_color_from_string(_NOT_PRESENT_COLOR)
- self._nick = nick
-
- self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
- else:
- self._update_buddy(buddy)
-
- def _get_buddies_cb(self, buddy_list):
- buddy = None
- for iter_buddy in buddy_list:
- if iter_buddy.props.key == self._key:
- buddy = iter_buddy
- break
-
- if buddy:
- if self._ba_handler:
- # Once we have the buddy, we no longer need to
- # monitor buddy-appeared events
- self._pservice.disconnect(self._ba_handler)
- self._ba_handler = None
-
- self._update_buddy(buddy)
-
- def _set_color_from_string(self, color_string):
- self._color = XoColor(color_string)
+ self._current_activity = None
- def get_key(self):
- return self._key
+ gobject.GObject.__init__(self, **kwargs)
def get_nick(self):
return self._nick
+ def set_nick(self, nick):
+ self._nick = nick
+
+ nick = gobject.property(type=object, getter=get_nick, setter=set_nick)
+
+ def get_key(self):
+ return self._key
+
+ def set_key(self, key):
+ self._key = key
+
+ key = gobject.property(type=object, getter=get_key, setter=set_key)
+
def get_color(self):
return self._color
+ def set_color(self, color):
+ self._color = color
+
+ color = gobject.property(type=object, getter=get_color, setter=set_color)
+
def get_tags(self):
return self._tags
- def get_buddy(self):
- return self._buddy
+ tags = gobject.property(type=object, getter=get_tags)
+
+ def get_current_activity(self):
+ return self._current_activity
+
+ def set_current_activity(self, current_activity):
+ if self._current_activity != current_activity:
+ self._current_activity = current_activity
+ self.notify('current-activity')
+
+ current_activity = gobject.property(type=object,
+ getter=get_current_activity,
+ setter=set_current_activity)
+
+ def is_owner(self):
+ raise NotImplementedError
+
+
+class OwnerBuddyModel(BaseBuddyModel):
+ __gtype_name__ = 'SugarOwnerBuddyModel'
+
+ def __init__(self):
+ BaseBuddyModel.__init__(self)
+
+ client = gconf.client_get_default()
+ self.props.nick = client.get_string('/desktop/sugar/user/nick')
+ color = client.get_string('/desktop/sugar/user/color')
+ self.props.color = XoColor(color)
+
+ self.props.key = get_profile().pubkey
+
+ self.connect('notify::nick', self.__property_changed_cb)
+ self.connect('notify::color', self.__property_changed_cb)
+ self.connect('notify::current-activity',
+ self.__current_activity_changed_cb)
+
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(
+ self.__name_owner_changed_cb,
+ signal_name='NameOwnerChanged',
+ dbus_interface='org.freedesktop.DBus')
+
+ bus_object = bus.get_object(dbus.BUS_DAEMON_NAME, dbus.BUS_DAEMON_PATH)
+ for service in bus_object.ListNames(
+ dbus_interface=dbus.BUS_DAEMON_IFACE):
+ if service.startswith(CONNECTION + '.'):
+ path = '/%s' % service.replace('.', '/')
+ Connection(service, path, bus,
+ ready_handler=self.__connection_ready_cb)
+
+ def __connection_ready_cb(self, connection):
+ self._sync_properties_on_connection(connection)
+
+ def __name_owner_changed_cb(self, name, old, new):
+ if name.startswith(CONNECTION + '.') and not old and new:
+ path = '/' + name.replace('.', '/')
+ Connection(name, path, ready_handler=self.__connection_ready_cb)
+
+ def __property_changed_cb(self, buddy, pspec):
+ self._sync_properties()
+
+ def __current_activity_changed_cb(self, buddy, pspec):
+ conn_watcher = connection_watcher.get_instance()
+ for connection in conn_watcher.get_connections():
+ if self.props.current_activity is not None:
+ activity_id = self.props.current_activity.activity_id
+ room_handle = self.props.current_activity.room_handle
+ else:
+ activity_id = ''
+ room_handle = 0
+
+ connection[CONNECTION_INTERFACE_BUDDY_INFO].SetCurrentActivity(
+ activity_id,
+ room_handle,
+ reply_handler=self.__set_current_activity_cb,
+ error_handler=self.__error_handler_cb)
+
+ def __set_current_activity_cb(self):
+ logging.debug('__set_current_activity_cb')
+
+ def _sync_properties(self):
+ conn_watcher = connection_watcher.get_instance()
+ for connection in conn_watcher.get_connections():
+ self._sync_properties_on_connection(connection)
+
+ def _sync_properties_on_connection(self, connection):
+ if CONNECTION_INTERFACE_BUDDY_INFO in connection:
+ properties = {}
+ if self.props.key is not None:
+ properties['key'] = dbus.ByteArray(self.props.key)
+ if self.props.color is not None:
+ properties['color'] = self.props.color.to_string()
+
+ logging.debug('calling SetProperties with %r', properties)
+ connection[CONNECTION_INTERFACE_BUDDY_INFO].SetProperties(
+ properties,
+ reply_handler=self.__set_properties_cb,
+ error_handler=self.__error_handler_cb)
+
+ def __set_properties_cb(self):
+ logging.debug('__set_properties_cb')
+
+ def __error_handler_cb(self, error):
+ raise RuntimeError(error)
+
+ def __connection_added_cb(self, conn_watcher, connection):
+ self._sync_properties_on_connection(connection)
def is_owner(self):
- if not self._buddy:
- return False
- return self._buddy.props.owner
+ return True
+
+
+def get_owner_instance():
+ global _owner_instance
+ if _owner_instance is None:
+ _owner_instance = OwnerBuddyModel()
+ return _owner_instance
+
+
+class BuddyModel(BaseBuddyModel):
+ __gtype_name__ = 'SugarBuddyModel'
+
+ def __init__(self, **kwargs):
+
+ self._account = None
+ self._contact_id = None
+ self._handle = None
+
+ BaseBuddyModel.__init__(self, **kwargs)
- def is_present(self):
- if self._buddy:
- return True
+ def is_owner(self):
return False
- def get_current_activity(self):
- if self._buddy:
- return self._buddy.props.current_activity
- return None
-
- def _update_buddy(self, buddy):
- if not buddy:
- raise ValueError("Buddy cannot be None.")
-
- 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',
- self._buddy_property_changed_cb)
- self._bic_handler = self._buddy.connect('icon-changed',
- self._buddy_icon_changed_cb)
-
- def _buddy_appeared_cb(self, pservice, buddy):
- if self._buddy or buddy.props.key != self._key:
- return
-
- if self._ba_handler:
- # Once we have the buddy, we no longer need to
- # monitor buddy-appeared events
- self._pservice.disconnect(self._ba_handler)
- self._ba_handler = None
-
- self._update_buddy(buddy)
- self.emit('appeared')
-
- def _buddy_property_changed_cb(self, buddy, keys):
- if not self._buddy:
- return
- if 'color' in keys:
- self._set_color_from_string(self._buddy.props.color)
- self.emit('color-changed', self.get_color())
- if 'current-activity' in keys:
- self.emit('current-activity-changed', buddy.props.current_activity)
- 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:
- return
- self._buddy.disconnect(self._pc_handler)
- self._buddy.disconnect(self._dis_handler)
- self._buddy.disconnect(self._bic_handler)
- self._buddy.disconnect(self._cac_handler)
- self._set_color_from_string(_NOT_PRESENT_COLOR)
- self.emit('disappeared')
- self._buddy = None
-
- def _buddy_icon_changed_cb(self, buddy):
- self.emit('icon-changed')
+ def get_account(self):
+ return self._account
+
+ def set_account(self, account):
+ self._account = account
+
+ account = gobject.property(type=object, getter=get_account,
+ setter=set_account)
+
+ def get_contact_id(self):
+ return self._contact_id
+
+ def set_contact_id(self, contact_id):
+ self._contact_id = contact_id
+
+ contact_id = gobject.property(type=object, getter=get_contact_id,
+ setter=set_contact_id)
+
+ def get_handle(self):
+ return self._handle
+
+ def set_handle(self, handle):
+ self._handle = handle
+
+ handle = gobject.property(type=object, getter=get_handle,
+ setter=set_handle)
diff --git a/src/jarabe/model/bundleregistry.py b/src/jarabe/model/bundleregistry.py
index 86a2738..84d55c0 100644
--- a/src/jarabe/model/bundleregistry.py
+++ b/src/jarabe/model/bundleregistry.py
@@ -17,14 +17,15 @@
import os
import logging
-import traceback
+import gconf
import gobject
import gio
import simplejson
from sugar.bundle.activitybundle import ActivityBundle
from sugar.bundle.contentbundle import ContentBundle
+from sugar.bundle.bundleversion import NormalizedVersion
from jarabe.journal.journalentrybundle import JournalEntryBundle
from sugar.bundle.bundle import MalformedBundleException, \
AlreadyInstalledException, RegistrationException
@@ -33,16 +34,20 @@ from sugar import env
from jarabe import config
from jarabe.model import mimeregistry
+
+_instance = None
+
+
class BundleRegistry(gobject.GObject):
"""Tracks the available activity bundles"""
__gsignals__ = {
- 'bundle-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
+ 'bundle-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
'bundle-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'bundle-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
+ ([gobject.TYPE_PYOBJECT])),
}
def __init__(self):
@@ -66,6 +71,14 @@ class BundleRegistry(gobject.GObject):
self._last_defaults_mtime = -1
self._favorite_bundles = {}
+ client = gconf.client_get_default()
+ self._protected_activities = client.get_list(
+ '/desktop/sugar/protected_activities',
+ gconf.VALUE_STRING)
+
+ if self._protected_activities is None:
+ self._protected_activities = []
+
try:
self._load_favorites()
except Exception:
@@ -144,14 +157,16 @@ class BundleRegistry(gobject.GObject):
return
for bundle_id in default_activities:
- max_version = -1
+ max_version = '0'
for bundle in self._bundles:
if bundle.get_bundle_id() == bundle_id and \
- max_version < bundle.get_activity_version():
+ NormalizedVersion(max_version) < \
+ NormalizedVersion(bundle.get_activity_version()):
max_version = bundle.get_activity_version()
key = self._get_favorite_key(bundle_id, max_version)
- if max_version > -1 and key not in self._favorite_bundles:
+ if NormalizedVersion(max_version) > NormalizedVersion('0') and \
+ key not in self._favorite_bundles:
self._favorite_bundles[key] = None
logging.debug('After merging: %r', self._favorite_bundles)
@@ -164,7 +179,7 @@ class BundleRegistry(gobject.GObject):
if bundle.get_bundle_id() == bundle_id:
return bundle
return None
-
+
def __iter__(self):
return self._bundles.__iter__()
@@ -185,19 +200,18 @@ class BundleRegistry(gobject.GObject):
if os.path.isdir(bundle_dir):
bundles[bundle_dir] = os.stat(bundle_dir).st_mtime
except Exception:
- logging.error('Error while processing installed activity ' \
- 'bundle %s:\n%s' % \
- (bundle_dir, traceback.format_exc()))
+ logging.exception('Error while processing installed activity'
+ ' bundle %s:', bundle_dir)
bundle_dirs = bundles.keys()
bundle_dirs.sort(lambda d1, d2: cmp(bundles[d1], bundles[d2]))
for folder in bundle_dirs:
try:
self._add_bundle(folder)
- except Exception, e:
- logging.error('Error while processing installed activity ' \
- 'bundle %s:\n%s' % \
- (folder, traceback.format_exc()))
+ except:
+ # pylint: disable=W0702
+ logging.exception('Error while processing installed activity'
+ ' bundle %s:', folder)
def add_bundle(self, bundle_path, install_mime_type=False):
bundle = self._add_bundle(bundle_path, install_mime_type)
@@ -224,8 +238,8 @@ class BundleRegistry(gobject.GObject):
installed = self.get_bundle(bundle_id)
if installed is not None:
- if installed.get_activity_version() >= \
- bundle.get_activity_version():
+ if NormalizedVersion(installed.get_activity_version()) >= \
+ NormalizedVersion(bundle.get_activity_version()):
logging.debug('Skip old version for %s', bundle_id)
return None
else:
@@ -251,8 +265,7 @@ class BundleRegistry(gobject.GObject):
default_bundle = None
for bundle in self._bundles:
- if bundle.get_mime_types() and mime_type in bundle.get_mime_types():
-
+ if mime_type in (bundle.get_mime_types() or []):
if bundle.get_bundle_id() == default_bundle_id:
default_bundle = bundle
elif self.get_default_for_type(mime_type) == \
@@ -267,10 +280,7 @@ class BundleRegistry(gobject.GObject):
return result
def get_default_for_type(self, mime_type):
- if self._mime_defaults.has_key(mime_type):
- return self._mime_defaults[mime_type]
- else:
- return None
+ return self._mime_defaults.get(mime_type)
def _find_bundle(self, bundle_id, version):
for bundle in self._bundles:
@@ -302,10 +312,14 @@ class BundleRegistry(gobject.GObject):
key = self._get_favorite_key(bundle_id, version)
return key in self._favorite_bundles
+ def is_activity_protected(self, bundle_id):
+ return bundle_id in self._protected_activities
+
def set_bundle_position(self, bundle_id, version, x, y):
key = self._get_favorite_key(bundle_id, version)
if key not in self._favorite_bundles:
- raise ValueError('Bundle %s %s not favorite' % (bundle_id, version))
+ raise ValueError('Bundle %s %s not favorite' %
+ (bundle_id, version))
if self._favorite_bundles[key] is None:
self._favorite_bundles[key] = {}
@@ -346,19 +360,22 @@ class BundleRegistry(gobject.GObject):
for installed_bundle in self._bundles:
if bundle.get_bundle_id() == installed_bundle.get_bundle_id() and \
- bundle.get_activity_version() == \
- installed_bundle.get_activity_version():
+ NormalizedVersion(bundle.get_activity_version()) <= \
+ NormalizedVersion(installed_bundle.get_activity_version()):
return True
return False
- def install(self, bundle, uid=None):
+ def install(self, bundle, uid=None, force_downgrade=False):
activities_path = env.get_user_activities_path()
for installed_bundle in self._bundles:
if bundle.get_bundle_id() == installed_bundle.get_bundle_id() and \
- bundle.get_activity_version() <= \
- installed_bundle.get_activity_version():
- raise AlreadyInstalledException
+ NormalizedVersion(bundle.get_activity_version()) <= \
+ NormalizedVersion(installed_bundle.get_activity_version()):
+ if not force_downgrade:
+ raise AlreadyInstalledException
+ else:
+ self.uninstall(installed_bundle, force=True)
elif bundle.get_bundle_id() == installed_bundle.get_bundle_id():
self.uninstall(installed_bundle, force=True)
@@ -376,7 +393,7 @@ class BundleRegistry(gobject.GObject):
elif not self.add_bundle(install_path):
raise RegistrationException
- def uninstall(self, bundle, force=False):
+ def uninstall(self, bundle, force=False, delete_profile=False):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
if isinstance(bundle, ContentBundle) or \
@@ -399,7 +416,7 @@ class BundleRegistry(gobject.GObject):
install_path = act.get_path()
- bundle.uninstall(install_path, force)
+ bundle.uninstall(install_path, force, delete_profile)
if not self.remove_bundle(install_path):
raise RegistrationException
@@ -415,20 +432,17 @@ class BundleRegistry(gobject.GObject):
try:
self.uninstall(bundle, force=True)
except Exception:
- logging.error('Uninstall failed, still trying to install ' \
- 'newer bundle:\n' + \
- traceback.format_exc())
+ logging.exception('Uninstall failed, still trying to install'
+ ' newer bundle:')
else:
- logging.warning('Unable to uninstall system activity, ' \
+ logging.warning('Unable to uninstall system activity, '
'installing upgraded version in user activities')
self.install(bundle)
-_instance = None
def get_registry():
global _instance
if not _instance:
_instance = BundleRegistry()
return _instance
-
diff --git a/src/jarabe/model/filetransfer.py b/src/jarabe/model/filetransfer.py
index 46b246d..710c3a4 100644
--- a/src/jarabe/model/filetransfer.py
+++ b/src/jarabe/model/filetransfer.py
@@ -31,6 +31,8 @@ from sugar.presence import presenceservice
from sugar import dispatch
from jarabe.util.telepathy import connection_watcher
+from jarabe.model import neighborhood
+
FT_STATE_NONE = 0
FT_STATE_PENDING = 1
@@ -51,14 +53,18 @@ FT_REASON_REMOTE_ERROR = 6
CHANNEL_TYPE_FILE_TRANSFER = \
'org.freedesktop.Telepathy.Channel.Type.FileTransfer'
+new_file_transfer = dispatch.Signal()
+
+
# TODO Move to use splice_async() in Sugar 0.88
class StreamSplicer(gobject.GObject):
- _CHUNK_SIZE = 10240 # 10K
+ _CHUNK_SIZE = 10240 # 10K
__gsignals__ = {
'finished': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([])),
}
+
def __init__(self, input_stream, output_stream):
gobject.GObject.__init__(self)
@@ -104,6 +110,7 @@ class StreamSplicer(gobject.GObject):
gobject.PRIORITY_LOW,
user_data=data)
+
class BaseFileTransfer(gobject.GObject):
def __init__(self, connection):
@@ -140,11 +147,7 @@ class BaseFileTransfer(gobject.GObject):
self.mime_type = props['ContentType']
handle = channel_properties.Get(CHANNEL, 'TargetHandle')
- presence_service = presenceservice.get_instance()
- self.buddy = presence_service.get_buddy_by_telepathy_handle(
- self._connection.service_name,
- self._connection.object_path,
- handle)
+ self.buddy = neighborhood.get_model().get_buddy_by_handle(handle)
def __transferred_bytes_changed_cb(self, transferred_bytes):
logging.debug('__transferred_bytes_changed_cb %r', transferred_bytes)
@@ -179,6 +182,7 @@ class BaseFileTransfer(gobject.GObject):
def cancel(self):
self.channel[CHANNEL].Close()
+
class IncomingFileTransfer(BaseFileTransfer):
def __init__(self, connection, object_path, props):
BaseFileTransfer.__init__(self, connection)
@@ -223,6 +227,7 @@ class IncomingFileTransfer(BaseFileTransfer):
self._splicer = StreamSplicer(input_stream, output_stream)
self._splicer.start()
+
class OutgoingFileTransfer(BaseFileTransfer):
def __init__(self, buddy, file_name, title, description, mime_type):
@@ -240,20 +245,18 @@ class OutgoingFileTransfer(BaseFileTransfer):
self._splicer = None
self._output_stream = None
- self.buddy = buddy.get_buddy()
+ self.buddy = buddy
self.title = title
self.file_size = os.stat(file_name).st_size
self.description = description
self.mime_type = mime_type
def __connection_ready_cb(self, connection):
- handle = self._get_buddy_handle()
-
requests = connection[CONNECTION_INTERFACE_REQUESTS]
object_path, properties_ = requests.CreateChannel({
CHANNEL + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
CHANNEL + '.TargetHandleType': CONNECTION_HANDLE_TYPE_CONTACT,
- CHANNEL + '.TargetHandle': handle,
+ CHANNEL + '.TargetHandle': self.buddy.handle,
CHANNEL_TYPE_FILE_TRANSFER + '.ContentType': self.mime_type,
CHANNEL_TYPE_FILE_TRANSFER + '.Filename': self.title,
CHANNEL_TYPE_FILE_TRANSFER + '.Size': self.file_size,
@@ -267,21 +270,6 @@ class OutgoingFileTransfer(BaseFileTransfer):
SOCKET_ADDRESS_TYPE_UNIX, SOCKET_ACCESS_CONTROL_LOCALHOST, '',
byte_arrays=True)
- def _get_buddy_handle(self):
- object_path = self.buddy.object_path()
-
- bus = dbus.SessionBus()
- remote_object = bus.get_object('org.laptop.Sugar.Presence', object_path)
- ps_buddy = dbus.Interface(remote_object,
- 'org.laptop.Sugar.Presence.Buddy')
-
- handles = ps_buddy.GetTelepathyHandles()
- 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)
if self.props.state == FT_STATE_OPEN:
@@ -303,6 +291,7 @@ class OutgoingFileTransfer(BaseFileTransfer):
def cancel(self):
self.channel[CHANNEL].Close()
+
def _new_channels_cb(connection, channels):
for object_path, props in channels:
if props[CHANNEL + '.ChannelType'] == CHANNEL_TYPE_FILE_TRANSFER and \
@@ -314,35 +303,39 @@ def _new_channels_cb(connection, channels):
object_path, props)
new_file_transfer.send(None, file_transfer=incoming_file_transfer)
+
def _monitor_connection(connection):
logging.debug('connection added %r', connection)
connection[CONNECTION_INTERFACE_REQUESTS].connect_to_signal('NewChannels',
lambda channels: _new_channels_cb(connection, channels))
-def _connection_addded_cb(conn_watcher, connection):
+
+def _connection_added_cb(conn_watcher, connection):
_monitor_connection(connection)
+
def _connection_removed_cb(conn_watcher, connection):
logging.debug('connection removed %r', connection)
-_conn_watcher = None
def init():
- global _conn_watcher
- _conn_watcher = connection_watcher.ConnectionWatcher()
- _conn_watcher.connect('connection-added', _connection_addded_cb)
- _conn_watcher.connect('connection-removed', _connection_removed_cb)
+ conn_watcher = connection_watcher.get_instance()
+ conn_watcher.connect('connection-added', _connection_added_cb)
+ conn_watcher.connect('connection-removed', _connection_removed_cb)
- for connection in _conn_watcher.get_connections():
+ for connection in conn_watcher.get_connections():
_monitor_connection(connection)
+
def start_transfer(buddy, file_name, title, description, mime_type):
outgoing_file_transfer = OutgoingFileTransfer(buddy, file_name, title,
description, mime_type)
new_file_transfer.send(None, file_transfer=outgoing_file_transfer)
+
def file_transfer_available():
- for connection in _conn_watcher.get_connections():
+ conn_watcher = connection_watcher.get_instance()
+ for connection in conn_watcher.get_connections():
properties_iface = connection[dbus.PROPERTIES_IFACE]
properties = properties_iface.GetAll(CONNECTION_INTERFACE_REQUESTS)
@@ -359,18 +352,17 @@ def file_transfer_available():
return False
-new_file_transfer = dispatch.Signal()
if __name__ == '__main__':
import tempfile
- input_stream = gio.File('/home/tomeu/isos/Soas2-200904031934.iso').read()
- output_stream = gio.File(tempfile.mkstemp()[1]).append_to()
+ test_file_name = '/home/tomeu/isos/Soas2-200904031934.iso'
+ test_input_stream = gio.File(test_file_name).read()
+ test_output_stream = gio.File(tempfile.mkstemp()[1]).append_to()
# TODO: Use splice_async when it gets implemented
- splicer = StreamSplicer(input_stream, output_stream)
+ splicer = StreamSplicer(test_input_stream, test_output_stream)
splicer.start()
loop = gobject.MainLoop()
loop.run()
-
diff --git a/src/jarabe/model/friends.py b/src/jarabe/model/friends.py
index b7bf7f1..192f683 100644
--- a/src/jarabe/model/friends.py
+++ b/src/jarabe/model/friends.py
@@ -21,15 +21,83 @@ from ConfigParser import ConfigParser
import gobject
import dbus
-from jarabe.model.buddy import BuddyModel
from sugar import env
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.model.buddy import BuddyModel
+from jarabe.model import neighborhood
+
+
+_model = None
+
+
+class FriendBuddyModel(BuddyModel):
+ __gtype_name__ = 'SugarFriendBuddyModel'
+
+ _NOT_PRESENT_COLOR = '#D5D5D5,#FFFFFF'
+
+ def __init__(self, nick, key):
+ self._online_buddy = None
+
+ BuddyModel.__init__(self, nick=nick, key=key)
+
+ neighborhood_model = neighborhood.get_model()
+ neighborhood_model.connect('buddy-added', self.__buddy_added_cb)
+ neighborhood_model.connect('buddy-removed', self.__buddy_removed_cb)
+
+ buddy = neighborhood_model.get_buddy_by_key(key)
+ if buddy is not None:
+ self._set_online_buddy(buddy)
+
+ def __buddy_added_cb(self, model_, buddy):
+ if buddy.key != self.key:
+ return
+ self._set_online_buddy(buddy)
+
+ def _set_online_buddy(self, buddy):
+ self._online_buddy = buddy
+ self._online_buddy.connect('notify::color', self.__notify_color_cb)
+ self.notify('color')
+ self.notify('present')
+
+ def __buddy_removed_cb(self, model_, buddy):
+ if buddy.key != self.key:
+ return
+ self._online_buddy = None
+ self.notify('color')
+ self.notify('present')
+
+ def __notify_color_cb(self, buddy, pspec):
+ self.notify('color')
+
+ def is_present(self):
+ return self._online_buddy is not None
+
+ present = gobject.property(type=bool, default=False, getter=is_present)
+
+ def get_color(self):
+ if self._online_buddy is not None:
+ return self._online_buddy.color
+ else:
+ return XoColor(FriendBuddyModel._NOT_PRESENT_COLOR)
+
+ color = gobject.property(type=object, getter=get_color)
+
+ def get_handle(self):
+ if self._online_buddy is not None:
+ return self._online_buddy.handle
+ else:
+ return None
+
+ handle = gobject.property(type=object, getter=get_handle)
+
class Friends(gobject.GObject):
__gsignals__ = {
- 'friend-added': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([object])),
- 'friend-removed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
+ 'friend-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'friend-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
}
def __init__(self):
@@ -41,7 +109,7 @@ class Friends(gobject.GObject):
self.load()
def has_buddy(self, buddy):
- return self._friends.has_key(buddy.get_key())
+ return buddy.get_key() in self._friends
def add_friend(self, buddy_info):
self._friends[buddy_info.get_key()] = buddy_info
@@ -49,6 +117,7 @@ class Friends(gobject.GObject):
def make_friend(self, buddy):
if not self.has_buddy(buddy):
+ buddy = FriendBuddyModel(key=buddy.key, nick=buddy.nick)
self.add_friend(buddy)
self.save()
@@ -70,7 +139,7 @@ class Friends(gobject.GObject):
# HACK: don't screw up on old friends files
if len(key) < 20:
continue
- buddy = BuddyModel(key=key, nick=cp.get(key, 'nick'))
+ buddy = FriendBuddyModel(key=key, nick=cp.get(key, 'nick'))
self.add_friend(buddy)
except Exception:
logging.exception('Error parsing friends file')
@@ -82,7 +151,6 @@ class Friends(gobject.GObject):
section = friend.get_key()
cp.add_section(section)
cp.set(section, 'nick', friend.get_nick())
- cp.set(section, 'color', friend.get_color().to_string())
fileobject = open(self._path, 'w')
cp.write(fileobject)
@@ -113,7 +181,6 @@ class Friends(gobject.GObject):
reply_handler=friends_synced,
error_handler=friends_synced_error)
-_model = None
def get_model():
global _model
diff --git a/src/jarabe/model/invites.py b/src/jarabe/model/invites.py
index c918308..d2a2e0c 100644
--- a/src/jarabe/model/invites.py
+++ b/src/jarabe/model/invites.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -14,110 +15,227 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import gobject
-from sugar.presence import presenceservice
-
-
-class BaseInvite:
- """Invitation to shared activity or private 1-1 Telepathy channel"""
- def __init__(self, bundle_id):
- """init for BaseInvite.
+import logging
+from functools import partial
- bundle_id: string, e.g. 'org.laptop.Chat'
- """
- self._bundle_id = bundle_id
+import gobject
+import dbus
+from telepathy.interfaces import CHANNEL, \
+ CHANNEL_DISPATCHER, \
+ CHANNEL_DISPATCH_OPERATION, \
+ CHANNEL_TYPE_CONTACT_LIST, \
+ CHANNEL_TYPE_DBUS_TUBE, \
+ CHANNEL_TYPE_STREAMED_MEDIA, \
+ CHANNEL_TYPE_STREAM_TUBE, \
+ CHANNEL_TYPE_TEXT, \
+ CLIENT
+from telepathy.constants import HANDLE_TYPE_ROOM
- def get_bundle_id(self):
- return self._bundle_id
+from sugar.graphics.xocolor import XoColor
+from jarabe.model import telepathyclient
+from jarabe.model import bundleregistry
+from jarabe.model import neighborhood
+from jarabe.journal import misc
-class ActivityInvite(BaseInvite):
- """Invitation to a shared activity."""
- def __init__(self, bundle_id, activity_id):
- BaseInvite.__init__(self, bundle_id)
- self._activity_id = activity_id
- def get_activity_id(self):
- return self._activity_id
+CONNECTION_INTERFACE_ACTIVITY_PROPERTIES = \
+ 'org.laptop.Telepathy.ActivityProperties'
+_instance = None
-class PrivateInvite(BaseInvite):
- """Invitation to a private 1-1 Telepathy channel.
- This includes text chat or streaming media.
- """
- def __init__(self, bundle_id, private_channel):
- """init for PrivateInvite.
+class ActivityInvite(object):
+ """Invitation to a shared activity."""
+ def __init__(self, dispatch_operation_path, handle, handler,
+ activity_properties):
+ self.dispatch_operation_path = dispatch_operation_path
+ self._handle = handle
+ self._handler = handler
- bundle_id: string, e.g. 'org.laptop.Chat'
- private_channel: string containing simplejson dump of Telepathy
- bus, connection and channel
- """
- BaseInvite.__init__(self, bundle_id)
- self._private_channel = private_channel
+ if activity_properties is not None:
+ self._activity_properties = activity_properties
+ else:
+ self._activity_properties = {}
- def get_private_channel(self):
- """Telepathy channel info from private invitation"""
- return self._private_channel
+ def get_bundle_id(self):
+ if CLIENT in self._handler:
+ return self._handler[len(CLIENT + '.'):]
+ else:
+ return None
+
+ def get_color(self):
+ color = self._activity_properties.get('color', None)
+ return XoColor(color)
+
+ def join(self):
+ logging.error('ActivityInvite.join handler %r', self._handler)
+
+ registry = bundleregistry.get_registry()
+ bundle_id = self.get_bundle_id()
+ bundle = registry.get_bundle(bundle_id)
+ if bundle is None:
+ self._call_handle_with()
+ else:
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(self.__name_owner_changed_cb,
+ 'NameOwnerChanged',
+ 'org.freedesktop.DBus',
+ arg0=self._handler)
+
+ model = neighborhood.get_model()
+ activity_id = model.get_activity_by_room(self._handle).activity_id
+ misc.launch(bundle, color=self.get_color(), invited=True,
+ activity_id=activity_id)
+
+ def __name_owner_changed_cb(self, name, old_owner, new_owner):
+ logging.debug('ActivityInvite.__name_owner_changed_cb %r %r %r', name,
+ new_owner, old_owner)
+ if name == self._handler and new_owner and not old_owner:
+ self._call_handle_with()
+
+ def _call_handle_with(self):
+ bus = dbus.Bus()
+ obj = bus.get_object(CHANNEL_DISPATCHER, self.dispatch_operation_path)
+ dispatch_operation = dbus.Interface(obj, CHANNEL_DISPATCH_OPERATION)
+ dispatch_operation.HandleWith(self._handler,
+ reply_handler=self.__handle_with_reply_cb,
+ error_handler=self.__handle_with_reply_cb)
+
+ def __handle_with_reply_cb(self, error=None):
+ if error is not None:
+ raise error
+ else:
+ logging.debug('__handle_with_reply_cb')
class Invites(gobject.GObject):
__gsignals__ = {
- 'invite-added': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([object])),
- 'invite-removed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([object]))
+ 'invite-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'invite-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
gobject.GObject.__init__(self)
- self._dict = {}
-
- ps = presenceservice.get_instance()
- owner = ps.get_owner()
- owner.connect('joined-activity', self._owner_joined_cb)
-
- def add_invite(self, bundle_id, activity_id):
- if activity_id in self._dict:
- # there is no point to add more than one time
- # an invite for the same activity
+ self._dispatch_operations = {}
+
+ client_handler = telepathyclient.get_instance()
+ client_handler.got_dispatch_operation.connect(
+ self.__got_dispatch_operation_cb)
+
+ def __got_dispatch_operation_cb(self, **kwargs):
+ logging.debug('__got_dispatch_operation_cb')
+ dispatch_operation_path = kwargs['dispatch_operation_path']
+ channel_path, channel_properties = kwargs['channels'][0]
+ properties = kwargs['properties']
+ channel_type = channel_properties[CHANNEL + '.ChannelType']
+ handle_type = channel_properties[CHANNEL + '.TargetHandleType']
+ handle = channel_properties[CHANNEL + '.TargetHandle']
+
+ if handle_type == HANDLE_TYPE_ROOM and \
+ channel_type == CHANNEL_TYPE_TEXT:
+ logging.debug('May be an activity, checking its properties')
+ connection_path = properties[CHANNEL_DISPATCH_OPERATION +
+ '.Connection']
+ connection_name = connection_path.replace('/', '.')[1:]
+
+ bus = dbus.Bus()
+ connection = bus.get_object(connection_name, connection_path)
+ connection.GetProperties(
+ channel_properties[CHANNEL + '.TargetHandle'],
+ dbus_interface=CONNECTION_INTERFACE_ACTIVITY_PROPERTIES,
+ reply_handler=partial(self.__get_properties_cb,
+ handle,
+ dispatch_operation_path),
+ error_handler=partial(self.__error_handler_cb,
+ handle,
+ channel_properties,
+ dispatch_operation_path))
+ else:
+ self._dispatch_non_sugar_invitation(channel_path,
+ channel_properties,
+ dispatch_operation_path)
+
+ def __get_properties_cb(self, handle, dispatch_operation_path, properties):
+ logging.debug('__get_properties_cb %r', properties)
+ handler = '%s.%s' % (CLIENT, properties['type'])
+ self._add_invite(dispatch_operation_path, handle, handler, properties)
+
+ def __error_handler_cb(self, handle, channel_properties,
+ dispatch_operation_path, error):
+ logging.debug('__error_handler_cb %r', error)
+ exception_name = 'org.freedesktop.Telepathy.Error.NotAvailable'
+ if error.get_dbus_name() == exception_name:
+ self._dispatch_non_sugar_invitation(handle,
+ channel_properties,
+ dispatch_operation_path)
+ else:
+ raise error
+
+ def _dispatch_non_sugar_invitation(self, handle, channel_properties,
+ dispatch_operation_path):
+ handler = None
+ channel_type = channel_properties[CHANNEL + '.ChannelType']
+ if channel_type == CHANNEL_TYPE_CONTACT_LIST:
+ self._handle_with(dispatch_operation_path, CLIENT + '.Sugar')
+ elif channel_type == CHANNEL_TYPE_TEXT:
+ handler = CLIENT + '.org.laptop.Chat'
+ elif channel_type == CHANNEL_TYPE_STREAMED_MEDIA:
+ handler = CLIENT + '.org.laptop.VideoChat'
+ elif channel_type == CHANNEL_TYPE_DBUS_TUBE:
+ handler = channel_properties[CHANNEL_TYPE_DBUS_TUBE +
+ '.ServiceName']
+ elif channel_type == CHANNEL_TYPE_STREAM_TUBE:
+ handler = channel_properties[CHANNEL_TYPE_STREAM_TUBE + '.Service']
+ else:
+ self._handle_with(dispatch_operation_path, '')
+
+ if handler is not None:
+ logging.debug('Adding an invite from a non-Sugar client')
+ self._add_invite(dispatch_operation_path, handle, handler)
+
+ def _handle_with(self, dispatch_operation_path, handler):
+ logging.debug('_handle_with %r %r', dispatch_operation_path, handler)
+ bus = dbus.Bus()
+ obj = bus.get_object(CHANNEL_DISPATCHER, dispatch_operation_path)
+ dispatch_operation = dbus.Interface(obj, CHANNEL_DISPATCH_OPERATION)
+ dispatch_operation.HandleWith(handler,
+ reply_handler=self.__handle_with_reply_cb,
+ error_handler=self.__handle_with_reply_cb)
+
+ def __handle_with_reply_cb(self, error=None):
+ if error is not None:
+ logging.error('__handle_with_reply_cb %r', error)
+ else:
+ logging.debug('__handle_with_reply_cb')
+
+ def _add_invite(self, dispatch_operation_path, handle, handler,
+ activity_properties=None):
+ logging.debug('_add_invite %r %r %r', dispatch_operation_path, handle,
+ handler)
+ if dispatch_operation_path in self._dispatch_operations:
+ # there is no point to have more than one invite for the same
+ # dispatch operation
return
- invite = ActivityInvite(bundle_id, activity_id)
- self._dict[activity_id] = invite
- self.emit('invite-added', invite)
-
- def add_private_invite(self, private_channel, bundle_id):
- if private_channel in self._dict:
- # there is no point to add more than one invite for the
- # same incoming connection
- return
-
- invite = PrivateInvite(bundle_id, private_channel)
- self._dict[private_channel] = invite
+ invite = ActivityInvite(dispatch_operation_path, handle, handler,
+ activity_properties)
+ self._dispatch_operations[dispatch_operation_path] = invite
self.emit('invite-added', invite)
def remove_invite(self, invite):
- del self._dict[invite.get_activity_id()]
+ del self._dispatch_operations[invite.dispatch_operation_path]
self.emit('invite-removed', invite)
- def remove_private_invite(self, invite):
- del self._dict[invite.get_private_channel()]
- self.emit('invite-removed', invite)
-
- def remove_activity(self, activity_id):
- invite = self._dict.get(activity_id)
- if invite is not None:
- self.remove_invite(invite)
-
- def remove_private_channel(self, private_channel):
- invite = self._dict.get(private_channel)
- if invite is not None:
- self.remove_private_invite(invite)
+ def __iter__(self):
+ return self._dispatch_operations.values().__iter__()
- def _owner_joined_cb(self, owner, activity):
- self.remove_activity(activity.props.id)
- def __iter__(self):
- return self._dict.values().__iter__()
+def get_instance():
+ global _instance
+ if not _instance:
+ _instance = Invites()
+ return _instance
diff --git a/src/jarabe/model/mimeregistry.py b/src/jarabe/model/mimeregistry.py
index 537f6f3..7fb5bcf 100644
--- a/src/jarabe/model/mimeregistry.py
+++ b/src/jarabe/model/mimeregistry.py
@@ -21,6 +21,7 @@ import gconf
_DEFAULTS_KEY = '/desktop/sugar/journal/defaults'
_GCONF_INVALID_CHARS = re.compile('[^a-zA-Z0-9-_/.]')
+
_instance = None
diff --git a/src/jarabe/model/neighborhood.py b/src/jarabe/model/neighborhood.py
index 53e5581..ca4c5bf 100644
--- a/src/jarabe/model/neighborhood.py
+++ b/src/jarabe/model/neighborhood.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -14,257 +14,992 @@
# 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 functools import partial
+from hashlib import sha1
+
import gobject
import gconf
-import logging
+import dbus
+from dbus import PROPERTIES_IFACE
+from telepathy.interfaces import ACCOUNT, \
+ ACCOUNT_MANAGER, \
+ CHANNEL, \
+ CHANNEL_INTERFACE_GROUP, \
+ CHANNEL_TYPE_CONTACT_LIST, \
+ CHANNEL_TYPE_FILE_TRANSFER, \
+ CLIENT, \
+ CONNECTION, \
+ CONNECTION_INTERFACE_ALIASING, \
+ CONNECTION_INTERFACE_CONTACTS, \
+ CONNECTION_INTERFACE_CONTACT_CAPABILITIES, \
+ CONNECTION_INTERFACE_REQUESTS, \
+ CONNECTION_INTERFACE_SIMPLE_PRESENCE
+from telepathy.constants import HANDLE_TYPE_CONTACT, \
+ HANDLE_TYPE_LIST, \
+ CONNECTION_PRESENCE_TYPE_OFFLINE, \
+ CONNECTION_STATUS_CONNECTED, \
+ CONNECTION_STATUS_DISCONNECTED
+from telepathy.client import Connection, Channel
from sugar.graphics.xocolor import XoColor
-from sugar.presence import presenceservice
-from sugar import activity
+from sugar.profile import get_profile
-from jarabe.model.buddy import BuddyModel
+from jarabe.model.buddy import BuddyModel, get_owner_instance
from jarabe.model import bundleregistry
-from jarabe.util.telepathy import connection_watcher
-from dbus import PROPERTIES_IFACE
-from telepathy.interfaces import CONNECTION_INTERFACE_REQUESTS
-from telepathy.interfaces import CHANNEL_INTERFACE
-from telepathy.client import Channel
-CONN_INTERFACE_GADGET = 'org.laptop.Telepathy.Gadget'
-CHAN_INTERFACE_VIEW = 'org.laptop.Telepathy.Channel.Interface.View'
-CHAN_INTERFACE_BUDBY_VIEW = 'org.laptop.Telepathy.Channel.Type.BuddyView'
-CHAN_INTERFACE_ACTIVITY_VIEW = 'org.laptop.Telepathy.Channel.Type.ActivityView'
+ACCOUNT_MANAGER_SERVICE = 'org.freedesktop.Telepathy.AccountManager'
+ACCOUNT_MANAGER_PATH = '/org/freedesktop/Telepathy/AccountManager'
+CHANNEL_DISPATCHER_SERVICE = 'org.freedesktop.Telepathy.ChannelDispatcher'
+CHANNEL_DISPATCHER_PATH = '/org/freedesktop/Telepathy/ChannelDispatcher'
+SUGAR_CLIENT_SERVICE = 'org.freedesktop.Telepathy.Client.Sugar'
+SUGAR_CLIENT_PATH = '/org/freedesktop/Telepathy/Client/Sugar'
+
+CONNECTION_INTERFACE_BUDDY_INFO = 'org.laptop.Telepathy.BuddyInfo'
+CONNECTION_INTERFACE_ACTIVITY_PROPERTIES = \
+ 'org.laptop.Telepathy.ActivityProperties'
-NB_RANDOM_BUDDIES = 20
-NB_RANDOM_ACTIVITIES = 40
+_QUERY_DBUS_TIMEOUT = 200
+"""
+Time in seconds to wait when querying contact properties. Some jabber servers
+will be very slow in returning these queries, so just be patient.
+"""
-class ActivityModel:
- def __init__(self, act, bundle):
- self.activity = act
- self.bundle = bundle
+_model = None
+
+
+class ActivityModel(gobject.GObject):
+ __gsignals__ = {
+ 'current-buddy-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'current-buddy-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ }
- def get_id(self):
- return self.activity.props.id
+ def __init__(self, activity_id, room_handle):
+ gobject.GObject.__init__(self)
- def get_icon_name(self):
- return self.bundle.get_icon()
+ self.activity_id = activity_id
+ self.room_handle = room_handle
+ self._bundle = None
+ self._color = None
+ self._private = True
+ self._name = None
+ self._current_buddies = []
+ self._buddies = []
def get_color(self):
- return XoColor(self.activity.props.color)
+ return self._color
+
+ def set_color(self, color):
+ self._color = color
+
+ color = gobject.property(type=object, getter=get_color, setter=set_color)
+
+ def get_bundle(self):
+ return self._bundle
+
+ def set_bundle(self, bundle):
+ self._bundle = bundle
+
+ bundle = gobject.property(type=object, getter=get_bundle,
+ setter=set_bundle)
+
+ def get_name(self):
+ return self._name
+
+ def set_name(self, name):
+ self._name = name
+
+ name = gobject.property(type=object, getter=get_name, setter=set_name)
+
+ def is_private(self):
+ return self._private
+
+ def set_private(self, private):
+ self._private = private
+
+ private = gobject.property(type=object, getter=is_private,
+ setter=set_private)
+
+ def get_buddies(self):
+ return self._buddies
+
+ def add_buddy(self, buddy):
+ self._buddies.append(buddy)
+ self.notify('buddies')
+ self.emit('buddy-added', buddy)
+
+ def remove_buddy(self, buddy):
+ self._buddies.remove(buddy)
+ self.notify('buddies')
+ self.emit('buddy-removed', buddy)
+
+ buddies = gobject.property(type=object, getter=get_buddies)
+
+ def get_current_buddies(self):
+ return self._current_buddies
+
+ def add_current_buddy(self, buddy):
+ self._current_buddies.append(buddy)
+ self.notify('current-buddies')
+ self.emit('current-buddy-added', buddy)
+
+ def remove_current_buddy(self, buddy):
+ self._current_buddies.remove(buddy)
+ self.notify('current-buddies')
+ self.emit('current-buddy-removed', buddy)
+
+ current_buddies = gobject.property(type=object, getter=get_current_buddies)
+
+
+class _Account(gobject.GObject):
+ __gsignals__ = {
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object])),
+ 'activity-updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object])),
+ 'activity-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object, object])),
+ 'buddy-updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object])),
+ 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object])),
+ 'buddy-left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object, object])),
+ 'current-activity-updated': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([object, object])),
+ 'connected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'disconnected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ }
+
+ def __init__(self, account_path):
+ gobject.GObject.__init__(self)
+
+ self.object_path = account_path
+
+ self._connection = None
+ self._buddy_handles = {}
+ self._activity_handles = {}
+ self._self_handle = None
+
+ self._buddies_per_activity = {}
+ self._activities_per_buddy = {}
+
+ self._start_listening()
+
+ def _start_listening(self):
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, self.object_path)
+ obj.Get(ACCOUNT, 'Connection',
+ reply_handler=self.__got_connection_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Account.GetConnection'))
+ obj.connect_to_signal(
+ 'AccountPropertyChanged', self.__account_property_changed_cb)
+
+ def __error_handler_cb(self, function_name, error):
+ raise RuntimeError('Error when calling %s: %s' % (function_name,
+ error))
+
+ def __got_connection_cb(self, connection_path):
+ logging.debug('_Account.__got_connection_cb %r', connection_path)
+
+ if connection_path == '/':
+ self._check_registration_error()
+ return
+
+ self._prepare_connection(connection_path)
+
+ def _check_registration_error(self):
+ """
+ See if a previous connection attempt failed and we need to unset
+ the register flag.
+ """
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, self.object_path)
+ obj.Get(ACCOUNT, 'ConnectionError',
+ reply_handler=self.__got_connection_error_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Account.GetConnectionError'))
+
+ def __got_connection_error_cb(self, error):
+ logging.debug('_Account.__got_connection_error_cb %r', error)
+ if error == 'org.freedesktop.Telepathy.Error.RegistrationExists':
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, self.object_path)
+ obj.UpdateParameters({'register': False}, [],
+ dbus_interface=ACCOUNT)
+
+ def __account_property_changed_cb(self, properties):
+ logging.debug('_Account.__account_property_changed_cb %r %r %r',
+ self.object_path, properties.get('Connection', None),
+ self._connection)
+ if 'Connection' not in properties:
+ return
+ if properties['Connection'] == '/':
+ self._check_registration_error()
+ self._connection = None
+ elif self._connection is None:
+ self._prepare_connection(properties['Connection'])
+
+ def _prepare_connection(self, connection_path):
+ connection_name = connection_path.replace('/', '.')[1:]
+
+ self._connection = Connection(connection_name, connection_path,
+ ready_handler=self.__connection_ready_cb)
+
+ def __connection_ready_cb(self, connection):
+ logging.debug('_Account.__connection_ready_cb %r',
+ connection.object_path)
+ connection.connect_to_signal('StatusChanged',
+ self.__status_changed_cb)
+
+ connection[PROPERTIES_IFACE].Get(CONNECTION,
+ 'Status',
+ reply_handler=self.__get_status_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Connection.GetStatus'))
+
+ def __get_status_cb(self, status):
+ logging.debug('_Account.__get_status_cb %r %r',
+ self._connection.object_path, status)
+ self._update_status(status)
+
+ def __status_changed_cb(self, status, reason):
+ logging.debug('_Account.__status_changed_cb %r %r', status, reason)
+ self._update_status(status)
+
+ def _update_status(self, status):
+ if status == CONNECTION_STATUS_CONNECTED:
+ self._connection[PROPERTIES_IFACE].Get(CONNECTION,
+ 'SelfHandle',
+ reply_handler=self.__get_self_handle_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Connection.GetSelfHandle'))
+ self.emit('connected')
+ else:
+ for contact_handle, contact_id in self._buddy_handles.items():
+ if contact_id is not None:
+ self.emit('buddy-removed', contact_id)
+
+ for room_handle, activity_id in self._activity_handles.items():
+ self.emit('activity-removed', activity_id)
+
+ self._buddy_handles = {}
+ self._activity_handles = {}
+ self._buddies_per_activity = {}
+ self._activities_per_buddy = {}
+
+ self.emit('disconnected')
+
+ if status == CONNECTION_STATUS_DISCONNECTED:
+ self._connection = None
+
+ def __get_self_handle_cb(self, self_handle):
+ self._self_handle = self_handle
+
+ if CONNECTION_INTERFACE_CONTACT_CAPABILITIES in self._connection:
+ interface = CONNECTION_INTERFACE_CONTACT_CAPABILITIES
+ connection = self._connection[interface]
+ client_name = CLIENT + '.Sugar.FileTransfer'
+ file_transfer_channel_class = {
+ CHANNEL + '.ChannelType': CHANNEL_TYPE_FILE_TRANSFER,
+ CHANNEL + '.TargetHandleType': HANDLE_TYPE_CONTACT}
+ capabilities = []
+ connection.UpdateCapabilities(
+ [(client_name, [file_transfer_channel_class], capabilities)],
+ reply_handler=self.__update_capabilities_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Connection.UpdateCapabilities'))
+
+ connection = self._connection[CONNECTION_INTERFACE_ALIASING]
+ connection.connect_to_signal('AliasesChanged',
+ self.__aliases_changed_cb)
+
+ connection = self._connection[CONNECTION_INTERFACE_SIMPLE_PRESENCE]
+ connection.connect_to_signal('PresencesChanged',
+ self.__presences_changed_cb)
+
+ if CONNECTION_INTERFACE_BUDDY_INFO in self._connection:
+ connection = self._connection[CONNECTION_INTERFACE_BUDDY_INFO]
+ connection.connect_to_signal('PropertiesChanged',
+ self.__buddy_info_updated_cb,
+ byte_arrays=True)
+
+ connection.connect_to_signal('ActivitiesChanged',
+ self.__buddy_activities_changed_cb)
+
+ connection.connect_to_signal('CurrentActivityChanged',
+ self.__current_activity_changed_cb)
+ else:
+ logging.warning('Connection %s does not support OLPC buddy '
+ 'properties', self._connection.object_path)
+
+ if CONNECTION_INTERFACE_ACTIVITY_PROPERTIES in self._connection:
+ connection = self._connection[
+ CONNECTION_INTERFACE_ACTIVITY_PROPERTIES]
+ connection.connect_to_signal(
+ 'ActivityPropertiesChanged',
+ self.__activity_properties_changed_cb)
+ else:
+ logging.warning('Connection %s does not support OLPC activity '
+ 'properties', self._connection.object_path)
+
+ properties = {
+ CHANNEL + '.ChannelType': CHANNEL_TYPE_CONTACT_LIST,
+ CHANNEL + '.TargetHandleType': HANDLE_TYPE_LIST,
+ CHANNEL + '.TargetID': 'subscribe',
+ }
+ properties = dbus.Dictionary(properties, signature='sv')
+ connection = self._connection[CONNECTION_INTERFACE_REQUESTS]
+ is_ours, channel_path, properties = \
+ connection.EnsureChannel(properties)
+
+ channel = Channel(self._connection.service_name, channel_path)
+ channel[CHANNEL_INTERFACE_GROUP].connect_to_signal(
+ 'MembersChanged', self.__members_changed_cb)
+
+ channel[PROPERTIES_IFACE].Get(CHANNEL_INTERFACE_GROUP,
+ 'Members',
+ reply_handler=self.__get_members_ready_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Connection.GetMembers'))
+
+ def __update_capabilities_cb(self):
+ pass
+
+ def __aliases_changed_cb(self, aliases):
+ logging.debug('_Account.__aliases_changed_cb')
+ for handle, alias in aliases:
+ if handle in self._buddy_handles:
+ logging.debug('Got handle %r with nick %r, going to update',
+ handle, alias)
+ properties = {CONNECTION_INTERFACE_ALIASING + '/alias': alias}
+ self.emit('buddy-updated', self._buddy_handles[handle],
+ properties)
+
+ def __presences_changed_cb(self, presences):
+ logging.debug('_Account.__presences_changed_cb %r', presences)
+ for handle, presence in presences.iteritems():
+ if handle in self._buddy_handles:
+ presence_type, status_, message_ = presence
+ if presence_type == CONNECTION_PRESENCE_TYPE_OFFLINE:
+ contact_id = self._buddy_handles[handle]
+ del self._buddy_handles[handle]
+ self.emit('buddy-removed', contact_id)
+
+ def __buddy_info_updated_cb(self, handle, properties):
+ logging.debug('_Account.__buddy_info_updated_cb %r', handle)
+ self.emit('buddy-updated', self._buddy_handles[handle], properties)
+
+ def __current_activity_changed_cb(self, contact_handle, activity_id,
+ room_handle):
+ logging.debug('_Account.__current_activity_changed_cb %r %r %r',
+ contact_handle, activity_id, room_handle)
+ if contact_handle in self._buddy_handles:
+ contact_id = self._buddy_handles[contact_handle]
+ if not activity_id and room_handle:
+ activity_id = self._activity_handles.get(room_handle, '')
+ self.emit('current-activity-updated', contact_id, activity_id)
+
+ def __get_current_activity_cb(self, contact_handle, activity_id,
+ room_handle):
+ logging.debug('_Account.__get_current_activity_cb %r %r %r',
+ contact_handle, activity_id, room_handle)
+ contact_id = self._buddy_handles[contact_handle]
+ self.emit('current-activity-updated', contact_id, activity_id)
+
+ def __buddy_activities_changed_cb(self, buddy_handle, activities):
+ self._update_buddy_activities(buddy_handle, activities)
+
+ def _update_buddy_activities(self, buddy_handle, activities):
+ logging.debug('_Account._update_buddy_activities')
+ if not buddy_handle in self._buddy_handles:
+ self._buddy_handles[buddy_handle] = None
+
+ if not buddy_handle in self._activities_per_buddy:
+ self._activities_per_buddy[buddy_handle] = set()
+
+ for activity_id, room_handle in activities:
+ if room_handle not in self._activity_handles:
+ self._activity_handles[room_handle] = activity_id
+ self.emit('activity-added', room_handle, activity_id)
+
+ connection = self._connection[
+ CONNECTION_INTERFACE_ACTIVITY_PROPERTIES]
+ connection.GetProperties(room_handle,
+ reply_handler=partial(self.__get_properties_cb,
+ room_handle),
+ error_handler=partial(self.__error_handler_cb,
+ 'ActivityProperties.GetProperties'))
+
+ # Sometimes we'll get CurrentActivityChanged before we get to
+ # know about the activity so we miss the event. In that case,
+ # request again the current activity for this buddy.
+ connection = self._connection[CONNECTION_INTERFACE_BUDDY_INFO]
+ connection.GetCurrentActivity(
+ buddy_handle,
+ reply_handler=partial(self.__get_current_activity_cb,
+ buddy_handle),
+ error_handler=partial(self.__error_handler_cb,
+ 'BuddyInfo.GetCurrentActivity'))
+
+ if not activity_id in self._buddies_per_activity:
+ self._buddies_per_activity[activity_id] = set()
+ self._buddies_per_activity[activity_id].add(buddy_handle)
+ if activity_id not in self._activities_per_buddy[buddy_handle]:
+ self._activities_per_buddy[buddy_handle].add(activity_id)
+ if self._buddy_handles[buddy_handle] is not None:
+ self.emit('buddy-joined-activity',
+ self._buddy_handles[buddy_handle],
+ activity_id)
+
+ current_activity_ids = \
+ [activity_id for activity_id, room_handle in activities]
+ for activity_id in self._activities_per_buddy[buddy_handle].copy():
+ if not activity_id in current_activity_ids:
+ self._remove_buddy_from_activity(buddy_handle, activity_id)
+
+ def __get_properties_cb(self, room_handle, properties):
+ logging.debug('_Account.__get_properties_cb %r %r', room_handle,
+ properties)
+ if properties:
+ self._update_activity(room_handle, properties)
+
+ def _remove_buddy_from_activity(self, buddy_handle, activity_id):
+ if buddy_handle in self._buddies_per_activity[activity_id]:
+ self._buddies_per_activity[activity_id].remove(buddy_handle)
+
+ if activity_id in self._activities_per_buddy[buddy_handle]:
+ self._activities_per_buddy[buddy_handle].remove(activity_id)
+
+ if self._buddy_handles[buddy_handle] is not None:
+ self.emit('buddy-left-activity',
+ self._buddy_handles[buddy_handle],
+ activity_id)
+
+ if not self._buddies_per_activity[activity_id]:
+ del self._buddies_per_activity[activity_id]
+
+ for room_handle in self._activity_handles.copy():
+ if self._activity_handles[room_handle] == activity_id:
+ del self._activity_handles[room_handle]
+ break
+
+ self.emit('activity-removed', activity_id)
+
+ def __activity_properties_changed_cb(self, room_handle, properties):
+ logging.debug('_Account.__activity_properties_changed_cb %r %r',
+ room_handle, properties)
+ self._update_activity(room_handle, properties)
+
+ def _update_activity(self, room_handle, properties):
+ if room_handle in self._activity_handles:
+ self.emit('activity-updated', self._activity_handles[room_handle],
+ properties)
+ else:
+ logging.debug('_Account.__activity_properties_changed_cb unknown '
+ 'activity')
+ # We don't get ActivitiesChanged for the owner of the connection,
+ # so we query for its activities in order to find out.
+ if CONNECTION_INTERFACE_BUDDY_INFO in self._connection:
+ handle = self._self_handle
+ connection = self._connection[CONNECTION_INTERFACE_BUDDY_INFO]
+ connection.GetActivities(
+ handle,
+ reply_handler=partial(self.__got_activities_cb, handle),
+ error_handler=partial(self.__error_handler_cb,
+ 'BuddyInfo.Getactivities'))
+
+ def __members_changed_cb(self, message, added, removed, local_pending,
+ remote_pending, actor, reason):
+ self._add_buddy_handles(added)
+
+ def __get_members_ready_cb(self, handles):
+ logging.debug('_Account.__get_members_ready_cb %r', handles)
+ if not handles:
+ return
+
+ self._add_buddy_handles(handles)
+
+ def _add_buddy_handles(self, handles):
+ logging.debug('_Account._add_buddy_handles %r', handles)
+ interfaces = [CONNECTION, CONNECTION_INTERFACE_ALIASING]
+ self._connection[CONNECTION_INTERFACE_CONTACTS].GetContactAttributes(
+ handles, interfaces, False,
+ reply_handler=self.__get_contact_attributes_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Contacts.GetContactAttributes'))
+
+ def __got_buddy_info_cb(self, handle, nick, properties):
+ logging.debug('_Account.__got_buddy_info_cb %r', handle)
+ self.emit('buddy-updated', self._buddy_handles[handle], properties)
+
+ def __get_contact_attributes_cb(self, attributes):
+ logging.debug('_Account.__get_contact_attributes_cb %r',
+ attributes.keys())
+
+ for handle in attributes.keys():
+ nick = attributes[handle][CONNECTION_INTERFACE_ALIASING + '/alias']
+
+ if handle in self._buddy_handles and \
+ not self._buddy_handles[handle] is None:
+ logging.debug('Got handle %r with nick %r, going to update',
+ handle, nick)
+ self.emit('buddy-updated', self._buddy_handles[handle],
+ attributes[handle])
+ else:
+ logging.debug('Got handle %r with nick %r, going to add',
+ handle, nick)
+
+ contact_id = attributes[handle][CONNECTION + '/contact-id']
+ self._buddy_handles[handle] = contact_id
+
+ if CONNECTION_INTERFACE_BUDDY_INFO in self._connection:
+ connection = \
+ self._connection[CONNECTION_INTERFACE_BUDDY_INFO]
+
+ connection.GetProperties(
+ handle,
+ reply_handler=partial(self.__got_buddy_info_cb, handle,
+ nick),
+ error_handler=partial(self.__error_handler_cb,
+ 'BuddyInfo.GetProperties'),
+ byte_arrays=True,
+ timeout=_QUERY_DBUS_TIMEOUT)
+
+ connection.GetActivities(
+ handle,
+ reply_handler=partial(self.__got_activities_cb,
+ handle),
+ error_handler=partial(self.__error_handler_cb,
+ 'BuddyInfo.GetActivities'),
+ timeout=_QUERY_DBUS_TIMEOUT)
+
+ connection.GetCurrentActivity(
+ handle,
+ reply_handler=partial(self.__get_current_activity_cb,
+ handle),
+ error_handler=partial(self.__error_handler_cb,
+ 'BuddyInfo.GetCurrentActivity'),
+ timeout=_QUERY_DBUS_TIMEOUT)
+
+ self.emit('buddy-added', contact_id, nick, handle)
+
+ def __got_activities_cb(self, buddy_handle, activities):
+ logging.debug('_Account.__got_activities_cb %r %r', buddy_handle,
+ activities)
+ self._update_buddy_activities(buddy_handle, activities)
+
+ def enable(self):
+ logging.debug('_Account.enable %s', self.object_path)
+ self._set_enabled(True)
+
+ def disable(self):
+ logging.debug('_Account.disable %s', self.object_path)
+ self._set_enabled(False)
+ self._connection = None
+
+ def _set_enabled(self, value):
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, self.object_path)
+ obj.Set(ACCOUNT, 'Enabled', value,
+ reply_handler=self.__set_enabled_cb,
+ error_handler=partial(self.__error_handler_cb,
+ 'Account.SetEnabled'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ def __set_enabled_cb(self):
+ logging.debug('_Account.__set_enabled_cb success')
- def get_bundle_id(self):
- return self.bundle.get_bundle_id()
class Neighborhood(gobject.GObject):
__gsignals__ = {
- 'activity-added': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
- 'activity-removed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
- 'buddy-added': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
- 'buddy-moved': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT,
- gobject.TYPE_PYOBJECT])),
- 'buddy-removed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'activity-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
}
def __init__(self):
gobject.GObject.__init__(self)
+ self._buddies = {None: get_owner_instance()}
self._activities = {}
- self._buddies = {}
-
- self._pservice = presenceservice.get_instance()
- self._pservice.connect("activity-appeared",
- self._activity_appeared_cb)
- self._pservice.connect('activity-disappeared',
- self._activity_disappeared_cb)
- self._pservice.connect("buddy-appeared",
- self._buddy_appeared_cb)
- self._pservice.connect("buddy-disappeared",
- self._buddy_disappeared_cb)
-
- # Add any buddies the PS knows about already
- self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
-
- self._pservice.get_activities_async(
- reply_handler=self._get_activities_cb)
-
- self._conn_watcher = connection_watcher.ConnectionWatcher()
- self._conn_watcher.connect('connection-added', self.__conn_addded_cb)
-
- for conn in self._conn_watcher.get_connections():
- self.__conn_addded_cb(self._conn_watcher, conn)
-
- gconf_client = gconf.client_get_default()
- gconf_client.add_dir('/desktop/sugar/collaboration',
- gconf.CLIENT_PRELOAD_NONE)
- gconf_client.notify_add('/desktop/sugar/collaboration/publish_gadget',
- self.__publish_gadget_changed_cb)
-
- def __conn_addded_cb(self, watcher, conn):
- if CONN_INTERFACE_GADGET not in conn:
+ self._link_local_account = None
+ self._server_account = None
+
+ client = gconf.client_get_default()
+ client.add_dir('/desktop/sugar/collaboration',
+ gconf.CLIENT_PRELOAD_NONE)
+ client.notify_add('/desktop/sugar/collaboration/jabber_server',
+ self.__jabber_server_changed_cb)
+ client.add_dir('/desktop/sugar/user/nick', gconf.CLIENT_PRELOAD_NONE)
+ client.notify_add('/desktop/sugar/user/nick', self.__nick_changed_cb)
+
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, ACCOUNT_MANAGER_PATH)
+ account_manager = dbus.Interface(obj, ACCOUNT_MANAGER)
+ account_manager.Get(ACCOUNT_MANAGER, 'ValidAccounts',
+ dbus_interface=PROPERTIES_IFACE,
+ reply_handler=self.__got_accounts_cb,
+ error_handler=self.__error_handler_cb)
+
+ def __got_accounts_cb(self, account_paths):
+ self._link_local_account = \
+ self._ensure_link_local_account(account_paths)
+ self._connect_to_account(self._link_local_account)
+
+ self._server_account = self._ensure_server_account(account_paths)
+ self._connect_to_account(self._server_account)
+
+ def __error_handler_cb(self, error):
+ raise RuntimeError(error)
+
+ def _connect_to_account(self, account):
+ account.connect('buddy-added', self.__buddy_added_cb)
+ account.connect('buddy-updated', self.__buddy_updated_cb)
+ account.connect('buddy-removed', self.__buddy_removed_cb)
+ account.connect('buddy-joined-activity',
+ self.__buddy_joined_activity_cb)
+ account.connect('buddy-left-activity', self.__buddy_left_activity_cb)
+ account.connect('activity-added', self.__activity_added_cb)
+ account.connect('activity-updated', self.__activity_updated_cb)
+ account.connect('activity-removed', self.__activity_removed_cb)
+ account.connect('current-activity-updated',
+ self.__current_activity_updated_cb)
+ account.connect('connected', self.__account_connected_cb)
+ account.connect('disconnected', self.__account_disconnected_cb)
+
+ def __account_connected_cb(self, account):
+ logging.debug('__account_connected_cb %s', account.object_path)
+ if account == self._server_account:
+ self._link_local_account.disable()
+
+ def __account_disconnected_cb(self, account):
+ logging.debug('__account_disconnected_cb %s', account.object_path)
+ if account == self._server_account:
+ self._link_local_account.enable()
+
+ def _ensure_link_local_account(self, account_paths):
+ for account_path in account_paths:
+ if 'salut' in account_path:
+ logging.debug('Already have a Salut account')
+ account = _Account(account_path)
+ account.enable()
+ return account
+
+ logging.debug('Still dont have a Salut account, creating one')
+
+ client = gconf.client_get_default()
+ nick = client.get_string('/desktop/sugar/user/nick')
+
+ params = {
+ 'nickname': nick,
+ 'first-name': '',
+ 'last-name': '',
+ 'jid': self._get_jabber_account_id(),
+ 'published-name': nick,
+ }
+
+ properties = {
+ ACCOUNT + '.Enabled': True,
+ ACCOUNT + '.Nickname': nick,
+ ACCOUNT + '.ConnectAutomatically': True,
+ }
+
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, ACCOUNT_MANAGER_PATH)
+ account_manager = dbus.Interface(obj, ACCOUNT_MANAGER)
+ account_path = account_manager.CreateAccount('salut', 'local-xmpp',
+ 'salut', params,
+ properties)
+ return _Account(account_path)
+
+ def _ensure_server_account(self, account_paths):
+ for account_path in account_paths:
+ if 'gabble' in account_path:
+ logging.debug('Already have a Gabble account')
+ account = _Account(account_path)
+ account.enable()
+ return account
+
+ logging.debug('Still dont have a Gabble account, creating one')
+
+ client = gconf.client_get_default()
+ nick = client.get_string('/desktop/sugar/user/nick')
+ server = client.get_string('/desktop/sugar/collaboration'
+ '/jabber_server')
+ key_hash = get_profile().privkey_hash
+
+ params = {
+ 'account': self._get_jabber_account_id(),
+ 'password': key_hash,
+ 'server': server,
+ 'resource': 'sugar',
+ 'require-encryption': True,
+ 'ignore-ssl-errors': True,
+ 'register': True,
+ 'old-ssl': True,
+ 'port': dbus.UInt32(5223),
+ }
+
+ properties = {
+ ACCOUNT + '.Enabled': True,
+ ACCOUNT + '.Nickname': nick,
+ ACCOUNT + '.ConnectAutomatically': True,
+ }
+
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, ACCOUNT_MANAGER_PATH)
+ account_manager = dbus.Interface(obj, ACCOUNT_MANAGER)
+ account_path = account_manager.CreateAccount('gabble', 'jabber',
+ 'jabber', params,
+ properties)
+ return _Account(account_path)
+
+ def _get_jabber_account_id(self):
+ public_key_hash = sha1(get_profile().pubkey).hexdigest()
+ client = gconf.client_get_default()
+ server = client.get_string('/desktop/sugar/collaboration'
+ '/jabber_server')
+ return '%s@%s' % (public_key_hash, server)
+
+ def __jabber_server_changed_cb(self, client, timestamp, entry, *extra):
+ logging.debug('__jabber_server_changed_cb')
+
+ bus = dbus.Bus()
+ account = bus.get_object(ACCOUNT_MANAGER_SERVICE,
+ self._server_account.object_path)
+
+ server = client.get_string(
+ '/desktop/sugar/collaboration/jabber_server')
+ account_id = self._get_jabber_account_id()
+ needs_reconnect = account.UpdateParameters({'server': server,
+ 'account': account_id,
+ 'register': True},
+ dbus.Array([], 's'),
+ dbus_interface=ACCOUNT)
+ if needs_reconnect:
+ account.Reconnect()
+
+ self._update_jid()
+
+ def __nick_changed_cb(self, client, timestamp, entry, *extra):
+ logging.debug('__nick_changed_cb')
+
+ nick = client.get_string('/desktop/sugar/user/nick')
+ for account in self._server_account, self._link_local_account:
+ bus = dbus.Bus()
+ obj = bus.get_object(ACCOUNT_MANAGER_SERVICE, account.object_path)
+ obj.Set(ACCOUNT, 'Nickname', nick, dbus_interface=PROPERTIES_IFACE)
+
+ self._update_jid()
+
+ def _update_jid(self):
+ bus = dbus.Bus()
+ account = bus.get_object(ACCOUNT_MANAGER_SERVICE,
+ self._link_local_account.object_path)
+
+ account_id = self._get_jabber_account_id()
+ needs_reconnect = account.UpdateParameters({'jid': account_id},
+ dbus.Array([], 's'),
+ dbus_interface=ACCOUNT)
+ if needs_reconnect:
+ account.Reconnect()
+
+ def __buddy_added_cb(self, account, contact_id, nick, handle):
+ logging.debug('__buddy_added_cb %r', contact_id)
+
+ if contact_id in self._buddies:
+ logging.debug('__buddy_added_cb buddy already tracked')
return
- conn[CONN_INTERFACE_GADGET].connect_to_signal('GadgetDiscovered',
- lambda: self._gadget_discovered(conn))
-
- gadget_discovered = conn[PROPERTIES_IFACE].Get(CONN_INTERFACE_GADGET,
- 'GadgetAvailable')
- if gadget_discovered:
- self._gadget_discovered(conn)
-
- def _gadget_discovered(self, conn):
- 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)
- 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)
-
- path, props_ = conn[CONNECTION_INTERFACE_REQUESTS].CreateChannel(
- { 'org.freedesktop.Telepathy.Channel.ChannelType':
- 'org.laptop.Telepathy.Channel.Type.BuddyView',
- 'org.laptop.Telepathy.Channel.Interface.View.MaxSize': nb
- })
-
- view = Channel(conn.service_name, path)
- view[CHANNEL_INTERFACE].connect_to_signal('Closed',
- lambda: self.__respawnable_view_closed_cb(
- lambda: self._request_random_buddies(conn, nb)))
-
- def _request_random_activities(self, conn, nb):
- logging.debug('Request %d random activities', nb)
-
- path, props_ = conn[CONNECTION_INTERFACE_REQUESTS].CreateChannel(
- { 'org.freedesktop.Telepathy.Channel.ChannelType':
- 'org.laptop.Telepathy.Channel.Type.ActivityView',
- 'org.laptop.Telepathy.Channel.Interface.View.MaxSize': nb
- })
-
- view = Channel(conn.service_name, path)
- view[CHANNEL_INTERFACE].connect_to_signal('Closed',
- lambda: self.__respawnable_view_closed_cb(
- lambda: self._request_random_activities(conn, nb)))
-
- def __publish_gadget_changed_cb(self, client_, cnxn_id_, entry,
- user_data=None):
- if entry.value.type == gconf.VALUE_BOOL:
- publish = entry.value.get_bool()
-
- for conn in self._conn_watcher.get_connections():
- if CONN_INTERFACE_GADGET not in conn:
- continue
-
- gadget_discovered = conn[PROPERTIES_IFACE].Get(
- CONN_INTERFACE_GADGET, 'GadgetAvailable')
- if gadget_discovered:
- logging.debug("publish_gadget gconf key changed."
- " Publish our status on %s: %r" %
- (conn.service_name.split('.')[-1], publish))
- conn[CONN_INTERFACE_GADGET].Publish(publish)
-
- def __respawnable_view_closed_cb(self, request_fct):
- # Views are closed if the Gadget component is restarted. As we always
- # want to have the random views opened, we re-request them if they are
- # closed.
- logging.debug('View closed. Re-request it')
- request_fct()
-
- def _get_buddies_cb(self, buddy_list):
- for buddy in buddy_list:
- self._buddy_appeared_cb(self._pservice, buddy)
-
- def _get_activities_cb(self, activity_list):
- for act in activity_list:
- self._check_activity(act)
+ buddy = BuddyModel(
+ nick=nick,
+ account=account.object_path,
+ contact_id=contact_id,
+ handle=handle)
+ self._buddies[contact_id] = buddy
+
+ def __buddy_updated_cb(self, account, contact_id, properties):
+ logging.debug('__buddy_updated_cb %r', contact_id)
+ if contact_id is None:
+ # Don't know the contact-id yet, will get the full state later
+ return
- def get_activities(self):
- return self._activities.values()
+ if contact_id not in self._buddies:
+ logging.debug('__buddy_updated_cb Unknown buddy with contact_id'
+ ' %r', contact_id)
+ return
- def get_buddies(self):
- return self._buddies.values()
+ buddy = self._buddies[contact_id]
- def _buddy_activity_changed_cb(self, model, cur_activity):
- if not self._buddies.has_key(model.get_buddy().object_path()):
- return
- if cur_activity and self._activities.has_key(cur_activity.props.id):
- activity_model = self._activities[cur_activity.props.id]
- self.emit('buddy-moved', model, activity_model)
- else:
- self.emit('buddy-moved', model, None)
+ is_new = buddy.props.key is None and 'key' in properties
+
+ if 'color' in properties:
+ buddy.props.color = XoColor(properties['color'])
+
+ if 'key' in properties:
+ buddy.props.key = properties['key']
+
+ if 'nick' in properties:
+ buddy.props.nick = properties['nick']
- def _buddy_appeared_cb(self, pservice, buddy):
- if self._buddies.has_key(buddy.object_path()):
+ if is_new:
+ self.emit('buddy-added', buddy)
+
+ def __buddy_removed_cb(self, account, contact_id):
+ logging.debug('Neighborhood.__buddy_removed_cb %r', contact_id)
+ if contact_id not in self._buddies:
+ logging.debug('Neighborhood.__buddy_removed_cb Unknown buddy with '
+ 'contact_id %r', contact_id)
return
- model = BuddyModel(buddy=buddy)
- model.connect('current-activity-changed',
- self._buddy_activity_changed_cb)
- self._buddies[buddy.object_path()] = model
- self.emit('buddy-added', model)
+ buddy = self._buddies[contact_id]
+ del self._buddies[contact_id]
- cur_activity = buddy.props.current_activity
- if cur_activity:
- self._buddy_activity_changed_cb(model, cur_activity)
+ if buddy.props.key is not None:
+ self.emit('buddy-removed', buddy)
- def _buddy_disappeared_cb(self, pservice, buddy):
- if not self._buddies.has_key(buddy.object_path()):
+ def __activity_added_cb(self, account, room_handle, activity_id):
+ logging.debug('__activity_added_cb %r %r', room_handle, activity_id)
+ if activity_id in self._activities:
+ logging.debug('__activity_added_cb activity already tracked')
return
- self.emit('buddy-removed', self._buddies[buddy.object_path()])
- del self._buddies[buddy.object_path()]
- def _activity_appeared_cb(self, pservice, act):
- self._check_activity(act)
+ activity = ActivityModel(activity_id, room_handle)
+ self._activities[activity_id] = activity
+
+ def __activity_updated_cb(self, account, activity_id, properties):
+ logging.debug('__activity_updated_cb %r %r', activity_id, properties)
+ if activity_id not in self._activities:
+ logging.debug('__activity_updated_cb Unknown activity with '
+ 'activity_id %r', activity_id)
+ return
- def _check_activity(self, presence_activity):
registry = bundleregistry.get_registry()
- bundle = registry.get_bundle(presence_activity.props.type)
+ bundle = registry.get_bundle(properties['type'])
if not bundle:
+ logging.warning('Ignoring shared activity we don''t have')
+ return
+
+ activity = self._activities[activity_id]
+
+ is_new = activity.props.bundle is None
+
+ activity.props.color = XoColor(properties['color'])
+ activity.props.bundle = bundle
+ activity.props.name = properties['name']
+ activity.props.private = properties['private']
+
+ if is_new:
+ self.emit('activity-added', activity)
+
+ def __activity_removed_cb(self, account, activity_id):
+ logging.debug('__activity_removed_cb %r', activity_id)
+ if activity_id not in self._activities:
+ logging.debug('Unknown activity with id %s. Already removed?',
+ activity_id)
+ return
+ activity = self._activities[activity_id]
+ del self._activities[activity_id]
+
+ if activity.props.bundle is not None:
+ self.emit('activity-removed', activity)
+
+ def __current_activity_updated_cb(self, account, contact_id, activity_id):
+ logging.debug('__current_activity_updated_cb %r %r', contact_id,
+ activity_id)
+ if contact_id not in self._buddies:
+ logging.debug('__current_activity_updated_cb Unknown buddy with '
+ 'contact_id %r', contact_id)
+ return
+ if activity_id and activity_id not in self._activities:
+ logging.debug('__current_activity_updated_cb Unknown activity with'
+ ' id %s', activity_id)
+ activity_id = ''
+
+ buddy = self._buddies[contact_id]
+ if buddy.props.current_activity is not None:
+ if buddy.props.current_activity.activity_id == activity_id:
+ return
+ buddy.props.current_activity.remove_current_buddy(buddy)
+
+ if activity_id:
+ activity = self._activities[activity_id]
+ buddy.props.current_activity = activity
+ activity.add_current_buddy(buddy)
+ else:
+ buddy.props.current_activity = None
+
+ def __buddy_joined_activity_cb(self, account, contact_id, activity_id):
+ if contact_id not in self._buddies:
+ logging.debug('__buddy_joined_activity_cb Unknown buddy with '
+ 'contact_id %r', contact_id)
+ return
+
+ if activity_id not in self._activities:
+ logging.debug('__buddy_joined_activity_cb Unknown activity with '
+ 'activity_id %r', activity_id)
+ return
+
+ self._activities[activity_id].add_buddy(self._buddies[contact_id])
+
+ def __buddy_left_activity_cb(self, account, contact_id, activity_id):
+ if contact_id not in self._buddies:
+ logging.debug('__buddy_left_activity_cb Unknown buddy with '
+ 'contact_id %r', contact_id)
return
- if self.has_activity(presence_activity.props.id):
+
+ if activity_id not in self._activities:
+ logging.debug('__buddy_left_activity_cb Unknown activity with '
+ 'activity_id %r', activity_id)
return
- self.add_activity(bundle, presence_activity)
- def has_activity(self, activity_id):
- return self._activities.has_key(activity_id)
+ self._activities[activity_id].remove_buddy(self._buddies[contact_id])
+
+ def get_buddies(self):
+ return self._buddies.values()
+
+ def get_buddy_by_key(self, key):
+ for buddy in self._buddies.values():
+ if buddy.key == key:
+ return buddy
+ return None
+
+ def get_buddy_by_handle(self, contact_handle):
+ for buddy in self._buddies.values():
+ if not buddy.is_owner() and buddy.handle == contact_handle:
+ return buddy
+ return None
def get_activity(self, activity_id):
- if self.has_activity(activity_id):
- return self._activities[activity_id]
- else:
- return None
-
- def add_activity(self, bundle, act):
- model = ActivityModel(act, bundle)
- self._activities[model.get_id()] = model
- self.emit('activity-added', model)
-
- for buddy in self._pservice.get_buddies():
- cur_activity = buddy.props.current_activity
- object_path = buddy.object_path()
- if cur_activity == activity and object_path in self._buddies:
- buddy_model = self._buddies[object_path]
- self.emit('buddy-moved', buddy_model, model)
-
- def _activity_disappeared_cb(self, pservice, act):
- if self._activities.has_key(act.props.id):
- activity_model = self._activities[act.props.id]
- self.emit('activity-removed', activity_model)
- del self._activities[act.props.id]
+ return self._activities.get(activity_id, None)
+
+ def get_activity_by_room(self, room_handle):
+ for activity in self._activities.values():
+ if activity.room_handle == room_handle:
+ return activity
+ return None
+
+ def get_activities(self):
+ return self._activities.values()
-_model = None
def get_model():
global _model
diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
index 3a949da..f265ae4 100644
--- a/src/jarabe/model/network.py
+++ b/src/jarabe/model/network.py
@@ -1,6 +1,6 @@
# Copyright (C) 2008 Red Hat, Inc.
# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
-# Copyright (C) 2009 One Laptop per Child
+# Copyright (C) 2009-2010 One Laptop per Child
# Copyright (C) 2009 Paraguay Educa, Martin Abente
# Copyright (C) 2010 Plan Ceibal, Daniel Castelo
#
@@ -18,19 +18,23 @@
# 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 _
import logging
import os
import time
import dbus
+import dbus.service
import gobject
import ConfigParser
import gconf
+import ctypes
from sugar import dispatch
from sugar import env
from sugar.util import unique_id
+
DEVICE_TYPE_802_3_ETHERNET = 1
DEVICE_TYPE_802_11_WIRELESS = 2
DEVICE_TYPE_GSM_MODEM = 3
@@ -54,6 +58,49 @@ NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0
NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1
NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2
+
+NM_DEVICE_STATE_REASON_UNKNOWN = 0
+NM_DEVICE_STATE_REASON_NONE = 1
+NM_DEVICE_STATE_REASON_NOW_MANAGED = 2
+NM_DEVICE_STATE_REASON_NOW_UNMANAGED = 3
+NM_DEVICE_STATE_REASON_CONFIG_FAILED = 4
+NM_DEVICE_STATE_REASON_CONFIG_UNAVAILABLE = 5
+NM_DEVICE_STATE_REASON_CONFIG_EXPIRED = 6
+NM_DEVICE_STATE_REASON_NO_SECRETS = 7
+NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8
+NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED = 9
+NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED = 10
+NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT = 11
+NM_DEVICE_STATE_REASON_PPP_START_FAILED = 12
+NM_DEVICE_STATE_REASON_PPP_DISCONNECT = 13
+NM_DEVICE_STATE_REASON_PPP_FAILED = 14
+NM_DEVICE_STATE_REASON_DHCP_START_FAILED = 15
+NM_DEVICE_STATE_REASON_DHCP_ERROR = 16
+NM_DEVICE_STATE_REASON_DHCP_FAILED = 17
+NM_DEVICE_STATE_REASON_SHARED_START_FAILED = 18
+NM_DEVICE_STATE_REASON_SHARED_FAILED = 19
+NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED = 20
+NM_DEVICE_STATE_REASON_AUTOIP_ERROR = 21
+NM_DEVICE_STATE_REASON_AUTOIP_FAILED = 22
+NM_DEVICE_STATE_REASON_MODEM_BUSY = 23
+NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE = 24
+NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER = 25
+NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT = 26
+NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED = 27
+NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED = 28
+NM_DEVICE_STATE_REASON_GSM_APN_FAILED = 29
+NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING = 30
+NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED = 31
+NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT = 32
+NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED = 33
+NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED = 34
+NM_DEVICE_STATE_REASON_FIRMWARE_MISSING = 35
+NM_DEVICE_STATE_REASON_REMOVED = 36
+NM_DEVICE_STATE_REASON_SLEEPING = 37
+NM_DEVICE_STATE_REASON_CONNECTION_REMOVED = 38
+NM_DEVICE_STATE_REASON_USER_REQUESTED = 39
+NM_DEVICE_STATE_REASON_CARRIER = 40
+
NM_802_11_AP_FLAGS_NONE = 0x00000000
NM_802_11_AP_FLAGS_PRIVACY = 0x00000001
@@ -83,11 +130,16 @@ NM_802_11_DEVICE_CAP_RSN = 0x00000020
SETTINGS_SERVICE = 'org.freedesktop.NetworkManagerUserSettings'
+NM_SERVICE = 'org.freedesktop.NetworkManager'
+NM_IFACE = 'org.freedesktop.NetworkManager'
+NM_PATH = '/org/freedesktop/NetworkManager'
+NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
NM_SETTINGS_PATH = '/org/freedesktop/NetworkManagerSettings'
NM_SETTINGS_IFACE = 'org.freedesktop.NetworkManagerSettings'
NM_CONNECTION_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection'
NM_SECRETS_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection.Secrets'
NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
+NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
GSM_USERNAME_PATH = '/desktop/sugar/network/gsm/username'
GSM_PASSWORD_PATH = '/desktop/sugar/network/gsm/password'
@@ -99,6 +151,137 @@ GSM_PUK_PATH = '/desktop/sugar/network/gsm/puk'
_nm_settings = None
_conn_counter = 0
+_nm_device_state_reason_description = None
+
+
+def get_error_by_reason(reason):
+ global _nm_device_state_reason_description
+
+ if _nm_device_state_reason_description is None:
+ _nm_device_state_reason_description = {
+ NM_DEVICE_STATE_REASON_UNKNOWN:
+ _('The reason for the device state change is unknown.'),
+ NM_DEVICE_STATE_REASON_NONE:
+ _('The state change is normal.'),
+ NM_DEVICE_STATE_REASON_NOW_MANAGED:
+ _('The device is now managed.'),
+ NM_DEVICE_STATE_REASON_NOW_UNMANAGED:
+ _('The device is no longer managed.'),
+ NM_DEVICE_STATE_REASON_CONFIG_FAILED:
+ _('The device could not be readied for configuration.'),
+ NM_DEVICE_STATE_REASON_CONFIG_UNAVAILABLE:
+ _('IP configuration could not be reserved '
+ '(no available address, timeout, etc).'),
+ NM_DEVICE_STATE_REASON_CONFIG_EXPIRED:
+ _('The IP configuration is no longer valid.'),
+ NM_DEVICE_STATE_REASON_NO_SECRETS:
+ _('Secrets were required, but not provided.'),
+ NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT:
+ _('The 802.1X supplicant disconnected from '
+ 'the access point or authentication server.'),
+ NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED:
+ _('Configuration of the 802.1X supplicant failed.'),
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED:
+ _('The 802.1X supplicant quit or failed unexpectedly.'),
+ NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT:
+ _('The 802.1X supplicant took too long to authenticate.'),
+ NM_DEVICE_STATE_REASON_PPP_START_FAILED:
+ _('The PPP service failed to start within the allowed time.'),
+ NM_DEVICE_STATE_REASON_PPP_DISCONNECT:
+ _('The PPP service disconnected unexpectedly.'),
+ NM_DEVICE_STATE_REASON_PPP_FAILED:
+ _('The PPP service quit or failed unexpectedly.'),
+ NM_DEVICE_STATE_REASON_DHCP_START_FAILED:
+ _('The DHCP service failed to start within the allowed time.'),
+ NM_DEVICE_STATE_REASON_DHCP_ERROR:
+ _('The DHCP service reported an unexpected error.'),
+ NM_DEVICE_STATE_REASON_DHCP_FAILED:
+ _('The DHCP service quit or failed unexpectedly.'),
+ NM_DEVICE_STATE_REASON_SHARED_START_FAILED:
+ _('The shared connection service failed to start.'),
+ NM_DEVICE_STATE_REASON_SHARED_FAILED:
+ _('The shared connection service quit or failed'
+ ' unexpectedly.'),
+ NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED:
+ _('The AutoIP service failed to start.'),
+ NM_DEVICE_STATE_REASON_AUTOIP_ERROR:
+ _('The AutoIP service reported an unexpected error.'),
+ NM_DEVICE_STATE_REASON_AUTOIP_FAILED:
+ _('The AutoIP service quit or failed unexpectedly.'),
+ NM_DEVICE_STATE_REASON_MODEM_BUSY:
+ _('Dialing failed because the line was busy.'),
+ NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE:
+ _('Dialing failed because there was no dial tone.'),
+ NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER:
+ _('Dialing failed because there was no carrier.'),
+ NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT:
+ _('Dialing timed out.'),
+ NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED:
+ _('Dialing failed.'),
+ NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
+ _('Modem initialization failed.'),
+ NM_DEVICE_STATE_REASON_GSM_APN_FAILED:
+ _('Failed to select the specified GSM APN'),
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
+ _('Not searching for networks.'),
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
+ _('Network registration was denied.'),
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT:
+ _('Network registration timed out.'),
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED:
+ _('Failed to register with the requested GSM network.'),
+ NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED:
+ _('PIN check failed.'),
+ NM_DEVICE_STATE_REASON_FIRMWARE_MISSING:
+ _('Necessary firmware for the device may be missing.'),
+ NM_DEVICE_STATE_REASON_REMOVED:
+ _('The device was removed.'),
+ NM_DEVICE_STATE_REASON_SLEEPING:
+ _('NetworkManager went to sleep.'),
+ NM_DEVICE_STATE_REASON_CONNECTION_REMOVED:
+ _("The device's active connection was removed "
+ "or disappeared."),
+ NM_DEVICE_STATE_REASON_USER_REQUESTED:
+ _('A user or client requested the disconnection.'),
+ NM_DEVICE_STATE_REASON_CARRIER:
+ _("The device's carrier/link changed.")}
+
+ return _nm_device_state_reason_description[reason]
+
+
+def frequency_to_channel(frequency):
+ """Returns the channel matching a given radio channel frequency. If a
+ frequency is not in the dictionary channel 1 will be returned.
+
+ Keyword arguments:
+ frequency -- The radio channel frequency in MHz.
+
+ Return: Channel
+
+ """
+ ftoc = {2412: 1, 2417: 2, 2422: 3, 2427: 4,
+ 2432: 5, 2437: 6, 2442: 7, 2447: 8,
+ 2452: 9, 2457: 10, 2462: 11, 2467: 12,
+ 2472: 13}
+ if frequency not in ftoc:
+ logging.warning('The frequency %s can not be mapped to a channel, '
+ 'defaulting to channel 1.', frequency)
+ return 1
+ return ftoc[frequency]
+
+
+def is_sugar_adhoc_network(ssid):
+ """Checks whether an access point is a sugar Ad-hoc network.
+
+ Keyword arguments:
+ ssid -- Ssid of the access point.
+
+ Return: Boolean
+
+ """
+ return ssid.startswith('Ad-hoc Network')
+
+
class WirelessSecurity(object):
def __init__(self):
self.key_mgmt = None
@@ -118,14 +301,16 @@ class WirelessSecurity(object):
wireless_security['group'] = self.group
return wireless_security
+
class Wireless(object):
- nm_name = "802-11-wireless"
+ nm_name = '802-11-wireless'
def __init__(self):
self.ssid = None
self.security = None
self.mode = None
self.band = None
+ self.channel = None
def get_dict(self):
wireless = {'ssid': self.ssid}
@@ -135,11 +320,13 @@ class Wireless(object):
wireless['mode'] = self.mode
if self.band:
wireless['band'] = self.band
+ if self.channel:
+ wireless['channel'] = self.channel
return wireless
class OlpcMesh(object):
- nm_name = "802-11-olpc-mesh"
+ nm_name = '802-11-olpc-mesh'
def __init__(self, channel, anycast_addr):
self.channel = channel
@@ -147,12 +334,12 @@ class OlpcMesh(object):
def get_dict(self):
ret = {
- "ssid": dbus.ByteArray("olpc-mesh"),
- "channel": self.channel,
+ 'ssid': dbus.ByteArray('olpc-mesh'),
+ 'channel': self.channel,
}
if self.anycast_addr:
- ret["dhcp-anycast-address"] = dbus.ByteArray(self.anycast_addr)
+ ret['dhcp-anycast-address'] = dbus.ByteArray(self.anycast_addr)
return ret
@@ -173,6 +360,7 @@ class Connection(object):
connection['timestamp'] = self.timestamp
return connection
+
class IP4Config(object):
def __init__(self):
self.method = None
@@ -183,6 +371,7 @@ class IP4Config(object):
ip4_config['method'] = self.method
return ip4_config
+
class Serial(object):
def __init__(self):
self.baud = None
@@ -195,6 +384,7 @@ class Serial(object):
return serial
+
class Ppp(object):
def __init__(self):
pass
@@ -203,6 +393,7 @@ class Ppp(object):
ppp = {}
return ppp
+
class Gsm(object):
def __init__(self):
self.apn = None
@@ -221,6 +412,7 @@ class Gsm(object):
return gsm
+
class Settings(object):
def __init__(self, wireless_cfg=None):
self.connection = Connection()
@@ -243,6 +435,7 @@ class Settings(object):
settings['ipv4'] = self.ip4_config.get_dict()
return settings
+
class Secrets(object):
def __init__(self, settings):
self.settings = settings
@@ -268,6 +461,7 @@ class Secrets(object):
return settings
+
class SettingsGsm(object):
def __init__(self):
self.connection = Connection()
@@ -287,22 +481,24 @@ class SettingsGsm(object):
return settings
+
class SecretsGsm(object):
def __init__(self):
self.password = None
self.pin = None
self.puk = None
-
+
def get_dict(self):
secrets = {}
if self.password is not None:
secrets['password'] = self.password
if self.pin is not None:
secrets['pin'] = self.pin
- if self.puk is not None:
+ if self.puk is not None:
secrets['puk'] = self.puk
return {'gsm': secrets}
+
class NMSettings(dbus.service.Object):
def __init__(self):
bus = dbus.SystemBus()
@@ -330,10 +526,19 @@ class NMSettings(dbus.service.Object):
self.secrets_request.send(self, connection=sender,
response=kwargs['response'])
+ def clear_wifi_connections(self):
+ for uuid in self.connections.keys():
+ conn = self.connections[uuid]
+ if conn._settings.connection.type == \
+ NM_CONNECTION_TYPE_802_11_WIRELESS:
+ conn.Removed()
+ self.connections.pop(uuid)
+
+
class SecretsResponse(object):
- ''' Intermediate object to report the secrets from the dialog
+ """Intermediate object to report the secrets from the dialog
back to the connection object and which will inform NM
- '''
+ """
def __init__(self, connection, reply_cb, error_cb):
self._connection = connection
self._reply_cb = reply_cb
@@ -346,6 +551,7 @@ class SecretsResponse(object):
def set_error(self, error):
self._error_cb(error)
+
class NMSettingsConnection(dbus.service.Object):
def __init__(self, path, settings, secrets):
bus = dbus.SystemBus()
@@ -358,27 +564,57 @@ class NMSettingsConnection(dbus.service.Object):
self._settings = settings
self._secrets = secrets
+ @dbus.service.signal(dbus_interface=NM_CONNECTION_IFACE,
+ signature='')
+ def Removed(self):
+ pass
+
+ @dbus.service.signal(dbus_interface=NM_CONNECTION_IFACE,
+ signature='a{sa{sv}}')
+ def Updated(self, settings):
+ pass
+
def set_connected(self):
if self._settings.connection.type == NM_CONNECTION_TYPE_GSM:
self._settings.connection.timestamp = int(time.time())
- else:
- if not self._settings.connection.autoconnect:
- self._settings.connection.autoconnect = True
- self._settings.connection.timestamp = int(time.time())
- if self._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS:
- self.save()
+ elif not self._settings.connection.autoconnect:
+ self._settings.connection.autoconnect = True
+ self._settings.connection.timestamp = int(time.time())
+ if (self._settings.connection.type ==
+ NM_CONNECTION_TYPE_802_11_WIRELESS):
+ self.Updated(self._settings.get_dict())
+ self.save()
+
+ try:
+ # try to flush resolver cache - SL#1940
+ # ctypes' syntactic sugar does not work
+ # so we must get the func ptr explicitly
+ libc = ctypes.CDLL('libc.so.6')
+ res_init = getattr(libc, '__res_init')
+ res_init(None)
+ except:
+ # pylint: disable=W0702
+ logging.exception('Error calling libc.__res_init')
+
+ def disable_autoconnect(self):
+ if self._settings.connection.type != NM_CONNECTION_TYPE_GSM and \
+ self._settings.connection.autoconnect:
+ self._settings.connection.autoconnect = False
+ self._settings.connection.timestamp = None
+ self.Updated(self._settings.get_dict())
+ self.save()
def set_secrets(self, secrets):
self._secrets = secrets
- if self._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS:
+ if self._settings.connection.type == \
+ NM_CONNECTION_TYPE_802_11_WIRELESS:
self.save()
def get_settings(self):
return self._settings
def save(self):
- profile_path = env.get_profile_path()
- config_path = os.path.join(profile_path, 'nm', 'connections.cfg')
+ config_path = _get_wifi_connections_path()
config = ConfigParser.ConfigParser()
try:
@@ -443,22 +679,31 @@ class NMSettingsConnection(dbus.service.Object):
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)
- 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:
- logging.exception('Error requesting the secrets via dialog')
+ self.path, request_new)
+ if self._settings.connection.type is not NM_CONNECTION_TYPE_GSM:
+ 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:
+ logging.exception('Error requesting the secrets via'
+ ' dialog')
+ else:
+ reply(self._secrets.get_dict())
else:
- reply(self._secrets.get_dict())
+ if not request_new:
+ reply(self._secrets.get_dict())
+ else:
+ raise Exception('The stored GSM secret has already been'
+ ' supplied')
class AccessPoint(gobject.GObject):
__gsignals__ = {
'props-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
+ ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, device, model):
@@ -475,10 +720,10 @@ class AccessPoint(gobject.GObject):
self.wpa_flags = 0
self.rsn_flags = 0
self.mode = 0
+ self.channel = 0
def initialize(self):
- model_props = dbus.Interface(self.model,
- 'org.freedesktop.DBus.Properties')
+ model_props = dbus.Interface(self.model, dbus.PROPERTIES_IFACE)
model_props.GetAll(NM_ACCESSPOINT_IFACE, byte_arrays=True,
reply_handler=self._ap_properties_changed_cb,
error_handler=self._get_all_props_error_cb)
@@ -523,7 +768,7 @@ class AccessPoint(gobject.GObject):
else:
fl |= 1 << 6
- hashstr = str(fl) + "@" + self.name
+ hashstr = str(fl) + '@' + self.name
return hash(hashstr)
def _update_properties(self, properties):
@@ -544,6 +789,9 @@ class AccessPoint(gobject.GObject):
self.rsn_flags = properties['RsnFlags']
if 'Mode' in properties:
self.mode = properties['Mode']
+ if 'Frequency' in properties:
+ self.channel = frequency_to_channel(properties['Frequency'])
+
self._initialized = True
self.emit('props-changed', old_hash)
@@ -559,6 +807,7 @@ class AccessPoint(gobject.GObject):
path=self.model.object_path,
dbus_interface=NM_ACCESSPOINT_IFACE)
+
def get_settings():
global _nm_settings
if _nm_settings is None:
@@ -569,17 +818,20 @@ def get_settings():
load_connections()
return _nm_settings
+
def find_connection_by_ssid(ssid):
connections = get_settings().connections
for conn_index in connections:
connection = connections[conn_index]
- if connection._settings.connection.type == NM_CONNECTION_TYPE_802_11_WIRELESS:
- if connection._settings.wireless.ssid == ssid:
- return connection
+ if connection._settings.connection.type == \
+ NM_CONNECTION_TYPE_802_11_WIRELESS and \
+ connection._settings.wireless.ssid == ssid:
+ return connection
return None
+
def add_connection(uuid, settings, secrets=None):
global _conn_counter
@@ -590,19 +842,26 @@ def add_connection(uuid, settings, secrets=None):
_nm_settings.add_connection(uuid, conn)
return conn
-def load_wifi_connections():
+
+def _get_wifi_connections_path():
profile_path = env.get_profile_path()
- config_path = os.path.join(profile_path, 'nm', 'connections.cfg')
+ return os.path.join(profile_path, 'nm', 'connections.cfg')
- config = ConfigParser.ConfigParser()
+
+def _create_wifi_connections(config_path):
+ if not os.path.exists(os.path.dirname(config_path)):
+ os.makedirs(os.path.dirname(config_path), 0755)
+ f = open(config_path, 'w')
+ f.close()
+
+
+def load_wifi_connections():
+ config_path = _get_wifi_connections_path()
if not os.path.exists(config_path):
- if not os.path.exists(os.path.dirname(config_path)):
- os.makedirs(os.path.dirname(config_path), 0755)
- f = open(config_path, 'w')
- config.write(f)
- f.close()
+ _create_wifi_connections(config_path)
+ config = ConfigParser.ConfigParser()
try:
if not config.read(config_path):
logging.error('Error reading the nm config file')
@@ -672,10 +931,10 @@ def load_gsm_connection():
if username and number and apn:
settings = SettingsGsm()
- settings.gsm.username = username
+ settings.gsm.username = username
settings.gsm.number = number
settings.gsm.apn = apn
-
+
secrets = SecretsGsm()
secrets.pin = pin
secrets.puk = puk
@@ -693,12 +952,14 @@ def load_gsm_connection():
except Exception:
logging.exception('Error adding gsm connection to NMSettings.')
else:
- logging.exception("No gsm connection was set in GConf.")
+ logging.warning('No gsm connection was set in GConf.')
+
def load_connections():
load_wifi_connections()
load_gsm_connection()
+
def find_gsm_connection():
connections = get_settings().connections
@@ -708,3 +969,39 @@ def find_gsm_connection():
logging.debug('There is no gsm connection in the NMSettings.')
return None
+
+
+def have_wifi_connections():
+ return bool(get_settings().connections)
+
+
+def clear_wifi_connections():
+ if _nm_settings is not None:
+ _nm_settings.clear_wifi_connections()
+
+ config_path = _get_wifi_connections_path()
+ _create_wifi_connections(config_path)
+
+
+def disconnect_access_points(ap_paths):
+ """
+ Disconnect all devices connected to any of the given access points.
+ """
+ bus = dbus.SystemBus()
+ netmgr_obj = bus.get_object(NM_SERVICE, NM_PATH)
+ netmgr = dbus.Interface(netmgr_obj, NM_IFACE)
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
+ active_connection_paths = netmgr_props.Get(NM_IFACE, 'ActiveConnections')
+
+ for conn_path in active_connection_paths:
+ conn_obj = bus.get_object(NM_IFACE, conn_path)
+ conn_props = dbus.Interface(conn_obj, dbus.PROPERTIES_IFACE)
+ ap_path = conn_props.Get(NM_ACTIVE_CONN_IFACE, 'SpecificObject')
+ if ap_path == '/' or ap_path not in ap_paths:
+ continue
+
+ dev_paths = conn_props.Get(NM_ACTIVE_CONN_IFACE, 'Devices')
+ for dev_path in dev_paths:
+ dev_obj = bus.get_object(NM_SERVICE, dev_path)
+ dev = dbus.Interface(dev_obj, NM_DEVICE_IFACE)
+ dev.Disconnect()
diff --git a/src/jarabe/model/notifications.py b/src/jarabe/model/notifications.py
index 10a0237..ec14056 100644
--- a/src/jarabe/model/notifications.py
+++ b/src/jarabe/model/notifications.py
@@ -23,9 +23,13 @@ from sugar import dispatch
from jarabe import config
-_DBUS_SERVICE = "org.freedesktop.Notifications"
-_DBUS_IFACE = "org.freedesktop.Notifications"
-_DBUS_PATH = "/org/freedesktop/Notifications"
+
+_DBUS_SERVICE = 'org.freedesktop.Notifications'
+_DBUS_IFACE = 'org.freedesktop.Notifications'
+_DBUS_PATH = '/org/freedesktop/Notifications'
+
+_instance = None
+
class NotificationService(dbus.service.Object):
def __init__(self):
@@ -43,7 +47,8 @@ class NotificationService(dbus.service.Object):
hints, expire_timeout):
logging.debug('Received notification: %r', [app_name, replaces_id,
- app_icon, summary, body, actions, hints, expire_timeout])
+ '<app_icon>', summary, body, actions, '<hints>',
+ expire_timeout])
if replaces_id > 0:
notification_id = replaces_id
@@ -73,16 +78,14 @@ class NotificationService(dbus.service.Object):
def GetServerInformation(self, name, vendor, version):
return 'Sugar Shell', 'Sugar', config.version
-
- @dbus.service.signal(_DBUS_IFACE, signature="uu")
+ @dbus.service.signal(_DBUS_IFACE, signature='uu')
def NotificationClosed(self, notification_id, reason):
pass
- @dbus.service.signal(_DBUS_IFACE, signature="us")
+ @dbus.service.signal(_DBUS_IFACE, signature='us')
def ActionInvoked(self, notification_id, action_key):
pass
-_instance = None
def get_service():
global _instance
@@ -90,6 +93,6 @@ def get_service():
_instance = NotificationService()
return _instance
+
def init():
get_service()
-
diff --git a/src/jarabe/model/olpcmesh.py b/src/jarabe/model/olpcmesh.py
index cbd7ddd..f070100 100644
--- a/src/jarabe/model/olpcmesh.py
+++ b/src/jarabe/model/olpcmesh.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 One Laptop per Child
+# Copyright (C) 2009, 2010 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
@@ -24,13 +24,14 @@ from jarabe.model.network import Settings
from jarabe.model.network import OlpcMesh as OlpcMeshSettings
from sugar.util import unique_id
+
_NM_SERVICE = 'org.freedesktop.NetworkManager'
_NM_IFACE = 'org.freedesktop.NetworkManager'
_NM_PATH = '/org/freedesktop/NetworkManager'
_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
-_XS_ANYCAST = "\xc0\x27\xc0\x27\xc0\x00"
+_XS_ANYCAST = '\xc0\x27\xc0\x27\xc0\x00'
DEVICE_STATE_UNKNOWN = 0
DEVICE_STATE_UNMANAGED = 1
@@ -43,6 +44,7 @@ DEVICE_STATE_IP_CONFIG = 7
DEVICE_STATE_ACTIVATED = 8
DEVICE_STATE_FAILED = 9
+
class OlpcMeshManager(object):
def __init__(self, mesh_device):
self._bus = dbus.SystemBus()
@@ -56,14 +58,12 @@ class OlpcMeshManager(object):
"""
- props = dbus.Interface(self.mesh_device,
- 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self.mesh_device, dbus.PROPERTIES_IFACE)
props.Get(_NM_DEVICE_IFACE, 'State',
reply_handler=self.__get_mesh_state_reply_cb,
error_handler=self.__get_state_error_cb)
- props = dbus.Interface(self.eth_device,
- 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self.eth_device, dbus.PROPERTIES_IFACE)
props.Get(_NM_DEVICE_IFACE, 'State',
reply_handler=self.__get_eth_state_reply_cb,
error_handler=self.__get_state_error_cb)
@@ -83,13 +83,12 @@ class OlpcMeshManager(object):
self._eth_device_state = DEVICE_STATE_UNKNOWN
if self._have_configured_connections():
- self._start_automesh()
- else:
self._start_automesh_timer()
+ else:
+ self._start_automesh()
def _get_companion_device(self):
- props = dbus.Interface(self.mesh_device,
- 'org.freedesktop.DBus.Properties')
+ props = dbus.Interface(self.mesh_device, dbus.PROPERTIES_IFACE)
eth_device_o = props.Get(_NM_OLPC_MESH_IFACE, 'Companion')
return self._bus.get_object(_NM_SERVICE, eth_device_o)
@@ -119,7 +118,7 @@ class OlpcMeshManager(object):
def __eth_device_state_changed_cb(self, new_state, old_state, reason):
"""If a connection is activated on the eth device, stop trying our
automatic connections.
-
+
"""
self._eth_device_state = new_state
self._maybe_schedule_idle_check()
@@ -147,7 +146,7 @@ class OlpcMeshManager(object):
def _idle_check(self):
if self._mesh_device_state == DEVICE_STATE_DISCONNECTED \
and self._eth_device_state == DEVICE_STATE_DISCONNECTED:
- logging.debug("starting automesh due to inactivity")
+ logging.debug('starting automesh due to inactivity')
self._start_automesh()
return False
@@ -170,8 +169,8 @@ class OlpcMeshManager(object):
logging.error('Failed to activate connection: %s', err)
def _activate_connection(self, channel, anycast_address=None):
- logging.debug("activate channel %d anycast %s",
- (channel, repr(anycast_address)))
+ logging.debug('activate channel %d anycast %r',
+ channel, anycast_address)
proxy = self._bus.get_object(_NM_SERVICE, _NM_PATH)
network_manager = dbus.Interface(proxy, _NM_IFACE)
connection = self._make_connection(channel, anycast_address)
@@ -211,4 +210,3 @@ class OlpcMeshManager(object):
self._connection_queue.append((6, _XS_ANYCAST))
self._connection_queue.append((1, _XS_ANYCAST))
self._try_next_connection_from_queue()
-
diff --git a/src/jarabe/model/owner.py b/src/jarabe/model/owner.py
deleted file mode 100644
index 17996e6..0000000
--- a/src/jarabe/model/owner.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright (C) 2006-2007 Red Hat, Inc.
-# Copyright (C) 2008 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 gobject
-import os
-import gconf
-import simplejson
-
-from telepathy.interfaces import CHANNEL_TYPE_TEXT
-
-from sugar import env
-from sugar.presence import presenceservice
-from sugar import util
-from jarabe.model.invites import Invites
-
-class Owner(gobject.GObject):
- """Class representing the owner of this machine/instance. This class
- runs in the shell and serves up the buddy icon and other stuff. It's the
- server portion of the Owner, paired with the client portion in Buddy.py.
- """
- __gtype_name__ = "ShellOwner"
-
- __gsignals__ = {
- 'nick-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_STRING])),
- 'color-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'icon-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
- }
-
- def __init__(self):
- gobject.GObject.__init__(self)
-
- client = gconf.client_get_default()
- self._nick = client.get_string("/desktop/sugar/user/nick")
-
- self._icon = None
- self._icon_hash = ""
- icon = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
- if not os.path.exists(icon):
- raise RuntimeError("missing buddy icon")
-
- fd = open(icon, "r")
- self._icon = fd.read()
- fd.close()
- if not self._icon:
- raise RuntimeError("invalid buddy icon")
-
- # Get the icon's hash
- import hashlib
- digest = hashlib.md5(self._icon).digest()
- self._icon_hash = util.printable_hash(digest)
-
- self._pservice = presenceservice.get_instance()
- self._pservice.connect('activity-invitation',
- self._activity_invitation_cb)
- self._pservice.connect('private-invitation',
- self._private_invitation_cb)
- self._pservice.connect('activity-disappeared',
- self._activity_disappeared_cb)
-
- self._invites = Invites()
-
- def get_invites(self):
- return self._invites
-
- def get_nick(self):
- return self._nick
-
- def _activity_invitation_cb(self, pservice, activity, buddy, message):
- self._invites.add_invite(activity.props.type,
- activity.props.id)
-
- def _private_invitation_cb(self, pservice, bus_name, connection,
- channel, channel_type):
- """Handle a private-invitation from Presence Service.
-
- This is a connection by a non-Sugar XMPP client, so
- launch Chat or VideoChat with the Telepathy connection and
- channel.
- """
- if channel_type == CHANNEL_TYPE_TEXT:
- bundle_id = 'org.laptop.Chat'
- else:
- bundle_id = 'org.laptop.VideoChat'
- tp_channel = simplejson.dumps([bus_name, connection, channel])
- self._invites.add_private_invite(tp_channel, bundle_id)
-
- def _activity_disappeared_cb(self, pservice, activity):
- self._invites.remove_activity(activity.props.id)
-
-_model = None
-
-def get_model():
- global _model
- if _model is None:
- _model = Owner()
- return _model
diff --git a/src/jarabe/model/screen.py b/src/jarabe/model/screen.py
index 4403c1c..7d34d45 100644
--- a/src/jarabe/model/screen.py
+++ b/src/jarabe/model/screen.py
@@ -18,12 +18,14 @@ import logging
import dbus
+
_HARDWARE_MANAGER_INTERFACE = 'org.freedesktop.ohm.Keystore'
_HARDWARE_MANAGER_SERVICE = 'org.freedesktop.ohm'
_HARDWARE_MANAGER_OBJECT_PATH = '/org/freedesktop/ohm/Keystore'
_ohm_service = None
+
def _get_ohm():
global _ohm_service
if _ohm_service is None:
@@ -35,9 +37,9 @@ def _get_ohm():
return _ohm_service
+
def set_dcon_freeze(frozen):
try:
- _get_ohm().SetKey("display.dcon_freeze", frozen)
+ _get_ohm().SetKey('display.dcon_freeze', frozen)
except dbus.DBusException:
logging.error('Cannot unfreeze the DCON')
-
diff --git a/src/jarabe/model/session.py b/src/jarabe/model/session.py
index 9e0f087..9b277ff 100644
--- a/src/jarabe/model/session.py
+++ b/src/jarabe/model/session.py
@@ -24,8 +24,10 @@ import logging
from sugar import session
from sugar import env
+
_session_manager = None
+
class SessionManager(session.SessionManager):
MODE_LOGOUT = 0
MODE_SHUTDOWN = 1
@@ -53,15 +55,15 @@ class SessionManager(session.SessionManager):
elif self._logout_mode != self.MODE_LOGOUT:
try:
bus = dbus.SystemBus()
- proxy = bus.get_object('org.freedesktop.Hal',
- '/org/freedesktop/Hal/devices/computer')
+ proxy = bus.get_object('org.freedesktop.ConsoleKit',
+ '/org/freedesktop/ConsoleKit/Manager')
pm = dbus.Interface(proxy,
- 'org.freedesktop.Hal.Device.SystemPowerManagement')
+ 'org.freedesktop.ConsoleKit.Manager')
if self._logout_mode == self.MODE_SHUTDOWN:
- pm.Shutdown()
+ pm.Stop()
elif self._logout_mode == self.MODE_REBOOT:
- pm.Reboot()
+ pm.Restart()
except:
logging.exception('Can not stop sugar')
self.session.cancel_shutdown()
@@ -73,14 +75,15 @@ class SessionManager(session.SessionManager):
def _close_emulator(self):
gtk.main_quit()
- if os.environ.has_key('SUGAR_EMULATOR_PID'):
+ if 'SUGAR_EMULATOR_PID' in os.environ:
pid = int(os.environ['SUGAR_EMULATOR_PID'])
os.kill(pid, signal.SIGTERM)
- # Need to call this ASAP so the atexit handlers get called before we get
- # killed by the X (dis)connection
+ # Need to call this ASAP so the atexit handlers get called before we
+ # get killed by the X (dis)connection
sys.exit()
+
def get_session_manager():
global _session_manager
diff --git a/src/jarabe/model/shell.py b/src/jarabe/model/shell.py
index e03e0f7..63f6173 100644
--- a/src/jarabe/model/shell.py
+++ b/src/jarabe/model/shell.py
@@ -27,13 +27,16 @@ import dbus
from sugar import wm
from sugar import dispatch
from sugar.graphics.xocolor import XoColor
-from sugar.presence import presenceservice
from jarabe.model.bundleregistry import get_registry
+from jarabe.model import neighborhood
+
+_SERVICE_NAME = 'org.laptop.Activity'
+_SERVICE_PATH = '/org/laptop/Activity'
+_SERVICE_INTERFACE = 'org.laptop.Activity'
+
+_model = None
-_SERVICE_NAME = "org.laptop.Activity"
-_SERVICE_PATH = "/org/laptop/Activity"
-_SERVICE_INTERFACE = "org.laptop.Activity"
class Activity(gobject.GObject):
"""Activity which appears in the "Home View" of the Sugar shell
@@ -46,10 +49,9 @@ class Activity(gobject.GObject):
__gtype_name__ = 'SugarHomeActivity'
- __gproperties__ = {
- 'launching' : (bool, None, None, False,
- gobject.PARAM_READWRITE),
- }
+ LAUNCHING = 0
+ LAUNCH_FAILED = 1
+ LAUNCHED = 2
def __init__(self, activity_info, activity_id, window=None):
"""Initialise the HomeActivity
@@ -60,19 +62,20 @@ class Activity(gobject.GObject):
the "type" of activity being created.
activity_id -- unique identifier for this instance
of the activity type
- window -- Main WnckWindow of the activity
+ _windows -- WnckWindows registered for the activity. The lowest
+ one in the stack is the main window.
"""
gobject.GObject.__init__(self)
- self._window = None
+ self._windows = []
self._service = None
self._activity_id = activity_id
self._activity_info = activity_info
self._launch_time = time.time()
- self._launching = True
+ self._launch_status = Activity.LAUNCHING
if window is not None:
- self.set_window(window)
+ self.add_window(window)
self._retrieve_service()
@@ -81,18 +84,32 @@ class Activity(gobject.GObject):
bus = dbus.SessionBus()
self._name_owner_changed_handler = bus.add_signal_receiver(
self._name_owner_changed_cb,
- signal_name="NameOwnerChanged",
- dbus_interface="org.freedesktop.DBus")
+ signal_name='NameOwnerChanged',
+ dbus_interface='org.freedesktop.DBus')
- def set_window(self, window):
- """Set the window for the activity
+ self._launch_completed_hid = get_model().connect('launch-completed',
+ self.__launch_completed_cb)
+ self._launch_failed_hid = get_model().connect('launch-failed',
+ self.__launch_failed_cb)
- We allow resetting the window for an activity so that we
- can replace the launcher once we get its real window.
- """
+ def get_launch_status(self):
+ return self._launch_status
+
+ launch_status = gobject.property(getter=get_launch_status)
+
+ def add_window(self, window):
+ """Add a window to the windows stack."""
if not window:
- raise ValueError("window must be valid")
- self._window = window
+ raise ValueError('window must be valid')
+ self._windows.append(window)
+
+ def remove_window_by_xid(self, xid):
+ """Remove a window from the windows stack."""
+ for wnd in self._windows:
+ if wnd.get_xid() == xid:
+ self._windows.remove(wnd)
+ return True
+ return False
def get_service(self):
"""Get the activity service
@@ -106,8 +123,8 @@ class Activity(gobject.GObject):
def get_title(self):
"""Retrieve the application's root window's suggested title"""
- if self._window:
- return self._window.get_name()
+ if self._windows:
+ return self._windows[0].get_name()
else:
return ''
@@ -135,21 +152,19 @@ class Activity(gobject.GObject):
have an entry (implying that this is not a Sugar-shared application)
uses the local user's profile colour for the icon.
"""
- pservice = presenceservice.get_instance()
-
# HACK to suppress warning in logs when activity isn't found
# (if it's locally launched and not shared yet)
activity = None
- for act in pservice.get_activities():
- if self._activity_id == act.props.id:
+ for act in neighborhood.get_model().get_activities():
+ if self._activity_id == act.activity_id:
activity = act
break
if activity != None:
- return XoColor(activity.props.color)
+ return activity.props.color
else:
client = gconf.client_get_default()
- return XoColor(client.get_string("/desktop/sugar/user/color"))
+ return XoColor(client.get_string('/desktop/sugar/user/color'))
def get_activity_id(self):
"""Retrieve the "activity_id" passed in to our constructor
@@ -161,31 +176,44 @@ class Activity(gobject.GObject):
def get_xid(self):
"""Retrieve the X-windows ID of our root window"""
- if self._window is not None:
- return self._window.get_xid()
+ if self._windows:
+ return self._windows[0].get_xid()
else:
return None
+ def has_xid(self, xid):
+ """Check if an X-window with the given xid is in the windows stack"""
+ if self._windows:
+ for wnd in self._windows:
+ if wnd.get_xid() == xid:
+ return True
+ return False
+
def get_window(self):
"""Retrieve the X-windows root window of this application
- This was stored by the set_window method, which was
+ This was stored by the add_window method, which was
called by HomeModel._add_activity, which was called
via a callback that looks for all 'window-opened'
events.
+ We keep a stack of the windows. The lowest window in the
+ stack that is still valid we consider the main one.
+
HomeModel currently uses a dbus service query on the
activity to determine to which HomeActivity the newly
launched window belongs.
"""
- return self._window
+ if self._windows:
+ return self._windows[0]
+ return None
def get_type(self):
"""Retrieve the activity bundle id for future reference"""
- if self._window is None:
+ if not self._windows:
return None
else:
- return wm.get_bundle_id(self._window)
+ return wm.get_bundle_id(self._windows[0])
def is_journal(self):
"""Returns boolean if the activity is of type JournalActivity"""
@@ -201,7 +229,9 @@ class Activity(gobject.GObject):
def get_pid(self):
"""Returns the activity's PID"""
- return self._window.get_pid()
+ if not self._windows:
+ return None
+ return self._windows[0].get_pid()
def get_bundle_path(self):
"""Returns the activity's bundle directory"""
@@ -220,18 +250,10 @@ class Activity(gobject.GObject):
def equals(self, activity):
if self._activity_id and activity.get_activity_id():
return self._activity_id == activity.get_activity_id()
- if self._window.get_xid() and activity.get_xid():
- return self._window.get_xid() == activity.get_xid()
+ if self._windows[0].get_xid() and activity.get_xid():
+ return self._windows[0].get_xid() == activity.get_xid()
return False
- def do_set_property(self, pspec, value):
- if pspec.name == 'launching':
- self._launching = value
-
- def do_get_property(self, pspec):
- if pspec.name == 'launching':
- return self._launching
-
def _get_service_name(self):
if self._activity_id:
return _SERVICE_NAME + self._activity_id
@@ -245,17 +267,24 @@ class Activity(gobject.GObject):
try:
bus = dbus.SessionBus()
proxy = bus.get_object(self._get_service_name(),
- _SERVICE_PATH + "/" + self._activity_id)
+ _SERVICE_PATH + '/' + self._activity_id)
self._service = dbus.Interface(proxy, _SERVICE_INTERFACE)
except dbus.DBusException:
self._service = None
def _name_owner_changed_cb(self, name, old, new):
if name == self._get_service_name():
- self._retrieve_service()
- self.set_active(True)
- self._name_owner_changed_handler.remove()
- self._name_owner_changed_handler = None
+ if old and not new:
+ logging.debug('Activity._name_owner_changed_cb: ' \
+ 'activity %s went away', name)
+ self._name_owner_changed_handler.remove()
+ self._name_owner_changed_handler = None
+ self._service = None
+ elif not old and new:
+ logging.debug('Activity._name_owner_changed_cb: ' \
+ 'activity %s started up', name)
+ self._retrieve_service()
+ self.set_active(True)
def set_active(self, state):
"""Propagate the current state to the activity object"""
@@ -268,7 +297,24 @@ 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)
+
+ def _set_launch_status(self, value):
+ get_model().disconnect(self._launch_completed_hid)
+ get_model().disconnect(self._launch_failed_hid)
+ self._launch_completed_hid = None
+ self._launch_failed_hid = None
+ self._launch_status = value
+ self.notify('launch_status')
+
+ def __launch_completed_cb(self, model, home_activity):
+ if home_activity is self:
+ self._set_launch_status(Activity.LAUNCHED)
+
+ def __launch_failed_cb(self, model, home_activity):
+ if home_activity is self:
+ self._set_launch_status(Activity.LAUNCH_FAILED)
+
class ShellModel(gobject.GObject):
"""Model of the shell (activity management)
@@ -286,27 +332,22 @@ class ShellModel(gobject.GObject):
"""
__gsignals__ = {
- 'activity-added': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'activity-removed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'activity-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
+ ([gobject.TYPE_PYOBJECT])),
'tabbing-activity-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'launch-started': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'launch-completed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT])),
- 'launch-failed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'launch-started': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'launch-completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
+ 'launch-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT])),
}
ZOOM_MESH = 0
@@ -331,10 +372,20 @@ class ShellModel(gobject.GObject):
self._activities = []
self._active_activity = None
self._tabbing_activity = None
- self._pservice = presenceservice.get_instance()
+ self._launchers = {}
self._screen.toggle_showing_desktop(True)
+ def get_launcher(self, activity_id):
+ return self._launchers.get(str(activity_id))
+
+ def register_launcher(self, activity_id, launcher):
+ self._launchers[activity_id] = launcher
+
+ def unregister_launcher(self, activity_id):
+ if activity_id in self._launchers:
+ del self._launchers[activity_id]
+
def _update_zoom_level(self, window):
if window.get_window_type() == wnck.WINDOW_DIALOG:
return
@@ -426,7 +477,7 @@ class ShellModel(gobject.GObject):
def set_tabbing_activity(self, activity):
"""Sets the activity that is currently highlighted during tabbing"""
self._tabbing_activity = activity
- self.emit("tabbing-activity-changed", self._tabbing_activity)
+ self.emit('tabbing-activity-changed', self._tabbing_activity)
def _set_active_activity(self, home_activity):
if self._active_activity == home_activity:
@@ -454,6 +505,21 @@ class ShellModel(gobject.GObject):
return self._activities.index(obj)
def _window_opened_cb(self, screen, window):
+ """Handle the callback for the 'window opened' event.
+
+ Most activities will register 2 windows during
+ their lifetime: the launcher window, and the 'main'
+ app window.
+
+ When the main window appears, we send a signal to
+ the launcher window to close.
+
+ Some activities (notably non-native apps) open several
+ windows during their lifetime, switching from one to
+ the next as the 'main' window. We use a stack to track
+ them.
+
+ """
if window.get_window_type() == wnck.WINDOW_NORMAL:
home_activity = None
@@ -476,31 +542,36 @@ class ShellModel(gobject.GObject):
window.maximize()
if not home_activity:
+ logging.debug('first window registered for %s' % activity_id)
home_activity = Activity(activity_info, activity_id, window)
self._add_activity(home_activity)
else:
- home_activity.set_window(window)
-
- if wm.get_sugar_window_type(window) != 'launcher':
- home_activity.props.launching = False
- if not home_activity.is_journal():
- self.emit('launch-completed', home_activity)
+ logging.debug('window registered for %s' % activity_id)
+ home_activity.add_window(window)
+ if wm.get_sugar_window_type(window) != 'launcher' \
+ and home_activity.get_launch_status() == Activity.LAUNCHING:
+ 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.' %
+ (activity_id, startup_time))
if self._active_activity is None:
self._set_active_activity(home_activity)
def _window_closed_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
- if self._get_activity_by_xid(window.get_xid()) is not None:
- self._remove_activity_by_xid(window.get_xid())
+ xid = window.get_xid()
+ activity = self._get_activity_by_xid(xid)
+ if activity is not None:
+ activity.remove_window_by_xid(xid)
+ if activity.get_window() is None:
+ logging.debug('last window gone - remove activity %s' % activity)
+ self._remove_activity(activity)
def _get_activity_by_xid(self, xid):
for home_activity in self._activities:
- if home_activity.get_xid() == xid:
+ if home_activity.has_xid(xid):
return home_activity
return None
@@ -545,13 +616,6 @@ class ShellModel(gobject.GObject):
self.emit('activity-removed', home_activity)
self._activities.remove(home_activity)
- def _remove_activity_by_xid(self, xid):
- home_activity = self._get_activity_by_xid(xid)
- if home_activity:
- self._remove_activity(home_activity)
- else:
- logging.error('Model for window %d does not exist.', xid)
-
def notify_launch(self, activity_id, service_name):
registry = get_registry()
activity_info = registry.get_bundle(service_name)
@@ -560,7 +624,6 @@ class ShellModel(gobject.GObject):
" was not found in the bundle registry."
% service_name)
home_activity = Activity(activity_info, activity_id)
- home_activity.props.launching = True
self._add_activity(home_activity)
self._set_active_activity(home_activity)
@@ -575,11 +638,12 @@ 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,
+ logging.debug('Activity %s (%s) launch failed', activity_id,
home_activity.get_type())
- if home_activity.props.launching:
+ if self.get_launcher(activity_id) is not None:
self.emit('launch-failed', home_activity)
else:
+ # activity sent failure notification after closing launcher
self._remove_activity(home_activity)
else:
logging.error('Model for activity id %s does not exist.',
@@ -592,18 +656,15 @@ class ShellModel(gobject.GObject):
logging.debug('Activity %s has been closed already.', activity_id)
return False
- if home_activity.props.launching:
+ if self.get_launcher(activity_id) is not None:
logging.debug('Activity %s still launching, assuming it failed.',
activity_id)
self.notify_launch_failed(activity_id)
return False
-_model = None
-
def get_model():
global _model
if _model is None:
_model = ShellModel()
return _model
-
diff --git a/src/jarabe/model/sound.py b/src/jarabe/model/sound.py
index 65090a4..9e1e748 100644
--- a/src/jarabe/model/sound.py
+++ b/src/jarabe/model/sound.py
@@ -20,17 +20,23 @@ from sugar import env
from sugar import _sugarext
from sugar import dispatch
+
VOLUME_STEP = 10
muted_changed = dispatch.Signal()
volume_changed = dispatch.Signal()
+_volume = _sugarext.VolumeAlsa()
+
+
def get_muted():
return _volume.get_mute()
+
def get_volume():
return _volume.get_volume()
+
def set_volume(new_volume):
old_volume = _volume.get_volume()
_volume.set_volume(new_volume)
@@ -38,6 +44,7 @@ def set_volume(new_volume):
volume_changed.send(None)
save()
+
def set_muted(new_state):
old_state = _volume.get_mute()
_volume.set_mute(new_state)
@@ -45,14 +52,14 @@ def set_muted(new_state):
muted_changed.send(None)
save()
+
def save():
if env.is_emulator() is False:
client = gconf.client_get_default()
client.set_int('/desktop/sugar/sound/volume', get_volume())
+
def restore():
if env.is_emulator() is False:
client = gconf.client_get_default()
set_volume(client.get_int('/desktop/sugar/sound/volume'))
-
-_volume = _sugarext.VolumeAlsa()
diff --git a/src/jarabe/model/telepathyclient.py b/src/jarabe/model/telepathyclient.py
new file mode 100644
index 0000000..c6fbac1
--- /dev/null
+++ b/src/jarabe/model/telepathyclient.py
@@ -0,0 +1,103 @@
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# 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 dbus
+from dbus import PROPERTIES_IFACE
+from telepathy.interfaces import CLIENT, \
+ CLIENT_APPROVER, \
+ CLIENT_HANDLER, \
+ CLIENT_INTERFACE_REQUESTS
+from telepathy.server import DBusProperties
+
+from sugar import dispatch
+
+
+SUGAR_CLIENT_SERVICE = 'org.freedesktop.Telepathy.Client.Sugar'
+SUGAR_CLIENT_PATH = '/org/freedesktop/Telepathy/Client/Sugar'
+
+_instance = None
+
+
+class TelepathyClient(dbus.service.Object, DBusProperties):
+ def __init__(self):
+ self._interfaces = set([CLIENT, CLIENT_HANDLER,
+ CLIENT_INTERFACE_REQUESTS, PROPERTIES_IFACE,
+ CLIENT_APPROVER])
+
+ bus = dbus.Bus()
+ bus_name = dbus.service.BusName(SUGAR_CLIENT_SERVICE, bus=bus)
+
+ dbus.service.Object.__init__(self, bus_name, SUGAR_CLIENT_PATH)
+ DBusProperties.__init__(self)
+
+ self._implement_property_get(CLIENT, {
+ 'Interfaces': lambda: list(self._interfaces),
+ })
+ self._implement_property_get(CLIENT_HANDLER, {
+ 'HandlerChannelFilter': self.__get_filters_cb,
+ })
+ self._implement_property_get(CLIENT_APPROVER, {
+ 'ApproverChannelFilter': self.__get_filters_cb,
+ })
+
+ self.got_channel = dispatch.Signal()
+ self.got_dispatch_operation = dispatch.Signal()
+
+ def __get_filters_cb(self):
+ logging.debug('__get_filters_cb')
+ filter_dict = dbus.Dictionary({}, signature='sv')
+ return dbus.Array([filter_dict], signature='a{sv}')
+
+ @dbus.service.method(dbus_interface=CLIENT_HANDLER,
+ in_signature='ooa(oa{sv})aota{sv}', out_signature='')
+ def HandleChannels(self, account, connection, channels, requests_satisfied,
+ user_action_time, handler_info):
+ logging.debug('HandleChannels\n%r\n%r\n%r\n%r\n%r\n%r\n', account,
+ connection, channels, requests_satisfied,
+ user_action_time, handler_info)
+ for channel in channels:
+ self.got_channel.send(self, account=account,
+ connection=connection, channel=channel)
+
+ @dbus.service.method(dbus_interface=CLIENT_INTERFACE_REQUESTS,
+ in_signature='oa{sv}', out_signature='')
+ def AddRequest(self, request, properties):
+ logging.debug('AddRequest\n%r\n%r', request, properties)
+
+ @dbus.service.method(dbus_interface=CLIENT_APPROVER,
+ in_signature='a(oa{sv})oa{sv}', out_signature='',
+ async_callbacks=('success_cb', 'error_cb_'))
+ def AddDispatchOperation(self, channels, dispatch_operation_path,
+ properties, success_cb, error_cb_):
+ success_cb()
+ try:
+ logging.debug('AddDispatchOperation\n%r\n%r\n%r', channels,
+ dispatch_operation_path, properties)
+
+ self.got_dispatch_operation.send(self, channels=channels,
+ dispatch_operation_path=dispatch_operation_path,
+ properties=properties)
+ except Exception, e:
+ logging.exception(e)
+
+
+def get_instance():
+ global _instance
+ if not _instance:
+ _instance = TelepathyClient()
+ return _instance
diff --git a/src/jarabe/util/__init__.py b/src/jarabe/util/__init__.py
index 1610dd0..9c80ecb 100644
--- a/src/jarabe/util/__init__.py
+++ b/src/jarabe/util/__init__.py
@@ -16,4 +16,3 @@
# 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/util/emulator.py b/src/jarabe/util/emulator.py
index d9dca7f..fda1b59 100644
--- a/src/jarabe/util/emulator.py
+++ b/src/jarabe/util/emulator.py
@@ -20,6 +20,7 @@ import subprocess
import sys
import time
from optparse import OptionParser
+from gettext import gettext as _
import gtk
import gobject
@@ -29,36 +30,37 @@ from sugar import env
ERROR_NO_DISPLAY = 30
ERROR_NO_SERVER = 31
+default_dimensions = (800, 600)
-default_dimensions = (800, 600)
def _run_xephyr(display, dpi, dimensions, fullscreen):
- cmd = [ 'Xephyr' ]
+ cmd = ['Xephyr']
cmd.append(':%d' % display)
- cmd.append('-ac')
+ cmd.append('-ac')
+ cmd += ['-title', _('Sugar in a window')]
screen_size = (gtk.gdk.screen_width(), gtk.gdk.screen_height())
if (not dimensions) and (fullscreen is None) and \
- (screen_size < default_dimensions) :
+ (screen_size <= default_dimensions):
# no forced settings, screen too small => fit screen
fullscreen = True
- elif (not dimensions) :
+ elif not dimensions:
# screen is big enough or user has en/disabled fullscreen manually
# => use default size (will get ignored for fullscreen)
dimensions = '%dx%d' % default_dimensions
- if not dpi :
+ if not dpi:
dpi = gtk.settings_get_default().get_property('gtk-xft-dpi') / 1024
- if fullscreen :
+ if fullscreen:
cmd.append('-fullscreen')
- if dimensions :
+ if dimensions:
cmd.append('-screen')
cmd.append(dimensions)
- if dpi :
+ if dpi:
cmd.append('-dpi')
cmd.append('%d' % dpi)
@@ -71,15 +73,13 @@ def _run_xephyr(display, dpi, dimensions, fullscreen):
sys.stderr.write('Error executing server: %s\n' % (exc, ))
return None
- os.environ['DISPLAY'] = ":%d" % (display)
- os.environ['SUGAR_EMULATOR_PID'] = str(pipe.pid)
return pipe
def _check_server(display):
result = subprocess.call(['xdpyinfo', '-display', ':%d' % display],
- stdout=open(os.devnull, "w"),
- stderr=open(os.devnull, "w"))
+ stdout=open(os.devnull, 'w'),
+ stderr=open(os.devnull, 'w'))
return result == 0
@@ -98,17 +98,17 @@ def _start_xephyr(dpi, dimensions, fullscreen):
if not _check_server(display):
pipe = _run_xephyr(display, dpi, dimensions, fullscreen)
if pipe is None:
- return None
+ return None, None
for i_ in range(10):
if _check_server(display):
- return pipe
+ return pipe, display
time.sleep(0.1)
_kill_pipe(pipe)
- return None
+ return None, None
def _start_window_manager():
@@ -118,21 +118,31 @@ def _start_window_manager():
gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
-def _setup_env():
+
+def _setup_env(display, scaling, emulator_pid):
os.environ['SUGAR_EMULATOR'] = 'yes'
os.environ['GABBLE_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-gabble.log')
os.environ['SALUT_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-salut.log')
+ os.environ['MC_LOGFILE'] = os.path.join(
+ env.get_profile_path(), 'logs', 'mission-control.log')
os.environ['STREAM_ENGINE_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-stream-engine.log')
+ os.environ['DISPLAY'] = ':%d' % (display)
+ os.environ['SUGAR_EMULATOR_PID'] = emulator_pid
+ os.environ['MC_ACCOUNT_DIR'] = os.path.join(
+ env.get_profile_path(), 'accounts')
+
+ if scaling:
+ os.environ['SUGAR_SCALING'] = scaling
def main():
"""Script-level operations"""
parser = OptionParser()
- parser.add_option('-d', '--dpi', dest='dpi', type="int",
+ parser.add_option('-d', '--dpi', dest='dpi', type='int',
help='Emulator dpi')
parser.add_option('-s', '--scaling', dest='scaling',
help='Sugar scaling in %')
@@ -150,16 +160,14 @@ def main():
sys.stderr.write('DISPLAY not set, cannot connect to host X server.\n')
return ERROR_NO_DISPLAY
- _setup_env()
-
- server = _start_xephyr(options.dpi, options.dimensions, options.fullscreen)
+ server, display = _start_xephyr(options.dpi, options.dimensions,
+ options.fullscreen)
if server is None:
sys.stderr.write('Failed to start server. Please check output above'
' for any error message.\n')
return ERROR_NO_SERVER
- if options.scaling:
- os.environ['SUGAR_SCALING'] = options.scaling
+ _setup_env(display, options.scaling, str(server.pid))
command = ['dbus-launch', '--exit-with-session']
diff --git a/src/jarabe/util/telepathy/__init__.py b/src/jarabe/util/telepathy/__init__.py
index 387d09c..eee4abb 100644
--- a/src/jarabe/util/telepathy/__init__.py
+++ b/src/jarabe/util/telepathy/__init__.py
@@ -16,4 +16,3 @@
# 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/util/telepathy/connection_watcher.py b/src/jarabe/util/telepathy/connection_watcher.py
index 4a4c6e0..96af1cf 100644
--- a/src/jarabe/util/telepathy/connection_watcher.py
+++ b/src/jarabe/util/telepathy/connection_watcher.py
@@ -28,12 +28,16 @@ from telepathy.interfaces import CONN_INTERFACE
from telepathy.constants import CONNECTION_STATUS_CONNECTED, \
CONNECTION_STATUS_DISCONNECTED
+
+_instance = None
+
+
class ConnectionWatcher(gobject.GObject):
__gsignals__ = {
'connection-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'connection-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([gobject.TYPE_PYOBJECT]))
+ ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, bus=None):
@@ -93,14 +97,22 @@ class ConnectionWatcher(gobject.GObject):
def get_connections(self):
return self._connections.values()
+
+def get_instance():
+ global _instance
+ if _instance is None:
+ _instance = ConnectionWatcher()
+ return _instance
+
+
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
def connection_added_cb(conn_watcher, conn):
- print "new connection", conn.service_name
+ print 'new connection', conn.service_name
def connection_removed_cb(conn_watcher, conn):
- print "removed connection", conn.service_name
+ print 'removed connection', conn.service_name
watcher = ConnectionWatcher()
watcher.connect('connection-added', connection_added_cb)
diff --git a/src/jarabe/view/__init__.py b/src/jarabe/view/__init__.py
index a9dd95a..85f6a24 100644
--- a/src/jarabe/view/__init__.py
+++ b/src/jarabe/view/__init__.py
@@ -13,4 +13,3 @@
# 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/view/buddyicon.py b/src/jarabe/view/buddyicon.py
index 13edb2c..a274605 100644
--- a/src/jarabe/view/buddyicon.py
+++ b/src/jarabe/view/buddyicon.py
@@ -19,42 +19,45 @@ from sugar.graphics import style
from jarabe.view.buddymenu import BuddyMenu
+
class BuddyIcon(CanvasIcon):
def __init__(self, buddy, size=style.STANDARD_ICON_SIZE):
CanvasIcon.__init__(self, icon_name='computer-xo', size=size)
self._greyed_out = False
self._buddy = buddy
- self._buddy.connect('appeared', self._buddy_presence_change_cb)
- self._buddy.connect('disappeared', self._buddy_presence_change_cb)
- self._buddy.connect('color-changed', self._buddy_presence_change_cb)
+ self._buddy.connect('notify::present', self.__buddy_notify_present_cb)
+ self._buddy.connect('notify::color', self.__buddy_notify_color_cb)
- palette = BuddyMenu(buddy)
- self.set_palette(palette)
+ self.palette_invoker.cache_palette = False
self._update_color()
- def _buddy_presence_change_cb(self, buddy, color=None):
+ def create_palette(self):
+ return BuddyMenu(self._buddy)
+
+ def __buddy_notify_present_cb(self, buddy, pspec):
# Update the icon's color when the buddy comes and goes
self._update_color()
- def _update_color(self):
+ def __buddy_notify_color_cb(self, buddy, pspec):
+ self._update_color()
+ def _update_color(self):
# keep the icon in the palette in sync with the view
palette = self.get_palette()
- palette_icon = palette.props.icon
-
if self._greyed_out:
self.props.stroke_color = '#D5D5D5'
self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
- palette_icon.props.stroke_color = '#D5D5D5'
- palette_icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+ if palette is not None:
+ palette.props.icon.props.stroke_color = self.props.stroke_color
+ palette.props.icon.props.fill_color = self.props.fill_color
else:
self.props.xo_color = self._buddy.get_color()
- palette_icon.props.xo_color = self._buddy.get_color()
+ if palette is not None:
+ palette.props.icon.props.xo_color = self._buddy.get_color()
def set_filter(self, query):
self._greyed_out = (self._buddy.get_nick().lower().find(query) == -1) \
and not self._buddy.is_owner()
self._update_color()
-
diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py
index 4637751..f824e70 100644
--- a/src/jarabe/view/buddymenu.py
+++ b/src/jarabe/view/buddymenu.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -19,6 +20,7 @@ from gettext import gettext as _
import gtk
import gconf
+import dbus
from sugar.graphics.palette import Palette
from sugar.graphics.menuitem import MenuItem
@@ -28,6 +30,8 @@ from jarabe.model import shell
from jarabe.model import friends
from jarabe.model.session import get_session_manager
from jarabe.controlpanel.gui import ControlPanel
+import jarabe.desktop.homewindow
+
class BuddyMenu(Palette):
def __init__(self, buddy):
@@ -42,8 +46,7 @@ class BuddyMenu(Palette):
self._active_activity_changed_hid = None
self.connect('destroy', self.__destroy_cb)
- self._buddy.connect('icon-changed', self._buddy_icon_changed_cb)
- self._buddy.connect('nick-changed', self._buddy_nick_changed_cb)
+ self._buddy.connect('notify::nick', self.__buddy_notify_nick_cb)
if buddy.is_owner():
self._add_my_items()
@@ -54,8 +57,7 @@ class BuddyMenu(Palette):
if self._active_activity_changed_hid is not None:
home_model = shell.get_model()
home_model.disconnect(self._active_activity_changed_hid)
- self._buddy.disconnect_by_func(self._buddy_icon_changed_cb)
- self._buddy.disconnect_by_func(self._buddy_nick_changed_cb)
+ self._buddy.disconnect_by_func(self.__buddy_notify_nick_cb)
def _add_buddy_items(self):
if friends.get_model().has_buddy(self._buddy):
@@ -86,6 +88,12 @@ class BuddyMenu(Palette):
client = gconf.client_get_default()
+ if client.get_bool('/desktop/sugar/show_restart'):
+ item = MenuItem(_('Restart'), 'system-restart')
+ item.connect('activate', self.__reboot_activate_cb)
+ self.menu.append(item)
+ item.show()
+
if client.get_bool('/desktop/sugar/show_logout'):
item = MenuItem(_('Logout'), 'system-logout')
item.connect('activate', self.__logout_activate_cb)
@@ -97,17 +105,18 @@ class BuddyMenu(Palette):
self.menu.append(item)
item.show()
+ def _quit(self, action):
+ home_window = jarabe.desktop.homewindow.get_instance()
+ home_window.busy_during_delayed_action(action)
+
def __logout_activate_cb(self, menu_item):
- session_manager = get_session_manager()
- session_manager.logout()
+ self._quit(get_session_manager().logout)
def __reboot_activate_cb(self, menu_item):
- session_manager = get_session_manager()
- session_manager.reboot()
+ self._quit(get_session_manager().reboot)
def __shutdown_activate_cb(self, menu_item):
- session_manager = get_session_manager()
- session_manager.shutdown()
+ self._quit(get_session_manager().shutdown)
def __controlpanel_activate_cb(self, menu_item):
panel = ControlPanel()
@@ -115,9 +124,9 @@ class BuddyMenu(Palette):
panel.show()
def _update_invite_menu(self, activity):
- buddy_activity = self._buddy.get_current_activity()
+ buddy_activity = self._buddy.props.current_activity
if buddy_activity is not None:
- buddy_activity_id = buddy_activity.props.id
+ buddy_activity_id = buddy_activity.activity_id
else:
buddy_activity_id = None
@@ -139,11 +148,8 @@ class BuddyMenu(Palette):
def _cur_activity_changed_cb(self, home_model, activity_model):
self._update_invite_menu(activity_model)
- def _buddy_icon_changed_cb(self, buddy):
- pass
-
- def _buddy_nick_changed_cb(self, buddy, nick):
- self.set_primary_text(nick)
+ def __buddy_notify_nick_cb(self, buddy, pspec):
+ self.set_primary_text(buddy.props.nick)
def _make_friend_cb(self, menuitem):
friends.get_model().make_friend(self._buddy)
@@ -155,7 +161,17 @@ class BuddyMenu(Palette):
activity = shell.get_model().get_active_activity()
service = activity.get_service()
if service:
- buddy = self._buddy.get_buddy()
- service.Invite(buddy.props.key)
+ try:
+ service.InviteContact(self._buddy.props.account,
+ self._buddy.props.contact_id)
+ except dbus.DBusException, e:
+ expected_exceptions = [
+ 'org.freedesktop.DBus.Error.UnknownMethod',
+ 'org.freedesktop.DBus.Python.NotImplementedError']
+ if e.get_dbus_name() in expected_exceptions:
+ logging.warning('Trying deprecated Activity.Invite')
+ service.Invite(self._buddy.props.key)
+ else:
+ raise
else:
logging.error('Invite failed, activity service not ')
diff --git a/src/jarabe/view/keyhandler.py b/src/jarabe/view/keyhandler.py
index 8c07975..d79bfe6 100644
--- a/src/jarabe/view/keyhandler.py
+++ b/src/jarabe/view/keyhandler.py
@@ -17,7 +17,6 @@
import os
import logging
-import traceback
import dbus
import gtk
@@ -32,39 +31,46 @@ from jarabe.model.shell import ShellModel
from jarabe import config
from jarabe.journal import journalactivity
+
_VOLUME_STEP = sound.VOLUME_STEP
_VOLUME_MAX = 100
_TABBING_MODIFIER = gtk.gdk.MOD1_MASK
+
_actions_table = {
- 'F1' : 'zoom_mesh',
- 'F2' : 'zoom_group',
- 'F3' : 'zoom_home',
- 'F4' : 'zoom_activity',
- 'XF86AudioMute' : 'volume_mute',
- 'F11' : 'volume_down',
- 'XF86AudioLowerVolume' : 'volume_down',
- 'F12' : 'volume_up',
- 'XF86AudioRaiseVolume' : 'volume_up',
- '<alt>F11' : 'volume_min',
- '<alt>F12' : 'volume_max',
- '0x93' : 'frame',
- '<alt>Tab' : 'next_window',
- '<alt><shift>Tab' : 'previous_window',
- '<alt>Escape' : 'close_window',
- '0xDC' : 'open_search',
+ 'F1': 'zoom_mesh',
+ 'F2': 'zoom_group',
+ 'F3': 'zoom_home',
+ 'F4': 'zoom_activity',
+ 'F5': 'open_search',
+ 'F6': 'frame',
+ 'XF86AudioMute': 'volume_mute',
+ 'F11': 'volume_down',
+ 'XF86AudioLowerVolume': 'volume_down',
+ 'F12': 'volume_up',
+ 'XF86AudioRaiseVolume': 'volume_up',
+ '<alt>F11': 'volume_min',
+ '<alt>F12': 'volume_max',
+ '0x93': 'frame',
+ '<alt>Tab': 'next_window',
+ '<alt><shift>Tab': 'previous_window',
+ '<alt>Escape': 'close_window',
+ '0xDC': 'open_search',
# the following are intended for emulator users
- '<alt><shift>f' : 'frame',
- '<alt><shift>q' : 'quit_emulator',
- 'XF86Search' : 'open_search',
- '<alt><shift>o' : 'open_search',
- '<alt><shift>s' : 'say_text',
+ '<alt><shift>f': 'frame',
+ '<alt><shift>q': 'quit_emulator',
+ 'XF86Search': 'open_search',
+ '<alt><shift>o': 'open_search',
+ '<alt><shift>s': 'say_text',
}
SPEECH_DBUS_SERVICE = 'org.laptop.Speech'
SPEECH_DBUS_PATH = '/org/laptop/Speech'
SPEECH_DBUS_INTERFACE = 'org.laptop.Speech'
+_instance = None
+
+
class KeyHandler(object):
def __init__(self, frame):
self._frame = frame
@@ -93,8 +99,7 @@ class KeyHandler(object):
raise ValueError('Key %r is already bound' % key)
_actions_table[key] = module
except Exception:
- logging.error('Exception while loading extension:\n' + \
- traceback.format_exc())
+ logging.exception('Exception while loading extension:')
self._key_grabber.grab_keys(_actions_table.keys())
@@ -124,11 +129,11 @@ class KeyHandler(object):
def _primary_selection_cb(self, clipboard, text, user_data):
logging.debug('KeyHandler._primary_selection_cb: %r', text)
if text:
- self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \
+ self._get_speech_proxy().SayText(text, reply_handler=lambda: None,
error_handler=self._on_speech_err)
def handle_say_text(self, event_time):
- clipboard = gtk.clipboard_get(selection="PRIMARY")
+ clipboard = gtk.clipboard_get(selection='PRIMARY')
clipboard.request_text(self._primary_selection_cb)
def handle_previous_window(self, event_time):
@@ -195,7 +200,7 @@ class KeyHandler(object):
if self._tabbing_handler.is_tabbing():
# Only accept window tabbing events, everything else
# cancels the tabbing operation.
- if not action in ["next_window", "previous_window"]:
+ if not action in ['next_window', 'previous_window']:
self._tabbing_handler.stop(event_time)
return True
@@ -218,7 +223,7 @@ class KeyHandler(object):
return False
def _key_released_cb(self, grabber, keycode, state, event_time):
- logging.debug('_key_released_cb: %i %i' % (keycode, state))
+ logging.debug('_key_released_cb: %i %i', keycode, state)
if self._tabbing_handler.is_tabbing():
# We stop tabbing and switch to the new window as soon as the
# modifier key is raised again.
@@ -228,7 +233,6 @@ class KeyHandler(object):
return True
return False
-_instance = None
def setup(frame):
global _instance
@@ -237,4 +241,3 @@ def setup(frame):
del _instance
_instance = KeyHandler(frame)
-
diff --git a/src/jarabe/view/launcher.py b/src/jarabe/view/launcher.py
index 422a49a..89251e5 100644
--- a/src/jarabe/view/launcher.py
+++ b/src/jarabe/view/launcher.py
@@ -156,9 +156,6 @@ class _Animation(animator.Animation):
self._icon.props.size = int(self.start_size + d)
-_launchers = {}
-
-
def setup():
model = shell.get_model()
model.connect('launch-started', __launch_started_cb)
@@ -167,14 +164,15 @@ def setup():
def add_launcher(activity_id, icon_path, icon_color):
+ model = shell.get_model()
- if activity_id in _launchers:
+ if model.get_launcher(activity_id) is not None:
return
launch_window = LaunchWindow(activity_id, icon_path, icon_color)
launch_window.show()
- _launchers[activity_id] = launch_window
+ model.register_launcher(activity_id, launch_window)
def __launch_started_cb(home_model, home_activity):
@@ -184,7 +182,7 @@ def __launch_started_cb(home_model, home_activity):
def __launch_failed_cb(home_model, home_activity):
activity_id = home_activity.get_activity_id()
- launcher = _launchers.get(activity_id)
+ launcher = shell.get_model().get_launcher(activity_id)
if launcher is None:
logging.error('Launcher for %s is missing', activity_id)
@@ -209,8 +207,11 @@ def __launch_completed_cb(home_model, home_activity):
def _destroy_launcher(home_activity):
activity_id = home_activity.get_activity_id()
- if activity_id in _launchers:
- _launchers[activity_id].destroy()
- del _launchers[activity_id]
- else:
- logging.error('Launcher for %s is missing', activity_id)
+ launcher = shell.get_model().get_launcher(activity_id)
+ if launcher is None:
+ if not home_activity.is_journal():
+ logging.error('Launcher was not registered for %s', activity_id)
+ return
+
+ shell.get_model().unregister_launcher(activity_id)
+ launcher.destroy()
diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py
index 2beceff..d9c1f6b 100644
--- a/src/jarabe/view/palettes.py
+++ b/src/jarabe/view/palettes.py
@@ -28,31 +28,42 @@ from sugar.graphics.menuitem import MenuItem
from sugar.graphics.icon import Icon
from sugar.graphics import style
from sugar.graphics.xocolor import XoColor
-from sugar.activity import activityfactory
-from sugar.activity.activityhandle import ActivityHandle
from jarabe.model import shell
-from jarabe.view import launcher
from jarabe.view.viewsource import setup_view_source
+from jarabe.journal import misc
+
class BasePalette(Palette):
def __init__(self, home_activity):
Palette.__init__(self)
- if home_activity.props.launching:
- home_activity.connect('notify::launching',
- self._launching_changed_cb)
+ self._notify_launch_hid = None
+
+ if home_activity.props.launch_status == shell.Activity.LAUNCHING:
+ self._notify_launch_hid = home_activity.connect( \
+ 'notify::launch-status', self.__notify_launch_status_cb)
self.set_primary_text(_('Starting...'))
+ elif home_activity.props.launch_status == shell.Activity.LAUNCH_FAILED:
+ self._on_failed_launch()
else:
self.setup_palette()
- def _launching_changed_cb(self, home_activity, pspec):
- if not home_activity.props.launching:
- self.setup_palette()
-
def setup_palette(self):
raise NotImplementedError
+ def _on_failed_launch(self):
+ self.set_primary_text(_('Activity failed to start'))
+
+ def __notify_launch_status_cb(self, home_activity, pspec):
+ home_activity.disconnect(self._notify_launch_hid)
+ self._notify_launch_hid = None
+ if home_activity.props.launch_status == shell.Activity.LAUNCH_FAILED:
+ self._on_failed_launch()
+ else:
+ self.setup_palette()
+
+
class CurrentActivityPalette(BasePalette):
def __init__(self, home_activity):
self._home_activity = home_activity
@@ -97,10 +108,6 @@ class CurrentActivityPalette(BasePalette):
self._home_activity.get_window().activate( \
gtk.get_current_event_time())
- def __active_window_changed_cb(self, screen, previous_window=None):
- setup_view_source()
- self._screen.disconnect(self._active_window_changed_sid)
-
def __stop_activate_cb(self, menu_item):
self._home_activity.get_window().close(1)
@@ -112,7 +119,7 @@ class ActivityPalette(Palette):
self._activity_info = activity_info
client = gconf.client_get_default()
- color = XoColor(client.get_string("/desktop/sugar/user/color"))
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
activity_icon = Icon(file=activity_info.get_icon(),
xo_color=color,
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
@@ -133,17 +140,8 @@ class ActivityPalette(Palette):
def __start_activate_cb(self, menu_item):
self.popdown(immediate=True)
+ misc.launch(self._activity_info)
- client = gconf.client_get_default()
- xo_color = XoColor(client.get_string('/desktop/sugar/user/color'))
-
- activity_id = activityfactory.create_activity_id()
- launcher.add_launcher(activity_id,
- self._activity_info.get_icon(),
- xo_color)
-
- handle = ActivityHandle(activity_id)
- activityfactory.create(self._activity_info, handle)
class JournalPalette(BasePalette):
def __init__(self, home_activity):
@@ -196,6 +194,7 @@ class JournalPalette(BasePalette):
self._free_space_label.props.label = _('%(free_space)d MB Free') % \
{'free_space': free_space / (1024 * 1024)}
+
class VolumePalette(Palette):
def __init__(self, mount):
Palette.__init__(self, label=mount.get_name())
@@ -245,4 +244,3 @@ class VolumePalette(Palette):
self._progress_bar.props.fraction = fraction
self._free_space_label.props.label = _('%(free_space)d MB Free') % \
{'free_space': free_space / (1024 * 1024)}
-
diff --git a/src/jarabe/view/pulsingicon.py b/src/jarabe/view/pulsingicon.py
index 43ec358..392a404 100644
--- a/src/jarabe/view/pulsingicon.py
+++ b/src/jarabe/view/pulsingicon.py
@@ -21,9 +21,11 @@ import gobject
from sugar.graphics.icon import Icon, CanvasIcon
+
_INTERVAL = 100
_STEP = math.pi / 10 # must be a fraction of pi, for clean caching
+
class Pulser(object):
def __init__(self, icon):
self._pulse_hid = None
@@ -83,6 +85,7 @@ class Pulser(object):
return True
+
class PulsingIcon(Icon):
__gtype_name__ = 'SugarPulsingIcon'
@@ -161,6 +164,7 @@ class PulsingIcon(Icon):
if self._palette is not None:
self._palette.destroy()
+
class CanvasPulsingIcon(CanvasIcon):
__gtype_name__ = 'SugarCanvasPulsingIcon'
diff --git a/src/jarabe/view/service.py b/src/jarabe/view/service.py
index fbcc961..29e46b2 100644
--- a/src/jarabe/view/service.py
+++ b/src/jarabe/view/service.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
#
# 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
@@ -20,13 +21,13 @@ import dbus
import gtk
from jarabe.model import shell
-from jarabe.model import owner
from jarabe.model import bundleregistry
-_DBUS_SERVICE = "org.laptop.Shell"
-_DBUS_SHELL_IFACE = "org.laptop.Shell"
-_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
-_DBUS_PATH = "/org/laptop/Shell"
+
+_DBUS_SERVICE = 'org.laptop.Shell'
+_DBUS_SHELL_IFACE = 'org.laptop.Shell'
+_DBUS_PATH = '/org/laptop/Shell'
+
class UIService(dbus.service.Object):
"""Provides d-bus service to script the shell's operations
@@ -54,17 +55,8 @@ class UIService(dbus.service.Object):
self._shell_model = shell.get_model()
- def start(self):
- owner_model = owner.get_model()
- owner_model.connect('nick-changed', self._owner_nick_changed_cb)
- owner_model.connect('icon-changed', self._owner_icon_changed_cb)
- owner_model.connect('color-changed', self._owner_color_changed_cb)
-
- self._shell_model.connect('active-activity-changed',
- self._cur_activity_changed_cb)
-
@dbus.service.method(_DBUS_SHELL_IFACE,
- in_signature="s", out_signature="s")
+ in_signature='s', out_signature='s')
def GetBundlePath(self, bundle_id):
bundle = bundleregistry.get_registry().get_bundle(bundle_id)
if bundle:
@@ -73,59 +65,26 @@ class UIService(dbus.service.Object):
return ''
@dbus.service.method(_DBUS_SHELL_IFACE,
- in_signature="s", out_signature="b")
+ in_signature='s', out_signature='b')
def ActivateActivity(self, activity_id):
- """Switch to the window related to this activity_id and return a boolean
- indicating if there is a real (ie. not a launcher window) activity
- already open.
+ """Switch to the window related to this activity_id and return a
+ boolean indicating if there is a real (ie. not a launcher window)
+ activity already open.
"""
activity = self._shell_model.get_activity_by_id(activity_id)
if activity is not None and activity.get_window() is not None:
activity.get_window().activate(gtk.get_current_event_time())
- return not activity.props.launching
+ return self._shell_model.get_launcher(activity_id) is None
return False
@dbus.service.method(_DBUS_SHELL_IFACE,
- in_signature="ss", out_signature="")
+ in_signature='ss', out_signature='')
def NotifyLaunch(self, bundle_id, activity_id):
shell.get_model().notify_launch(activity_id, bundle_id)
@dbus.service.method(_DBUS_SHELL_IFACE,
- in_signature="s", out_signature="")
+ in_signature='s', out_signature='')
def NotifyLaunchFailure(self, activity_id):
shell.get_model().notify_launch_failed(activity_id)
-
- @dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
- def ColorChanged(self, color):
- pass
-
- def _owner_color_changed_cb(self, new_color):
- self.ColorChanged(new_color.to_string())
-
- @dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
- def NickChanged(self, nick):
- pass
-
- def _owner_nick_changed_cb(self, new_nick):
- self.NickChanged(new_nick)
-
- @dbus.service.signal(_DBUS_OWNER_IFACE, signature="ay")
- def IconChanged(self, icon_data):
- pass
-
- def _owner_icon_changed_cb(self, new_icon):
- self.IconChanged(dbus.ByteArray(new_icon))
-
- @dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
- def CurrentActivityChanged(self, activity_id):
- pass
-
- def _cur_activity_changed_cb(self, shell_model, new_activity):
- new_id = ""
- if new_activity:
- new_id = new_activity.get_activity_id()
- if new_id:
- self.CurrentActivityChanged(new_id)
-
diff --git a/src/jarabe/view/tabbinghandler.py b/src/jarabe/view/tabbinghandler.py
index f52bda3..0889792 100644
--- a/src/jarabe/view/tabbinghandler.py
+++ b/src/jarabe/view/tabbinghandler.py
@@ -21,8 +21,10 @@ import gtk
from jarabe.model import shell
+
_RAISE_DELAY = 250
+
class TabbingHandler(object):
def __init__(self, frame, modifier):
self._frame = frame
@@ -145,4 +147,3 @@ class TabbingHandler(object):
def is_tabbing(self):
return self._tabbing
-
diff --git a/src/jarabe/view/viewsource.py b/src/jarabe/view/viewsource.py
index 9905713..a1c0be3 100644
--- a/src/jarabe/view/viewsource.py
+++ b/src/jarabe/view/viewsource.py
@@ -17,7 +17,6 @@
import os
import logging
-import traceback
from gettext import gettext as _
import gobject
@@ -37,11 +36,13 @@ from sugar.bundle.activitybundle import ActivityBundle
from sugar.datastore import datastore
from sugar import mime
+
_SOURCE_FONT = pango.FontDescription('Monospace %d' % style.FONT_SIZE)
_logger = logging.getLogger('ViewSource')
map_activity_to_window = {}
+
def setup_view_source(activity):
service = activity.get_service()
if service is not None:
@@ -52,9 +53,9 @@ def setup_view_source(activity):
expected_exceptions = ['org.freedesktop.DBus.Error.UnknownMethod',
'org.freedesktop.DBus.Python.NotImplementedError']
if e.get_dbus_name() not in expected_exceptions:
- logging.error(traceback.format_exc())
+ logging.exception('Exception occured in HandleViewSource():')
except Exception:
- logging.error(traceback.format_exc())
+ logging.exception('Exception occured in HandleViewSource():')
window_xid = activity.get_xid()
if window_xid is None:
@@ -76,9 +77,9 @@ def setup_view_source(activity):
expected_exceptions = ['org.freedesktop.DBus.Error.UnknownMethod',
'org.freedesktop.DBus.Python.NotImplementedError']
if e.get_dbus_name() not in expected_exceptions:
- logging.error(traceback.format_exc())
+ logging.exception('Exception occured in GetDocumentPath():')
except Exception:
- logging.error(traceback.format_exc())
+ logging.exception('Exception occured in GetDocumentPath():')
if bundle_path is None and document_path is None:
_logger.debug('Activity without bundle_path nor document_path')
@@ -89,6 +90,7 @@ def setup_view_source(activity):
map_activity_to_window[window_xid] = view_source
view_source.show()
+
class ViewSource(gtk.Window):
__gtype_name__ = 'SugarViewSource'
@@ -129,10 +131,10 @@ class ViewSource(gtk.Window):
file_name = ''
activity_bundle = ActivityBundle(bundle_path)
- command = activity_bundle.get_command()
+ command = activity_bundle.get_command()
if len(command.split(' ')) > 1:
name = command.split(' ')[1].split('.')[0]
- file_name = name + '.py'
+ file_name = name + '.py'
path = os.path.join(activity_bundle.get_path(), file_name)
self._selected_file = path
@@ -195,6 +197,7 @@ class ViewSource(gtk.Window):
else:
self._source_display.file_path = None
+
class DocumentButton(RadioToolButton):
__gtype_name__ = 'SugarDocumentButton'
@@ -244,22 +247,20 @@ class DocumentButton(RadioToolButton):
error_handler=self.__internal_save_error_cb)
def __internal_save_cb(self):
- logging.debug("Saved Source object to datastore.")
+ logging.debug('Saved Source object to datastore.')
self._jobject.destroy()
def __internal_save_error_cb(self, err):
logging.debug('Error saving Source object to datastore: %s', err)
self._jobject.destroy()
+
class Toolbar(gtk.Toolbar):
__gtype_name__ = 'SugarViewSourceToolbar'
__gsignals__ = {
- 'stop-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([])),
- 'source-selected': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
+ 'stop-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ 'source-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([str])),
}
@@ -309,7 +310,6 @@ class Toolbar(gtk.Toolbar):
stop = ToolButton(icon_name='dialog-cancel')
stop.set_tooltip(_('Close'))
stop.connect('clicked', self.__stop_clicked_cb)
- stop.show()
self.insert(stop, -1)
stop.show()
@@ -340,6 +340,7 @@ class Toolbar(gtk.Toolbar):
if button.props.active:
self.emit('source-selected', path)
+
class FileViewer(gtk.ScrolledWindow):
__gtype_name__ = 'SugarFileViewer'
@@ -406,6 +407,7 @@ class FileViewer(gtk.ScrolledWindow):
file_path = model.get_value(tree_iter, 1)
self.emit('file-selected', file_path)
+
class SourceDisplay(gtk.ScrolledWindow):
__gtype_name__ = 'SugarSourceDisplay'
@@ -462,4 +464,3 @@ class SourceDisplay(gtk.ScrolledWindow):
return self._file_path
file_path = property(_get_file_path, _set_file_path)
-