Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Silbe <silbe@activitycentral.com>2011-03-12 17:44:09 (GMT)
committer Sascha Silbe <silbe@activitycentral.com>2011-03-12 17:44:09 (GMT)
commitdb56ac4562a9ab10308de0998b56262956f51b64 (patch)
treee9ea3923a6203538480822593354146c01d1341b
parent8cd75e783fec952497eda68ae06e00323a4c179b (diff)
parentff47b24c5aa032f830c6f3dc3ae32dfed3c72229 (diff)
Merge commit 'refs/top-bases/t/bug-1755' into t/bug-1755t/bug-1755
* commit 'refs/top-bases/t/bug-1755': (261 commits) Fix up style issues introduced by commit 3f8a1e1 Don't set default timezone Restore setting a language with the command line OLPC #10681 battery frame device: replace HAL with UPower use ConsoleKit instead of HAL for shutdown/reboot fix recognition of JEBs outside of data store Adjust the year in the licence visible in the control panel Handle activities that cycle through windows dlo#10695 Speaker frame device: pop up palette on left-click instead of toggling mute recognise translations using pgettext Remove last few pieces of buddy-icon.jpg handling fix whitespace error introduced by 4b6a534 (revert of 4a3416b) Commit from Sugar Labs: Translation System by user RafaelOrtiz.: 374 of 374 messages translated (0 fuzzy). Intro: Fall back to user name if GECOS real name field is empty Sugar Ad-hoc icons show in search results when connect/disconnect to AP OLPC #10412 Show busy cursor during session shutdown Show free space for the Journal in the Journal volumes toolbar (SL#2318) Fix incorrect logging level for warning about missing GSM configuration simplify extension loading exception error Revert "Add cpu and memory resource indicator to frame" as agreed on with erikos. Scheduling for inclusion in 0.94 after potential artwork changes based on feedback from the Design Team. ... Conflicts: src/jarabe/util/emulator.py
-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)
-