Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Schampijer <simon@schampijer.de>2011-02-10 20:05:42 (GMT)
committer Simon Schampijer <simon@schampijer.de>2011-02-10 20:05:42 (GMT)
commit37e027e391ab0c561f5d87fbb60871708c9f58af (patch)
tree01777222d8fbaba61c78674877fd903b8a9f2fde
parent72fafe0f52e5e9387fb0668c5e726151da51722c (diff)
Clean up the the mess introdueced by pootle that had an outdated repo
-rw-r--r--bin/sugar-session11
-rw-r--r--configure.ac5
-rw-r--r--data/activities.defaults2
-rw-r--r--data/icons/Makefile.am1
-rw-r--r--data/icons/module-modemconfiguration.svg11
-rw-r--r--data/sugar.schemas.in95
-rw-r--r--extensions/cpsection/Makefile.am3
-rw-r--r--extensions/cpsection/aboutcomputer/view.py2
-rw-r--r--extensions/cpsection/modemconfiguration/Makefile.am6
-rw-r--r--extensions/cpsection/modemconfiguration/__init__.py22
-rw-r--r--extensions/cpsection/modemconfiguration/model.py70
-rw-r--r--extensions/cpsection/modemconfiguration/view.py250
-rw-r--r--extensions/cpsection/network/model.py115
-rw-r--r--extensions/cpsection/network/view.py8
-rw-r--r--extensions/cpsection/power/model.py31
-rw-r--r--extensions/cpsection/power/view.py58
-rw-r--r--extensions/deviceicon/network.py628
-rw-r--r--po/POTFILES.in3
-rw-r--r--po/es.po847
-rw-r--r--po/fr.po545
-rw-r--r--po/pt_BR.po915
-rw-r--r--src/jarabe/desktop/activitieslist.py126
-rw-r--r--src/jarabe/desktop/favoritesview.py26
-rw-r--r--src/jarabe/desktop/homebox.py43
-rw-r--r--src/jarabe/desktop/homewindow.py4
-rw-r--r--src/jarabe/desktop/meshbox.py592
-rw-r--r--src/jarabe/desktop/schoolserver.py19
-rw-r--r--src/jarabe/journal/journalactivity.py23
-rw-r--r--src/jarabe/journal/journalentrybundle.py2
-rw-r--r--src/jarabe/journal/journaltoolbox.py22
-rw-r--r--src/jarabe/journal/listview.py39
-rw-r--r--src/jarabe/journal/misc.py6
-rw-r--r--src/jarabe/journal/model.py321
-rw-r--r--src/jarabe/journal/palettes.py42
-rw-r--r--src/jarabe/journal/volumestoolbar.py153
-rw-r--r--src/jarabe/model/Makefile.am2
-rw-r--r--src/jarabe/model/adhoc.py280
-rw-r--r--src/jarabe/model/bundleregistry.py34
-rw-r--r--src/jarabe/model/network.py287
-rw-r--r--src/jarabe/model/olpcmesh.py214
-rw-r--r--src/jarabe/view/buddymenu.py13
-rw-r--r--src/jarabe/view/keyhandler.py4
-rw-r--r--src/jarabe/view/launcher.py17
-rw-r--r--src/jarabe/view/palettes.py80
44 files changed, 4553 insertions, 1424 deletions
diff --git a/bin/sugar-session b/bin/sugar-session
index f5d4c6c..57a361e 100644
--- a/bin/sugar-session
+++ b/bin/sugar-session
@@ -138,7 +138,16 @@ def set_fonts():
settings.set_property("gtk-font-name", "%s %f" % (face, size))
def main():
- cleanup_logs()
+ try:
+ # 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()
+ except OSError, e:
+ # logs cleanup is not critical; it should not prevent sugar from
+ # starting if (for example) the disk is full or read-only.
+ print 'logs cleanup failed: %s' % e
logger.start('shell')
set_fonts()
diff --git a/configure.ac b/configure.ac
index 90fa633..e188fa4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT([Sugar],[0.84.11],[],[sugar])
+AC_INIT([Sugar],[0.84.31],[],[sugar])
AC_PREREQ([2.59])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([configure.ac])
-SUCROSE_VERSION="0.84.11"
+SUCROSE_VERSION="0.84.31"
AC_SUBST(SUCROSE_VERSION)
AM_INIT_AUTOMAKE([1.9 foreign dist-bzip2 no-dist-gzip])
@@ -57,6 +57,7 @@ extensions/cpsection/aboutcomputer/Makefile
extensions/cpsection/datetime/Makefile
extensions/cpsection/frame/Makefile
extensions/cpsection/language/Makefile
+extensions/cpsection/modemconfiguration/Makefile
extensions/cpsection/network/Makefile
extensions/cpsection/power/Makefile
extensions/deviceicon/Makefile
diff --git a/data/activities.defaults b/data/activities.defaults
index b726355..c294b99 100644
--- a/data/activities.defaults
+++ b/data/activities.defaults
@@ -10,7 +10,6 @@ org.laptop.Analyze
org.laptop.Calculate
org.laptop.Chat
org.laptop.HelpActivity
-org.laptop.Log
org.laptop.MeasureActivity
org.laptop.Memorize
org.laptop.Oficina
@@ -20,7 +19,6 @@ org.laptop.TamTamEdit
org.laptop.TamTamJam
org.laptop.TamTamMini
org.laptop.TamTamSynthLab
-org.laptop.Terminal
org.laptop.TurtleArtActivity
org.laptop.WebActivity
org.laptop.WikipediaActivityEN
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
index e1f8fa7..f719292 100644
--- a/data/icons/Makefile.am
+++ b/data/icons/Makefile.am
@@ -6,6 +6,7 @@ sugar_DATA = \
module-date_and_time.svg \
module-frame.svg \
module-language.svg \
+ module-modemconfiguration.svg \
module-network.svg \
module-power.svg
diff --git a/data/icons/module-modemconfiguration.svg b/data/icons/module-modemconfiguration.svg
new file mode 100644
index 0000000..02ccc81
--- /dev/null
+++ b/data/icons/module-modemconfiguration.svg
@@ -0,0 +1,11 @@
+<?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 "#ffffff">
+ <!ENTITY fill_color "none">
+]><svg enable-background="new 0 0 56.167 55" height="55px" version="1.1" viewBox="0 0 56.167 55" width="56.167px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="network-gsm">
+ <g>
+ <path d="M13.759,11.28 v28.937h0.002c0,0.004-0.002,0.008-0.002,0.014c0,7.05,5.715,12.763,12.764,12.763c7.047,0,12.762-5.713,12.762-12.763v-0.014 V11.28H13.759z" fill="&fill_color;" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
+ <rect fill="&stroke_color;" height="9.702" width="14.063" x="19.43" y="16.902"/>
+
+ <line fill="&fill_color;" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="39.286" x2="39.286" y1="11.28" y2="1.993"/>
+ </g>
+</g></svg>
diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in
index b58f2a8..cf234a1 100644
--- a/data/sugar.schemas.in
+++ b/data/sugar.schemas.in
@@ -204,5 +204,100 @@
</locale>
</schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/username</key>
+ <applyto>/desktop/sugar/network/gsm/username</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>GSM network username</short>
+ <long>GSM network username configuration</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/password</key>
+ <applyto>/desktop/sugar/network/gsm/password</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>GSM network password</short>
+ <long>GSM network password configuration</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/number</key>
+ <applyto>/desktop/sugar/network/gsm/number</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default>*99#</default>
+ <locale name="C">
+ <short>GSM network number</short>
+ <long>GSM network telephone number configuration</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/apn</key>
+ <applyto>/desktop/sugar/network/gsm/apn</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>GSM network APN</short>
+ <long>GSM network access point name configuration</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/pin</key>
+ <applyto>/desktop/sugar/network/gsm/pin</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>GSM network PIN</short>
+ <long>GSM network personal identification number configuration</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/sugar/network/gsm/puk</key>
+ <applyto>/desktop/sugar/network/gsm/puk</applyto>
+ <owner>sugar</owner>
+ <type>string</type>
+ <default></default>
+ <locale name="C">
+ <short>GSM network PUK</short>
+ <long>GSM network personal unlock key configuration</long>
+ </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/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am
index 73e5164..90d36ea 100644
--- a/extensions/cpsection/Makefile.am
+++ b/extensions/cpsection/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS = aboutme aboutcomputer datetime frame language network power
+SUBDIRS = aboutme aboutcomputer datetime frame language \
+ modemconfiguration network power
sugardir = $(pkgdatadir)/extensions/cpsection
sugar_PYTHON = __init__.py
diff --git a/extensions/cpsection/aboutcomputer/view.py b/extensions/cpsection/aboutcomputer/view.py
index dd4f8f3..4b638ff 100644
--- a/extensions/cpsection/aboutcomputer/view.py
+++ b/extensions/cpsection/aboutcomputer/view.py
@@ -174,7 +174,7 @@ 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 "
+ label_copyright = gtk.Label("© 2006-2010 One Laptop per Child "
"Association Inc; Red Hat Inc; Collabora Ltd; "
"and Contributors.")
label_copyright.set_alignment(0, 0)
diff --git a/extensions/cpsection/modemconfiguration/Makefile.am b/extensions/cpsection/modemconfiguration/Makefile.am
new file mode 100644
index 0000000..3e2613e
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/Makefile.am
@@ -0,0 +1,6 @@
+sugardir = $(pkgdatadir)/extensions/cpsection/modemconfiguration
+
+sugar_PYTHON = \
+ __init__.py \
+ model.py \
+ view.py
diff --git a/extensions/cpsection/modemconfiguration/__init__.py b/extensions/cpsection/modemconfiguration/__init__.py
new file mode 100644
index 0000000..8a219dc
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/__init__.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2009 Paraguay Educa, Martin Abente
+#
+# 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 US
+
+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
new file mode 100644
index 0000000..2545ce1
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/model.py
@@ -0,0 +1,70 @@
+# Copyright (C) 2009 Paraguay Educa, Martin Abente
+#
+# 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 US
+
+import gconf
+
+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
new file mode 100644
index 0000000..b236f3f
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/view.py
@@ -0,0 +1,250 @@
+# Copyright (C) 2009 Paraguay Educa, Martin Abente
+#
+# 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 US
+
+import os
+import logging
+from gettext import gettext as _
+
+import gtk
+import gobject
+
+from sugar.graphics import style
+
+from jarabe.controlpanel.sectionview import SectionView
+
+APPLY_TIMEOUT = 1000
+
+class EntryWithLabel(gtk.HBox):
+ __gtype_name__ = "SugarEntryWithLabel"
+
+ def __init__(self, label_text):
+ gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
+
+ self._timeout_sid = 0
+ self._changed_handler = None
+ self._is_valid = True
+
+ self.label = gtk.Label(label_text)
+ self.label.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ self.label.set_alignment(1, 0.5)
+ self.pack_start(self.label, expand=False)
+ self.label.show()
+
+ self._entry = gtk.Entry(25)
+ self._entry.connect('changed', self.__entry_changed_cb)
+ self._entry.set_width_chars(25)
+ self.pack_start(self._entry, expand=False)
+ self._entry.show()
+
+ 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_cb)
+
+ def __timeout_cb(self):
+ self._timeout_sid = 0
+
+ if self._entry.get_text() == self.get_value():
+ return False
+
+ try:
+ self.set_value(self._entry.get_text())
+ except ValueError:
+ self._is_valid = False
+ else:
+ self._is_valid = True
+
+ self.notify('is-valid')
+
+ return False
+
+ def set_text_from_model(self):
+ self._entry.set_text(self.get_value())
+
+ def get_value(self):
+ raise NotImplementedError
+
+ def set_value(self):
+ 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:'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_username()
+
+ def set_value(self, username):
+ self._model.set_username(username)
+
+class PasswordEntry(EntryWithLabel):
+ def __init__(self, model):
+ EntryWithLabel.__init__(self, _('Password:'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_password()
+
+ def set_value(self, password):
+ self._model.set_password(password)
+
+class NumberEntry(EntryWithLabel):
+ def __init__(self, model):
+ EntryWithLabel.__init__(self, _('Number:'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_number()
+
+ def set_value(self, number):
+ self._model.set_number(number)
+
+class ApnEntry(EntryWithLabel):
+ def __init__(self, model):
+ EntryWithLabel.__init__(self, _('Access Point Name (APN):'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_apn()
+
+ def set_value(self, apn):
+ self._model.set_apn(apn)
+
+class PinEntry(EntryWithLabel):
+ def __init__(self, model):
+ EntryWithLabel.__init__(self, _('Personal Identity Number (PIN):'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_pin()
+
+ def set_value(self, pin):
+ self._model.set_pin(pin)
+
+class PukEntry(EntryWithLabel):
+ def __init__(self, model):
+ EntryWithLabel.__init__(self, _('Personal Unblocking Key (PUK):'))
+ self._model = model
+
+ def get_value(self):
+ return self._model.get_puk()
+
+ def set_value(self, puk):
+ self._model.set_puk(puk)
+
+
+class ModemConfiguration(SectionView):
+ def __init__(self, model, alerts=None):
+ SectionView.__init__(self)
+
+ self._model = model
+ self.restart_alerts = alerts
+
+ self.set_border_width(style.DEFAULT_SPACING)
+ 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.")
+ self._text = gtk.Label(explanation)
+ self._text.set_width_chars(100)
+ self._text.set_line_wrap(True)
+ self._text.set_alignment(0, 0)
+ self.pack_start(self._text, False)
+ self._text.show()
+
+ self._username_entry = UsernameEntry(model)
+ self._username_entry.connect('notify::is-valid',
+ self.__notify_is_valid_cb)
+ self._group.add_widget(self._username_entry.label)
+ self.pack_start(self._username_entry, expand=False)
+ self._username_entry.show()
+
+ self._password_entry = PasswordEntry(model)
+ self._password_entry.connect('notify::is-valid',
+ self.__notify_is_valid_cb)
+ self._group.add_widget(self._password_entry.label)
+ self.pack_start(self._password_entry, expand=False)
+ self._password_entry.show()
+
+ self._number_entry = NumberEntry(model)
+ self._number_entry.connect('notify::is-valid',
+ self.__notify_is_valid_cb)
+ self._group.add_widget(self._number_entry.label)
+ self.pack_start(self._number_entry, expand=False)
+ self._number_entry.show()
+
+ self._apn_entry = ApnEntry(model)
+ self._apn_entry.connect('notify::is-valid',
+ self.__notify_is_valid_cb)
+ self._group.add_widget(self._apn_entry.label)
+ self.pack_start(self._apn_entry, expand=False)
+ self._apn_entry.show()
+
+ self._pin_entry = PinEntry(model)
+ self._pin_entry.connect('notify::is-valid',
+ self.__notify_is_valid_cb)
+ 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._puk_entry.show()
+
+ self.setup()
+
+ def setup(self):
+ self._username_entry.set_text_from_model()
+ self._password_entry.set_text_from_model()
+ self._number_entry.set_text_from_model()
+ self._apn_entry.set_text_from_model()
+ self._pin_entry.set_text_from_model()
+ self._puk_entry.set_text_from_model()
+
+ self.needs_restart = False
+
+ def undo(self):
+ self._model.undo()
+
+ def _validate(self):
+ if self._username_entry.is_valid and \
+ self._password_entry.is_valid and \
+ self._number_entry.is_valid and \
+ self._apn_entry.is_valid and \
+ self._pin_entry.is_valid and \
+ self._puk_entry.is_valid:
+ self.props.is_valid = True
+ else:
+ self.props.is_valid = False
+
+ def __notify_is_valid_cb(self, entry, pspec):
+ if entry.is_valid:
+ self.needs_restart = True
+ self._validate()
+
diff --git a/extensions/cpsection/network/model.py b/extensions/cpsection/network/model.py
index 87db6d9..945254a 100644
--- a/extensions/cpsection/network/model.py
+++ b/extensions/cpsection/network/model.py
@@ -15,9 +15,14 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
+import logging
import dbus
from gettext import gettext as _
+from jarabe.model import network
import gconf
+import os
+
+_logger = logging.getLogger('ControlPanel - Network')
_NM_SERVICE = 'org.freedesktop.NetworkManager'
_NM_PATH = '/org/freedesktop/NetworkManager'
@@ -62,50 +67,107 @@ def _restart_jabber():
_PS_INTERFACE)
except dbus.DBusException:
raise ReadError('%s service not available' % _PS_SERVICE)
- ps.RestartServerConnection()
+ ps.RetryConnections()
-def get_radio():
+def print_radio():
+ print ('off', 'on')[get_radio()]
+
+def get_radio_nm():
+ """ Get the state of NetworkManager
+ The user can enable/disable wireless and/or networking
+ return true only if wireless and network are enabled
+ """
bus = dbus.SystemBus()
try:
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)
- state = nm_props.Get(_NM_IFACE, 'WirelessEnabled')
- if state in (0, 1):
- return state
+ state = nm_props.Get(_NM_IFACE, 'NetworkingEnabled')
+ wireless_state = nm_props.Get(_NM_IFACE, 'WirelessEnabled')
+ _logger.debug('nm state: %s' % state)
+ _logger.debug('nm wireless_state: %s' % wireless_state)
+ if state in (0, 1) and wireless_state in (0, 1):
+ return (state == 1) and (wireless_state == 1)
else:
raise ReadError(_('State is unknown.'))
-def print_radio():
- print ('off', 'on')[get_radio()]
-
-def set_radio(state):
+def set_radio_nm(state):
+ """Enable/disable NetworkManager
+ state : 'on/off'
+ """
+ if not state in ('on', 1, 'off', 0):
+ raise ValueError(_("Error in specified radio argument use on/off."))
+
+ bus = dbus.SystemBus()
+ try:
+ obj = bus.get_object(_NM_SERVICE, _NM_PATH)
+ nm_props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ nm = dbus.Interface(obj, _NM_IFACE)
+ except dbus.DBusException:
+ raise ReadError('%s service not available' % _NM_SERVICE)
+
+ if state == 'on' or state == 1:
+ new_state = True
+ else:
+ new_state = False
+
+ prev_state = nm_props.Get(_NM_IFACE, 'NetworkingEnabled')
+ if prev_state != new_state:
+ nm.Enable(new_state)
+ nm_props.Set(_NM_IFACE, 'WirelessEnabled', new_state)
+
+ return 0
+
+def get_radio_rfkill():
+ pipe_stdout = os.popen('/sbin/rfkill list wifi', 'r')
+ try:
+ output = pipe_stdout.read()
+ _logger.debug('rfkill said: %s' % output)
+ blocked = " blocked: yes" in output
+ # if not soft- or hard-blocked, radio is on
+ return not blocked
+
+ finally:
+ pipe_stdout.close()
+
+RFKILL_STATE_FILE = '/home/olpc/.rfkill_block_wifi'
+
+def set_radio_rfkill(state):
"""Turn Radio 'on' or 'off'
state : 'on/off'
- """
+ """
if state == 'on' or state == 1:
- bus = dbus.SystemBus()
+ os.spawnl(os.P_WAIT, "/sbin/rfkill", "rfkill", "unblock", "wifi")
+ # remove the flag file (checked at boot)
try:
- obj = bus.get_object(_NM_SERVICE, _NM_PATH)
- nm_props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
- except dbus.DBusException:
- raise ReadError('%s service not available' % _NM_SERVICE)
- nm_props.Set(_NM_IFACE, 'WirelessEnabled', True)
+ os.unlink(RFKILL_STATE_FILE)
+ except:
+ _logger.debug('File %s was not unlinked' % RFKILL_STATE_FILE)
elif state == 'off' or state == 0:
- bus = dbus.SystemBus()
+ os.spawnl(os.P_WAIT, "/sbin/rfkill", "rfkill", "block", "wifi")
+ # touch the flag file
try:
- obj = bus.get_object(_NM_SERVICE, _NM_PATH)
- nm_props = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
- except dbus.DBusException:
- raise ReadError('%s service not available' % _NM_SERVICE)
- nm_props.Set(_NM_IFACE, 'WirelessEnabled', False)
+ fd = open(RFKILL_STATE_FILE, 'w')
+ except IOError:
+ _logger.debug('File %s is not writeable' % RFKILL_STATE_FILE)
+ else:
+ fd.close()
else:
raise ValueError(_("Error in specified radio argument use on/off."))
return 0
+def get_radio():
+ """Get status from rfkill and nm"""
+ return get_radio_rfkill() and get_radio_nm()
+
+def set_radio(state):
+ """ Set status to dot-file and rfkill, and nm"""
+ set_radio_rfkill(state)
+ set_radio_nm(state)
+
def clear_registration():
"""Clear the registration with the schoolserver
"""
@@ -116,13 +178,16 @@ def clear_registration():
def clear_networks():
"""Clear saved passwords and network configurations.
"""
- pass
+ network.clear_connections()
+
+def count_networks():
+ return network.count_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()
diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
index ef28f00..c387667 100644
--- a/extensions/cpsection/network/view.py
+++ b/extensions/cpsection/network/view.py
@@ -101,6 +101,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 self._model.count_networks() == 0:
+ 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()
@@ -208,7 +210,9 @@ 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.count_networks() != 0:
+ self._clear_history_button.set_sensitive(True)
self._validate()
return False
@@ -239,3 +243,5 @@ class Network(SectionView):
def __network_configuration_reset_cb(self, widget):
self._model.clear_networks()
+ if self._model.count_networks() == 0:
+ self._clear_history_button.set_sensitive(False)
diff --git a/extensions/cpsection/power/model.py b/extensions/cpsection/power/model.py
index 33ec905..c580439 100644
--- a/extensions/cpsection/power/model.py
+++ b/extensions/cpsection/power/model.py
@@ -66,7 +66,10 @@ def set_automatic_pm(enabled):
else:
fd.close()
else:
- os.unlink(POWERD_INHIBIT_FLAG)
+ try:
+ os.unlink(POWERD_INHIBIT_FLAG)
+ except:
+ _logger.debug('File %s was not unlinked' % POWERD_INHIBIT_FLAG)
return 0
# ohmd
@@ -87,29 +90,3 @@ def set_automatic_pm(enabled):
client.set_bool('/desktop/sugar/power/automatic', enabled)
return 0
-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)
- enabled = True
- elif enabled == 'off' or enabled == 0:
- keystore.SetKey("suspend.extreme_pm", 0)
- enabled = False
- else:
- raise ValueError(_("Error in extreme pm argument, use on/off."))
-
- client = gconf.client_get_default()
- client.set_bool('/desktop/sugar/power/extreme', enabled)
- return 0
diff --git a/extensions/cpsection/power/view.py b/extensions/cpsection/power/view.py
index 8f1ed56..d6e2b8c 100644
--- a/extensions/cpsection/power/view.py
+++ b/extensions/cpsection/power/view.py
@@ -29,8 +29,6 @@ class Power(SectionView):
self._model = model
self.restart_alerts = alerts
self._automatic_pm_valid = True
- self._extreme_pm_valid = True
- self._extreme_pm_change_handler = None
self._automatic_pm_change_handler = None
self.set_border_width(style.DEFAULT_SPACING * 2)
@@ -38,7 +36,6 @@ class Power(SectionView):
group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
self._automatic_pm_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
- self._extreme_pm_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
separator_pm = gtk.HSeparator()
self.pack_start(separator_pm, expand=False)
@@ -80,35 +77,6 @@ class Power(SectionView):
self._automatic_pm_alert.props.msg = self.restart_msg
self._automatic_pm_alert.show()
- box_extreme_pm = gtk.HBox(spacing=style.DEFAULT_SPACING)
- label_extreme_pm = gtk.Label(
- _('Extreme power management (disables' \
- 'wireless radio, increases battery life)'))
- label_extreme_pm.set_alignment(0, 0.5)
- self._extreme_button = gtk.CheckButton()
- self._extreme_button.set_alignment(0, 0)
- box_extreme_pm.pack_start(self._extreme_button, expand=False)
- self._extreme_button.show()
- box_extreme_pm.pack_start(label_extreme_pm, expand=False)
- group.add_widget(label_extreme_pm)
- label_extreme_pm.show()
- box_pm.pack_start(box_extreme_pm, expand=False)
- box_extreme_pm.show()
-
- self._extreme_pm_alert = InlineAlert()
- label_extreme_pm_error = gtk.Label()
- group.add_widget(label_extreme_pm_error)
- self._extreme_pm_alert_box.pack_start(label_extreme_pm_error,
- expand=False)
- label_extreme_pm_error.show()
- self._extreme_pm_alert_box.pack_start(self._extreme_pm_alert,
- expand=False)
- box_pm.pack_end(self._extreme_pm_alert_box, expand=False)
- self._extreme_pm_alert_box.show()
- if 'extreme_pm' in self.restart_alerts:
- self._extreme_pm_alert.props.msg = self.restart_msg
- self._extreme_pm_alert.show()
-
self.pack_start(box_pm, expand=False)
box_pm.show()
@@ -117,38 +85,26 @@ class Power(SectionView):
def setup(self):
try:
automatic_state = self._model.get_automatic_pm()
- extreme_state = self._model.get_extreme_pm()
except Exception, detail:
self._automatic_pm_alert.props.msg = detail
self._automatic_pm_alert.show()
- self._extreme_pm_alert.props.msg = detail
- self._extreme_pm_alert.show()
else:
self._automatic_button.set_active(automatic_state)
- self._extreme_button.set_active(extreme_state)
- self._extreme_pm_valid = True
self._automatic_pm_valid = True
self.needs_restart = False
self._automatic_pm_change_handler = self._automatic_button.connect( \
'toggled', self.__automatic_pm_toggled_cb)
- self._extreme_pm_change_handler = self._extreme_button.connect( \
- 'toggled', self.__extreme_pm_toggled_cb)
def undo(self):
self._automatic_button.disconnect(self._automatic_pm_change_handler)
- self._extreme_button.disconnect(self._extreme_pm_change_handler)
self._model.undo()
- self._extreme_pm_alert.hide()
self._automatic_pm_alert.hide()
def _validate(self):
- if self._extreme_pm_valid and self._automatic_pm_valid:
- self.props.is_valid = True
- else:
- self.props.is_valid = False
+ self.props.is_valid = self._automatic_pm_valid
def __automatic_pm_toggled_cb(self, widget, data=None):
state = widget.get_active()
@@ -163,15 +119,3 @@ class Power(SectionView):
self._validate()
return False
- def __extreme_pm_toggled_cb(self, widget, data=None):
- state = widget.get_active()
- try:
- self._model.set_extreme_pm(state)
- except Exception, detail:
- print detail
- self._extreme_pm_alert.props.msg = detail
- else:
- self._extreme_pm_valid = True
-
- self._validate()
- return False
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
index dc14f9c..12ea9f6 100644
--- a/extensions/deviceicon/network.py
+++ b/extensions/deviceicon/network.py
@@ -1,6 +1,7 @@
#
-# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2008-2010 One Laptop Per Child
# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
+# Copyright (C) 2009 Paraguay Educa, Martin Abente
#
# 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
@@ -22,7 +23,8 @@ import sha
import socket
import struct
import re
-
+import datetime
+import time
import gtk
import gobject
import gconf
@@ -35,6 +37,7 @@ from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.tray import TrayIcon
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
@@ -50,35 +53,23 @@ _NM_PATH = '/org/freedesktop/NetworkManager'
_NM_DEVICE_IFACE = 'org.freedesktop.NetworkManager.Device'
_NM_WIRED_IFACE = 'org.freedesktop.NetworkManager.Device.Wired'
_NM_WIRELESS_IFACE = 'org.freedesktop.NetworkManager.Device.Wireless'
+_NM_OLPC_MESH_IFACE = 'org.freedesktop.NetworkManager.Device.OlpcMesh'
+_NM_SERIAL_IFACE = 'org.freedesktop.NetworkManager.Device.Serial'
_NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
_NM_ACTIVE_CONN_IFACE = 'org.freedesktop.NetworkManager.Connection.Active'
-_NM_DEVICE_STATE_UNKNOWN = 0
-_NM_DEVICE_STATE_UNMANAGED = 1
-_NM_DEVICE_STATE_UNAVAILABLE = 2
-_NM_DEVICE_STATE_DISCONNECTED = 3
-_NM_DEVICE_STATE_PREPARE = 4
-_NM_DEVICE_STATE_CONFIG = 5
-_NM_DEVICE_STATE_NEED_AUTH = 6
-_NM_DEVICE_STATE_IP_CONFIG = 7
-_NM_DEVICE_STATE_ACTIVATED = 8
-_NM_DEVICE_STATE_FAILED = 9
-
-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]
+_GSM_STATE_NOT_READY = 0
+_GSM_STATE_DISCONNECTED = 1
+_GSM_STATE_CONNECTING = 2
+_GSM_STATE_CONNECTED = 3
+_GSM_STATE_NEED_AUTH = 4
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, ([])),
+ gobject.TYPE_NONE, ([]))
}
def __init__(self, primary_text):
@@ -112,21 +103,23 @@ class WirelessPalette(Palette):
self._disconnect_item.connect('activate', self.__disconnect_activate_cb)
self.menu.append(self._disconnect_item)
- 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...')
- def set_connected(self, frequency, iaddress):
+ def _set_connected(self, iaddress):
self.set_content(self._info)
self.props.secondary_text = _('Connected')
- self._set_channel(frequency)
self._set_ip_address(iaddress)
self._disconnect_item.show()
+ def set_connected_with_frequency(self, frequency, iaddress):
+ self._set_connected(iaddress)
+ self._set_frequency(frequency)
+
+ def set_connected_with_channel(self, channel, iaddress):
+ self._set_connected(iaddress)
+ self._set_channel(channel)
+
def set_disconnected(self):
self.props.primary_text = ''
self.props.secondary_text = ''
@@ -136,14 +129,14 @@ 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_channel(self, frequency):
+ def _set_frequency(self, frequency):
try:
- channel = frequency_to_channel(frequency)
+ channel = network.frequency_to_channel(frequency)
except KeyError:
channel = 0
+ self._set_channel(channel)
+
+ def _set_channel(self, channel):
self._channel_label.set_text("%s: %d" % (_("Channel"), channel))
def _set_ip_address(self, ip_address):
@@ -202,10 +195,105 @@ 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, ([])),
+ }
+
+ def __init__(self):
+ Palette.__init__(self, label=_('Wireless modem'))
+
+ self._current_state = None
+
+ self._toggle_state_item = gtk.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_gsm_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()
+ 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()
+ label_alignment.show()
+
+ self.info_box.show()
+ self.set_content(self.info_box)
+
+ def _add_widget_with_padding(self, child, xalign=0, yalign=0.5):
+ alignment = gtk.Alignment(xalign=xalign, yalign=yalign,
+ xscale=1, yscale=0.33)
+ alignment.set_padding(style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING)
+ alignment.add(child)
+ return alignment
+
+ def set_gsm_state(self, state):
+ self._current_state = state
+ self._update_label_and_text()
+
+ def _update_label_and_text(self):
+ 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'))
+ self.props.secondary_text = _('Disconnected')
+
+ elif self._current_state == _GSM_STATE_CONNECTING:
+ self._toggle_state_item.get_child().set_label(_('Cancel'))
+ self.props.secondary_text = _('Connecting...')
+
+ elif self._current_state == _GSM_STATE_CONNECTED:
+ 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')
+
+ else:
+ raise ValueError('Invalid GSM state while updating label and ' \
+ 'text, %s' % str(self._current_state))
+
+ def __toggle_state_cb(self, menuitem):
+ if self._current_state == _GSM_STATE_NOT_READY:
+ pass
+ elif self._current_state == _GSM_STATE_DISCONNECTED:
+ 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))
+
class WirelessDeviceView(ToolButton):
- _ICON_NAME = 'network-wireless'
FRAME_POSITION_RELATIVE = 302
def __init__(self, device):
@@ -224,7 +312,7 @@ class WirelessDeviceView(ToolButton):
self._active_ap_op = None
self._icon = PulsingIcon()
- self._icon.props.icon_name = get_icon_state(self._ICON_NAME, 0)
+ 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()))
@@ -238,13 +326,10 @@ 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 = 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)
@@ -285,7 +370,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,
@@ -309,11 +394,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']
@@ -329,11 +409,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 \
+ self._name.startswith('Ad-hoc Network'):
+ self._color = profile.get_color()
else:
sh = sha.new()
data = self._name + hex(self._flags)
@@ -369,14 +447,27 @@ 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 \
+ self._name.startswith('Ad-hoc Network') == 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:
+ try:
+ channel = network.frequency_to_channel(self._frequency)
+ except KeyError:
+ channel = 1
+ 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 \
@@ -386,7 +477,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(self._frequency, address)
+ self._palette.set_connected_with_frequency(self._frequency,
+ address)
self._icon.props.pulsing = False
else:
self._icon.props.badge_name = None
@@ -399,75 +491,146 @@ class WirelessDeviceView(ToolButton):
self._icon.props.base_color = self._color
def __deactivate_connection_cb(self, palette, data=None):
+ connection = network.find_connection_by_ssid(self._name)
+ if connection:
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection.set_disconnected()
+
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')
+ 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)
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]
- nick = re.sub("([\xf6-\xf7][\x80-\xbf]{0,2}|[\xe0-\xef][\x80-\xbf]{0,1}|[\xc0-\xdf])$", '', nick)
-
- connection_name = format % nick
- connection_name += color_suffix
-
- connection = network.find_connection(connection_name)
- if connection is None:
- settings = Settings()
- settings.connection.id = 'Auto ' + connection_name
- 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(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 __activate_reply_cb(self, connection):
logging.debug('Network created: %s', connection)
def __activate_error_cb(self, err):
logging.debug('Failed to create network: %s', err)
+class OlpcMeshDeviceView(ToolButton):
+ _ICON_NAME = 'network-mesh'
+ FRAME_POSITION_RELATIVE = 302
+
+ def __init__(self, device, state):
+ ToolButton.__init__(self)
+
+ self._bus = dbus.SystemBus()
+ self._device = device
+ self._device_props = None
+ self._device_state = None
+ self._channel = 0
+
+ self._icon = PulsingIcon(icon_name=self._ICON_NAME)
+ self._inactive_color = xocolor.XoColor( \
+ "%s,%s" % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ 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.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.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.__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.__wireless_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._device.object_path,
+ dbus_interface=_NM_OLPC_MESH_IFACE)
+
+ def __get_active_channel_reply_cb(self, channel):
+ self._channel = channel
+ self._update_text()
+
+ def __get_active_channel_error_cb(self, err):
+ logging.error('Error getting the active channel: %s', err)
+
+ def __state_changed_cb(self, new_state, old_state, reason):
+ self._device_state = new_state
+ self._update()
+
+ def __wireless_properties_changed_cb(self, properties):
+ if 'ActiveChannel' in properties:
+ self._channel = properties['ActiveChannel']
+ self._update_text()
+
+ def _update_text(self):
+ text = _("Mesh Network") + " " + str(self._channel)
+ self._palette.props.primary_text = text
+
+ def _update(self):
+ state = self._device_state
+
+ if state in [network.DEVICE_STATE_PREPARE,
+ 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')
+ 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')
+ 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:
+ netmgr.DeactivateConnection(conn_o)
+ break
+ except dbus.exceptions.DBusException:
+ pass
+
class WiredDeviceView(TrayIcon):
_ICON_NAME = 'network-wired'
@@ -486,12 +649,172 @@ class WiredDeviceView(TrayIcon):
self._palette.set_connected(speed, address)
+class GsmDeviceView(TrayIcon):
+
+ _ICON_NAME = 'network-gsm'
+ FRAME_POSITION_RELATIVE = 303
+
+ def __init__(self, device):
+ self._connection_time_handler = None
+ self._connection_timestamp = 0
+
+ client = gconf.client_get_default()
+ color = xocolor.XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ TrayIcon.__init__(self, icon_name=self._ICON_NAME, xo_color=color)
+
+ self._bus = dbus.SystemBus()
+ self._device = device
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+ self._palette = self._create_gsm_palette()
+ self.set_palette(self._palette)
+
+ 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.__ppp_stats_changed_cb,
+ signal_name='PppStats',
+ path=self._device.object_path,
+ dbus_interface=_NM_SERIAL_IFACE)
+
+ def _create_gsm_palette(self):
+ palette = GsmPalette()
+
+ palette.set_group_id('frame')
+ palette.connect('gsm-connect', self.__gsm_connect_cb)
+ palette.connect('gsm-disconnect', self.__gsm_disconnect_cb)
+
+ props = dbus.Interface(self._device, 'org.freedesktop.DBus.Properties')
+ props.GetAll(_NM_DEVICE_IFACE, byte_arrays=True,
+ reply_handler=self.__current_state_check_cb,
+ error_handler=self.__current_state_check_error_cb)
+
+ return palette
+
+ def __gsm_connect_cb(self, palette, data=None):
+ connection = network.find_gsm_connection()
+ if connection is not None:
+ 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.__connect_cb,
+ error_handler=self.__connect_error_cb)
+
+ def __connect_cb(self, active_connection):
+ logging.debug('Connected successfully to gsm device, %s',
+ active_connection)
+
+ def __connect_error_cb(self, error):
+ raise RuntimeError('Error when connecting to gsm device, %s' % error)
+
+ 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')
+ 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')
+ devices = props.Get(_NM_ACTIVE_CONN_IFACE, 'Devices')
+ if self._device.object_path in devices:
+ netmgr.DeactivateConnection(
+ conn_o,
+ reply_handler=self.__disconnect_cb,
+ error_handler=self.__disconnect_error_cb)
+ break
+
+ def __disconnect_cb(self):
+ logging.debug('Disconnected successfully gsm device')
+
+ def __disconnect_error_cb(self, error):
+ raise RuntimeError('Error when disconnecting gsm device, %s' % error)
+
+ def __state_changed_cb(self, new_state, old_state, reason):
+ logging.debug('GSM State: %s to %s, reason %s', old_state, new_state, reason)
+ self._update_state(int(new_state))
+
+ def __current_state_check_cb(self, properties):
+ self._update_state(int(properties['State']))
+
+ def __current_state_check_error_cb(self, error):
+ raise RuntimeError('Error when checking gsm device state, %s' % error)
+
+ def _update_state(self, state):
+ gsm_state = None
+
+ if state == network.DEVICE_STATE_ACTIVATED:
+ gsm_state = _GSM_STATE_CONNECTED
+ connection = network.find_gsm_connection()
+ if connection is not None:
+ connection.set_connected()
+ self._connection_timestamp = time.time() - \
+ connection.get_settings().connection.timestamp
+ self._connection_time_handler = gobject.timeout_add( \
+ 1000, self.__connection_timecount_cb)
+ self._update_stats(0, 0)
+ self._update_connection_time()
+ self._palette.info_box.show()
+
+ if state == 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()
+
+ elif state in [network.DEVICE_STATE_UNMANAGED,
+ network.DEVICE_STATE_UNAVAILABLE,
+ network.DEVICE_STATE_UNKNOWN]:
+ gsm_state = _GSM_STATE_NOT_READY
+
+ elif state in [network.DEVICE_STATE_PREPARE,
+ network.DEVICE_STATE_CONFIG,
+ network.DEVICE_STATE_IP_CONFIG]:
+ gsm_state = _GSM_STATE_CONNECTING
+
+ elif state in [network.DEVICE_STATE_NEED_AUTH]:
+ gsm_state = _GSM_STATE_NEED_AUTH
+
+ if self._palette is not None:
+ self._palette.set_gsm_state(gsm_state)
+
+ 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)
+
+ 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)
+
+ def __connection_timecount_cb(self):
+ self._connection_timestamp = self._connection_timestamp + 1
+ self._update_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):
self._device = device
self._device_view = None
self._tray = tray
-
self._device_view = WirelessDeviceView(self._device)
self._tray.add_device(self._device_view)
@@ -502,6 +825,65 @@ 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()
@@ -510,7 +892,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)
@@ -538,8 +920,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)
@@ -550,6 +931,19 @@ class WiredDeviceObserver(object):
del self._device_view
self._device_view = None
+class GsmDeviceObserver(object):
+ def __init__(self, device, tray):
+ self._device = device
+ self._device_view = None
+ self._tray = tray
+
+ self._device_view = GsmDeviceView(device)
+ self._tray.add_device(self._device_view)
+
+ def disconnect(self):
+ self._device_view.disconnect()
+ self._tray.remove_device(self._device_view)
+ self._device_view = None
class NetworkManagerObserver(object):
def __init__(self, tray):
@@ -584,7 +978,7 @@ 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:
@@ -593,6 +987,12 @@ class NetworkManagerObserver(object):
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)
+ self._devices[device_op] = device
def __device_added_cb(self, device_op):
self._check_device(device_op)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5f19663..b87175f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -13,6 +13,8 @@ extensions/cpsection/frame/view.py
extensions/cpsection/language/__init__.py
extensions/cpsection/language/model.py
extensions/cpsection/language/view.py
+extensions/cpsection/modemconfiguration/__init__.py
+extensions/cpsection/modemconfiguration/view.py
extensions/cpsection/network/__init__.py
extensions/cpsection/network/model.py
extensions/cpsection/network/view.py
@@ -29,6 +31,7 @@ src/jarabe/controlpanel/cmd.py
src/jarabe/controlpanel/gui.py
src/jarabe/controlpanel/sectionview.py
src/jarabe/controlpanel/toolbar.py
+src/jarabe/desktop/activitieslist.py
src/jarabe/desktop/favoriteslayout.py
src/jarabe/desktop/favoritesview.py
src/jarabe/desktop/homebox.py
diff --git a/po/es.po b/po/es.po
index a1e5fe3..b4572de 100644
--- a/po/es.po
+++ b/po/es.po
@@ -6,14 +6,14 @@ msgid ""
msgstr ""
"Project-Id-Version: olpc-sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-04-29 17:36-0400\n"
-"PO-Revision-Date: 2010-01-18 06:17+0200\n"
-"Last-Translator: Chris <cjl@laptop.org>\n"
+"POT-Creation-Date: 2011-01-05 01:10-0200\n"
+"PO-Revision-Date: 2010-03-12 18:44+0200\n"
+"Last-Translator: Roger Orellana <rjorellana@gmail.com>\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"
+"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Pootle 2.0.1\n"
"X-Poedit-Language: Spanish\n"
@@ -62,7 +62,7 @@ msgstr "Nombre:"
#: ../extensions/cpsection/aboutme/view.py:128
msgid "Click to change your color:"
-msgstr "Clic para cambiar su color:"
+msgstr "Haga Clic para cambiar su color:"
#: ../extensions/cpsection/aboutcomputer/__init__.py:21
msgid "About my Computer"
@@ -99,11 +99,11 @@ msgstr "Firmware:"
#: ../extensions/cpsection/aboutcomputer/view.py:146
msgid "Wireless Firmware:"
-msgstr "Firmware Wireless:"
+msgstr "Firmware de la red inalámbrica:"
#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
-msgstr "Licencia y Copyright"
+msgstr "Licencia y derechos de autor"
#: ../extensions/cpsection/aboutcomputer/view.py:184
msgid ""
@@ -129,7 +129,7 @@ msgstr "Fecha y hora"
msgid "Error timezone does not exist."
msgstr "Error, zona horaria no existe."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:19
+#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:38
msgid "Timezone"
msgstr "Zona horaria"
@@ -174,8 +174,8 @@ msgstr "Idioma"
#: ../extensions/cpsection/language/model.py:28
msgid "Could not access ~/.i18n. Create standard settings."
-msgstr ""
-"No se puede acceder a ~/.i18n. Crear configuración internacional estándar."
+msgstr "No se puede acceder a ~/.i18n. Crear una configuración internacional "
+"estándar."
#: ../extensions/cpsection/language/model.py:124
#, python-format
@@ -187,20 +187,57 @@ msgstr "El lenguaje del código=%s no pudo ser determinado."
msgid "Sorry I do not speak '%s'."
msgstr "Lo siento, yo no hablo '%s'."
+#: ../extensions/cpsection/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Configuración del módem"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:91
+msgid "Username:"
+msgstr "Nombre de usuario:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:102
+msgid "Password:"
+msgstr "Contraseña:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:113
+msgid "Number:"
+msgstr "Número:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:124
+msgid "Access Point Name (APN):"
+msgstr "Nombre del Punto de Acceso (APN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:135
+msgid "Personal Identity Number (PIN):"
+msgstr "Numero de Identificacion Personal"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:146
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Clave personal de desbloqueo"
+
+#: ../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 ""
+"Necesitará dar la siguiente informacion para configurar una conexion mobil "
+"de banda ancha hacia una red celular (3G)."
+
#: ../extensions/cpsection/network/__init__.py:21
#: ../extensions/cpsection/network/view.py:28
msgid "Network"
msgstr "Red"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:94
msgid "State is unknown."
msgstr "Estado desconocido."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:101
+#: ../extensions/cpsection/network/model.py:158
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:202
msgid "Error in specified argument use 0/1."
msgstr "Error en argumento especificado use 0/1."
@@ -224,11 +261,11 @@ msgstr "Descarte el historial de la red si tiene problemas de conexión"
msgid "Discard network history"
msgstr "Descarte historial de la red"
-#: ../extensions/cpsection/network/view.py:115
+#: ../extensions/cpsection/network/view.py:117
msgid "Collaboration"
msgstr "Colaboración"
-#: ../extensions/cpsection/network/view.py:123
+#: ../extensions/cpsection/network/view.py:125
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 "
@@ -237,7 +274,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:133
+#: ../extensions/cpsection/network/view.py:135
msgid "Server:"
msgstr "Servidor:"
@@ -245,30 +282,18 @@ msgstr "Servidor:"
msgid "Power"
msgstr "Energía"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:87
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
-msgid "Error in extreme pm argument, use on/off."
-msgstr "Error en argumento extremo de pm, use on/off."
-
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:44
msgid "Power management"
msgstr "Manejo de energía"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:54
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
-msgid ""
-"Extreme power management (disableswireless radio, increases battery life)"
-msgstr ""
-"Manejo extremo de energía (deshabilita el radio wireless, incrementa la vida "
-"de la batería)"
-
#: ../extensions/deviceicon/battery.py:58
msgid "My Battery"
msgstr "Mi batería"
@@ -294,7 +319,7 @@ msgstr "Quedan %(hour)d:%(min).2d"
msgid "Charged"
msgstr "Cargada"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:48
#, python-format
msgid "IP address: %s"
msgstr "Direccion IP: %s"
@@ -303,34 +328,92 @@ 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:104
+#: ../extensions/deviceicon/network.py:102
msgid "Disconnect..."
msgstr "Desconectando..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:250
+#: ../extensions/deviceicon/network.py:107
+#: ../extensions/deviceicon/network.py:265
+#: ../src/jarabe/desktop/meshbox.py:241 ../src/jarabe/desktop/meshbox.py:572
+#: ../src/jarabe/desktop/meshbox.py:702
msgid "Connecting..."
msgstr "Conectando..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:113
-#: ../extensions/deviceicon/network.py:166
-#: ../src/jarabe/desktop/meshbox.py:256
+#: ../extensions/deviceicon/network.py:111
+#: ../extensions/deviceicon/network.py:180
+#: ../extensions/deviceicon/network.py:269
+#: ../src/jarabe/desktop/meshbox.py:251 ../src/jarabe/desktop/meshbox.py:578
+#: ../src/jarabe/desktop/meshbox.py:708
msgid "Connected"
msgstr "Conectado"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:140
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:155
msgid "Wired Network"
msgstr "Red Cableada"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:183
msgid "Speed"
msgstr "Velocidad"
+#: ../extensions/deviceicon/network.py:209
+msgid "Wireless modem"
+msgstr "Módem inalámbrico"
+
+#: ../extensions/deviceicon/network.py:257
+msgid "Please wait..."
+msgstr "Espere por favor..."
+
+#: ../extensions/deviceicon/network.py:260
+#: ../src/jarabe/desktop/meshbox.py:149 ../src/jarabe/desktop/meshbox.py:529
+#: ../src/jarabe/desktop/meshbox.py:659
+msgid "Connect"
+msgstr "Conectar"
+
+#: ../extensions/deviceicon/network.py:261
+msgid "Disconnected"
+msgstr "Desconectado"
+
+#: ../extensions/deviceicon/network.py:264
+#: ../src/jarabe/controlpanel/toolbar.py:115
+#: ../src/jarabe/desktop/homebox.py:68
+#: ../src/jarabe/frame/activitiestray.py:726
+#: ../src/jarabe/frame/activitiestray.py:821
+#: ../src/jarabe/frame/activitiestray.py:849
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: ../extensions/deviceicon/network.py:268
+#: ../src/jarabe/desktop/meshbox.py:153 ../src/jarabe/desktop/meshbox.py:533
+msgid "Disconnect"
+msgstr "Desconectar"
+
+#: ../extensions/deviceicon/network.py:272
+msgid "Sim requires Pin/Puk"
+msgstr "Sim requiere Pin/Puk"
+
+#: ../extensions/deviceicon/network.py:273
+msgid "Authentication Error"
+msgstr "Error de autenticación"
+
+#: ../extensions/deviceicon/network.py:544
+#: ../extensions/deviceicon/network.py:586
+msgid "Mesh Network"
+msgstr "Red Malla"
+
+#: ../extensions/deviceicon/network.py:799
+#, python-format
+msgid "Data sent %d kb / received %d kb"
+msgstr "Datos enviados %d kb / recibidos %d kb"
+
+#: ../extensions/deviceicon/network.py:810
+msgid "Connection time "
+msgstr "Tiempo de conexión "
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "Mis parlantes"
@@ -353,6 +436,10 @@ msgid "Backup URL"
msgstr "URL de Respaldo"
#: ../data/sugar.schemas.in.h:2
+msgid "Bundle IDs of protected activities"
+msgstr "Bundle IDs de actividades protegidas"
+
+#: ../data/sugar.schemas.in.h:3
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. "
@@ -360,35 +447,99 @@ msgid ""
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:4
msgid "Corner Delay"
msgstr "Retraso de las Esquinas"
-#: ../data/sugar.schemas.in.h:4
+#: ../data/sugar.schemas.in.h:5
+msgid "Default font face"
+msgstr "Tipo de letra predeterminado"
+
+#: ../data/sugar.schemas.in.h:6
+msgid "Default font size"
+msgstr "Tamaño de letra predeterminado"
+
+#: ../data/sugar.schemas.in.h:7
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:8
msgid "Delay for the activation of the frame using the edges."
msgstr "Retraso para la activación del cuadro utilizando los bordes."
# es la mejor traduccion ?
-#: ../data/sugar.schemas.in.h:6
+#: ../data/sugar.schemas.in.h:9
msgid "Edge Delay"
msgstr "Retraso del Borde"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:10
msgid "Favorites Layout"
msgstr "Diseño de favoritos"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:11
msgid "Favorites resume mode"
msgstr "Modo de reanudar favoritos"
-#: ../data/sugar.schemas.in.h:9
+#: ../data/sugar.schemas.in.h:12
+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:13
+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:14
+msgid "GSM network APN"
+msgstr "APN de red GSM"
+
+#: ../data/sugar.schemas.in.h:15
+msgid "GSM network PIN"
+msgstr "PIN de red GSM"
+
+#: ../data/sugar.schemas.in.h:16
+msgid "GSM network PUK"
+msgstr "PUK de red GSM"
+
+#: ../data/sugar.schemas.in.h:17
+msgid "GSM network access point name configuration"
+msgstr "Configuracion del nombre del punto de acceso a una red GSM"
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network number"
+msgstr "Numero de red GSM"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network password"
+msgstr "Contraseña de la red GSM"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network password configuration"
+msgstr "Configuración de password de red GSM"
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network personal identification number configuration"
+msgstr "Configuracion del numero de identificacion personal de una red GSM"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network personal unlock key configuration"
+msgstr "Configuracion de la clave de desbloqueo personal de una red GSM"
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network telephone number configuration"
+msgstr "Configuracion del numero telefonico de una red GSM"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network username"
+msgstr "Nombre de usario de una red GSM"
+
+#: ../data/sugar.schemas.in.h:25
+msgid "GSM network username configuration"
+msgstr "Configuración de nombre de usuario de red GSM"
+
+#: ../data/sugar.schemas.in.h:26
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
@@ -396,75 +547,96 @@ 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:27
+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 es TRUE, Azúcar mostrará redes defecto Ad-hoc para los canales 1, 6 y 11. Si "
+"Azúcar no encuentra ninguna red conocida cuando comienza, se autoconectará a "
+"una red Ad-hoc"
+
+#: ../data/sugar.schemas.in.h:28
msgid "Jabber Server"
msgstr "Servidor Jabber"
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:29
msgid "Layout of the favorites view."
msgstr "Distribución de las actividades favoritas."
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:30
msgid "Power Automatic"
msgstr "Manejo automática de energía"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:31
msgid "Power Automatic."
msgstr "Manejo automática de energía."
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:32
msgid "Power Extreme"
msgstr "Manejo extremo de energía"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:33
msgid "Power Extreme."
msgstr "Manejo extremo de energía."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:34
msgid "Publish to Gadget"
msgstr "Publicar en Gadget"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:35
msgid "Setting for muting the sound device."
msgstr "Configuración para silenciar el dispositivo de sonido."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:36
+msgid "Show Sugar Ad-hoc networks"
+msgstr "Mostrar redes Ad-hoc"
+
+#: ../data/sugar.schemas.in.h:37
msgid "Sound Muted"
msgstr "Sonido silenciado"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:39
msgid "Timezone setting for the system."
msgstr "Configuración de zona horaria para el sistema."
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:40
msgid "Url of the jabber server to use."
msgstr "URL del servidor de Jabber para usar."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:41
msgid "Url where the backup is saved to."
msgstr "URL donde se guarda el backup."
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:42
msgid "User Color"
msgstr "Color del usuario"
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:43
msgid "User Name"
msgstr "Nombre de usuario"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:44
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:26
+#: ../data/sugar.schemas.in.h:45
+msgid ""
+"Users will not be allowed to erase these activities through the list view."
+msgstr ""
+"Los usuarios no estarán habilitados a borrar estas actividades desde la "
+"vista lista."
+
+#: ../data/sugar.schemas.in.h:46
msgid "Volume Level"
msgstr "Nivel de volumen"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:47
msgid "Volume level for the sound device."
msgstr "Nivel de volumen para el dispositivo de sonido."
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:48
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -479,7 +651,7 @@ msgid ""
"%s module: %r"
msgstr ""
"sugar-control-panel: ADVERTENCIA, hay más de una opción con el mismo nombre: "
-"%s módulo: %r"
+"módulo %s: %r"
#: ../src/jarabe/controlpanel/cmd.py:30
#, python-format
@@ -491,7 +663,7 @@ msgstr "sugar-control-panel: clave=%s no es una opción disponible"
msgid "sugar-control-panel: %s"
msgstr "sugar-control-panel: %s"
-# TRANS: Translators, there's a empty line at the end of this string,
+# TRANS: Translators, there's a empty line at the end of this string,<br /><br />
# 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.
@@ -508,22 +680,24 @@ msgid ""
" -c key clear the current value for the key \n"
" "
msgstr ""
-"Uso: sugar-control-panel [opción] clave [args ...] \n"
+"Uso: sugar-control-panel [ opción ] clave [ args ... ] \n"
" Control para el ambiente de sugar. \n"
" Opciones: \n"
-" -h muestra este mensaje de ayuda y sale \n"
-" -l enumera todas las opciones disponibles \n"
+" -h muestra este mensaje de ayuda y sale \n"
+" -l enumera todas las opciones disponibles \n"
" -h clave muestra la información sobre esta clave \n"
" -g clave obtiene el valor actual de la clave \n"
-" -s clave establece el valor actual para la clave \n"
-" -c clave borrar el valor actual para la clave \n"
+" -s clave establece el valor actual de la clave \n"
+" -c clave vaciar el valor actual de la clave \n"
" "
#: ../src/jarabe/controlpanel/cmd.py:50
msgid "To apply your changes you have to restart sugar.\n"
-msgstr "Para aplicar sus cambios tiene que reiniciar sugar.\n"
+msgstr "Para aplicar sus cambios tiene que reiniciar Azúcar.\n"
#: ../src/jarabe/controlpanel/gui.py:275
+#: ../src/jarabe/journal/journaltoolbox.py:408
+#: ../src/jarabe/journal/volumestoolbar.py:280
msgid "Warning"
msgstr "Advertencia"
@@ -536,7 +710,7 @@ msgstr "Los cambios requieren reiniciar"
msgid "Cancel changes"
msgstr "Cancelar cambios"
-#: ../src/jarabe/controlpanel/gui.py:284 ../src/jarabe/desktop/homebox.py:113
+#: ../src/jarabe/controlpanel/gui.py:284 ../src/jarabe/desktop/homebox.py:70
msgid "Later"
msgstr "Después"
@@ -548,18 +722,45 @@ msgstr "Reiniciar ahora"
msgid "Done"
msgstr "Hecho"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:111
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:821
-#: ../src/jarabe/frame/activitiestray.py:849
-msgid "Cancel"
-msgstr "Cancelar"
-
#: ../src/jarabe/controlpanel/toolbar.py:121
-#: ../src/jarabe/desktop/favoritesview.py:339
+#: ../src/jarabe/desktop/favoritesview.py:332
+#: ../src/jarabe/journal/journalactivity.py:147
msgid "Ok"
-msgstr "Ok"
+msgstr "Aceptar"
+
+#: ../src/jarabe/desktop/activitieslist.py:100
+msgid "Confirm erase"
+msgstr "Confirmar borrado"
+
+#: ../src/jarabe/desktop/activitieslist.py:102
+#, python-format
+msgid "Confirm erase: Do you want to permanently erase %s?"
+msgstr "Confirmar el borrado: ¿Quiere borrar %s de forma permanente?"
+
+# self._stop_item = MenuItem(_('Stop download'), 'stock-close')
+# TODO: Implement stopping downloads
+# self._stop_item.connect('activate', self._stop_item_activate_cb)
+# self.append_menu_item(self._stop_item)
+#: ../src/jarabe/desktop/activitieslist.py:106
+#: ../src/jarabe/frame/clipboardmenu.py:62
+#: ../src/jarabe/view/viewsource.py:218
+msgid "Keep"
+msgstr "Guardar"
+
+#: ../src/jarabe/desktop/activitieslist.py:109
+#: ../src/jarabe/desktop/activitieslist.py:388
+#: ../src/jarabe/journal/journaltoolbox.py:362
+#: ../src/jarabe/journal/palettes.py:119
+msgid "Erase"
+msgstr "Borrar"
+
+#: ../src/jarabe/desktop/activitieslist.py:404
+msgid "Remove favorite"
+msgstr "Remover favorito"
+
+#: ../src/jarabe/desktop/activitieslist.py:408
+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
@@ -591,128 +792,104 @@ msgstr "Caja"
msgid "Triangle"
msgstr "Triángulo"
-#: ../src/jarabe/desktop/favoritesview.py:330
+#: ../src/jarabe/desktop/favoritesview.py:323
msgid "Registration Failed"
-msgstr "Registro fallido"
+msgstr "Error al registrar"
-#: ../src/jarabe/desktop/favoritesview.py:331
+#: ../src/jarabe/desktop/favoritesview.py:324
#, python-format
msgid "%s"
msgstr "%s"
-#: ../src/jarabe/desktop/favoritesview.py:333
+#: ../src/jarabe/desktop/favoritesview.py:326
msgid "Registration Successful"
msgstr "Registro exitoso"
-#: ../src/jarabe/desktop/favoritesview.py:334
+#: ../src/jarabe/desktop/favoritesview.py:327
msgid "You are now registered with your school server."
msgstr "Ahora estás registrado en el servidor de colegio."
-#: ../src/jarabe/desktop/favoritesview.py:674
+#: ../src/jarabe/desktop/favoritesview.py:661
msgid "Register"
msgstr "Registro"
-#: ../src/jarabe/desktop/homebox.py:67
-msgid "Confirm erase"
-msgstr "Confirmar borrado"
-
-#: ../src/jarabe/desktop/homebox.py:69
-#, python-format
-msgid "Confirm erase: Do you want to permanently erase %s?"
-msgstr "Confirmar el borrado: ¿Quiere borrar %s de forma permanente?"
-
-# self._stop_item = MenuItem(_('Stop download'), 'stock-close')
-# TODO: Implement stopping downloads
-# self._stop_item.connect('activate', self._stop_item_activate_cb)
-# self.append_menu_item(self._stop_item)
-#: ../src/jarabe/desktop/homebox.py:73 ../src/jarabe/frame/clipboardmenu.py:62
-#: ../src/jarabe/view/viewsource.py:218
-msgid "Keep"
-msgstr "Guardar"
-
-#: ../src/jarabe/desktop/homebox.py:76
-#: ../src/jarabe/journal/journaltoolbox.py:357
-#: ../src/jarabe/journal/palettes.py:112 ../src/jarabe/view/palettes.py:153
-msgid "Erase"
-msgstr "Borrar"
-
-#: ../src/jarabe/desktop/homebox.py:106
+#: ../src/jarabe/desktop/homebox.py:63
msgid "Software Update"
msgstr "Actualización de Software"
-#: ../src/jarabe/desktop/homebox.py:107
+#: ../src/jarabe/desktop/homebox.py:64
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:116
+#: ../src/jarabe/desktop/homebox.py:73
msgid "Check now"
msgstr "Pruebe ahora"
-#: ../src/jarabe/desktop/homebox.py:233
+#: ../src/jarabe/desktop/homebox.py:190
msgid "List view"
msgstr "Vista en lista"
-#: ../src/jarabe/desktop/homebox.py:234
+#: ../src/jarabe/desktop/homebox.py:191
msgid "<Ctrl>2"
msgstr "<Ctrl>2"
-#: ../src/jarabe/desktop/homebox.py:296
+#: ../src/jarabe/desktop/homebox.py:253
msgid "Favorites view"
msgstr "Vista de favoritos"
-#: ../src/jarabe/desktop/homebox.py:297
+#: ../src/jarabe/desktop/homebox.py:254
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:135
msgid "Key Type:"
msgstr "Tipo de clave:"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:155
msgid "Authentication Type:"
msgstr "Tipo de autenticación:"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:220
msgid "WPA & WPA2 Personal"
msgstr "WPA y WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "Wireless Security:"
msgstr "Seguridad inalámbrica:"
-#: ../src/jarabe/desktop/meshbox.py:134
-msgid "Connect"
-msgstr "Conectar"
+#: ../src/jarabe/desktop/meshbox.py:526
+msgid "Ad-hoc Network %d"
+msgstr "Red Ad-hoc %d"
-#: ../src/jarabe/desktop/meshbox.py:138
-msgid "Disconnect"
-msgstr "Desconectar"
+#: ../src/jarabe/desktop/meshbox.py:657
+msgid "Mesh Network %d"
+msgstr "Red Malla %d"
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:444
+#: ../src/jarabe/desktop/meshbox.py:796
#: ../src/jarabe/frame/activitiestray.py:761
-#: ../src/jarabe/journal/journaltoolbox.py:425
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:66
+#: ../src/jarabe/journal/journaltoolbox.py:445
+#: ../src/jarabe/journal/palettes.py:73 ../src/jarabe/view/palettes.py:64
msgid "Resume"
msgstr "Retomar"
-#: ../src/jarabe/desktop/meshbox.py:449
+#: ../src/jarabe/desktop/meshbox.py:801
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Unirse"
-#: ../src/jarabe/desktop/schoolserver.py:34
+#: ../src/jarabe/desktop/schoolserver.py:35
msgid "Cannot obtain data needed for registration."
-msgstr "No se puede obtener datos necesarios para el registro."
+msgstr "No se puede obtener datos necesarios para el registro"
-#: ../src/jarabe/desktop/schoolserver.py:51
+#: ../src/jarabe/desktop/schoolserver.py:52
msgid "Cannot connect to the server."
msgstr "No se puede conectar al servidor."
-#: ../src/jarabe/desktop/schoolserver.py:56
+#: ../src/jarabe/desktop/schoolserver.py:57
msgid "The server could not complete the request."
msgstr "El servidor no pudo completar el pedido."
@@ -756,6 +933,11 @@ msgstr "Aceptar"
msgid "%s (%s)"
msgstr "%s (%s)"
+#: ../src/jarabe/frame/activitiestray.py:750
+#: ../src/jarabe/frame/activitiestray.py:873
+msgid "Dismiss"
+msgstr "Descartar"
+
#: ../src/jarabe/frame/activitiestray.py:810
#, python-format
msgid "Transfer to %r"
@@ -807,30 +989,31 @@ msgstr "Atrás"
msgid "Next"
msgstr "Siguiente"
-#: ../src/jarabe/journal/collapsedentry.py:258
-#: ../src/jarabe/journal/expandedentry.py:159
+#: ../src/jarabe/journal/collapsedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:158
#: ../src/jarabe/journal/palettes.py:66
+#: ../src/jarabe/journal/volumestoolbar.py:114
msgid "Untitled"
msgstr "Sin título"
-#: ../src/jarabe/journal/expandedentry.py:205
+#: ../src/jarabe/journal/expandedentry.py:211
msgid "No preview"
msgstr "Sin vista previa"
-#: ../src/jarabe/journal/expandedentry.py:224
+#: ../src/jarabe/journal/expandedentry.py:230
msgid "Participants:"
msgstr "Participantes:"
-#: ../src/jarabe/journal/expandedentry.py:247
+#: ../src/jarabe/journal/expandedentry.py:253
msgid "Description:"
msgstr "Descripción:"
-#: ../src/jarabe/journal/expandedentry.py:273
+#: ../src/jarabe/journal/expandedentry.py:282
msgid "Tags:"
msgstr "Etiquetas:"
-#: ../src/jarabe/journal/journalactivity.py:108
-#: ../src/jarabe/journal/volumestoolbar.py:47
+#: ../src/jarabe/journal/journalactivity.py:111
+#: ../src/jarabe/journal/volumestoolbar.py:166
msgid "Journal"
msgstr "Diario"
@@ -887,27 +1070,42 @@ msgid "Anything"
msgstr "Cualquiera"
# TODO: Add "Start with" menu item
-#: ../src/jarabe/journal/journaltoolbox.py:347
-#: ../src/jarabe/journal/palettes.py:90
+#: ../src/jarabe/journal/journaltoolbox.py:352
+#: ../src/jarabe/journal/palettes.py:97
msgid "Copy"
msgstr "Copiar"
+#: ../src/jarabe/journal/journaltoolbox.py:407
+#: ../src/jarabe/journal/volumestoolbar.py:279
+msgid "Entries without a file cannot be copied."
+msgstr "Entradas sin un archivo no pueden ser copiadas"
+
+#: ../src/jarabe/journal/journaltoolbox.py:416
+#: ../src/jarabe/journal/volumestoolbar.py:288
+msgid "Error while copying the entry. %s"
+msgstr "Error copiando la entrada. %s"
+
+#: ../src/jarabe/journal/journaltoolbox.py:417
+#: ../src/jarabe/journal/volumestoolbar.py:289
+msgid "Error"
+msgstr "Error"
+
# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
-#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:75 ../src/jarabe/view/palettes.py:135
+#: ../src/jarabe/journal/journaltoolbox.py:448
+#: ../src/jarabe/journal/palettes.py:76 ../src/jarabe/view/palettes.py:122
msgid "Start"
msgstr "Iniciar"
-#: ../src/jarabe/journal/listview.py:40
+#: ../src/jarabe/journal/listview.py:35
msgid "Your Journal is empty"
msgstr "Su diario está vacío"
-#: ../src/jarabe/journal/listview.py:41
+#: ../src/jarabe/journal/listview.py:36
msgid "No matching entries "
msgstr "No hay entradas coincidentes "
-#: ../src/jarabe/journal/listview.py:370
+#: ../src/jarabe/journal/listview.py:366
msgid "Clear search"
msgstr "Limpiar búsqueda"
@@ -938,40 +1136,40 @@ msgstr "Escoja un objeto"
msgid "Close"
msgstr "Cerrar"
-#: ../src/jarabe/journal/palettes.py:73
+#: ../src/jarabe/journal/palettes.py:74
msgid "Resume with"
msgstr "Reiniciar con"
-#: ../src/jarabe/journal/palettes.py:76
+#: ../src/jarabe/journal/palettes.py:77
msgid "Start with"
msgstr "Empezar con"
-#: ../src/jarabe/journal/palettes.py:98
+#: ../src/jarabe/journal/palettes.py:90 ../src/jarabe/journal/palettes.py:222
+msgid "No activity to start entry"
+msgstr "No se encontró una actividad para iniciar la entrada"
+
+#: ../src/jarabe/journal/palettes.py:105
msgid "Send to"
msgstr "Enviar a"
-#: ../src/jarabe/journal/palettes.py:107
+#: ../src/jarabe/journal/palettes.py:114
msgid "View Details"
msgstr "Ver detalles"
-#: ../src/jarabe/journal/palettes.py:185
+#: ../src/jarabe/journal/palettes.py:187
msgid "No friends present"
msgstr "No hay amigos presentes"
# tildes
-#: ../src/jarabe/journal/palettes.py:190
+#: ../src/jarabe/journal/palettes.py:192
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:220
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"
-
# "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
@@ -983,57 +1181,45 @@ msgid "Make friend"
msgstr "Agregar amigo"
#: ../src/jarabe/view/buddymenu.py:82
-msgid "My Settings"
-msgstr "Mis ajustes"
+msgid "Shutdown"
+msgstr "Apagar"
#: ../src/jarabe/view/buddymenu.py:90
msgid "Logout"
msgstr "Salir"
#: ../src/jarabe/view/buddymenu.py:95
-msgid "Restart"
-msgstr "Reiniciar"
-
-#: ../src/jarabe/view/buddymenu.py:100
-msgid "Shutdown"
-msgstr "Apagar"
+msgid "My Settings"
+msgstr "Mis ajustes"
-#: ../src/jarabe/view/buddymenu.py:135
+#: ../src/jarabe/view/buddymenu.py:130
#, python-format
msgid "Invite to %s"
msgstr "Invitar a %s"
-#: ../src/jarabe/view/palettes.py:47
+#: ../src/jarabe/view/palettes.py:45
msgid "Starting..."
msgstr "Iniciando..."
#. TODO: share-with, keep
-#: ../src/jarabe/view/palettes.py:73
+#: ../src/jarabe/view/palettes.py:71
msgid "View Source"
msgstr "Ver fuente"
-#: ../src/jarabe/view/palettes.py:84
+#: ../src/jarabe/view/palettes.py:82
msgid "Stop"
msgstr "Parar"
-#: ../src/jarabe/view/palettes.py:174
-msgid "Remove favorite"
-msgstr "Remover favorito"
-
-#: ../src/jarabe/view/palettes.py:178
-msgid "Make favorite"
-msgstr "Hacer favorito"
-
-#: ../src/jarabe/view/palettes.py:241
+#: ../src/jarabe/view/palettes.py:171
msgid "Show contents"
msgstr "Mostrar contenidos"
-#: ../src/jarabe/view/palettes.py:263 ../src/jarabe/view/palettes.py:313
+#: ../src/jarabe/view/palettes.py:193 ../src/jarabe/view/palettes.py:243
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB libres"
-#: ../src/jarabe/view/palettes.py:288
+#: ../src/jarabe/view/palettes.py:218
msgid "Unmount"
msgstr "Desmontar"
@@ -1054,10 +1240,195 @@ msgstr "Fuente del paquete de la actividad"
msgid "View source: %r"
msgstr "Ver código fuente: %r"
+#~ msgid "Create new wireless network"
+#~ msgstr "Crear nueva red inalámbrica"
+
+#~ msgid "%s's network"
+#~ msgstr "Red de %s"
+
+#~ msgid "Restart"
+#~ msgstr "Reiniciar"
+
+#~ msgid "Keyboard"
+#~ msgstr "Teclado"
+
+#~ msgid "Keyboard Model"
+#~ msgstr "Modelo de teclado"
+
+#~ msgid "Key(s) to change layout"
+#~ msgstr "Tecla(s) para cambiar el diseño"
+
+#~ msgid "Keyboard Layout(s)"
+#~ msgstr "Diseño(s) de teclado"
+
#~ msgid ""
-#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
+#~ "Add languages in the order you prefer. If a translation is not available, "
+#~ "the next in the list will be used."
#~ msgstr ""
-#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; y Contribuyentes."
+#~ "Añade idiomas en el orden que prefieres. Si una traducción no se "
+#~ "encuentra disponible, se usará la siguiente en la lista."
+
+# Access point name for GPRS network
+#~ msgid "APN:"
+#~ msgstr "Nombre del Punto de Acceso:"
+
+#~ msgid "Error in extreme pm argument, use on/off."
+#~ msgstr "Error en argumento extremo de manejo de energía, use on/off."
+
+# best translationfor now
+#~ msgid ""
+#~ "Extreme power management (disableswireless radio, increases battery life)"
+#~ msgstr ""
+#~ "Manejo extremo de energía (deshabilita el radio wireless, incrementa la "
+#~ "vida de la batería)"
+
+#~ msgid "Software update"
+#~ msgstr "Actualización de software"
+
+#~ msgid ""
+#~ "Software updates correct errors, eliminate security vulnerabilities, and "
+#~ "provide new features."
+#~ msgstr ""
+#~ "Las actualizaciones de software corrigen errores, eliminan "
+#~ "vulnerabilidades de seguridad y proveen nuevas características."
+
+#~ msgid "Checking %s..."
+#~ msgstr "Probando %s..."
+
+#~ msgid "Downloading %s..."
+#~ msgstr "Descargando %s..."
+
+#~ msgid "Updating %s..."
+#~ msgstr "Actualizando %s..."
+
+#~ msgid "Your software is up-to-date"
+#~ msgstr "Tu software esta actualizado"
+
+#~ 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"
+
+#~ msgid "Checking for updates..."
+#~ msgstr "Buscando actualizaciones..."
+
+#~ msgid "Installing updates..."
+#~ msgstr "Instalando actualizaciones..."
+
+#~ msgid "%s update was installed"
+#~ msgid_plural "%s updates were installed"
+#~ msgstr[0] "%s actualización fue instalada"
+#~ msgstr[1] "%s actualizaciones fueron instaladas"
+
+#~ msgid "Install selected"
+#~ msgstr "Instalación seleccionada"
+
+#~ msgid "Download size: %s"
+#~ msgstr "Tamaño de descarga: %s"
+
+#~ msgid "From version %(current)d to %(new)s (Size: %(size)s)"
+#~ msgstr "Desde la version %(current)d hacia %(new)s (Size: %(size)s)"
+
+#~ msgid "None"
+#~ msgstr "Ninguno"
+
+#~ msgid "1 KB"
+#~ msgstr "1 KB"
+
+#~ msgid "%.0f KB"
+#~ msgstr "%.0f KB"
+
+#~ msgid "%.1f MB"
+#~ msgstr "%.1f MB"
+
+#~ msgid "Mesh"
+#~ msgstr "Malla"
+
+#~ msgid "Screenshot of \"%s\""
+#~ msgstr "Captura pantalla de \"%s\""
+
+#~ 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."
+
+#~ msgid "Default nick"
+#~ msgstr "Apodo predeterminado"
+
+#~ msgid "If TRUE, Sugar will show a \"Log out\" option."
+#~ msgstr "Si es TRUE, Azúcar mostrará una opción \"Terminar Sesión\"."
+
+#~ msgid "Keyboard layouts"
+#~ msgstr "Distribuciones del teclado"
+
+#~ msgid "Keyboard model"
+#~ msgstr "Modelo del teclado"
+
+#~ msgid "Keyboard options"
+#~ msgstr "Opciones del teclado"
+
+#~ 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)"
+
+#~ msgid "List of keyboard options."
+#~ msgstr "Lista de las opciones del teclado."
+
+#~ msgid "Show Log out"
+#~ msgstr "Mostrar Terminar Sesión"
+
+#~ msgid "The keyboard model to be used"
+#~ msgstr "El modelo del teclado que se utilizará"
+
+#~ msgid "Version %s"
+#~ msgstr "Versión %s"
+
+#~ msgid "F1"
+#~ msgstr "F1"
+
+#~ msgid "F2"
+#~ msgstr "F2"
+
+#~ msgid "F3"
+#~ msgstr "F3"
+
+#~ msgid "F4"
+#~ msgstr "F4"
+
+#~ msgid "Kind: %s"
+#~ msgstr "Tipo: %s"
+
+#~ msgid "Unknown"
+#~ msgstr "Desconocido"
+
+#~ msgid "Date: %s"
+#~ msgstr "Fecha: %s"
+
+#~ msgid "Size: %s"
+#~ msgstr "Tamaño: %s"
+
+#~ msgid "Start new"
+#~ msgstr "Empezar nuevo"
+
+#~ msgid "Title"
+#~ msgstr "Título"
+
+#~ msgid "Version"
+#~ msgstr "Versión"
+
+#~ msgid "Date"
+#~ msgstr "Fecha"
+
+#~ msgid ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and "
+#~ "Contributors."
+#~ msgstr ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; y "
+#~ "Contribuyentes."
#~ msgid "Document"
#~ msgstr "Documento"
@@ -1076,18 +1447,9 @@ 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"
-#~ msgid "Mesh"
-#~ msgstr "Malla"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "Conectado a un enlace escolar de red malla"
@@ -1113,7 +1475,6 @@ msgstr "Ver código fuente: %r"
#~ msgid "Settings"
#~ msgstr "Configuración "
-#, python-format
#~ msgid "Clipboard object: %s."
#~ msgstr "Objeto de portapapel: %s."
@@ -1128,7 +1489,7 @@ msgstr "Ver código fuente: %r"
#, fuzzy
#~ msgid "Ring view"
-#~ msgstr "Vista de llamada"
+#~ msgstr "Vista de anillo"
#~ msgid "Remove from ring"
#~ msgstr "Eliminar del anillo"
@@ -1158,16 +1519,12 @@ msgstr "Ver código fuente: %r"
#~ msgstr "encendido"
#~ msgid "Permission denied. You need to be root to run this method."
-#~ msgstr "permiso denegado. Usted necesita ser root para ejecutar este método."
+#~ msgstr ""
+#~ "permiso denegado. Usted necesita ser root para ejecutar este método."
#~ msgid "Error in reading timezone"
#~ msgstr "Error en la lectura de la zona horaria"
-#, python-format
-#~ msgid "Error copying timezone (from %s): %s"
-#~ msgstr "Error copiando zona horaria (desde %s): %s"
-
-#, python-format
#~ msgid "Changing permission of timezone: %s"
#~ msgstr "Cambiando permisos de zona horaria: %s"
@@ -1231,43 +1588,18 @@ msgstr "Ver código fuente: %r"
#~ msgid "Paste"
#~ msgstr "Pegar"
-#, python-format
#~ msgid "%s Activity"
#~ msgstr "Actividad %s"
-#, python-format
-#~ msgid "Text snippet"
-#~ "Web Page"
-#~ "PDF file"
-#~ "MS Word file"
-#~ "RTF file"
-#~ "Abiword file"
-#~ "Squeak project"
-#~ "OpenOffice text file"
-#~ "Object"
-#~ "Pick a buddy picture"
-#~ "My Picture:"
-#~ "My Color:"
-#~ "Stop download"
-#~ "Close"
-#~ "No options"
-#~ "Send"
-#~ msgstr "Recorte de texto"
-#~ "Página web"
-#~ "Archivo PDF"
-#~ "Archivo MS-Word"
-#~ "Archivo RTF"
-#~ "Archivo Abiword"
-#~ "Proyecto de Squeak"
-#~ "Archivo de texto de OpenOffice"
-#~ "Objeto"
-#~ "Elegir la imagen de amigo"
-#~ "Mi imagen:"
-#~ "Mi color:"
-#~ "Interrumpir la bajada"
-#~ "Cerrar"
-#~ "Ninguna opción"
-#~ "Enviar"
+#~ msgid ""
+#~ "Text snippetWeb PagePDF fileMS Word fileRTF fileAbiword fileSqueak "
+#~ "projectOpenOffice text fileObjectPick a buddy pictureMy Picture:My Color:"
+#~ "Stop downloadCloseNo optionsSend"
+#~ msgstr ""
+#~ "Recorte de textoPágina webArchivo PDFArchivo MS-WordArchivo RTFArchivo "
+#~ "AbiwordProyecto de SqueakArchivo de texto de OpenOfficeObjetoElegir la "
+#~ "imagen de amigoMi imagen:Mi color:Interrumpir la bajadaCerrarNinguna "
+#~ "opciónEnviar"
#~ msgid "Keep error"
#~ msgstr "Error de guardado"
@@ -1287,55 +1619,42 @@ msgstr "Ver código fuente: %r"
#~ msgid "OK"
#~ msgstr "OK"
-#, python-format
#~ msgid "%d year"
#~ msgstr "%d año"
-#, python-format
#~ msgid "%d years"
#~ msgstr "%d años"
-#, python-format
#~ msgid "%d month"
#~ msgstr "%d mes"
-#, python-format
#~ msgid "%d months"
#~ msgstr "%d meses"
-#, python-format
#~ msgid "%d week"
#~ msgstr "%d semana"
-#, python-format
#~ msgid "%d weeks"
#~ msgstr "%d semanas"
-#, python-format
#~ msgid "%d day"
#~ msgstr "%d día"
-#, python-format
#~ msgid "%d days"
#~ msgstr "%d días"
-#, python-format
#~ msgid "%d hour"
#~ msgstr "%d hora"
-#, python-format
#~ msgid "%d hours"
#~ msgstr "%d horas"
-#, python-format
#~ msgid "%d minute"
#~ msgstr "%d minuto"
-#, python-format
#~ msgid "%d minutes"
#~ msgstr "%d minutos"
-#, python-format
#~ msgid "%d second"
#~ msgstr "%d segundo"
@@ -1344,17 +1663,3 @@ msgstr "Ver código fuente: %r"
#~ msgid ", "
#~ msgstr ", "
-
-#: ../extensions/deviceicon/network.py:114
-msgid "Create new wireless network"
-msgstr "Crear nueva red inalámbrica"
-
-#: ../src/jarabe/frame/activitiestray.py:750
-#: ../src/jarabe/frame/activitiestray.py:875
-msgid "Dismiss"
-msgstr "Descartar"
-
-#: ../extensions/deviceicon/network.py:415
-#, python-format
-msgid "%s's network %s"
-msgstr "%s's red %s"
diff --git a/po/fr.po b/po/fr.po
index 360f771..49c14cd 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -6,16 +6,16 @@ msgid ""
msgstr ""
"Project-Id-Version: sugar\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-04-29 17:36-0400\n"
-"PO-Revision-Date: 2009-05-01 11:55-0400\n"
+"POT-Creation-Date: 2010-05-11 01:07+0530\n"
+"PO-Revision-Date: 2010-02-27 11:34+0200\n"
"Last-Translator: samy boutayeb <s.boutayeb@free.fr>\n"
"Language-Team: French <traduc@traduc.org>\n"
-"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Pootle 1.2.1\n"
+"X-Generator: Pootle 2.0.1\n"
#: ../extensions/cpsection/aboutme/__init__.py:24
msgid "About Me"
@@ -125,7 +125,7 @@ msgstr "Date & heure"
msgid "Error timezone does not exist."
msgstr "Erreur : le fuseau horaire n'existe pas."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:19
+#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:35
msgid "Timezone"
msgstr "Fuseau horaire"
@@ -182,20 +182,55 @@ msgstr "La langue associée au code = %s n'a pas pu être déterminée."
msgid "Sorry I do not speak '%s'."
msgstr "Désolé je ne parle pas '%s'."
+#: ../extensions/cpsection/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Configuration du modem"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:91
+msgid "Username:"
+msgstr "Identifiant :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:102
+msgid "Password:"
+msgstr "Mot de passe :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:113
+msgid "Number:"
+msgstr "Nombre :"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:124
+msgid "Access Point Name (APN):"
+msgstr "APN:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:135
+msgid "Personal Identity Number (PIN):"
+msgstr "PIN:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:146
+msgid "Personal Unblocking Key (PUK):"
+msgstr "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 ""
+
#: ../extensions/cpsection/network/__init__.py:21
#: ../extensions/cpsection/network/view.py:28
msgid "Network"
msgstr "Réseau"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:87
msgid "State is unknown."
msgstr "État inconnu."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:115
+#: ../extensions/cpsection/network/model.py:154
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:197
msgid "Error in specified argument use 0/1."
msgstr "Argument spécifié incorrect. Utiliser 0/1."
@@ -214,18 +249,17 @@ msgstr "Radio"
#: ../extensions/cpsection/network/view.py:93
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"
+"Ignorer l'historique du réseau si vous avez du mal à vous connecter au réseau"
#: ../extensions/cpsection/network/view.py:102
msgid "Discard network history"
msgstr "Ignorer l'historique du réseau"
-#: ../extensions/cpsection/network/view.py:115
+#: ../extensions/cpsection/network/view.py:117
msgid "Collaboration"
msgstr "Collaboration"
-#: ../extensions/cpsection/network/view.py:123
+#: ../extensions/cpsection/network/view.py:125
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 "
@@ -235,7 +269,7 @@ msgstr ""
"personnes présentes sur le même serveur pourront se voir même si elles ne se "
"trouvent pas sur le même réseau."
-#: ../extensions/cpsection/network/view.py:133
+#: ../extensions/cpsection/network/view.py:135
msgid "Server:"
msgstr "Serveur :"
@@ -243,36 +277,25 @@ msgstr "Serveur :"
msgid "Power"
msgstr "Alimentation"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:87
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
-msgid "Error in extreme pm argument, use on/off."
-msgstr "Erreur dans l'argument gestion de l'alimentation extrême"
-
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:44
msgid "Power management"
msgstr "Gestion de l'alimentation"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:54
msgid "Automatic power management (increases battery life)"
msgstr "Gestion automatique de l'alimentation (prolonge la batterie)"
-#: ../extensions/cpsection/power/view.py:85
-msgid ""
-"Extreme power management (disableswireless radio, increases battery life)"
-msgstr ""
-"Gestion extrême de l'alimentation (désactive la radio sans fil, prolonge la "
-"durée de vie de la batterie)"
-
#: ../extensions/deviceicon/battery.py:58
msgid "My Battery"
msgstr "Ma batterie"
#: ../extensions/deviceicon/battery.py:137
msgid "Removed"
-msgstr "Retirer"
+msgstr "Retiré"
#: ../extensions/deviceicon/battery.py:140
msgid "Charging"
@@ -291,7 +314,7 @@ msgstr "%(hour)d:%(min).2d restantes"
msgid "Charged"
msgstr "Charge complète"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:47
#, python-format
msgid "IP address: %s"
msgstr "Adresse IP : %s"
@@ -300,34 +323,94 @@ msgstr "Adresse IP : %s"
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
-#: ../extensions/deviceicon/network.py:104
+#: ../extensions/deviceicon/network.py:109
msgid "Disconnect..."
msgstr "Déconnexion..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:250
+#: ../extensions/deviceicon/network.py:113
+msgid "Create new wireless network"
+msgstr "Créer un nouveau réseau sans fil"
+
+#: ../extensions/deviceicon/network.py:119
+#: ../extensions/deviceicon/network.py:270
+#: ../src/jarabe/desktop/meshbox.py:236
msgid "Connecting..."
msgstr "Connexion..."
# TODO: show the channel number
-#: ../extensions/deviceicon/network.py:113
-#: ../extensions/deviceicon/network.py:166
-#: ../src/jarabe/desktop/meshbox.py:256
+#: ../extensions/deviceicon/network.py:123
+#: ../extensions/deviceicon/network.py:185
+#: ../extensions/deviceicon/network.py:274
+#: ../src/jarabe/desktop/meshbox.py:242
msgid "Connected"
msgstr "Connecté"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:145
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:160
msgid "Wired Network"
msgstr "Réseau filaire"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:188
msgid "Speed"
msgstr "Vitesse"
+#: ../extensions/deviceicon/network.py:214
+msgid "Wireless modem"
+msgstr "Modem sans fil"
+
+#: ../extensions/deviceicon/network.py:262
+msgid "Please wait..."
+msgstr "Patienter..."
+
+#: ../extensions/deviceicon/network.py:265
+#: ../src/jarabe/desktop/meshbox.py:150
+msgid "Connect"
+msgstr "Connecter"
+
+#: ../extensions/deviceicon/network.py:266
+msgid "Disconnected"
+msgstr "Déconnecté"
+
+#: ../extensions/deviceicon/network.py:269
+#: ../src/jarabe/controlpanel/toolbar.py:115
+#: ../src/jarabe/desktop/homebox.py:111
+#: ../src/jarabe/frame/activitiestray.py:726
+#: ../src/jarabe/frame/activitiestray.py:821
+#: ../src/jarabe/frame/activitiestray.py:849
+msgid "Cancel"
+msgstr "Annuler"
+
+#: ../extensions/deviceicon/network.py:273
+#: ../src/jarabe/desktop/meshbox.py:154
+msgid "Disconnect"
+msgstr "Déconnecter"
+
+#: ../extensions/deviceicon/network.py:277
+msgid "Sim requires Pin/Puk"
+msgstr ""
+
+#: ../extensions/deviceicon/network.py:278
+#, fuzzy
+msgid "Authentication Error"
+msgstr "Type d'authentification :"
+
+#: ../extensions/deviceicon/network.py:526
+#, python-format
+msgid "%s's network"
+msgstr "Réseau %s"
+
+#: ../extensions/deviceicon/network.py:734
+#, python-format
+msgid "Data sent %d kb / received %d kb"
+msgstr "Données envoyées %d ko / reçues %d ko"
+
+#: ../extensions/deviceicon/network.py:745
+msgid "Connection time "
+msgstr "Durée de connexion"
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
msgstr "Haut-parleurs"
@@ -355,110 +438,179 @@ msgid ""
"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"
+"et du remplissage. Le format correspond aux couleurs RVB. Exemple : #AC32FF,"
+"#9A5200"
#: ../data/sugar.schemas.in.h:3
msgid "Corner Delay"
msgstr "Délai des coins"
#: ../data/sugar.schemas.in.h:4
+msgid "Default font face"
+msgstr "Police par défaut"
+
+#: ../data/sugar.schemas.in.h:5
+msgid "Default font size"
+msgstr "Corps de la police par défaut"
+
+#: ../data/sugar.schemas.in.h:6
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:7
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:8
msgid "Edge Delay"
msgstr "Délai des bords"
-#: ../data/sugar.schemas.in.h:7
+#: ../data/sugar.schemas.in.h:9
msgid "Favorites Layout"
msgstr "Disposition favorite"
-#: ../data/sugar.schemas.in.h:8
+#: ../data/sugar.schemas.in.h:10
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:11
+msgid "Font face that is used throughout the desktop."
+msgstr "Police utilisée sur le bureau."
+
+#: ../data/sugar.schemas.in.h:12
+msgid "Font size that is used throughout the desktop."
+msgstr "Corps de la police utilisée sur le bureau."
+
+#: ../data/sugar.schemas.in.h:13
+#, fuzzy
+msgid "GSM network APN"
+msgstr "Réseau maillé %d"
+
+#: ../data/sugar.schemas.in.h:14
+#, fuzzy
+msgid "GSM network PIN"
+msgstr "Réseau maillé %d"
+
+#: ../data/sugar.schemas.in.h:15
+#, fuzzy
+msgid "GSM network PUK"
+msgstr "Réseau maillé %d"
+
+#: ../data/sugar.schemas.in.h:16
+msgid "GSM network access point name configuration"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:17
+msgid "GSM network number"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network password"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:19
+#, fuzzy
+msgid "GSM network password configuration"
+msgstr "Configuration du modem"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network personal identification number configuration"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network personal unlock key configuration"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network telephone number configuration"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network username"
+msgstr ""
+
+#: ../data/sugar.schemas.in.h:24
+#, fuzzy
+msgid "GSM network username configuration"
+msgstr "Configuration du modem"
+
+#: ../data/sugar.schemas.in.h:25
msgid ""
"If TRUE, Sugar will make us searchable for the other users of the Jabber "
"server."
msgstr ""
-"Si VRAI, Sugar les autres utilisateurs du serveur Jabber pourront nous "
+"Si VRAI, Sugar permettra aux autres utilisateurs du serveur Jabber de nous "
"retrouver."
-#: ../data/sugar.schemas.in.h:10
+#: ../data/sugar.schemas.in.h:26
msgid "Jabber Server"
msgstr "Serveur Jabber"
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:27
msgid "Layout of the favorites view."
msgstr "Disposition de la vue favorite."
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:28
msgid "Power Automatic"
msgstr "Alimentation automatique"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:29
msgid "Power Automatic."
msgstr "Alimentation automatique."
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:30
msgid "Power Extreme"
msgstr "Alimentation extrême"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:31
msgid "Power Extreme."
msgstr "Alimentation extrême."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:32
msgid "Publish to Gadget"
msgstr "Publication vers Gadget"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:33
msgid "Setting for muting the sound device."
msgstr "Configuration de la mise en sourdine du périphérique audio."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:34
msgid "Sound Muted"
msgstr "Audio désactivé"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:36
msgid "Timezone setting for the system."
msgstr "Configuration du fuseau horaire du système."
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:37
msgid "Url of the jabber server to use."
msgstr "URL du serveur Jabber à utiliser."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:38
msgid "Url where the backup is saved to."
msgstr "URL d'enregistrement de la sauvegarde."
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:39
msgid "User Color"
msgstr "Couleurs de l'utilisateur"
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:40
msgid "User Name"
msgstr "Nom de l'utilisateur"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:41
msgid "User name that is used throughout the desktop."
msgstr "Nom identifiant l'utilisateur sur le bureau."
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:42
msgid "Volume Level"
msgstr "Niveau de volume"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:43
msgid "Volume level for the sound device."
msgstr "Niveau de volume du périphérique audio."
-#: ../data/sugar.schemas.in.h:28
+#: ../data/sugar.schemas.in.h:44
msgid ""
"When in resume mode, clicking on a favorite icon will cause the last entry "
"for that activity to be resumed."
@@ -541,14 +693,6 @@ msgstr "Maintenant"
msgid "Done"
msgstr "Accepter"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:111
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:821
-#: ../src/jarabe/frame/activitiestray.py:849
-msgid "Cancel"
-msgstr "Annuler"
-
#: ../src/jarabe/controlpanel/toolbar.py:121
#: ../src/jarabe/desktop/favoritesview.py:339
msgid "Ok"
@@ -599,9 +743,9 @@ msgstr "Enregistrement réussi"
#: ../src/jarabe/desktop/favoritesview.py:334
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:674
+#: ../src/jarabe/desktop/favoritesview.py:677
msgid "Register"
msgstr "S'enregistrer"
@@ -658,40 +802,32 @@ msgstr "Écran favoris"
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:131
+#: ../src/jarabe/desktop/keydialog.py:135
msgid "Key Type:"
msgstr "Type de clé :"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:155
msgid "Authentication Type:"
msgstr "Type d'authentification :"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:220
msgid "WPA & WPA2 Personal"
msgstr "WPA & WPA2 Personal"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "Wireless Security:"
msgstr "Sécurité sans fil :"
-#: ../src/jarabe/desktop/meshbox.py:134
-msgid "Connect"
-msgstr "Connecter"
-
-#: ../src/jarabe/desktop/meshbox.py:138
-msgid "Disconnect"
-msgstr "Déconnecter"
-
# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:444
+#: ../src/jarabe/desktop/meshbox.py:498
#: ../src/jarabe/frame/activitiestray.py:761
#: ../src/jarabe/journal/journaltoolbox.py:425
#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:66
msgid "Resume"
msgstr "Reprendre"
-#: ../src/jarabe/desktop/meshbox.py:449
+#: ../src/jarabe/desktop/meshbox.py:503
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Rejoindre"
@@ -804,25 +940,25 @@ msgstr "Précédent"
msgid "Next"
msgstr "Suivant"
-#: ../src/jarabe/journal/collapsedentry.py:258
-#: ../src/jarabe/journal/expandedentry.py:159
+#: ../src/jarabe/journal/collapsedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:158
#: ../src/jarabe/journal/palettes.py:66
msgid "Untitled"
msgstr "Sans titre"
-#: ../src/jarabe/journal/expandedentry.py:205
+#: ../src/jarabe/journal/expandedentry.py:211
msgid "No preview"
msgstr "Pas de prévisualisation"
-#: ../src/jarabe/journal/expandedentry.py:224
+#: ../src/jarabe/journal/expandedentry.py:230
msgid "Participants:"
msgstr "Participants :"
-#: ../src/jarabe/journal/expandedentry.py:247
+#: ../src/jarabe/journal/expandedentry.py:253
msgid "Description:"
msgstr "Description :"
-#: ../src/jarabe/journal/expandedentry.py:273
+#: ../src/jarabe/journal/expandedentry.py:282
msgid "Tags:"
msgstr "Étiquettes :"
@@ -901,10 +1037,11 @@ msgid "Your Journal is empty"
msgstr "Le journal est vide"
#: ../src/jarabe/journal/listview.py:41
+#, fuzzy
msgid "No matching entries "
msgstr "Aucune entrée correspondante"
-#: ../src/jarabe/journal/listview.py:370
+#: ../src/jarabe/journal/listview.py:372
msgid "Clear search"
msgstr "Effacer la recherche"
@@ -1047,8 +1184,184 @@ msgstr "Source du paquet activité"
msgid "View source: %r"
msgstr "Afficher le code source : %r"
+#~ msgid "Keyboard"
+#~ msgstr "Clavier"
+
+#~ msgid "Keyboard Model"
+#~ msgstr "Modèle de clavier"
+
+#~ msgid "Key(s) to change layout"
+#~ msgstr "Touche(s) de modification de la disposition"
+
+#~ msgid "Keyboard Layout(s)"
+#~ msgstr "Disposition(s) du clavier"
+
#~ msgid ""
-#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and Contributors."
+#~ "Add languages in the order you prefer. If a translation is not available, "
+#~ "the next in the list will be used."
+#~ msgstr ""
+#~ "Ajoutez des langues dans l'ordre souhaité. Si la traduction n'est pas "
+#~ "disponible, la suivante dans la liste sera utilisée."
+
+#~ msgid "APN:"
+#~ msgstr "APN :"
+
+#~ msgid "Error in extreme pm argument, use on/off."
+#~ msgstr "Erreur dans l'argument gestion de l'alimentation extrême."
+
+#~ msgid ""
+#~ "Extreme power management (disableswireless radio, increases battery life)"
+#~ msgstr ""
+#~ "Gestion extrême de l'alimentation (désactive la radio sans fil, prolonge "
+#~ "la durée de vie de la batterie)"
+
+#~ msgid "Software update"
+#~ msgstr "Mise à jour logicielle"
+
+#~ msgid ""
+#~ "Software updates correct errors, eliminate security vulnerabilities, and "
+#~ "provide new features."
+#~ msgstr ""
+#~ "Les mises à jour logicielles corrigent les erreurs, éliminent les failles "
+#~ "de sécurité et apportent de nouvelles fonctionnalités."
+
+#~ msgid "Checking %s..."
+#~ msgstr "Vérification de %s..."
+
+#~ msgid "Downloading %s..."
+#~ msgstr "Téléchargement de %s..."
+
+#~ msgid "Updating %s..."
+#~ msgstr "Mise à jour de %s..."
+
+#~ msgid "Your software is up-to-date"
+#~ msgstr "Vos logiciels sont à jour"
+
+#~ 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"
+
+#~ msgid "Checking for updates..."
+#~ msgstr "Vérification des mises à jour..."
+
+#~ msgid "Installing updates..."
+#~ msgstr "Installation des mises à jour..."
+
+#~ 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"
+
+#~ msgid "Install selected"
+#~ msgstr "Installer les activités sélectionnées"
+
+#~ msgid "Download size: %s"
+#~ msgstr "Taille du téléchargement : %s"
+
+#~ msgid "From version %(current)d to %(new)s (Size: %(size)s)"
+#~ msgstr "De la version %(current)d à %(new)s (taille : %(size)s)"
+
+#~ msgid "None"
+#~ msgstr "Zéro"
+
+#~ msgid "1 KB"
+#~ msgstr "1 Ko"
+
+#~ msgid "%.0f KB"
+#~ msgstr "%.0f Ko"
+
+#~ msgid "%.1f MB"
+#~ msgstr "%.1f Mo"
+
+#~ msgid "Mesh Network"
+#~ msgstr "Réseau maillé"
+
+#~ msgid "Mesh"
+#~ msgstr "Réseau maillé"
+
+#~ msgid "Screenshot of \"%s\""
+#~ msgstr "Capture d'écran de \"%s\""
+
+#~ 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."
+
+#~ msgid "Default nick"
+#~ msgstr "Pseudo par défaut"
+
+#~ msgid "If TRUE, Sugar will show a \"Log out\" option."
+#~ msgstr "Si VRAI, Sugar affichera une option \"Déconnexion\"."
+
+#~ msgid "Keyboard layouts"
+#~ msgstr "Dispositions du clavier"
+
+#~ msgid "Keyboard model"
+#~ msgstr "Modèle de clavier"
+
+#~ msgid "Keyboard options"
+#~ msgstr "Options du clavier"
+
+#~ 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)"
+
+#~ msgid "List of keyboard options."
+#~ msgstr "Liste des options de clavier."
+
+#~ msgid "Show Log out"
+#~ msgstr "Afficher Déconnexion"
+
+#~ msgid "The keyboard model to be used"
+#~ msgstr "Modèle de clavier à utiliser"
+
+#~ msgid "Version %s"
+#~ msgstr "Version %s"
+
+#~ msgid "F1"
+#~ msgstr "F1"
+
+#~ msgid "F2"
+#~ msgstr "F2"
+
+#~ msgid "F3"
+#~ msgstr "F3"
+
+#~ msgid "F4"
+#~ msgstr "F4"
+
+#~ msgid "Kind: %s"
+#~ msgstr "Variante : %s"
+
+#~ msgid "Unknown"
+#~ msgstr "Inconnu"
+
+#~ msgid "Date: %s"
+#~ msgstr "Date : %s"
+
+#~ msgid "Size: %s"
+#~ msgstr "Taille : %s"
+
+#~ msgid "Start new"
+#~ msgstr "Commencer un nouveau"
+
+#~ msgid "Title"
+#~ msgstr "Titre"
+
+#~ msgid "Version"
+#~ msgstr "Version"
+
+#~ msgid "Date"
+#~ msgstr "Date"
+
+#~ msgid ""
+#~ "© 2008 One Laptop per Child Association Inc; Red Hat Inc; and "
+#~ "Contributors."
#~ msgstr ""
#~ "© 2008 One Laptop per Child Association Inc ; Red Hat Inc ; et "
#~ "contributeurs."
@@ -1069,18 +1382,9 @@ 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"
-#~ msgid "Mesh"
-#~ msgstr "Réseau maillé"
-
#~ msgid "Connected to a School Mesh Portal"
#~ msgstr "Connecté au portail du réseau maillé d'école"
@@ -1105,7 +1409,6 @@ msgstr "Afficher le code source : %r"
#~ msgid "Settings"
#~ msgstr "Configuration"
-#, python-format
#~ msgid "Clipboard object: %s."
#~ msgstr "Objet dans le presse-papier : %s."
@@ -1166,11 +1469,9 @@ msgstr "Afficher le code source : %r"
#~ msgid "Error in reading timezone"
#~ msgstr "Erreur de lecture de la zone temporelle"
-#, python-format
#~ msgid "Error copying timezone (from %s): %s"
#~ msgstr "Erreur en copiant la zone temporelle (de %s): %s"
-#, python-format
#~ msgid "Changing permission of timezone: %s"
#~ msgstr "Changement de la permission de zone temporelle : %s"
@@ -1225,7 +1526,6 @@ msgstr "Afficher le code source : %r"
#~ msgid "Share"
#~ msgstr "Partager"
-#, python-format
#~ msgid "%s Activity"
#~ msgstr "Activité %s"
@@ -1265,55 +1565,42 @@ msgstr "Afficher le code source : %r"
#~ msgid "OK"
#~ msgstr "OK"
-#, python-format
#~ msgid "%d year"
#~ msgstr "%d an"
-#, python-format
#~ msgid "%d years"
#~ msgstr "%d ans"
-#, python-format
#~ msgid "%d month"
#~ msgstr "%d mois"
-#, python-format
#~ msgid "%d months"
#~ msgstr "%d mois"
-#, python-format
#~ msgid "%d week"
#~ msgstr "%d semaine"
-#, python-format
#~ msgid "%d weeks"
#~ msgstr "%d semaines"
-#, python-format
#~ msgid "%d day"
#~ msgstr "%d jour"
-#, python-format
#~ msgid "%d days"
#~ msgstr "%d jours"
-#, python-format
#~ msgid "%d hour"
#~ msgstr "%d heure"
-#, python-format
#~ msgid "%d hours"
#~ msgstr "%d heures"
-#, python-format
#~ msgid "%d minute"
#~ msgstr "%d minute"
-#, python-format
#~ msgid "%d minutes"
#~ msgstr "%d minutes"
-#, python-format
#~ msgid "%d second"
#~ msgstr "%d seconde"
@@ -1322,13 +1609,3 @@ msgstr "Afficher le code source : %r"
#~ msgid ", "
#~ msgstr ", "
-
-#: ../extensions/deviceicon/network.py:114
-msgid "Create new wireless network"
-msgstr "Créer un nouveau réseau sans fil"
-
-
-#: ../extensions/deviceicon/network.py:415
-#, python-format
-msgid "%s's network %s"
-msgstr "Réseau %s %s"
diff --git a/po/pt_BR.po b/po/pt_BR.po
index 9d3d467..e71729b 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -6,39 +6,39 @@ msgid ""
msgstr ""
"Project-Id-Version: olpc-sugar-pt_BR\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-04-29 17:36-0400\n"
-"PO-Revision-Date: 2010-01-17 02:28+0200\n"
+"POT-Creation-Date: 2010-05-11 01:07+0530\n"
+"PO-Revision-Date: 2010-05-11 23:59-0300\n"
"Last-Translator: Robson Mendonça <robsonmwoc@gmail.com>\n"
"Language-Team: Brazilian Portuguese <fedora-docs-br@redhat.com>\n"
-"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Pootle 2.0.1\n"
#: ../extensions/cpsection/aboutme/__init__.py:24
msgid "About Me"
-msgstr "Sobre Mim"
+msgstr "Sobre mim"
#: ../extensions/cpsection/aboutme/model.py:43
msgid "You must enter a name."
-msgstr "Você deve digitar um nome."
+msgstr "Você precisa digitar um nome."
#: ../extensions/cpsection/aboutme/model.py:68
#, python-format
msgid "stroke: color=%s hue=%s"
-msgstr "contorno: cor=%s matiz=%s"
+msgstr "traço: cor=%s tonalidade=%s"
#: ../extensions/cpsection/aboutme/model.py:71
#, python-format
msgid "stroke: %s"
-msgstr "contorno: %s"
+msgstr "traço: %s"
#: ../extensions/cpsection/aboutme/model.py:73
#, python-format
msgid "fill: color=%s hue=%s"
-msgstr "preenchimento: cor=%s matiz=%s"
+msgstr "preenchimento: cor=%s tonalidade=%s"
#: ../extensions/cpsection/aboutme/model.py:75
#, python-format
@@ -53,17 +53,18 @@ msgstr "Erro nos alteradores de cor selecionados."
msgid "Error in specified colors."
msgstr "Erro nas cores especificadas."
-#: ../extensions/cpsection/aboutme/view.py:94 ../src/jarabe/intro/window.py:92
+#: ../extensions/cpsection/aboutme/view.py:94
+#: ../src/jarabe/intro/window.py:92
msgid "Name:"
msgstr "Nome:"
#: ../extensions/cpsection/aboutme/view.py:128
msgid "Click to change your color:"
-msgstr "Clique para mudar a sua cor:"
+msgstr "Clique para mudar a cor:"
#: ../extensions/cpsection/aboutcomputer/__init__.py:21
msgid "About my Computer"
-msgstr "Sobre o meu Computador"
+msgstr "Sobre meu Computador"
#: ../extensions/cpsection/aboutcomputer/model.py:28
msgid "Not available"
@@ -75,19 +76,19 @@ msgstr "Identidade"
#: ../extensions/cpsection/aboutcomputer/view.py:69
msgid "Serial Number:"
-msgstr "Número Serial:"
+msgstr "Número de Série:"
#: ../extensions/cpsection/aboutcomputer/view.py:91
msgid "Software"
-msgstr "Programa"
+msgstr "Software"
#: ../extensions/cpsection/aboutcomputer/view.py:100
msgid "Build:"
-msgstr "Compilação:"
+msgstr "Construção:"
#: ../extensions/cpsection/aboutcomputer/view.py:115
msgid "Sugar:"
-msgstr "Glucose:"
+msgstr "Sugar:"
#: ../extensions/cpsection/aboutcomputer/view.py:131
msgid "Firmware:"
@@ -95,27 +96,19 @@ msgstr "Firmware:"
#: ../extensions/cpsection/aboutcomputer/view.py:146
msgid "Wireless Firmware:"
-msgstr "Firmware da Rede sem Fio:"
+msgstr "Firmware sem Fio:"
#: ../extensions/cpsection/aboutcomputer/view.py:169
msgid "Copyright and License"
-msgstr "Copyright e Licensa"
+msgstr "Direito Autoral e Licença"
#: ../extensions/cpsection/aboutcomputer/view.py:184
-msgid ""
-"Sugar is the graphical user interface that you are looking at. Sugar is free "
-"software, covered by the GNU General Public License, and you are welcome to "
-"change it and/or distribute copies of it under certain conditions described "
-"therein."
-msgstr ""
-"Sugar é a interface gráfica para qual você está olhando. Sugar é um Software "
-"Livre, coberto pela Licensa GNU General Public License, e você é bem-vindo "
-"para mudá-lo e/ou distribuir cópias dele sobre certas condicões descritas "
-"aqui."
+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 "Sugar é um interface gráfico para usuário que você estava procurando. Sugar é software livre, coberto pela Licença Geral Pública GNU, e você está convidado(a) para modificá-lo e/ou distribuir copias dele, sob certas condições descritas na Licença."
#: ../extensions/cpsection/aboutcomputer/view.py:196
msgid "Full license:"
-msgstr "Licensa completa:"
+msgstr "Licença Completa:"
#: ../extensions/cpsection/datetime/__init__.py:21
msgid "Date & Time"
@@ -125,7 +118,8 @@ msgstr "Data e Hora"
msgid "Error timezone does not exist."
msgstr "Erro: fuso horário não existe."
-#: ../extensions/cpsection/datetime/view.py:68 ../data/sugar.schemas.in.h:19
+#: ../extensions/cpsection/datetime/view.py:68
+#: ../data/sugar.schemas.in.h:35
msgid "Timezone"
msgstr "Fuso horário"
@@ -136,7 +130,7 @@ msgstr "Moldura"
#: ../extensions/cpsection/frame/model.py:38
#: ../extensions/cpsection/frame/model.py:60
msgid "Value must be an integer."
-msgstr "Valor precisa ser um número inteiro."
+msgstr "Valor precisa ser um inteiro."
#: ../extensions/cpsection/frame/view.py:26
msgid "never"
@@ -153,7 +147,7 @@ msgstr "%s segundos"
#: ../extensions/cpsection/frame/view.py:52
msgid "Activation Delay"
-msgstr "Espera na Ativação"
+msgstr "Atraso de Ativação"
#: ../extensions/cpsection/frame/view.py:76
msgid "Corner"
@@ -166,7 +160,7 @@ msgstr "Borda"
#: ../extensions/cpsection/language/__init__.py:21
#: ../extensions/cpsection/language/view.py:32
msgid "Language"
-msgstr "Língua"
+msgstr "Idioma"
#: ../extensions/cpsection/language/model.py:28
msgid "Could not access ~/.i18n. Create standard settings."
@@ -182,90 +176,107 @@ msgstr "Não foi possível determinar a língua para o código=%s."
msgid "Sorry I do not speak '%s'."
msgstr "Desculpe, eu não falo '%s'."
+#: ../extensions/cpsection/modemconfiguration/__init__.py:21
+msgid "Modem Configuration"
+msgstr "Configuração do Modem"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:91
+msgid "Username:"
+msgstr "Nome de usuário:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:102
+msgid "Password:"
+msgstr "Senha:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:113
+msgid "Number:"
+msgstr "Número:"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:124
+msgid "Access Point Name (APN):"
+msgstr "Nome do Ponto de Acesso (APN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:135
+msgid "Personal Identity Number (PIN):"
+msgstr "Número Indentificador Pessoal (PIN):"
+
+#: ../extensions/cpsection/modemconfiguration/view.py:146
+msgid "Personal Unblocking Key (PUK):"
+msgstr "Chave de Desbloqueio Pessoal (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 "Você precisa fornecer as seguintes informações para configurar sua conexão de banda larga móvel (3G) por celular."
+
#: ../extensions/cpsection/network/__init__.py:21
#: ../extensions/cpsection/network/view.py:28
msgid "Network"
-msgstr "Rede"
+msgstr "rede"
-#: ../extensions/cpsection/network/model.py:79
+#: ../extensions/cpsection/network/model.py:87
msgid "State is unknown."
msgstr "Estado desconhecido."
-#: ../extensions/cpsection/network/model.py:105
+#: ../extensions/cpsection/network/model.py:115
+#: ../extensions/cpsection/network/model.py:154
msgid "Error in specified radio argument use on/off."
msgstr "Erro no argumento de rádio especificado, use on/off."
-#: ../extensions/cpsection/network/model.py:137
+#: ../extensions/cpsection/network/model.py:197
msgid "Error in specified argument use 0/1."
-msgstr "Erro no argumento especificado, use 0 ou 1."
+msgstr "Erro no argumento de rádio especificado, use ligado / desligado."
#: ../extensions/cpsection/network/view.py:56
msgid "Wireless"
-msgstr "Rede sem fio"
+msgstr "rede sem fio"
#: ../extensions/cpsection/network/view.py:64
msgid "Turn off the wireless radio to save battery life"
-msgstr "Desligue a transmissão sem fio para economizar bateria"
+msgstr "Desligue a placa sem fio para economizar energia e aumentar a durabilidade da bateria"
#: ../extensions/cpsection/network/view.py:77
msgid "Radio"
-msgstr "Rádio"
+msgstr "Radio"
#: ../extensions/cpsection/network/view.py:93
msgid "Discard network history if you have trouble connecting to the network"
-msgstr "Desprezar o histórico se você tem problemas para conectar na rede"
+msgstr "Desprezar o histórico da rede se você tem problemas para conectar a rede"
#: ../extensions/cpsection/network/view.py:102
msgid "Discard network history"
-msgstr "Desprezar o histórico de rede"
+msgstr "Desprezar o histórico da rede"
-#: ../extensions/cpsection/network/view.py:115
+#: ../extensions/cpsection/network/view.py:117
msgid "Collaboration"
msgstr "Colaboração"
-#: ../extensions/cpsection/network/view.py:123
-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 "
-"network."
-msgstr ""
-"O servidor é equivalente a sala que você esta; pessoas no mesmo servidor "
-"estão aptas a ver umas às outras, mesmo que elas não estejam na mesma rede."
+#: ../extensions/cpsection/network/view.py:125
+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 network."
+msgstr "O servidor é o equivalente a uma sala na qual você está. Pessoas no mesmo servidor são capazes de ver umas às outras, mesmo quando elas não estão na mesma rede."
-#: ../extensions/cpsection/network/view.py:133
+#: ../extensions/cpsection/network/view.py:135
msgid "Server:"
msgstr "Servidor:"
#: ../extensions/cpsection/power/__init__.py:21
msgid "Power"
-msgstr "Carga"
+msgstr "Energia"
-#: ../extensions/cpsection/power/model.py:54
+#: ../extensions/cpsection/power/model.py:87
msgid "Error in automatic pm argument, use on/off."
-msgstr "Erro no argumento automático pm, utilize ligado/desligado."
+msgstr "Erro no argumento automático pm, use ligado / desligado."
-#: ../extensions/cpsection/power/model.py:81
-msgid "Error in extreme pm argument, use on/off."
-msgstr "Erro no argumento extremo pm, use ligado/desligado."
-
-#: ../extensions/cpsection/power/view.py:47
+#: ../extensions/cpsection/power/view.py:44
msgid "Power management"
msgstr "Gerenciamento de energia"
-#: ../extensions/cpsection/power/view.py:57
+#: ../extensions/cpsection/power/view.py:54
msgid "Automatic power management (increases battery life)"
-msgstr "Gerenciamento automático de energia (estende a duração da bateria)"
-
-#: ../extensions/cpsection/power/view.py:85
-msgid ""
-"Extreme power management (disableswireless radio, increases battery life)"
-msgstr ""
-"Gerenciamento extremo de energia (desabilita rede sem fio, incrementa a vida "
-"da bateria)"
+msgstr "Gerenciamento de energia automático (aumenta a duração da bateria)"
#: ../extensions/deviceicon/battery.py:58
msgid "My Battery"
-msgstr "Minha Bateria"
+msgstr "Minha bateria"
#: ../extensions/deviceicon/battery.py:137
msgid "Removed"
@@ -277,52 +288,123 @@ msgstr "Carregando"
#: ../extensions/deviceicon/battery.py:143
msgid "Very little power remaining"
-msgstr "Pouca energia restando"
+msgstr "Resta um pouquinho de energia"
#: ../extensions/deviceicon/battery.py:149
#, python-format
msgid "%(hour)d:%(min).2d remaining"
-msgstr "%(hour)d:%(min).2d restando"
+msgstr "resta %(hour)d:%(min).2d"
#: ../extensions/deviceicon/battery.py:152
msgid "Charged"
-msgstr "Carregado"
+msgstr "Carregada"
-#: ../extensions/deviceicon/network.py:40
+#: ../extensions/deviceicon/network.py:47
#, python-format
msgid "IP address: %s"
msgstr "Endereço IP: %s"
-#: ../extensions/deviceicon/network.py:104
+# Only show disconnect when there's a mesh device, because mesh takes<br />
+# priority over the normal wireless device.<span class="translation-space"> </span>
+# <span class="translation-space"> </span>
+# NM doesn't have a "disconnect"<br />
+# method for a device either (for various reasons) so this doesn't<br />
+# have a good mapping
+#: ../extensions/deviceicon/network.py:109
msgid "Disconnect..."
msgstr "Desconectar..."
-#: ../extensions/deviceicon/network.py:109
-#: ../src/jarabe/desktop/meshbox.py:250
+#: ../extensions/deviceicon/network.py:113
+msgid "Create new wireless network"
+msgstr "Criar nova rede wireless"
+
+# Only show disconnect when there's a mesh device, because mesh takes<br />
+# priority over the normal wireless device.<span class="translation-space"> </span>
+# <span class="translation-space"> </span>
+# NM doesn't have a "disconnect"<br />
+# method for a device either (for various reasons) so this doesn't<br />
+# have a good mapping
+#: ../extensions/deviceicon/network.py:119
+#: ../extensions/deviceicon/network.py:270
+#: ../src/jarabe/desktop/meshbox.py:236
msgid "Connecting..."
msgstr "Conectando..."
-#: ../extensions/deviceicon/network.py:113
-#: ../extensions/deviceicon/network.py:166
-#: ../src/jarabe/desktop/meshbox.py:256
+#: ../extensions/deviceicon/network.py:123
+#: ../extensions/deviceicon/network.py:185
+#: ../extensions/deviceicon/network.py:274
+#: ../src/jarabe/desktop/meshbox.py:242
msgid "Connected"
msgstr "Conectado"
-#: ../extensions/deviceicon/network.py:126
+#: ../extensions/deviceicon/network.py:145
msgid "Channel"
msgstr "Canal"
-#: ../extensions/deviceicon/network.py:141
+#: ../extensions/deviceicon/network.py:160
msgid "Wired Network"
-msgstr "Rede sem fio"
+msgstr "Rede por fio"
-#: ../extensions/deviceicon/network.py:169
+#: ../extensions/deviceicon/network.py:188
msgid "Speed"
msgstr "Velocidade"
+#: ../extensions/deviceicon/network.py:214
+msgid "Wireless modem"
+msgstr "Modem wireless"
+
+#: ../extensions/deviceicon/network.py:262
+msgid "Please wait..."
+msgstr "Por favor espere..."
+
+#: ../extensions/deviceicon/network.py:265
+#: ../src/jarabe/desktop/meshbox.py:150
+msgid "Connect"
+msgstr "Conectar"
+
+#: ../extensions/deviceicon/network.py:266
+msgid "Disconnected"
+msgstr "Desconectado"
+
+#: ../extensions/deviceicon/network.py:269
+#: ../src/jarabe/controlpanel/toolbar.py:115
+#: ../src/jarabe/desktop/homebox.py:111
+#: ../src/jarabe/frame/activitiestray.py:726
+#: ../src/jarabe/frame/activitiestray.py:821
+#: ../src/jarabe/frame/activitiestray.py:849
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: ../extensions/deviceicon/network.py:273
+#: ../src/jarabe/desktop/meshbox.py:154
+msgid "Disconnect"
+msgstr "Desconectar"
+
+#: ../extensions/deviceicon/network.py:277
+msgid "Sim requires Pin/Puk"
+msgstr "Sim requer Pin/Puk"
+
+#: ../extensions/deviceicon/network.py:278
+msgid "Authentication Error"
+msgstr "Erro de Autenticação"
+
+#: ../extensions/deviceicon/network.py:526
+#, python-format
+msgid "%s's network"
+msgstr "rede da %s"
+
+#: ../extensions/deviceicon/network.py:734
+#, python-format
+msgid "Data sent %d kb / received %d kb"
+msgstr "Dados enviados %d kb / recebidos %d kb"
+
+#: ../extensions/deviceicon/network.py:745
+msgid "Connection time "
+msgstr "Tempo de conexão"
+
#: ../extensions/deviceicon/speaker.py:59
msgid "My Speakers"
-msgstr "Meus Alto Falantes"
+msgstr "Meus Alto-falantes"
#: ../extensions/deviceicon/speaker.py:133
msgid "Unmute"
@@ -338,145 +420,190 @@ msgstr "Foto da tela"
#: ../data/sugar.schemas.in.h:1
msgid "Backup URL"
-msgstr "URL reserva"
+msgstr "URL de backup"
#: ../data/sugar.schemas.in.h:2
-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. "
-"Example: #AC32FF,#9A5200"
-msgstr ""
-"Cor para o ícone XO que será usado na área de trabalho. A sequência é "
-"composta de tom e preenchimento da cor, formato das cores rgb (vermelho, "
-"verde e azul). Exemplo: #AC32FF,#9A5200"
+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. Example: #AC32FF,#9A5200"
+msgstr "Cor para o ícone XO que é usado na área de trabalho. A string é composta pela cor do contorno e preenchimento, no formato de cores rgb. Exemplo: #AC32FF,#9A5200"
#: ../data/sugar.schemas.in.h:3
msgid "Corner Delay"
-msgstr "Atraso de Canto"
+msgstr "Atraso do Canto"
#: ../data/sugar.schemas.in.h:4
-msgid "Delay for the activation of the frame using the corners."
-msgstr "Atraso para ativação da moldura utilizando os cantos."
+msgid "Default font face"
+msgstr "Fonte padrão"
#: ../data/sugar.schemas.in.h:5
-msgid "Delay for the activation of the frame using the edges."
-msgstr "Atraso na ativação das molduras utilizando as bordas."
+msgid "Default font size"
+msgstr "Tamanho padrão da fonte"
#: ../data/sugar.schemas.in.h:6
-msgid "Edge Delay"
-msgstr "Atraso de Borda"
+msgid "Delay for the activation of the frame using the corners."
+msgstr "Atraso para ativação da moldura usando os cantos."
#: ../data/sugar.schemas.in.h:7
-msgid "Favorites Layout"
-msgstr "Organização ou Arranjo Preferido"
+msgid "Delay for the activation of the frame using the edges."
+msgstr "Atraso para a ativação da moldura usando bordas."
#: ../data/sugar.schemas.in.h:8
-msgid "Favorites resume mode"
-msgstr "Modo preferido de retomada"
+msgid "Edge Delay"
+msgstr "Atraso da Borda"
#: ../data/sugar.schemas.in.h:9
-msgid ""
-"If TRUE, Sugar will make us searchable for the other users of the Jabber "
-"server."
-msgstr ""
-"Se VERDADEIRO, Sugar deve tornar-nos pesquisável para os outros usuários do "
-"servidor Jabber."
+msgid "Favorites Layout"
+msgstr "Disposição dos Favoritos"
#: ../data/sugar.schemas.in.h:10
+msgid "Favorites resume mode"
+msgstr "Modo de resumo dos favoritos"
+
+#: ../data/sugar.schemas.in.h:11
+msgid "Font face that is used throughout the desktop."
+msgstr "Fonte que é utilizada por toda área de trabalho."
+
+#: ../data/sugar.schemas.in.h:12
+msgid "Font size that is used throughout the desktop."
+msgstr "Tamanho da fonte que é utilizada por toda área de trabalho."
+
+#: ../data/sugar.schemas.in.h:13
+msgid "GSM network APN"
+msgstr "Rede GSM APN"
+
+#: ../data/sugar.schemas.in.h:14
+msgid "GSM network PIN"
+msgstr "Rede GSM PIN"
+
+#: ../data/sugar.schemas.in.h:15
+msgid "GSM network PUK"
+msgstr "Rede GSM PUK"
+
+#: ../data/sugar.schemas.in.h:16
+msgid "GSM network access point name configuration"
+msgstr "Configuração do nome do ponto de acesso da rede GSM"
+
+#: ../data/sugar.schemas.in.h:17
+msgid "GSM network number"
+msgstr "Número da rede GSM"
+
+#: ../data/sugar.schemas.in.h:18
+msgid "GSM network password"
+msgstr "Senha da rede GSM"
+
+#: ../data/sugar.schemas.in.h:19
+msgid "GSM network password configuration"
+msgstr "Configuração de rede GSM senha"
+
+#: ../data/sugar.schemas.in.h:20
+msgid "GSM network personal identification number configuration"
+msgstr "Configuração de rede GSM número de identificação pessoal"
+
+#: ../data/sugar.schemas.in.h:21
+msgid "GSM network personal unlock key configuration"
+msgstr "Configuração de rede GSM chave de desbloqueio"
+
+#: ../data/sugar.schemas.in.h:22
+msgid "GSM network telephone number configuration"
+msgstr "Configuração de rede GSM número de telefone"
+
+#: ../data/sugar.schemas.in.h:23
+msgid "GSM network username"
+msgstr "Nome de usuário da rede GSM"
+
+#: ../data/sugar.schemas.in.h:24
+msgid "GSM network username configuration"
+msgstr "Configuração de rede GSM nome de usuário"
+
+#: ../data/sugar.schemas.in.h:25
+msgid "If TRUE, Sugar will make us searchable for the other users of the Jabber server."
+msgstr "Se VERDADEIRO, o Sugar nos tornará buscáveis por outros usuários do servidor Jabber."
+
+#: ../data/sugar.schemas.in.h:26
msgid "Jabber Server"
msgstr "Servidor Jabber"
-#: ../data/sugar.schemas.in.h:11
+#: ../data/sugar.schemas.in.h:27
msgid "Layout of the favorites view."
-msgstr "Arranjo da visão dos favoritos."
+msgstr "Disposição da visão de favoritos."
-#: ../data/sugar.schemas.in.h:12
+#: ../data/sugar.schemas.in.h:28
msgid "Power Automatic"
-msgstr "Energia em Automático"
+msgstr "Gerenciamento de Energia Automático"
-#: ../data/sugar.schemas.in.h:13
+#: ../data/sugar.schemas.in.h:29
msgid "Power Automatic."
-msgstr "Energia em Automático."
+msgstr "Gerenciamento de Energia Automático."
-# Energia modo Extremo
-#: ../data/sugar.schemas.in.h:14
+#: ../data/sugar.schemas.in.h:30
msgid "Power Extreme"
-msgstr "Energia modo Extremo"
+msgstr "Gerenciamento de Energia Extremo"
-#: ../data/sugar.schemas.in.h:15
+#: ../data/sugar.schemas.in.h:31
msgid "Power Extreme."
-msgstr "Energia em modo Extremo."
+msgstr "Gerenciamento de Energia Extremo."
-#: ../data/sugar.schemas.in.h:16
+#: ../data/sugar.schemas.in.h:32
msgid "Publish to Gadget"
msgstr "Publicar para Dispositivos"
-#: ../data/sugar.schemas.in.h:17
+#: ../data/sugar.schemas.in.h:33
msgid "Setting for muting the sound device."
-msgstr "Colocar o som do equipamento no modo mudo."
+msgstr "Configurar ou silenciar o dispositivo de som."
-#: ../data/sugar.schemas.in.h:18
+#: ../data/sugar.schemas.in.h:34
msgid "Sound Muted"
-msgstr "Som em Mudo"
+msgstr "Som mudo"
-#: ../data/sugar.schemas.in.h:20
+#: ../data/sugar.schemas.in.h:36
msgid "Timezone setting for the system."
-msgstr "Ajuste de fuso horário para o sistema."
+msgstr "Configuração de fuso-horário do sistema."
-#: ../data/sugar.schemas.in.h:21
+#: ../data/sugar.schemas.in.h:37
msgid "Url of the jabber server to use."
-msgstr "Endereço do servidor jabber a ser usado."
+msgstr "Url do servidor Jabber para usar."
-#: ../data/sugar.schemas.in.h:22
+#: ../data/sugar.schemas.in.h:38
msgid "Url where the backup is saved to."
-msgstr "Endereço onde o backup será salvo."
+msgstr "Url onde o backup é salvo."
-#: ../data/sugar.schemas.in.h:23
+#: ../data/sugar.schemas.in.h:39
msgid "User Color"
msgstr "Cor do Usuário"
-#: ../data/sugar.schemas.in.h:24
+#: ../data/sugar.schemas.in.h:40
msgid "User Name"
msgstr "Nome do Usuário"
-#: ../data/sugar.schemas.in.h:25
+#: ../data/sugar.schemas.in.h:41
msgid "User name that is used throughout the desktop."
-msgstr "Nome do usuário que será utilizado pelo computador."
+msgstr "Nome do usuário que é usado por toda área de trabalho."
-#: ../data/sugar.schemas.in.h:26
+#: ../data/sugar.schemas.in.h:42
msgid "Volume Level"
-msgstr "Nivel do Volume"
+msgstr "Nível do volume"
-#: ../data/sugar.schemas.in.h:27
+#: ../data/sugar.schemas.in.h:43
msgid "Volume level for the sound device."
-msgstr "Nivel do volume para o equipamento de som."
+msgstr "Nível do volume para o dispositivo de som."
-#: ../data/sugar.schemas.in.h:28
-msgid ""
-"When in resume mode, clicking on a favorite icon will cause the last entry "
-"for that activity to be resumed."
-msgstr ""
-"Quando em modo retomada, clicando num item favorito ocasionará a última "
-"atividade solicitada ser retomada."
+#: ../data/sugar.schemas.in.h:44
+msgid "When in resume mode, clicking on a favorite icon will cause the last entry for that activity to be resumed."
+msgstr "Quando estiver no modo continuar, clicando num ícone favorito fará com que a última entrada para essa atividade seja retomada."
#: ../src/jarabe/controlpanel/cmd.py:28
#, python-format
-msgid ""
-"sugar-control-panel: WARNING, found more than one option with the same name: "
-"%s module: %r"
-msgstr ""
-"painel-de-controle-sugar: AVISO, foi encontrado mais de uma opção com o "
-"mesmo nome: %s módulo: %r"
+msgid "sugar-control-panel: WARNING, found more than one option with the same name: %s module: %r"
+msgstr "sugar-control-panel: AVISO, encontrado mais de uma opção com o mesmo nome: %s módulo: %r"
#: ../src/jarabe/controlpanel/cmd.py:30
#, python-format
msgid "sugar-control-panel: key=%s not an available option"
-msgstr "painel-de-controle-sugar: chave=%s não é uma opção disponível"
+msgstr "sugar-control-panel: chave=%s não é uma opção válida"
#: ../src/jarabe/controlpanel/cmd.py:31
#, python-format
msgid "sugar-control-panel: %s"
-msgstr "painel-de-controle-sugar: %s"
+msgstr "sugar-control-panel: %s"
#. TRANS: Translators, there's a empty line at the end of this string,
#. which must appear in the translated string (msgstr) as well.
@@ -493,15 +620,15 @@ msgid ""
" -c key clear the current value for the key \n"
" "
msgstr ""
-"Uso: sugar-control-panel [ opção ] chave [ args ... ] \n"
-" Ambiente de controle para sugar. \n"
+"Uso: sugar-control-panel[opção] chave [argumentos ...] \n"
+" Controle para o ambiente do sugar. \n"
" Opções: \n"
-" -h (exibe mensagem de ajuda e sai) \n"
-" -l (lista todas opções disponíveis) \n"
-" -h chave (exibe informações sobre a chave solicitada) \n"
-" -g chave (obtem o valor atual da chave solicitada) \n"
-" -s chave (estabelece o valor para a chave solicitada) \n"
-" -c chave (limpa o valor para a chave solicitada \n"
+" -h mostrar esta mensagem de ajuda e sair \n"
+" -l lista todas as opções disponíveis \n"
+" -h chave mostrar informações sobre essa chave \n"
+" -g chave pegar o valor atual da chave \n"
+" -s chave definir o valor atual da chave \n"
+" -c chave limpar o valor atual da chave \n"
" "
#: ../src/jarabe/controlpanel/cmd.py:50
@@ -510,37 +637,31 @@ msgstr "Para terminar suas mudanças você deve reiniciar o sugar.\n"
#: ../src/jarabe/controlpanel/gui.py:275
msgid "Warning"
-msgstr "Cuidado"
+msgstr "Aviso"
#: ../src/jarabe/controlpanel/gui.py:276
#: ../src/jarabe/controlpanel/sectionview.py:42
msgid "Changes require restart"
-msgstr "Mudanças requerem reiniciar"
+msgstr "As mudanças exigem um reinício"
#: ../src/jarabe/controlpanel/gui.py:279
msgid "Cancel changes"
-msgstr "Cancelar modificações"
+msgstr "Cancele as mudanças"
-#: ../src/jarabe/controlpanel/gui.py:284 ../src/jarabe/desktop/homebox.py:113
+#: ../src/jarabe/controlpanel/gui.py:284
+#: ../src/jarabe/desktop/homebox.py:113
msgid "Later"
-msgstr "Mais tarde"
+msgstr "Posterior"
#: ../src/jarabe/controlpanel/gui.py:288
msgid "Restart now"
-msgstr "Reiniciar ngora"
+msgstr "Reiniciar agora"
-#: ../src/jarabe/controlpanel/toolbar.py:61 ../src/jarabe/intro/window.py:188
+#: ../src/jarabe/controlpanel/toolbar.py:61
+#: ../src/jarabe/intro/window.py:188
msgid "Done"
msgstr "Pronto"
-#: ../src/jarabe/controlpanel/toolbar.py:115
-#: ../src/jarabe/desktop/homebox.py:111
-#: ../src/jarabe/frame/activitiestray.py:726
-#: ../src/jarabe/frame/activitiestray.py:821
-#: ../src/jarabe/frame/activitiestray.py:849
-msgid "Cancel"
-msgstr "Cancelar"
-
#: ../src/jarabe/controlpanel/toolbar.py:121
#: ../src/jarabe/desktop/favoritesview.py:339
msgid "Ok"
@@ -549,12 +670,12 @@ msgstr "Ok"
#. TRANS: label for the freeform layout in the favorites view
#: ../src/jarabe/desktop/favoriteslayout.py:116
msgid "Freeform"
-msgstr "Liberdade"
+msgstr "Forma livre"
#. TRANS: label for the ring layout in the favorites view
#: ../src/jarabe/desktop/favoriteslayout.py:198
msgid "Ring"
-msgstr "Toque"
+msgstr "Anel"
#. TRANS: label for the spiral layout in the favorites view
#: ../src/jarabe/desktop/favoriteslayout.py:334
@@ -573,7 +694,7 @@ msgstr "Triângulo"
#: ../src/jarabe/desktop/favoritesview.py:330
msgid "Registration Failed"
-msgstr "O cadastro falhou"
+msgstr "Cadastro falhou"
#: ../src/jarabe/desktop/favoritesview.py:331
#, python-format
@@ -586,49 +707,53 @@ msgstr "Cadastrado com sucesso"
#: ../src/jarabe/desktop/favoritesview.py:334
msgid "You are now registered with your school server."
-msgstr "Agora você está registrado no servidor de sua escola."
+msgstr "Você está cadastrado no servidor de sua escola agora."
-#: ../src/jarabe/desktop/favoritesview.py:674
+#: ../src/jarabe/desktop/favoritesview.py:677
msgid "Register"
msgstr "Cadastrar"
#: ../src/jarabe/desktop/homebox.py:67
msgid "Confirm erase"
-msgstr "Confirme a remoção"
+msgstr "Confirmar a remoção"
#: ../src/jarabe/desktop/homebox.py:69
#, python-format
msgid "Confirm erase: Do you want to permanently erase %s?"
-msgstr "Confirme a remoção: Você deseja remover %s permanentemente?"
-
-#: ../src/jarabe/desktop/homebox.py:73 ../src/jarabe/frame/clipboardmenu.py:62
+msgstr "Confirmar remoção: Você deseja remover %s permanentemente?"
+
+# self._stop_item = MenuItem(_('Stop download'), 'stock-close')
+# TODO: Implement stopping downloads
+# self._stop_item.connect('activate', self._stop_item_activate_cb)
+# self.append_menu_item(self._stop_item)
+#: ../src/jarabe/desktop/homebox.py:73
+#: ../src/jarabe/frame/clipboardmenu.py:62
#: ../src/jarabe/view/viewsource.py:218
msgid "Keep"
msgstr "Manter"
#: ../src/jarabe/desktop/homebox.py:76
#: ../src/jarabe/journal/journaltoolbox.py:357
-#: ../src/jarabe/journal/palettes.py:112 ../src/jarabe/view/palettes.py:153
+#: ../src/jarabe/journal/palettes.py:112
+#: ../src/jarabe/view/palettes.py:153
msgid "Erase"
-msgstr "Excluir"
+msgstr "Apagar"
#: ../src/jarabe/desktop/homebox.py:106
msgid "Software Update"
-msgstr "Atualização de Software"
+msgstr "Atualização de software"
#: ../src/jarabe/desktop/homebox.py:107
msgid "Update your activities to ensure compatibility with your new software"
-msgstr ""
-"Atualize suas atividades para assegurar a compatibilidade com seu novo "
-"software"
+msgstr "Atualize suas atividades para assegurar compatibilidade com seu novo software"
#: ../src/jarabe/desktop/homebox.py:116
msgid "Check now"
-msgstr "Verifique agora"
+msgstr "Verificar agora"
#: ../src/jarabe/desktop/homebox.py:233
msgid "List view"
-msgstr "Exibição de lista"
+msgstr "Visão de lista"
#: ../src/jarabe/desktop/homebox.py:234
msgid "<Ctrl>2"
@@ -636,56 +761,50 @@ msgstr "<Ctrl>2"
#: ../src/jarabe/desktop/homebox.py:296
msgid "Favorites view"
-msgstr "Exibição de favoritos"
+msgstr "Visão de favoritos"
#: ../src/jarabe/desktop/homebox.py:297
msgid "<Ctrl>1"
msgstr "<Ctrl>1"
-#: ../src/jarabe/desktop/keydialog.py:131
+#: ../src/jarabe/desktop/keydialog.py:135
msgid "Key Type:"
msgstr "Tipo de chave:"
-#: ../src/jarabe/desktop/keydialog.py:151
+#: ../src/jarabe/desktop/keydialog.py:155
msgid "Authentication Type:"
msgstr "Tipo de Autenticação:"
-#: ../src/jarabe/desktop/keydialog.py:215
+#: ../src/jarabe/desktop/keydialog.py:220
msgid "WPA & WPA2 Personal"
msgstr "WPA & WPA2 Pessoal"
-#: ../src/jarabe/desktop/keydialog.py:224
+#: ../src/jarabe/desktop/keydialog.py:229
msgid "Wireless Security:"
msgstr "Segurança do Wireless:"
-#: ../src/jarabe/desktop/meshbox.py:134
-msgid "Connect"
-msgstr "Conectar"
-
-#: ../src/jarabe/desktop/meshbox.py:138
-msgid "Disconnect"
-msgstr "Desconectado"
-
+# TRANS: Action label for resuming an activity.
#. TRANS: Action label for resuming an activity.
-#: ../src/jarabe/desktop/meshbox.py:444
+#: ../src/jarabe/desktop/meshbox.py:498
#: ../src/jarabe/frame/activitiestray.py:761
#: ../src/jarabe/journal/journaltoolbox.py:425
-#: ../src/jarabe/journal/palettes.py:72 ../src/jarabe/view/palettes.py:66
+#: ../src/jarabe/journal/palettes.py:72
+#: ../src/jarabe/view/palettes.py:66
msgid "Resume"
msgstr "Continuar"
-#: ../src/jarabe/desktop/meshbox.py:449
+#: ../src/jarabe/desktop/meshbox.py:503
#: ../src/jarabe/frame/activitiestray.py:235
msgid "Join"
msgstr "Juntar-se"
#: ../src/jarabe/desktop/schoolserver.py:34
msgid "Cannot obtain data needed for registration."
-msgstr "Não pude obter dados necessários para o cadastro."
+msgstr "Não foi possível obter os dados necessários para o resgistro."
#: ../src/jarabe/desktop/schoolserver.py:51
msgid "Cannot connect to the server."
-msgstr "Não pude conectar com o servidor."
+msgstr "Não consegui conectar com o servidor."
#: ../src/jarabe/desktop/schoolserver.py:56
msgid "The server could not complete the request."
@@ -694,7 +813,7 @@ msgstr "O servidor não completou a requisição."
#: ../src/jarabe/frame/activitiestray.py:240
#: ../src/jarabe/frame/activitiestray.py:698
msgid "Decline"
-msgstr "Rejeitar"
+msgstr "Recusar"
#: ../src/jarabe/frame/activitiestray.py:650
#, python-format
@@ -734,12 +853,12 @@ msgstr "%s (%s)"
#: ../src/jarabe/frame/activitiestray.py:750
#: ../src/jarabe/frame/activitiestray.py:873
msgid "Dismiss"
-msgstr "Recusar"
+msgstr "Dispensar"
#: ../src/jarabe/frame/activitiestray.py:810
#, python-format
msgid "Transfer to %r"
-msgstr "Transferência para %r"
+msgstr "Transferir para %r"
#: ../src/jarabe/frame/clipboardmenu.py:52
msgid "Remove"
@@ -757,7 +876,7 @@ msgstr "Abrir com"
#: ../src/jarabe/frame/clipboardobject.py:49
#, python-format
msgid "%s clipping"
-msgstr "recorte de %s"
+msgstr "Recorte de %s"
#: ../src/jarabe/frame/zoomtoolbar.py:36
msgid "Neighborhood"
@@ -779,7 +898,8 @@ msgstr "Atividade"
msgid "Click to change color:"
msgstr "Clique para mudar a cor:"
-#: ../src/jarabe/intro/window.py:174 ../src/jarabe/journal/detailview.py:103
+#: ../src/jarabe/intro/window.py:174
+#: ../src/jarabe/journal/detailview.py:103
msgid "Back"
msgstr "Voltar"
@@ -787,27 +907,27 @@ msgstr "Voltar"
msgid "Next"
msgstr "Próximo"
-#: ../src/jarabe/journal/collapsedentry.py:258
-#: ../src/jarabe/journal/expandedentry.py:159
+#: ../src/jarabe/journal/collapsedentry.py:260
+#: ../src/jarabe/journal/expandedentry.py:158
#: ../src/jarabe/journal/palettes.py:66
msgid "Untitled"
msgstr "Sem título"
-#: ../src/jarabe/journal/expandedentry.py:205
+#: ../src/jarabe/journal/expandedentry.py:211
msgid "No preview"
msgstr "Sem previsão"
-#: ../src/jarabe/journal/expandedentry.py:224
+#: ../src/jarabe/journal/expandedentry.py:230
msgid "Participants:"
msgstr "Participantes:"
-#: ../src/jarabe/journal/expandedentry.py:247
+#: ../src/jarabe/journal/expandedentry.py:253
msgid "Description:"
msgstr "Descrição:"
-#: ../src/jarabe/journal/expandedentry.py:273
+#: ../src/jarabe/journal/expandedentry.py:282
msgid "Tags:"
-msgstr "Tags:"
+msgstr "Etiquetas:"
#: ../src/jarabe/journal/journalactivity.py:108
#: ../src/jarabe/journal/volumestoolbar.py:47
@@ -816,11 +936,11 @@ msgstr "Diário"
#: ../src/jarabe/journal/journaltoolbox.py:65
msgid "Search"
-msgstr "Busca"
+msgstr "Buscar"
#: ../src/jarabe/journal/journaltoolbox.py:124
msgid "Anytime"
-msgstr "Qualquer momento"
+msgstr "Em qualquer momento"
#: ../src/jarabe/journal/journaltoolbox.py:126
msgid "Today"
@@ -830,16 +950,19 @@ msgstr "Hoje"
msgid "Since yesterday"
msgstr "Desde ontem"
+# TRANS: Filter entries modified during the last 7 days.
#. TRANS: Filter entries modified during the last 7 days.
#: ../src/jarabe/journal/journaltoolbox.py:130
msgid "Past week"
msgstr "Semana passada"
+# TRANS: Filter entries modified during the last 30 days.
#. TRANS: Filter entries modified during the last 30 days.
#: ../src/jarabe/journal/journaltoolbox.py:132
msgid "Past month"
msgstr "Mês passado"
+# TRANS: Filter entries modified during the last 356 days.
#. TRANS: Filter entries modified during the last 356 days.
#: ../src/jarabe/journal/journaltoolbox.py:134
msgid "Past year"
@@ -855,21 +978,25 @@ msgstr "Meus amigos"
#: ../src/jarabe/journal/journaltoolbox.py:144
msgid "My class"
-msgstr "Minha classe"
+msgstr "Minha turma"
+# TRANS: Item in a combo box that filters by entry type.
#. TRANS: Item in a combo box that filters by entry type.
#: ../src/jarabe/journal/journaltoolbox.py:271
msgid "Anything"
msgstr "Qualquer coisa"
+# TODO: Add "Start with" menu item
#: ../src/jarabe/journal/journaltoolbox.py:347
#: ../src/jarabe/journal/palettes.py:90
msgid "Copy"
msgstr "Copiar"
+# TRANS: Action label for starting an entry.
#. TRANS: Action label for starting an entry.
#: ../src/jarabe/journal/journaltoolbox.py:428
-#: ../src/jarabe/journal/palettes.py:75 ../src/jarabe/view/palettes.py:135
+#: ../src/jarabe/journal/palettes.py:75
+#: ../src/jarabe/view/palettes.py:135
msgid "Start"
msgstr "Iniciar"
@@ -879,9 +1006,9 @@ msgstr "Seu Diário está vazio"
#: ../src/jarabe/journal/listview.py:41
msgid "No matching entries "
-msgstr "Nenhum registro encontrado "
+msgstr "Nenhuma entrada correspondente"
-#: ../src/jarabe/journal/listview.py:370
+#: ../src/jarabe/journal/listview.py:372
msgid "Clear search"
msgstr "Limpar busca"
@@ -895,15 +1022,15 @@ msgstr "Seu Diário está cheio"
#: ../src/jarabe/journal/modalalert.py:67
msgid "Please delete some old Journal entries to make space for new ones."
-msgstr "Por favor, apague registros antigos do Diário para liberar mais espaço."
+msgstr "Por favor, remova algumas entradas antigas do Diário para liberar mais espaço para as novas."
#: ../src/jarabe/journal/modalalert.py:79
msgid "Show Journal"
-msgstr "Mostrar Diário"
+msgstr "Mostar Diário"
#: ../src/jarabe/journal/objectchooser.py:147
msgid "Choose an object"
-msgstr "Escolha um objeto"
+msgstr "Escolher um objeto"
#: ../src/jarabe/journal/objectchooser.py:152
#: ../src/jarabe/view/viewsource.py:308
@@ -994,21 +1121,21 @@ msgstr "Tornar favorito"
#: ../src/jarabe/view/palettes.py:241
msgid "Show contents"
-msgstr "Mostrar conteúdos"
+msgstr "Mostar conteúdos"
-#: ../src/jarabe/view/palettes.py:263 ../src/jarabe/view/palettes.py:313
+#: ../src/jarabe/view/palettes.py:263
+#: ../src/jarabe/view/palettes.py:313
#, python-format
msgid "%(free_space)d MB Free"
msgstr "%(free_space)d MB Livre"
-# Pode ser Ejetar
#: ../src/jarabe/view/palettes.py:288
msgid "Unmount"
-msgstr "Desmontar"
+msgstr "Desconectar"
#: ../src/jarabe/view/viewsource.py:208
msgid "Instance Source"
-msgstr "Fonte de instância"
+msgstr "Instância de Fonte"
#: ../src/jarabe/view/viewsource.py:233
msgid "Source"
@@ -1023,144 +1150,165 @@ msgstr "Pacote Fonte da Atividade"
msgid "View source: %r"
msgstr "Ver fonte: %r"
-#~ msgid "Invite"
-#~ msgstr "Convidar"
-
-#~ msgid "Add to journal"
-#~ msgstr "Adicionar ao diário"
-
-#, python-format
-#~ msgid "Clipboard object: %s."
-#~ msgstr "Objeto da prancheta: %s"
-
-#~ msgid "Text"
-#~ msgstr "Texto"
-
-#~ msgid "Image"
-#~ msgstr "Imagem"
-
+#~ msgid "Keyboard"
+#~ msgstr "Teclado"
+
+# in Brasil we have keyboard: US-International, abnt, abnt-2 (abnt-associação brasileira de normas técnicas)
+#~ msgid "Keyboard Model"
+#~ msgstr "Modelo do Teclado"
+#~ msgid "Key(s) to change layout"
+#~ msgstr "Teclas(s) para trocar a configuração"
+#~ msgid "Keyboard Layout(s)"
+#~ msgstr "Disposições de Teclado"
+#~ msgid ""
+#~ "Add languages in the order you prefer. If a translation is not available, "
+#~ "the next in the list will be used."
+#~ msgstr ""
+#~ "Adicione idiomas na ordem que preferir. Se uma tradução não estiver "
+#~ "disponível, a próxima da lista será usada."
+#~ msgid "APN:"
+#~ msgstr "APN:"
+#~ msgid "Error in extreme pm argument, use on/off."
+#~ msgstr "Erro no argumento pm extremo, usar ligar/desligar."
+#~ msgid ""
+#~ "Extreme power management (disableswireless radio, increases battery life)"
+#~ msgstr ""
+#~ "Gerenciamento de energia extremo (desliga o wireless, aumentando a "
+#~ "duração da bateria)"
+#~ msgid "Software update"
+#~ msgstr "Atualização de software"
+#~ msgid ""
+#~ "Software updates correct errors, eliminate security vulnerabilities, and "
+#~ "provide new features."
+#~ msgstr ""
+#~ "Atualização de software corrige erros, elimina vulnerabilidades de "
+#~ "segurança, e fornece novas funcionalidades."
+#~ msgid "Checking %s..."
+#~ msgstr "Verificando %s..."
+#~ msgid "Downloading %s..."
+#~ msgstr "Transferindo %s..."
+#~ msgid "Updating %s..."
+#~ msgstr "Atualizando %s..."
+#~ msgid "Your software is up-to-date"
+#~ msgstr "Seu software está atualizado"
+#~ msgid "You can install %s update"
+#~ msgid_plural "You can install %s updates"
+#~ msgstr[0] "Você pode instalar %s atualização"
+#~ msgstr[1] "Você pode instalar %s atualizações"
+#~ msgid "Checking for updates..."
+#~ msgstr "Verificando atualizações..."
+#~ msgid "Installing updates..."
+#~ msgstr "Instalando atualizações..."
+#~ msgid "%s update was installed"
+#~ msgid_plural "%s updates were installed"
+#~ msgstr[0] "A atualização %s foi instalada"
+#~ msgstr[1] "As atualizações %s foram instaladas"
+#~ msgid "Install selected"
+#~ msgstr "Instalar selecionados"
+#~ msgid "Download size: %s"
+#~ msgstr "Tamanho da transferência: %s"
+#~ msgid "From version %(current)d to %(new)s (Size: %(size)s)"
+#~ msgstr "Da versão %(current)d para %(new)s (Tamanho: %(size)s)"
+#~ msgid "None"
+#~ msgstr "Nulo"
+#~ msgid "1 KB"
+#~ msgstr "1 KB"
+#~ msgid "%.0f KB"
+#~ msgstr "%.0f KB"
+#~ msgid "%.1f MB"
+#~ msgstr "%.1f MB"
#~ msgid "Mesh Network"
#~ msgstr "Rede Mesh"
-
-#~ msgid "My Battery life"
-#~ msgstr "Carga de minha Bateria"
-
-#~ msgid "Battery charging"
-#~ msgstr "Carregando a bateria"
-
-#~ msgid "Battery discharging"
-#~ msgstr "Descarregando a bateria"
-
-#~ msgid "Battery fully charged"
-#~ msgstr "Bateria completamente carregada"
-
-#~ msgid "Share"
-#~ msgstr "Compartilhar"
-
-#, python-format
-#~ msgid "%s Activity"
-#~ msgstr "Atividade %s"
-
+#~ msgid "Mesh"
+#~ msgstr "Mesh"
+#~ msgid "Screenshot of \"%s\""
+#~ msgstr "Tela capturada de \"%s\""
+#~ msgid ""
+#~ "\"disabled\" to ask nick on initialization; \"system\" to reuse UNIX "
+#~ "account long name."
+#~ msgstr ""
+#~ "\"desativado\" para pedir apelido na inicialização; \"sistema\" para "
+#~ "reusar nome completo da conta UNIX."
+#~ msgid "Default nick"
+#~ msgstr "Apelido padrão"
+#~ msgid "If TRUE, Sugar will show a \"Log out\" option."
+#~ msgstr "Se TRUE (verdadeiro), o Sugar mostrará uma opção \"Deslogar\"."
+#~ msgid "Keyboard layouts"
+#~ msgstr "Disposições de teclado"
+#~ msgid "Keyboard model"
+#~ msgstr "Modelo do teclado"
+#~ msgid "Keyboard options"
+#~ msgstr "Opções do teclado"
+#~ msgid ""
+#~ "List of keyboard layouts. Each entry should be in the form layout(variant)"
+#~ msgstr ""
+#~ "Lista de disposições de teclado. Cada entrada deve estar no esquema "
+#~ "(forma variante)"
+#~ msgid "List of keyboard options."
+#~ msgstr "Lista de opções do teclado."
+#~ msgid "Show Log out"
+#~ msgstr "Motrar Deslogar"
+#~ msgid "The keyboard model to be used"
+#~ msgstr "Modelo de teclado a ser usado"
+#~ msgid "Version %s"
+#~ msgstr "Versão %s"
+#~ msgid "F1"
+#~ msgstr "F1"
+#~ msgid "F2"
+#~ msgstr "F2"
+#~ msgid "F3"
+#~ msgstr "F3"
+#~ msgid "F4"
+#~ msgstr "F4"
+#~ msgid "Kind: %s"
+#~ msgstr "Tipo: %s"
+#~ msgid "Unknown"
+#~ msgstr "Desconhecido"
+#~ msgid "Date: %s"
+#~ msgstr "Data: %s"
+#~ msgid "Size: %s"
+#~ msgstr "Tamanho: %s"
+#~ msgid "Start new"
+#~ msgstr "Iniciar novo"
+#~ msgid "Title"
+#~ msgstr "Título"
+#~ msgid "Version"
+#~ msgstr "Versão"
+#~ msgid "Date"
+#~ msgstr "Data"
#~ msgid "Encryption Type:"
#~ msgstr "Tipo de Encriptação:"
+# 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
+#, fuzzy
+#~ msgid "Disconnecting..."
+#~ msgstr "Desconectar"
-#~ msgid "Reboot"
-#~ msgstr "Reiniciar"
-
-#~ msgid "Share with:"
-#~ msgstr "Partilhar com:"
-
-#~ msgid "Private"
-#~ msgstr "Privado"
-
-#~ msgid "My Neighborhood"
-#~ msgstr "Minha Vizinhança"
-
-#~ msgid "Undo"
-#~ msgstr "Desfazer"
-
-# optei pela forma de "fazer de novo" ao invés de refazer por acreditar que ela seja mais fácil de compreender para crianças.
-#~ msgid "Redo"
-#~ msgstr "Fazer de novo"
-
-#~ msgid "Paste"
-#~ msgstr "Colar"
-
-#~ msgid "Keep error"
-#~ msgstr "Erro ao manter"
-
-#~ msgid "Keep error: all changes will be lost"
-#~ msgstr "Erro ao manter: todas as alterações serão desfeitas"
-
-#~ msgid "Don't stop"
-#~ msgstr "Não pare"
-
-#~ msgid "Stop anyway"
-#~ msgstr "Pare mesmo assim"
-
-#~ msgid "Continue"
-#~ msgstr "Continuar"
-
-#~ msgid "OK"
-#~ msgstr "OK"
-
-#, python-format
-#~ msgid "%d year"
-#~ msgstr "%d ano"
-
-#, python-format
-#~ msgid "%d years"
-#~ msgstr "%d anos"
-
-#, python-format
-#~ msgid "%d month"
-#~ msgstr "%d mês"
-
-#, python-format
-#~ msgid "%d months"
-#~ msgstr "%d meses"
-
-#, python-format
-#~ msgid "%d week"
-#~ msgstr "%d semana"
-
-#, python-format
-#~ msgid "%d weeks"
-#~ msgstr "%d semanas"
-
-#, python-format
-#~ msgid "%d day"
-#~ msgstr "%d dia"
-
-#, python-format
-#~ msgid "%d days"
-#~ msgstr "%d dias"
+#~ msgid "Connected to a School Mesh Portal"
+#~ msgstr "Conectado ao Portal Mesh da Escola"
-#, python-format
-#~ msgid "%d hour"
-#~ msgstr "%d hora"
+#~ msgid "Looking for a School Mesh Portal..."
+#~ msgstr "Procurando por um Portal Mesh da Escola..."
-#, python-format
-#~ msgid "%d hours"
-#~ msgstr "%d horas"
+#~ msgid "Connected to an XO Mesh Portal"
+#~ msgstr "Conectado ao Portal Mesh de um XO"
-#, python-format
-#~ msgid "%d minute"
-#~ msgstr "%d minuto"
+#~ msgid "Looking for an XO Mesh Portal..."
+#~ msgstr "Procurando pelo Portal Mesh de algum XO..."
-#, python-format
-#~ msgid "%d minutes"
-#~ msgstr "%d minutos"
+#~ msgid "Connected to a Simple Mesh"
+#~ msgstr "Conectado a uma Mesh Simples"
-#, python-format
-#~ msgid "%d second"
-#~ msgstr "%d segundo"
+#~ msgid "Starting a Simple Mesh"
+#~ msgstr "Iniciando uma Mesh Simples"
-#~ msgid " and "
-#~ msgstr " e "
+#~ msgid "Unknown Mesh"
+#~ msgstr "Mesh desconhecida"
-#~ msgid ", "
-#~ msgstr ", "
+#~ msgid "Clipboard object: %s."
+#~ msgstr "Objeto da prancheta: %s"
#~ msgid "off"
#~ msgstr "desligado"
@@ -1170,48 +1318,17 @@ msgstr "Ver fonte: %r"
#~ msgid "Permission denied. You need to be root to run this method."
#~ msgstr ""
-#~ "Permissão negada. Você precisa ser o usuário root para executar esse método."
+#~ "Permissão negada. Você precisa ser o usuário root para executar esse "
+#~ "método."
#~ msgid "Error in reading timezone"
#~ msgstr "Erro ao ler o fuso horário"
-#, python-format
#~ msgid "Error copying timezone (from %s): %s"
#~ msgstr "Erro ao copiar o fuso horário (de %s): %s"
-#, python-format
#~ msgid "Changing permission of timezone: %s"
#~ msgstr "Mudando a permissão do fuso horário: %s"
-#~ msgid "Connected to a School Mesh Portal"
-#~ msgstr "Conectado ao Portal Mesh da Escola"
-
-#~ msgid "Looking for a School Mesh Portal..."
-#~ msgstr "Procurando por um Portal Mesh da Escola..."
-
-#~ msgid "Connected to an XO Mesh Portal"
-#~ msgstr "Conectado ao Portal Mesh de um XO"
-
-#~ msgid "Looking for an XO Mesh Portal..."
-#~ msgstr "Procurando pelo Portal Mesh de algum XO..."
-
-#~ msgid "Connected to a Simple Mesh"
-#~ msgstr "Conectado a uma Mesh Simples"
-
-#~ msgid "Starting a Simple Mesh"
-#~ msgstr "Iniciando uma Mesh Simples"
-
-#~ msgid "Unknown Mesh"
-#~ msgstr "Mesh desconhecida"
-
#~ msgid "About this XO"
#~ msgstr "Sobre este XO"
-
-#: ../extensions/deviceicon/network.py:114
-msgid "Create new wireless network"
-msgstr "Criar nova rede wireless"
-
-#: ../extensions/deviceicon/network.py:415
-#, python-format
-msgid "%s's network %s"
-msgstr "rede da %s %s"
diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
index 5d6f900..90b0752 100644
--- a/src/jarabe/desktop/activitieslist.py
+++ b/src/jarabe/desktop/activitieslist.py
@@ -14,7 +14,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+import os
import logging
+from gettext import gettext as _
import gobject
import gtk
@@ -23,8 +25,10 @@ import gconf
from sugar import util
from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon
+from sugar.graphics.icon import CanvasIcon, Icon
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
@@ -35,11 +39,6 @@ from jarabe.view import launcher
class ActivitiesList(gtk.VBox):
__gtype_name__ = 'SugarActivitiesList'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self):
logging.debug('STARTUP: Loading the activities list')
@@ -94,7 +93,33 @@ class ActivitiesList(gtk.VBox):
entry.set_visible(entry.matches(self._query))
def __erase_activated_cb(self, activity_icon, bundle_id):
- self.emit('erase-activated', bundle_id)
+ registry = bundleregistry.get_registry()
+ activity_info = registry.get_bundle(bundle_id)
+
+ alert = Alert()
+ alert.props.title = _('Confirm erase')
+ alert.props.msg = \
+ _('Confirm erase: Do you want to permanently erase %s?') \
+ % activity_info.get_name()
+
+ cancel_icon = Icon(icon_name='dialog-cancel')
+ alert.add_button(gtk.RESPONSE_CANCEL, _('Keep'), cancel_icon)
+
+ erase_icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_OK, _('Erase'), erase_icon)
+
+ alert.connect('response', self.__erase_confirmation_dialog_response_cb,
+ bundle_id)
+
+ self.add_alert(alert)
+
+ def __erase_confirmation_dialog_response_cb(self, alert, response_id,
+ bundle_id):
+ self.remove_alert()
+ if response_id == gtk.RESPONSE_OK:
+ registry = bundleregistry.get_registry()
+ bundle = registry.get_bundle(bundle_id)
+ registry.uninstall(bundle)
def set_filter(self, query):
self._query = query
@@ -150,11 +175,11 @@ class ActivityIcon(CanvasIcon):
self._xocolor = XoColor(client.get_string("/desktop/sugar/user/color"))
def create_palette(self):
- palette = ActivityPalette(self._activity_info)
+ palette = ActivityListPalette(self._activity_info)
palette.connect('erase-activated', self.__erase_activated_cb)
return palette
- def __erase_activated_cb(self, palette):
+ def __erase_activated_cb(self, palette, bundle_id):
self.emit('erase-activated', self._activity_info.get_bundle_id())
def _color(self):
@@ -178,7 +203,7 @@ class ActivityEntry(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarActivityEntry'
_TITLE_COL_WIDTH = style.GRID_CELL_SIZE * 3
- _VERSION_COL_WIDTH = style.GRID_CELL_SIZE * 1
+ _VERSION_COL_WIDTH = style.GRID_CELL_SIZE * 3
_DATE_COL_WIDTH = style.GRID_CELL_SIZE * 5
def __init__(self, activity_info):
@@ -322,3 +347,84 @@ class FavoriteIcon(CanvasIcon):
icon.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+
+class ActivityListPalette(ActivityPalette):
+ __gtype_name__ = 'SugarActivityListPalette'
+
+ __gsignals__ = {
+ 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([str]))
+ }
+
+ def __init__(self, activity_info):
+ ActivityPalette.__init__(self, activity_info)
+
+ self._bundle_id = activity_info.get_bundle_id()
+ self._version = activity_info.get_activity_version()
+
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+
+ self._favorite_item = MenuItem('')
+ self._favorite_icon = Icon(icon_name='emblem-favorite',
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._favorite_item.set_image(self._favorite_icon)
+ self._favorite_item.connect('activate',
+ self.__change_favorite_activate_cb)
+ self.menu.append(self._favorite_item)
+ self._favorite_item.show()
+
+ self._add_erase_option(registry, activity_info)
+
+ registry = bundleregistry.get_registry()
+ self._activity_changed_sid = registry.connect('bundle_changed',
+ self.__activity_changed_cb)
+ self._update_favorite_item()
+
+ self.connect('destroy', self.__destroy_cb)
+
+ def _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)
+
+ def _update_favorite_item(self):
+ label = self._favorite_item.child
+ if self._favorite:
+ label.set_text(_('Remove favorite'))
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ else:
+ label.set_text(_('Make favorite'))
+ client = gconf.client_get_default()
+ xo_color = XoColor(client.get_string("/desktop/sugar/user/color"))
+
+ self._favorite_icon.props.xo_color = xo_color
+
+ def __change_favorite_activate_cb(self, menu_item):
+ registry = bundleregistry.get_registry()
+ registry.set_bundle_favorite(self._bundle_id,
+ self._version,
+ not self._favorite)
+
+ def __activity_changed_cb(self, activity_registry, activity_info):
+ if activity_info.get_bundle_id() == self._bundle_id and \
+ activity_info.get_activity_version() == self._version:
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+ self._update_favorite_item()
+
+ def __erase_activate_cb(self, menu_item):
+ self.emit('erase-activated', self._bundle_id)
+
diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py
index 5ea76b8..848ee9e 100644
--- a/src/jarabe/desktop/favoritesview.py
+++ b/src/jarabe/desktop/favoritesview.py
@@ -65,11 +65,6 @@ about the layout can be accessed with fields of the class."""
class FavoritesView(hippo.Canvas):
__gtype_name__ = 'SugarFavoritesView'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self, **kwargs):
logging.debug('STARTUP: Loading the favorites view')
@@ -134,14 +129,10 @@ class FavoritesView(hippo.Canvas):
if activity_info.get_bundle_id() == 'org.laptop.JournalActivity':
return
icon = ActivityIcon(activity_info, self._datastore_listener)
- icon.connect('erase-activated', self.__erase_activated_cb)
icon.props.size = style.STANDARD_ICON_SIZE
self._box.insert_sorted(icon, 0, self._layout.compare_activities)
self._layout.append(icon)
- def __erase_activated_cb(self, activity_icon, bundle_id):
- self.emit('erase-activated', bundle_id)
-
def __activity_added_cb(self, activity_registry, activity_info):
registry = bundleregistry.get_registry()
if registry.is_bundle_favorite(activity_info.get_bundle_id(),
@@ -282,7 +273,9 @@ class FavoritesView(hippo.Canvas):
def _set_layout(self, layout):
if layout not in LAYOUT_MAP:
- raise ValueError('Unknown favorites layout: %r' % layout)
+ logging.warn('Unknown favorites layout: %r' % layout)
+ layout = favoriteslayout.RingLayout.key
+ assert layout in LAYOUT_MAP
if type(self._layout) == LAYOUT_MAP[layout]:
return
@@ -402,11 +395,6 @@ class ActivityIcon(CanvasIcon):
_BORDER_WIDTH = style.zoom(3)
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([str]))
- }
-
def __init__(self, activity_info, datastore_listener):
CanvasIcon.__init__(self, cache=True,
file_name=activity_info.get_icon())
@@ -469,12 +457,8 @@ class ActivityIcon(CanvasIcon):
def create_palette(self):
palette = FavoritePalette(self._activity_info, self._journal_entries)
palette.connect('activate', self.__palette_activate_cb)
- palette.connect('erase-activated', self.__erase_activated_cb)
return palette
- def __erase_activated_cb(self, palette):
- self.emit('erase-activated', self._activity_info.get_bundle_id())
-
def __palette_activate_cb(self, palette):
self._activate()
@@ -609,7 +593,7 @@ class FavoritePalette(ActivityPalette):
def __resume_entry_cb(self, menu_item, entry):
if entry is not None:
- journal.misc.resume(entry, self._bundle_id)
+ journal.misc.resume(entry, entry['activity'])
class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
def __init__(self):
@@ -688,7 +672,7 @@ class _MyIcon(MyIcon):
self.emit('register-activate')
def remove_register_menu(self):
- self.palette.remove(self._register_menu)
+ self.palette.menu.remove(self._register_menu)
class FavoritesSetting(object):
diff --git a/src/jarabe/desktop/homebox.py b/src/jarabe/desktop/homebox.py
index 6fdc8f1..fdfb7a4 100644
--- a/src/jarabe/desktop/homebox.py
+++ b/src/jarabe/desktop/homebox.py
@@ -27,7 +27,6 @@ from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.alert import Alert
from sugar.graphics.icon import Icon
-from jarabe.model import bundleregistry
from jarabe.desktop import favoritesview
from jarabe.desktop.activitieslist import ActivitiesList
@@ -47,10 +46,6 @@ class HomeBox(gtk.VBox):
self._favorites_view = favoritesview.FavoritesView()
self._list_view = ActivitiesList()
- self._favorites_view.connect('erase-activated',
- self.__erase_activated_cb)
- self._list_view.connect('erase-activated', self.__erase_activated_cb)
-
self._toolbar = HomeToolbar()
self._toolbar.connect('query-changed', self.__toolbar_query_changed_cb)
self._toolbar.connect('view-changed', self.__toolbar_view_changed_cb)
@@ -59,44 +54,6 @@ class HomeBox(gtk.VBox):
self._set_view(_FAVORITES_VIEW)
- def __erase_activated_cb(self, view, bundle_id):
- registry = bundleregistry.get_registry()
- activity_info = registry.get_bundle(bundle_id)
-
- alert = Alert()
- alert.props.title = _('Confirm erase')
- alert.props.msg = \
- _('Confirm erase: Do you want to permanently erase %s?') \
- % activity_info.get_name()
-
- cancel_icon = Icon(icon_name='dialog-cancel')
- alert.add_button(gtk.RESPONSE_CANCEL, _('Keep'), cancel_icon)
-
- erase_icon = Icon(icon_name='dialog-ok')
- alert.add_button(gtk.RESPONSE_OK, _('Erase'), erase_icon)
-
- if self._list_view in self.get_children():
- self._list_view.add_alert(alert)
- else:
- self._favorites_view.add_alert(alert)
- # TODO: If the favorite layouts didn't hardcoded the box size, we could
- # just pack an alert between the toolbar and the canvas.
- #self.pack_start(alert, False)
- #self.reorder_child(alert, 1)
- alert.connect('response', self.__erase_confirmation_dialog_response_cb,
- bundle_id)
-
- def __erase_confirmation_dialog_response_cb(self, alert, response_id,
- bundle_id):
- if self._list_view in self.get_children():
- self._list_view.remove_alert()
- else:
- self._favorites_view.remove_alert()
- if response_id == gtk.RESPONSE_OK:
- registry = bundleregistry.get_registry()
- bundle = registry.get_bundle(bundle_id)
- registry.uninstall(bundle)
-
def show_software_updates_alert(self):
alert = Alert()
updater_icon = Icon(icon_name='module-updater',
diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py
index bbb0db1..19cc5a2 100644
--- a/src/jarabe/desktop/homewindow.py
+++ b/src/jarabe/desktop/homewindow.py
@@ -82,9 +82,9 @@ class HomeWindow(gtk.Window):
def _visibility_notify_event_cb(self, window, event):
if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:
- self._deactivate_view()
+ self._deactivate_view(shell.get_model().zoom_level)
else:
- self._activate_view()
+ self._activate_view(shell.get_model().zoom_level)
def __zoom_level_changed_cb(self, **kwargs):
old_level = kwargs['old_level']
diff --git a/src/jarabe/desktop/meshbox.py b/src/jarabe/desktop/meshbox.py
index e7bba7b..41ffcc6 100644
--- a/src/jarabe/desktop/meshbox.py
+++ b/src/jarabe/desktop/meshbox.py
@@ -1,6 +1,6 @@
# 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
#
# 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,6 +24,7 @@ import dbus
import hippo
import gobject
import gtk
+import gconf
from sugar.graphics.icon import CanvasIcon, Icon
from sugar.graphics.xocolor import XoColor
@@ -36,6 +37,7 @@ 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.view.buddyicon import BuddyIcon
@@ -51,17 +53,21 @@ 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.network import OlpcMesh as OlpcMeshSettings
+from jarabe.model.olpcmesh import OlpcMeshManager
+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'
-_ICON_NAME = 'network-wireless'
-
+_AP_ICON_NAME = 'network-wireless'
+_OLPC_MESH_ICON_NAME = 'network-mesh'
class WirelessNetworkView(CanvasPulsingIcon):
def __init__(self, initial_ap):
@@ -83,14 +89,11 @@ class WirelessNetworkView(CanvasPulsingIcon):
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])
+ if self._mode == network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._name):
+ self._color = profile.get_color()
else:
sh = sha.new()
data = self._name + hex(self._flags)
@@ -112,18 +115,9 @@ class WirelessNetworkView(CanvasPulsingIcon):
self.set_palette(self._palette)
self._palette_icon.props.xo_color = self._color
- if network.find_connection(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
+ self._update_badge()
- interface_props = dbus.Interface(self._device,
- 'org.freedesktop.DBus.Properties')
+ 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)
@@ -143,17 +137,12 @@ class WirelessNetworkView(CanvasPulsingIcon):
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(_ICON_NAME, self._strength)
+ 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)
@@ -171,6 +160,8 @@ class WirelessNetworkView(CanvasPulsingIcon):
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:
@@ -178,12 +169,10 @@ class WirelessNetworkView(CanvasPulsingIcon):
# 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:
@@ -203,14 +192,38 @@ class WirelessNetworkView(CanvasPulsingIcon):
def __get_device_state_reply_cb(self, state):
self._device_state = state
- self._update()
+ 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(self):
- self._update_state()
- self._update_color()
+ 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_state(self):
if self._active_ap is not None:
@@ -218,22 +231,6 @@ class WirelessNetworkView(CanvasPulsingIcon):
else:
state = network.DEVICE_STATE_UNKNOWN
- if state == network.DEVICE_STATE_ACTIVATED:
- connection = network.find_connection(self._name)
- if connection:
- if self._mode == network.NM_802_11_MODE_INFRA:
- connection.set_connected()
-
- icon_name = '%s-connected' % _ICON_NAME
- else:
- icon_name = _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 \
@@ -244,6 +241,10 @@ class WirelessNetworkView(CanvasPulsingIcon):
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()
@@ -256,15 +257,51 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._palette.props.secondary_text = None
self.props.pulsing = False
- def _update_color(self):
+ 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 _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 _disconnect_activate_cb(self, item):
- pass
+ connection = network.find_connection_by_ssid(self._name)
+ if connection:
+ if self._mode == network.NM_802_11_MODE_INFRA:
+ connection.set_disconnected()
+
+ obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
+ netmgr = dbus.Interface(obj, _NM_IFACE)
+
+ 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, dbus.PROPERTIES_IFACE)
+ state = props.Get(_NM_ACTIVE_CONN_IFACE, 'State')
+ if state == network.NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+ ap_o = props.Get(_NM_ACTIVE_CONN_IFACE, 'SpecificObject')
+ if ap_o != '/' and self.find_ap(ap_o) is not None:
+ netmgr.DeactivateConnection(conn_o)
+ else:
+ logging.error('Could not determine AP for'
+ ' specific object %s' % conn_o)
def _add_ciphers_from_flags(self, flags, pairwise):
ciphers = []
@@ -336,11 +373,11 @@ class WirelessNetworkView(CanvasPulsingIcon):
self._connect()
def _connect(self):
- connection = network.find_connection(self._name)
+ connection = network.find_connection_by_ssid(self._name)
if connection is None:
settings = Settings()
settings.connection.id = 'Auto ' + self._name
- settings.connection.uuid = unique_id()
+ uuid = settings.connection.uuid = unique_id()
settings.connection.type = '802-11-wireless'
settings.wireless.ssid = self._name
@@ -358,7 +395,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
if wireless_security is not None:
settings.wireless.security = '802-11-wireless-security'
- connection = network.add_connection(self._name, settings)
+ connection = network.add_connection(uuid, settings)
obj = self._bus.get_object(_NM_SERVICE, _NM_PATH)
netmgr = dbus.Interface(obj, _NM_IFACE)
@@ -377,7 +414,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
def set_filter(self, query):
self._greyed_out = self._name.lower().find(query) == -1
- self._update_state()
+ self._update_icon()
self._update_color()
def create_keydialog(self, settings, response):
@@ -396,7 +433,7 @@ class WirelessNetworkView(CanvasPulsingIcon):
if new_strength != self._strength:
self._strength = new_strength
- self._update_state()
+ self._update_icon()
def add_ap(self, ap):
self._access_points[ap.model.object_path] = ap
@@ -419,6 +456,17 @@ class WirelessNetworkView(CanvasPulsingIcon):
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',
@@ -430,6 +478,280 @@ class WirelessNetworkView(CanvasPulsingIcon):
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._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):
+ 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)
+
+
class ActivityView(hippo.CanvasBox):
def __init__(self, model):
hippo.CanvasBox.__init__(self)
@@ -616,13 +938,19 @@ 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 = dbus.Interface(device, _NM_WIRELESS_IFACE)
wireless.GetAccessPoints(reply_handler=self._get_access_points_reply_cb,
error_handler=self._get_access_points_error_cb)
@@ -638,35 +966,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 = dbus.SystemBus()
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:
@@ -685,6 +1020,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:
@@ -694,13 +1032,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')
@@ -724,11 +1061,20 @@ 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):
logging.error('Failed to get device type: %s', err)
@@ -741,6 +1087,28 @@ 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
+
+ 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):
@@ -752,11 +1120,13 @@ class MeshBox(gtk.VBox):
gobject.GObject.__init__(self)
self.wireless_networks = {}
+ self._adhoc_manager = None
+ self._adhoc_networks = []
self._model = neighborhood.get_model()
self._buddies = {}
self._activities = {}
- self._mesh = {}
+ self._mesh = []
self._buddy_to_activity = {}
self._suspended = True
self._query = ''
@@ -901,6 +1271,23 @@ class MeshBox(gtk.VBox):
del self.wireless_networks[hash]
def _ap_props_changed_cb(self, ap, old_hash):
+ # 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")
+ ap.disconnect()
+ return
+
+ 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 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 is None: # new AP finished initializing
self._add_ap_to_network(ap)
return
@@ -923,6 +1310,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():
@@ -935,18 +1327,68 @@ class MeshBox(gtk.VBox):
self._remove_net_if_empty(net, ap.network_hash())
return
- logging.error('Can not remove access point %s', ap_o)
+ # 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)
+
+ 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)
+ self._layout.add(icon)
+ self._mesh.append(icon)
+
+ def enable_olpc_mesh(self, mesh_device):
+ mesh_mgr = OlpcMeshManager(mesh_device)
+ self._add_olpc_mesh_icon(mesh_mgr, 1)
+ self._add_olpc_mesh_icon(mesh_mgr, 6)
+ self._add_olpc_mesh_icon(mesh_mgr, 11)
+
+ # 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():
+ if not net.is_olpc_mesh():
+ continue
+
+ logging.debug("removing OLPC mesh IBSS")
+ net.remove_all_aps()
+ net.disconnect()
+ self._layout.remove(net)
+ del self.wireless_networks[hash]
+
+ def disable_olpc_mesh(self, mesh_device):
+ for icon in self._mesh:
+ icon.disconnect()
+ self._layout.remove(icon)
+ self._mesh = []
def suspend(self):
if not self._suspended:
self._suspended = True
- for net in self.wireless_networks.values():
+ for net in self.wireless_networks.values() + self._mesh:
net.props.paused = True
def resume(self):
if self._suspended:
self._suspended = False
- for net in self.wireless_networks.values():
+ for net in self.wireless_networks.values() + self._mesh:
net.props.paused = False
def _toolbar_query_changed_cb(self, toolbar, query):
diff --git a/src/jarabe/desktop/schoolserver.py b/src/jarabe/desktop/schoolserver.py
index 1dd9edc..a7d0e63 100644
--- a/src/jarabe/desktop/schoolserver.py
+++ b/src/jarabe/desktop/schoolserver.py
@@ -20,6 +20,7 @@ from xmlrpclib import ServerProxy, Error
import socket
import os
import gconf
+import dbus
from sugar.profile import get_profile
@@ -57,6 +58,8 @@ def register_laptop(url=REGISTER_URL):
client.set_string('/desktop/sugar/collaboration/jabber_server',
data['jabberserver'])
+ _restart_jabber()
+
client.set_string('/desktop/sugar/backup_url', data['backupurl'])
return True
@@ -72,3 +75,19 @@ def read_ofw(path):
data = fh.read().rstrip('\0\n')
fh.close()
return data
+
+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 RegisterError('%s service not available' % _PS_SERVICE)
+ ps.RetryConnections()
diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py
index 18cc64a..657f03c 100644
--- a/src/jarabe/journal/journalactivity.py
+++ b/src/jarabe/journal/journalactivity.py
@@ -27,6 +27,9 @@ import statvfs
import os
from sugar.graphics.window import Window
+from sugar.graphics.alert import Alert
+from sugar.graphics.icon import Icon
+
from sugar.bundle.bundle import ZipExtractException, RegistrationException
from sugar import env
from sugar.activity import activityfactory
@@ -138,6 +141,18 @@ class JournalActivity(Window):
self._critical_space_alert = None
self._check_available_space()
+ def __volume_error_cb(self, gobject, message, severity):
+ alert = Alert(title=severity, msg=message)
+ icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_OK, _('Ok'), icon)
+ icon.show()
+ 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 +176,8 @@ 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,8 +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)
self._secondary_view.pack_end(self._detail_view)
@@ -180,8 +197,6 @@ class JournalActivity(Window):
def _key_press_event_cb(self, widget, event):
keyname = gtk.gdk.keyval_name(event.keyval)
- logging.info(keyname)
- logging.info(event.state)
if keyname == 'Escape':
self.show_main_view()
diff --git a/src/jarabe/journal/journalentrybundle.py b/src/jarabe/journal/journalentrybundle.py
index 9e68c06..41777c7 100644
--- a/src/jarabe/journal/journalentrybundle.py
+++ b/src/jarabe/journal/journalentrybundle.py
@@ -40,7 +40,7 @@ class JournalEntryBundle(Bundle):
def __init__(self, path):
Bundle.__init__(self, path)
- def install(self, install_path, uid=''):
+ def install(self, uid=''):
if os.environ.has_key('SUGAR_ACTIVITY_ROOT'):
install_dir = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'],
'data')
diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py
index 17a65e6..f71049e 100644
--- a/src/jarabe/journal/journaltoolbox.py
+++ b/src/jarabe/journal/journaltoolbox.py
@@ -325,6 +325,11 @@ class DetailToolbox(Toolbox):
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)
@@ -394,7 +399,22 @@ class EntryToolbar(gtk.Toolbar):
misc.resume(self._metadata, service_name)
def _copy_menu_item_activate_cb(self, menu_item, mount):
- model.copy(self._metadata, mount.get_root().get_path())
+ 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.get_root().get_path())
+ except (IOError, OSError), 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()
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 6556b08..e1ca620 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, One Laptop Per Child
+# Copyright (C) 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
@@ -23,7 +23,6 @@ import time
import hippo
import gobject
import gtk
-import dbus
from sugar.graphics import style
from sugar.graphics.icon import CanvasIcon, Icon
@@ -31,10 +30,6 @@ from sugar.graphics.icon import CanvasIcon, Icon
from jarabe.journal.collapsedentry import CollapsedEntry
from jarabe.journal import model
-DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
-DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
-DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
-
UPDATE_INTERVAL = 300
EMPTY_JOURNAL = _("Your Journal is empty")
@@ -109,19 +104,18 @@ class BaseListView(gtk.HBox):
self._refresh_idle_handler = None
self._update_dates_timer = None
- bus = dbus.SessionBus()
- datastore = dbus.Interface(
- bus.get_object(DS_DBUS_SERVICE, DS_DBUS_PATH), DS_DBUS_INTERFACE)
- self._datastore_created_handler = \
- datastore.connect_to_signal('Created',
- self.__datastore_created_cb)
- self._datastore_updated_handler = \
- datastore.connect_to_signal('Updated',
- self.__datastore_updated_cb)
+ model.created.connect(self.__model_created_cb)
+ model.updated.connect(self.__model_updated_cb)
+ model.deleted.connect(self.__model_deleted_cb)
+
+ def __model_created_cb(self, sender, **kwargs):
+ self._set_dirty()
+
+ def __model_updated_cb(self, sender, **kwargs):
+ self._set_dirty()
- self._datastore_deleted_handler = \
- datastore.connect_to_signal('Deleted',
- self.__datastore_deleted_cb)
+ def __model_deleted_cb(self, sender, **kwargs):
+ self._set_dirty()
def __destroy_cb(self, widget):
self._datastore_created_handler.remove()
@@ -463,15 +457,6 @@ class BaseListView(gtk.HBox):
if entry.get_visible():
entry.update_date()
- def __datastore_created_cb(self, uid):
- self._set_dirty()
-
- def __datastore_updated_cb(self, uid):
- self._set_dirty()
-
- def __datastore_deleted_cb(self, uid):
- self._set_dirty()
-
def _set_dirty(self):
if self._fully_obscured:
self._dirty = True
diff --git a/src/jarabe/journal/misc.py b/src/jarabe/journal/misc.py
index b29b744..890fe60 100644
--- a/src/jarabe/journal/misc.py
+++ b/src/jarabe/journal/misc.py
@@ -95,21 +95,21 @@ def get_date(metadata):
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
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index 1b4e236..a93321e 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, 2008, 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,16 +16,18 @@
import logging
import os
+import errno
from datetime import datetime
import time
import shutil
-from stat import S_IFMT, S_IFDIR, S_IFREG
-import traceback
+import tempfile
+from stat import S_IFLNK, S_IFMT, S_IFDIR, S_IFREG
import re
+import json
+from gettext import gettext as _
import gobject
import dbus
-import gconf
import gio
from sugar import dispatch
@@ -43,6 +45,8 @@ PROPERTIES = ['uid', 'title', 'mtime', 'timestamp', 'keep', 'buddies',
PAGES_TO_CACHE = 5
+JOURNAL_METADATA_DIR = '.Sugar-Metadata'
+
class _Cache(object):
__gtype_name__ = 'model_Cache'
@@ -258,7 +262,9 @@ class InplaceResultSet(BaseResultSet):
BaseResultSet.__init__(self, query, cache_limit)
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', '')
@@ -283,7 +289,10 @@ class InplaceResultSet(BaseResultSet):
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
@@ -308,8 +317,9 @@ class InplaceResultSet(BaseResultSet):
files = self._file_list[offset:offset + limit]
entries = []
- for file_path, stat, mtime_ in files:
- metadata = _get_file_metadata(file_path, stat)
+ for file_path, stat, mtime_, metadata in files:
+ if metadata is None:
+ metadata = _get_file_metadata(file_path, stat)
metadata['mountpoint'] = self._mount_point
entries.append(metadata)
@@ -317,63 +327,166 @@ class InplaceResultSet(BaseResultSet):
return entries, total_count
- def _recurse_dir(self, dir_path):
+ def _scan(self):
if self._stopped:
+ return False
+
+ self.progress.send(self)
+
+ 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)
+ metadata = None
+
+ try:
+ stat = os.lstat(full_path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ logging.exception(
+ 'Error reading metadata of file %r', full_path)
return
- for entry in os.listdir(dir_path):
- if entry.startswith('.'):
- continue
- full_path = dir_path + '/' + entry
+ 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
+
try:
stat = os.stat(full_path)
- if S_IFMT(stat.st_mode) == S_IFDIR:
- self._pending_directories += 1
- gobject.idle_add(lambda s=full_path: self._recurse_dir(s))
- 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):
+ filename = os.path.basename(full_path)
+ dir_path = os.path.dirname(full_path)
+ metadata = _get_file_metadata_from_json( \
+ dir_path, filename, preview=False)
+ add_to_list = False
+ if metadata is not None:
+ for f in ['fulltext', 'title',
+ 'description', 'tags']:
+ if f in metadata and \
+ self._regex.match(metadata[f]):
+ add_to_list = True
+ break
+ if not add_to_list:
+ return
+
+ if self._date_start is not None and stat.st_mtime < self._date_start:
+ 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_end is not None and stat.st_mtime > self._date_end:
+ 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._mime_types:
+ mime_type = gio.content_type_guess(filename=full_path)
+ if mime_type not in self._mime_types:
+ return
- if add_to_list:
- file_info = (full_path, stat, int(stat.st_mtime))
- self._file_list.append(file_info)
+ file_info = (full_path, stat, int(stat.st_mtime), metadata)
+ self._file_list.append(file_info)
- self.progress.send(self)
+ return
- except Exception:
- logging.error('Error reading file %r: %s' % \
- (full_path, traceback.format_exc()))
+ def _scan_a_directory(self):
+ dir_path = self._pending_directories.pop(0)
- if self._pending_directories == 0:
- self.setup_ready()
- else:
- self._pending_directories -= 1
+ 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
def _get_file_metadata(path, stat):
- client = gconf.client_get_default()
+ """Returns the metadata from the corresponding file
+ on the external device or does create the metadata
+ based on the file properties.
+
+ """
+ filename = os.path.basename(path)
+ dir_path = os.path.dirname(path)
+ metadata = _get_file_metadata_from_json(dir_path, filename, preview=True)
+ if metadata:
+ return metadata
+
return {'uid': path,
'title': os.path.basename(path),
'timestamp': stat.st_mtime,
'mime_type': gio.content_type_guess(filename=path),
'activity': '',
'activity_id': '',
- 'icon-color': client.get_string('/desktop/sugar/user/color'),
+ 'icon-color': '',
'description': path}
+def _get_file_metadata_from_json(dir_path, filename, preview=False):
+ """Returns the metadata from the json file and the preview
+ stored on the external device.
+
+ """
+ metadata = None
+ metadata_path = os.path.join(dir_path, JOURNAL_METADATA_DIR,
+ filename + '.metadata')
+ if os.path.exists(metadata_path):
+ try:
+ metadata = json.load(open(metadata_path))
+ except ValueError:
+ logging.debug("Could not read metadata for file %r on" \
+ "external device.", filename)
+ else:
+ metadata['uid'] = os.path.join(dir_path, filename)
+ if preview:
+ preview_path = os.path.join(dir_path, JOURNAL_METADATA_DIR,
+ filename + '.preview')
+ if os.path.exists(preview_path):
+ try:
+ metadata['preview'] = dbus.ByteArray(open(preview_path).read())
+ except:
+ logging.debug("Could not read preview for file %r on" \
+ "external device.", filename)
+ else:
+ if metadata and 'preview' in metadata:
+ del(metadata['preview'])
+ return metadata
+
_datastore = None
def _get_datastore():
global _datastore
@@ -460,6 +573,19 @@ def delete(object_id):
"""
if os.path.exists(object_id):
os.unlink(object_id)
+ dir_path = os.path.dirname(object_id)
+ filename = os.path.basename(object_id)
+ old_files = [os.path.join(dir_path, JOURNAL_METADATA_DIR,
+ filename + '.metadata'),
+ os.path.join(dir_path, JOURNAL_METADATA_DIR,
+ filename + '.preview')]
+ for old_file in old_files:
+ if os.path.exists(old_file):
+ try:
+ os.unlink(old_file)
+ except:
+ pass
+ deleted.send(None, object_id=object_id)
else:
_get_datastore().delete(object_id)
@@ -472,9 +598,9 @@ def copy(metadata, mount_point):
metadata['mountpoint'] = mount_point
del metadata['uid']
- return write(metadata, file_path)
+ return write(metadata, file_path, transfer_ownership=False)
-def write(metadata, file_path='', update_mtime=True):
+def write(metadata, file_path='', update_mtime=True, transfer_ownership=True):
"""Creates or updates an entry for that id
"""
logging.debug('model.write %r %r %r' % (metadata.get('uid', ''), file_path,
@@ -488,31 +614,110 @@ def write(metadata, file_path='', update_mtime=True):
object_id = _get_datastore().update(metadata['uid'],
dbus.Dictionary(metadata),
file_path,
- True)
+ transfer_ownership)
else:
object_id = _get_datastore().create(dbus.Dictionary(metadata),
file_path,
- True)
+ transfer_ownership)
else:
- if not os.path.exists(file_path):
- raise ValueError('Entries without a file cannot be copied to '
- 'removable devices')
+ object_id = _write_entry_on_external_device(metadata, file_path)
- file_name = _get_file_name(metadata['title'], metadata['mime_type'])
- file_name = _get_unique_file_name(metadata['mountpoint'], file_name)
+ return object_id
+
+def _write_entry_on_external_device(metadata, file_path):
+ """This creates and updates an entry copied from the
+ DS to external storage device. Besides copying the
+ associated file a hidden file for the preview and one
+ for the metadata are stored. We make sure that the
+ metadata and preview file are in the same directory
+ as the data file.
+
+ This function handles renames of an entry on the
+ external device and avoids name collisions. Renames are
+ handled failsafe.
+
+ """
+ if 'uid' in metadata and os.path.exists(metadata['uid']):
+ file_path = metadata['uid']
+
+ if not file_path or not os.path.exists(file_path):
+ raise ValueError('Entries without a file cannot be copied to '
+ 'removable devices')
+ if metadata['title'] == '':
+ metadata['title'] = _('Untitled')
+ file_name = get_file_name(metadata['title'], metadata['mime_type'])
+
+ destination_path = os.path.join(metadata['mountpoint'], file_name)
+ if destination_path != file_path:
+ file_name = get_unique_file_name(metadata['mountpoint'], file_name)
destination_path = os.path.join(metadata['mountpoint'], file_name)
+ clean_name, extension_ = os.path.splitext(file_name)
+ metadata['title'] = clean_name
+
+ metadata_copy = metadata.copy()
+ del metadata_copy['mountpoint']
+ if 'uid' in metadata_copy:
+ del metadata_copy['uid']
+
+ metadata_dir_path = os.path.join(metadata['mountpoint'],
+ JOURNAL_METADATA_DIR)
+ if not os.path.exists(metadata_dir_path):
+ os.mkdir(metadata_dir_path)
+
+ if 'preview' in metadata_copy:
+ preview = metadata_copy['preview']
+ preview_fname = file_name + '.preview'
+ preview_path = os.path.join(metadata['mountpoint'],
+ JOURNAL_METADATA_DIR, preview_fname)
+ metadata_copy['preview'] = preview_fname
+
+ (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint'])
+ os.write(fh, preview)
+ os.close(fh)
+ os.rename(fn, preview_path)
+
+ metadata_path = os.path.join(metadata['mountpoint'],
+ JOURNAL_METADATA_DIR,
+ file_name + '.metadata')
+ (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint'])
+ os.write(fh, json.dumps(metadata_copy))
+ os.close(fh)
+ os.rename(fn, metadata_path)
+
+ if os.path.dirname(destination_path) == os.path.dirname(file_path):
+ old_file_path = file_path
+ if old_file_path != destination_path:
+ os.rename(file_path, destination_path)
+ old_fname = os.path.basename(file_path)
+ old_files = [os.path.join(metadata['mountpoint'],
+ JOURNAL_METADATA_DIR,
+ old_fname + '.metadata'),
+ os.path.join(metadata['mountpoint'],
+ JOURNAL_METADATA_DIR,
+ old_fname + '.preview')]
+ for ofile in old_files:
+ if os.path.exists(ofile):
+ try:
+ os.unlink(ofile)
+ except:
+ pass
+ else:
shutil.copy(file_path, destination_path)
- object_id = destination_path
+
+ object_id = destination_path
+ created.send(None, object_id=object_id)
return object_id
-def _get_file_name(title, mime_type):
+def get_file_name(title, mime_type):
file_name = title
- extension = '.' + mime.get_primary_extension(mime_type)
- if not file_name.endswith(extension):
- file_name += extension
+ mime_extension = mime.get_primary_extension(mime_type)
+ if mime_extension:
+ extension = '.' + mime_extension
+ if not file_name.endswith(extension):
+ file_name += extension
# Invalid characters in VFAT filenames. From
# http://en.wikipedia.org/wiki/File_Allocation_Table
@@ -529,11 +734,11 @@ def _get_file_name(title, mime_type):
return file_name
-def _get_unique_file_name(mount_point, 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
diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py
index 2c15591..c16f374 100644
--- a/src/jarabe/journal/palettes.py
+++ b/src/jarabe/journal/palettes.py
@@ -68,22 +68,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'))
@@ -134,11 +141,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):
diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py
index b21832e..9a49cdf 100644
--- a/src/jarabe/journal/volumestoolbar.py
+++ b/src/jarabe/journal/volumestoolbar.py
@@ -15,7 +15,13 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
+import os
from gettext import gettext as _
+import cPickle
+import xapian
+import json
+import tempfile
+import shutil
import gobject
import gio
@@ -29,13 +35,126 @@ from sugar.graphics.xocolor import XoColor
from jarabe.journal import model
from jarabe.view.palettes import VolumePalette
+_JOURNAL_0_METADATA_DIR = '.olpc.store'
+
+def _get_id(document):
+ """Get the ID for the document in the xapian database."""
+ tl = document.termlist()
+ try:
+ term = tl.skip_to('Q').term
+ if len(term) == 0 or term[0] != 'Q':
+ return None
+ return term[1:]
+ except StopIteration:
+ return None
+
+def _convert_entries(root):
+ """Converts the entries written by the datastore version 0.
+ The metadata and the preview will be written using the new
+ scheme for writing Journal entries to removable storage
+ devices.
+
+ - entries that do not have an associated file are not
+ converted.
+ - if an entry has no title we set it to Untitled and rename
+ the file accordingly, taking care of creating a unique
+ filename
+
+ """
+ try:
+ database = xapian.Database(os.path.join(root, _JOURNAL_0_METADATA_DIR,
+ 'index'))
+ except xapian.DatabaseError, e:
+ logging.error('Convert DS-0 Journal entry. Error reading db: %s',
+ os.path.join(root, _JOURNAL_0_METADATA_DIR, 'index'))
+ return
+
+ metadata_dir_path = os.path.join(root, model.JOURNAL_METADATA_DIR)
+ if not os.path.exists(metadata_dir_path):
+ os.mkdir(metadata_dir_path)
+
+ for i in range(1, database.get_lastdocid() + 1):
+ try:
+ document = database.get_document(i)
+ except xapian.DocNotFoundError, e:
+ logging.debug('Convert DS-0 Journal entry. ' \
+ 'Error getting document %s: %s', i, e)
+ continue
+
+ try:
+ metadata_loaded = cPickle.loads(document.get_data())
+ except cPickle.PickleError, e:
+ logging.debug('Convert DS-0 Journal entry. ' \
+ 'Error converting metadata: %s', e)
+ continue
+
+ if 'activity_id' in metadata_loaded and \
+ 'mime_type' in metadata_loaded and \
+ 'title' in metadata_loaded:
+ metadata = {}
+
+ uid = _get_id(document)
+ if uid is None:
+ continue
+
+ for key, value in metadata_loaded.items():
+ metadata[str(key)] = str(value[0])
+
+ if 'uid' not in metadata:
+ metadata['uid'] = uid
+
+ if 'filename' in metadata:
+ filename = metadata['filename']
+ else:
+ continue
+ if not os.path.exists(os.path.join(root, filename)):
+ continue
+
+ if metadata['title'] == '':
+ metadata['title'] = _('Untitled')
+ fn = model.get_file_name(metadata['title'],
+ metadata['mime_type'])
+ new_filename = model.get_unique_file_name(root, fn)
+ metadata['filename'] = new_filename
+ os.rename(os.path.join(root, filename),
+ os.path.join(root, new_filename))
+ filename = new_filename
+
+ preview_path = os.path.join(root, _JOURNAL_0_METADATA_DIR,
+ 'preview', uid)
+ if os.path.exists(preview_path):
+ preview_fname = filename + '.preview'
+ new_preview_path = os.path.join(root,
+ model.JOURNAL_METADATA_DIR,
+ preview_fname)
+ if not os.path.exists(new_preview_path):
+ metadata['preview'] = preview_fname
+ shutil.copy(preview_path, new_preview_path)
+
+ metadata_fname = filename + '.metadata'
+ metadata_path = os.path.join(root, model.JOURNAL_METADATA_DIR,
+ metadata_fname)
+ if not os.path.exists(metadata_path):
+ (fh, fn) = tempfile.mkstemp(dir=root)
+ os.write(fh, json.dumps(metadata))
+ os.close(fh)
+ os.rename(fn, metadata_path)
+
+ logging.debug('Convert DS-0 Journal entry. Entry converted: ' \
+ 'File=%s Metadata=%s',
+ os.path.join(root, filename), metadata)
+
+
class VolumesToolbar(gtk.Toolbar):
__gtype_name__ = 'VolumesToolbar'
__gsignals__ = {
'volume-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([str]))
+ ([str])),
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str, str]))
}
def __init__(self):
@@ -78,9 +197,15 @@ class VolumesToolbar(gtk.Toolbar):
def _add_button(self, mount):
logging.debug('VolumeToolbar._add_button: %r' % mount.get_name())
+ if os.path.exists(os.path.join(mount.get_root().get_path(),
+ _JOURNAL_0_METADATA_DIR)):
+ logging.debug('Convert DS-0 Journal entries.')
+ gobject.idle_add(_convert_entries, mount.get_root().get_path())
+
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 +215,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)
@@ -123,6 +251,12 @@ class VolumesToolbar(gtk.Toolbar):
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)
@@ -137,7 +271,22 @@ class BaseButton(RadioToolButton):
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, OSError), 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):
diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
index 399db65..1df2cde 100644
--- a/src/jarabe/model/Makefile.am
+++ b/src/jarabe/model/Makefile.am
@@ -1,11 +1,13 @@
sugardir = $(pythondir)/jarabe/model
sugar_PYTHON = \
+ adhoc.py \
__init__.py \
buddy.py \
bundleregistry.py \
filetransfer.py \
friends.py \
invites.py \
+ olpcmesh.py \
owner.py \
neighborhood.py \
network.py \
diff --git a/src/jarabe/model/adhoc.py b/src/jarabe/model/adhoc.py
new file mode 100644
index 0000000..5c9d6f5
--- /dev/null
+++ b/src/jarabe/model/adhoc.py
@@ -0,0 +1,280 @@
+# 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, 'org.freedesktop.DBus.Properties')
+ 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 autoconnect(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/bundleregistry.py b/src/jarabe/model/bundleregistry.py
index ac785fd..924c18f 100644
--- a/src/jarabe/model/bundleregistry.py
+++ b/src/jarabe/model/bundleregistry.py
@@ -20,6 +20,7 @@ import logging
import traceback
import sys
+import gconf
import gobject
import gio
import simplejson
@@ -27,6 +28,7 @@ import simplejson
from sugar.bundle.activitybundle import ActivityBundle
from sugar.bundle.contentbundle import ContentBundle
from jarabe.journal.journalentrybundle import JournalEntryBundle
+from sugar.bundle.bundleversion import NormalizedVersion
from sugar.bundle.bundle import MalformedBundleException, \
AlreadyInstalledException, RegistrationException
from sugar import env
@@ -62,6 +64,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:
@@ -141,14 +151,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)
@@ -272,6 +284,9 @@ 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:
@@ -324,8 +339,8 @@ 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
@@ -338,15 +353,15 @@ 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()):
raise AlreadyInstalledException
elif bundle.get_bundle_id() == installed_bundle.get_bundle_id():
self.uninstall(installed_bundle, force=True)
install_dir = env.get_user_activities_path()
if isinstance(bundle, JournalEntryBundle):
- install_path = bundle.install(install_dir, uid)
+ install_path = bundle.install(uid)
else:
install_path = bundle.install(install_dir)
@@ -371,7 +386,8 @@ class BundleRegistry(gobject.GObject):
act = self.get_bundle(bundle.get_bundle_id())
if not force and \
- act.get_activity_version() != bundle.get_activity_version():
+ NormalizedVersion(act.get_activity_version()) != \
+ NormalizedVersion(bundle.get_activity_version()):
logging.warning('Not uninstalling, different bundle present')
return
elif not act.get_path().startswith(env.get_user_activities_path()):
diff --git a/src/jarabe/model/network.py b/src/jarabe/model/network.py
index c1f7969..f0297c9 100644
--- a/src/jarabe/model/network.py
+++ b/src/jarabe/model/network.py
@@ -1,6 +1,7 @@
# 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
#
# 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
@@ -21,14 +22,20 @@ 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
+DEVICE_TYPE_802_11_OLPC_MESH = 6
DEVICE_STATE_UNKNOWN = 0
DEVICE_STATE_UNMANAGED = 1
@@ -41,6 +48,9 @@ DEVICE_STATE_IP_CONFIG = 7
DEVICE_STATE_ACTIVATED = 8
DEVICE_STATE_FAILED = 9
+NM_CONNECTION_TYPE_802_11_WIRELESS = '802-11-wireless'
+NM_CONNECTION_TYPE_GSM = 'gsm'
+
NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0
NM_ACTIVE_CONNECTION_STATE_ACTIVATING = 1
NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2
@@ -80,9 +90,48 @@ NM_CONNECTION_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection'
NM_SECRETS_IFACE = 'org.freedesktop.NetworkManagerSettings.Connection.Secrets'
NM_ACCESSPOINT_IFACE = 'org.freedesktop.NetworkManager.AccessPoint'
+GSM_USERNAME_PATH = '/desktop/sugar/network/gsm/username'
+GSM_PASSWORD_PATH = '/desktop/sugar/network/gsm/password'
+GSM_NUMBER_PATH = '/desktop/sugar/network/gsm/number'
+GSM_APN_PATH = '/desktop/sugar/network/gsm/apn'
+GSM_PIN_PATH = '/desktop/sugar/network/gsm/pin'
+GSM_PUK_PATH = '/desktop/sugar/network/gsm/puk'
+
_nm_settings = None
_conn_counter = 0
+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
@@ -103,11 +152,14 @@ class WirelessSecurity(object):
return wireless_security
class Wireless(object):
+ 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}
@@ -117,8 +169,27 @@ 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"
+
+ def __init__(self, channel, anycast_addr):
+ self.channel = channel
+ self.anycast_addr = anycast_addr
+
+ def get_dict(self):
+ ret = {
+ "ssid": dbus.ByteArray("olpc-mesh"),
+ "channel": self.channel,
+ }
+
+ if self.anycast_addr:
+ ret["dhcp-anycast-address"] = dbus.ByteArray(self.anycast_addr)
+ return ret
+
class Connection(object):
def __init__(self):
self.id = None
@@ -146,17 +217,60 @@ class IP4Config(object):
ip4_config['method'] = self.method
return ip4_config
-class Settings(object):
+class Serial(object):
+ def __init__(self):
+ self.baud = None
+
+ def get_dict(self):
+ serial = {}
+
+ if self.baud is not None:
+ serial['baud'] = self.baud
+
+ return serial
+
+class Ppp(object):
def __init__(self):
+ pass
+
+ def get_dict(self):
+ ppp = {}
+ return ppp
+
+class Gsm(object):
+ def __init__(self):
+ self.apn = None
+ self.number = None
+ self.username = None
+
+ def get_dict(self):
+ gsm = {}
+
+ if self.apn is not None:
+ gsm['apn'] = self.apn
+ if self.number is not None:
+ gsm['number'] = self.number
+ if self.username is not None:
+ gsm['username'] = self.username
+
+ return gsm
+
+class Settings(object):
+ def __init__(self, wireless_cfg=None):
self.connection = Connection()
self.wireless = Wireless()
self.ip4_config = None
self.wireless_security = None
+ if wireless_cfg is not None:
+ self.wireless = wireless_cfg
+ else:
+ self.wireless = Wireless()
+
def get_dict(self):
settings = {}
settings['connection'] = self.connection.get_dict()
- settings['802-11-wireless'] = self.wireless.get_dict()
+ settings[self.wireless.nm_name] = self.wireless.get_dict()
if self.wireless_security is not None:
settings['802-11-wireless-security'] = \
self.wireless_security.get_dict()
@@ -189,6 +303,41 @@ class Secrets(object):
return settings
+class SettingsGsm(object):
+ def __init__(self):
+ self.connection = Connection()
+ self.ip4_config = IP4Config()
+ self.serial = Serial()
+ self.ppp = Ppp()
+ self.gsm = Gsm()
+
+ def get_dict(self):
+ settings = {}
+
+ settings['connection'] = self.connection.get_dict()
+ settings['serial'] = self.serial.get_dict()
+ settings['ppp'] = self.ppp.get_dict()
+ settings['gsm'] = self.gsm.get_dict()
+ settings['ipv4'] = self.ip4_config.get_dict()
+
+ 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:
+ secrets['puk'] = self.puk
+ return {'gsm': secrets}
+
class NMSettings(dbus.service.Object):
def __init__(self):
bus = dbus.SystemBus()
@@ -207,8 +356,8 @@ class NMSettings(dbus.service.Object):
def NewConnection(self, connection_path):
pass
- def add_connection(self, ssid, conn):
- self.connections[ssid] = conn
+ def add_connection(self, uuid, conn):
+ self.connections[uuid] = conn
conn.secrets_request.connect(self.__secrets_request_cb)
self.NewConnection(conn.path)
@@ -216,6 +365,11 @@ class NMSettings(dbus.service.Object):
self.secrets_request.send(self, connection=sender,
response=kwargs['response'])
+ def clear_connections(self):
+ for connection in self.connections.values():
+ connection.Removed()
+ self.connections = {}
+
class SecretsResponse(object):
''' Intermediate object to report the secrets from the dialog
back to the connection object and which will inform NM
@@ -244,10 +398,40 @@ 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 not self._settings.connection.autoconnect:
- self._settings.connection.autoconnect = True
+ if self._settings.connection.type == NM_CONNECTION_TYPE_GSM:
+ self._settings.connection.timestamp = int(time.time())
+ elif not self._settings.connection.autoconnect:
self._settings.connection.timestamp = int(time.time())
+ self._settings.connection.autoconnect = True
+ 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:
+ logging.exception('Error calling libc.__res_init')
+
+ def set_disconnected(self):
+ if 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):
@@ -258,6 +442,10 @@ class NMSettingsConnection(dbus.service.Object):
return self._settings
def save(self):
+ # We only save wifi settins
+ if self._settings.connection.type != NM_CONNECTION_TYPE_802_11_WIRELESS:
+ return
+
profile_path = env.get_profile_path()
config_path = os.path.join(profile_path, 'nm', 'connections.cfg')
@@ -336,7 +524,6 @@ class NMSettingsConnection(dbus.service.Object):
else:
reply(self._secrets.get_dict())
-
class AccessPoint(gobject.GObject):
__gsignals__ = {
'props-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
@@ -357,10 +544,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)
@@ -426,6 +613,8 @@ 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)
@@ -452,36 +641,38 @@ def get_settings():
load_connections()
return _nm_settings
-def find_connection(ssid):
+def find_connection_by_ssid(ssid):
connections = get_settings().connections
- if ssid in connections:
- return connections[ssid]
- else:
- return None
-def add_connection(ssid, settings, secrets=None):
+ 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
+
+ return None
+
+def add_connection(uuid, settings, secrets=None):
global _conn_counter
path = NM_SETTINGS_PATH + '/' + str(_conn_counter)
_conn_counter += 1
conn = NMSettingsConnection(path, settings, secrets)
- _nm_settings.add_connection(ssid, conn)
+ _nm_settings.add_connection(uuid, conn)
return conn
-def load_connections():
+def load_wifi_connections():
profile_path = env.get_profile_path()
config_path = os.path.join(profile_path, 'nm', 'connections.cfg')
- config = ConfigParser.ConfigParser()
-
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()
+ config = ConfigParser.ConfigParser()
try:
if not config.read(config_path):
logging.error('Error reading the nm config file')
@@ -534,4 +725,56 @@ def load_connections():
except ConfigParser.Error, e:
logging.error('Error reading section: %s' % e)
else:
- add_connection(ssid, settings, secrets)
+ add_connection(uuid, settings, secrets)
+
+def count_connections():
+ return len(get_settings().connections)
+
+def clear_connections():
+ _nm_settings.clear_connections()
+
+ profile_path = env.get_profile_path()
+ config_path = os.path.join(profile_path, 'nm', 'connections.cfg')
+
+ 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_gsm_connection():
+ client = gconf.client_get_default()
+
+ settings = SettingsGsm()
+ settings.gsm.username = client.get_string(GSM_USERNAME_PATH) or ''
+ settings.gsm.number = client.get_string(GSM_NUMBER_PATH) or ''
+ settings.gsm.apn = client.get_string(GSM_APN_PATH) or ''
+
+ secrets = SecretsGsm()
+ secrets.pin = client.get_string(GSM_PIN_PATH) or ''
+ secrets.puk = client.get_string(GSM_PUK_PATH) or ''
+ secrets.password = client.get_string(GSM_PASSWORD_PATH) or ''
+
+ settings.connection.id = 'gsm'
+ settings.connection.type = NM_CONNECTION_TYPE_GSM
+ uuid = settings.connection.uuid = unique_id()
+ settings.connection.autoconnect = False
+ settings.ip4_config.method = 'auto'
+ settings.serial.baud = 115200
+
+ try:
+ add_connection(uuid, settings, secrets)
+ except Exception:
+ logging.exception('While adding gsm connection')
+
+def load_connections():
+ load_wifi_connections()
+ load_gsm_connection()
+
+def find_gsm_connection():
+ connections = get_settings().connections
+
+ for connection in connections.values():
+ if connection.get_settings().connection.type == NM_CONNECTION_TYPE_GSM:
+ return connection
+
+ return None
diff --git a/src/jarabe/model/olpcmesh.py b/src/jarabe/model/olpcmesh.py
new file mode 100644
index 0000000..7873692
--- /dev/null
+++ b/src/jarabe/model/olpcmesh.py
@@ -0,0 +1,214 @@
+# 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
+
+import logging
+
+import dbus
+import gobject
+
+from jarabe.model import network
+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"
+
+DEVICE_STATE_UNKNOWN = 0
+DEVICE_STATE_UNMANAGED = 1
+DEVICE_STATE_UNAVAILABLE = 2
+DEVICE_STATE_DISCONNECTED = 3
+DEVICE_STATE_PREPARE = 4
+DEVICE_STATE_CONFIG = 5
+DEVICE_STATE_NEED_AUTH = 6
+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()
+
+ self.mesh_device = mesh_device
+ self.eth_device = self._get_companion_device()
+
+ self._connection_queue = []
+ """Stack of connections that we'll iterate through until we find one
+ that works.
+
+ """
+
+ props = dbus.Interface(self.mesh_device,
+ 'org.freedesktop.DBus.Properties')
+ 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.Get(_NM_DEVICE_IFACE, 'State',
+ reply_handler=self.__get_eth_state_reply_cb,
+ error_handler=self.__get_state_error_cb)
+
+ self._bus.add_signal_receiver(self.__eth_device_state_changed_cb,
+ signal_name='StateChanged',
+ path=self.eth_device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+
+ self._bus.add_signal_receiver(self.__mshdev_state_changed_cb,
+ signal_name='StateChanged',
+ path=self.mesh_device.object_path,
+ dbus_interface=_NM_DEVICE_IFACE)
+
+ self._idle_source = 0
+ self._mesh_device_state = DEVICE_STATE_UNKNOWN
+ self._eth_device_state = DEVICE_STATE_UNKNOWN
+
+ if self._have_configured_connections():
+ self._start_automesh_timer()
+ else:
+ self._start_automesh()
+
+ def _get_companion_device(self):
+ props = dbus.Interface(self.mesh_device,
+ 'org.freedesktop.DBus.Properties')
+ eth_device_o = props.Get(_NM_OLPC_MESH_IFACE, 'Companion')
+ return self._bus.get_object(_NM_SERVICE, eth_device_o)
+
+ def _have_configured_connections(self):
+ return len(network.get_settings().connections) > 0
+
+ def _start_automesh_timer(self):
+ """Start our timer system which basically looks for 10 seconds of
+ inactivity on both devices, then starts automesh.
+
+ """
+ if self._idle_source != 0:
+ gobject.source_remove(self._idle_source)
+ self._idle_source = gobject.timeout_add_seconds(10, self._idle_check)
+
+ def __get_state_error_cb(self, err):
+ logging.debug('Error getting the device state: %s', err)
+
+ def __get_mesh_state_reply_cb(self, state):
+ self._mesh_device_state = state
+ self._maybe_schedule_idle_check()
+
+ def __get_eth_state_reply_cb(self, state):
+ self._eth_device_state = state
+ self._maybe_schedule_idle_check()
+
+ 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()
+
+ if new_state >= DEVICE_STATE_PREPARE \
+ and new_state <= DEVICE_STATE_ACTIVATED \
+ and len(self._connection_queue) > 0:
+ self._connection_queue = []
+
+ def __mshdev_state_changed_cb(self, new_state, old_state, reason):
+ self._mesh_device_state = new_state
+ self._maybe_schedule_idle_check()
+
+ if new_state == DEVICE_STATE_FAILED:
+ self._try_next_connection_from_queue()
+ elif new_state == DEVICE_STATE_ACTIVATED \
+ and len(self._connection_queue) > 0:
+ self._empty_connection_queue()
+
+ def _maybe_schedule_idle_check(self):
+ if self._mesh_device_state == DEVICE_STATE_DISCONNECTED \
+ and self._eth_device_state == DEVICE_STATE_DISCONNECTED:
+ self._start_automesh_timer()
+
+ 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")
+ self._start_automesh()
+ return False
+
+ def _make_connection(self, channel, anycast_address=None):
+ wireless_config = OlpcMeshSettings(channel, anycast_address)
+ settings = Settings(wireless_cfg=wireless_config)
+ if not anycast_address:
+ settings.ip4_config = network.IP4Config()
+ settings.ip4_config.method = 'link-local'
+ settings.connection.id = 'olpc-mesh-' + str(channel)
+ settings.connection.uuid = unique_id()
+ settings.connection.type = '802-11-olpc-mesh'
+ connection = network.add_connection(settings.connection.id, settings)
+ return connection
+
+ 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 _activate_connection(self, channel, anycast_address=None):
+ 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)
+
+ network_manager.ActivateConnection(network.SETTINGS_SERVICE,
+ connection.path,
+ self.mesh_device.object_path,
+ self.mesh_device.object_path,
+ reply_handler=self.__activate_reply_cb,
+ error_handler=self.__activate_error_cb)
+
+ def _try_next_connection_from_queue(self):
+ if len(self._connection_queue) == 0:
+ return
+
+ channel, anycast = self._connection_queue.pop()
+ self._activate_connection(channel, anycast)
+
+ def _empty_connection_queue(self):
+ self._connection_queue = []
+
+ def user_activate_channel(self, channel):
+ """Activate a mesh connection on a user-specified channel.
+ Looks for XS first, then resorts to simple mesh."""
+ self._empty_connection_queue()
+ self._connection_queue.append((channel, None))
+ self._connection_queue.append((channel, _XS_ANYCAST))
+ self._try_next_connection_from_queue()
+
+ def _start_automesh(self):
+ """Start meshing automatically, intended when there are no better
+ networks to connect to. First looks for XS on all channels, then falls
+ back to simple mesh on channel 1."""
+ self._empty_connection_queue()
+ self._connection_queue.append((1, None))
+ self._connection_queue.append((11, _XS_ANYCAST))
+ 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/view/buddymenu.py b/src/jarabe/view/buddymenu.py
index 35a8301..e9e9f8e 100644
--- a/src/jarabe/view/buddymenu.py
+++ b/src/jarabe/view/buddymenu.py
@@ -79,8 +79,8 @@ class BuddyMenu(Palette):
self._update_invite_menu(activity)
def _add_my_items(self):
- item = MenuItem(_('My Settings'), 'preferences-system')
- item.connect('activate', self.__controlpanel_activate_cb)
+ item = MenuItem(_('Shutdown'), 'system-shutdown')
+ item.connect('activate', self.__shutdown_activate_cb)
self.menu.append(item)
item.show()
@@ -92,13 +92,8 @@ class BuddyMenu(Palette):
self.menu.append(item)
item.show()
- item = MenuItem(_('Restart'), 'system-restart')
- item.connect('activate', self.__reboot_activate_cb)
- self.menu.append(item)
- item.show()
-
- item = MenuItem(_('Shutdown'), 'system-shutdown')
- item.connect('activate', self.__shutdown_activate_cb)
+ item = MenuItem(_('My Settings'), 'preferences-system')
+ item.connect('activate', self.__controlpanel_activate_cb)
self.menu.append(item)
item.show()
diff --git a/src/jarabe/view/keyhandler.py b/src/jarabe/view/keyhandler.py
index 1da1f6a..5358da8 100644
--- a/src/jarabe/view/keyhandler.py
+++ b/src/jarabe/view/keyhandler.py
@@ -47,6 +47,8 @@ _actions_table = {
'F2' : 'zoom_group',
'F3' : 'zoom_home',
'F4' : 'zoom_activity',
+ 'F5' : 'open_search',
+ 'F6' : 'frame',
'F9' : 'brightness_down',
'F10' : 'brightness_up',
'<alt>F9' : 'brightness_min',
@@ -249,9 +251,9 @@ class KeyHandler(object):
# If either the xmodmap or xrandr command fails, check_call will fail
# with CalledProcessError, which we raise.
try:
- subprocess.check_call(argv)
subprocess.check_call(['xrandr', '-o',
states[self._screen_rotation]])
+ subprocess.check_call(argv)
except OSError, e:
if e.errno != errno.EINTR:
raise
diff --git a/src/jarabe/view/launcher.py b/src/jarabe/view/launcher.py
index 6ddb04a..3071790 100644
--- a/src/jarabe/view/launcher.py
+++ b/src/jarabe/view/launcher.py
@@ -28,14 +28,20 @@ from sugar.graphics.xocolor import XoColor
from jarabe.model import shell
from jarabe.view.pulsingicon import CanvasPulsingIcon
-class LaunchWindow(hippo.CanvasWindow):
+class LaunchWindow(gtk.Window):
def __init__(self, activity_id, icon_path, icon_color):
- gobject.GObject.__init__(
- self, type_hint=gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
+ gobject.GObject.__init__(self)
+
+ self.props.type_hint = gtk.gdk.WINDOW_TYPE_HINT_NORMAL
+
+ canvas = hippo.Canvas()
+ canvas.modify_bg(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())
+ self.add(canvas)
+ canvas.show()
self._activity_id = activity_id
self._box = LaunchBox(activity_id, icon_path, icon_color)
- self.set_root(self._box)
+ canvas.set_root(self._box)
self.connect('realize', self.__realize_cb)
@@ -61,8 +67,7 @@ class LaunchWindow(hippo.CanvasWindow):
class LaunchBox(hippo.CanvasBox):
def __init__(self, activity_id, icon_path, icon_color):
- gobject.GObject.__init__(self, orientation=hippo.ORIENTATION_VERTICAL,
- background_color=style.COLOR_WHITE.get_int())
+ gobject.GObject.__init__(self, orientation=hippo.ORIENTATION_VERTICAL)
self._activity_id = activity_id
self._activity_icon = CanvasPulsingIcon(
diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py
index b222fc7..170f42f 100644
--- a/src/jarabe/view/palettes.py
+++ b/src/jarabe/view/palettes.py
@@ -17,10 +17,9 @@
import os
import statvfs
from gettext import gettext as _
-import gconf
import logging
-import gobject
+import gconf
import gtk
from sugar import env
@@ -32,7 +31,6 @@ from sugar.graphics.xocolor import XoColor
from sugar.activity import activityfactory
from sugar.activity.activityhandle import ActivityHandle
-from jarabe.model import bundleregistry
from jarabe.model import shell
from jarabe.view import launcher
from jarabe.view.viewsource import setup_view_source
@@ -107,12 +105,9 @@ class CurrentActivityPalette(BasePalette):
class ActivityPalette(Palette):
__gtype_name__ = 'SugarActivityPalette'
- __gsignals__ = {
- 'erase-activated' : (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE, ([]))
- }
-
def __init__(self, activity_info):
+ self._activity_info = activity_info
+
client = gconf.client_get_default()
color = XoColor(client.get_string("/desktop/sugar/user/color"))
activity_icon = Icon(file=activity_info.get_icon(),
@@ -122,14 +117,6 @@ class ActivityPalette(Palette):
Palette.__init__(self, primary_text=activity_info.get_name(),
icon=activity_icon)
- registry = bundleregistry.get_registry()
-
- self._bundle = activity_info
- self._bundle_id = activity_info.get_bundle_id()
- self._version = activity_info.get_activity_version()
- self._favorite = registry.is_bundle_favorite(self._bundle_id,
- self._version)
-
xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
menu_item = MenuItem(text_label=_('Start'),
@@ -141,46 +128,6 @@ class ActivityPalette(Palette):
# TODO: start-with
- self._favorite_item = MenuItem('')
- self._favorite_icon = Icon(icon_name='emblem-favorite',
- icon_size=gtk.ICON_SIZE_MENU)
- self._favorite_item.set_image(self._favorite_icon)
- self._favorite_item.connect('activate',
- self.__change_favorite_activate_cb)
- self.menu.append(self._favorite_item)
- self._favorite_item.show()
-
- menu_item = MenuItem(_('Erase'), 'list-remove')
- menu_item.connect('activate', self.__erase_activate_cb)
- self.menu.append(menu_item)
- menu_item.show()
-
- if not os.access(self._bundle.get_path(), os.W_OK):
- menu_item.props.sensitive = False
-
- registry = bundleregistry.get_registry()
- self._activity_changed_sid = registry.connect('bundle_changed',
- self.__activity_changed_cb)
- self._update_favorite_item()
-
- self.connect('destroy', self.__destroy_cb)
-
- def __destroy_cb(self, palette):
- self.disconnect(self._activity_changed_sid)
-
- def _update_favorite_item(self):
- label = self._favorite_item.child
- if self._favorite:
- label.set_text(_('Remove favorite'))
- xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
- style.COLOR_TRANSPARENT.get_svg()))
- else:
- label.set_text(_('Make favorite'))
- client = gconf.client_get_default()
- xo_color = XoColor(client.get_string("/desktop/sugar/user/color"))
-
- self._favorite_icon.props.xo_color = xo_color
-
def __start_activate_cb(self, menu_item):
self.popdown(immediate=True)
@@ -189,28 +136,11 @@ class ActivityPalette(Palette):
activity_id = activityfactory.create_activity_id()
launcher.add_launcher(activity_id,
- self._bundle.get_icon(),
+ self._activity_info.get_icon(),
xo_color)
handle = ActivityHandle(activity_id)
- activityfactory.create(self._bundle, handle)
-
- def __change_favorite_activate_cb(self, menu_item):
- registry = bundleregistry.get_registry()
- registry.set_bundle_favorite(self._bundle_id,
- self._version,
- not self._favorite)
-
- def __activity_changed_cb(self, activity_registry, activity_info):
- if activity_info.get_bundle_id() == self._bundle_id and \
- activity_info.get_activity_version() == self._version:
- registry = bundleregistry.get_registry()
- self._favorite = registry.is_bundle_favorite(self._bundle_id,
- self._version)
- self._update_favorite_item()
-
- def __erase_activate_cb(self, menu_item):
- self.emit('erase-activated')
+ activityfactory.create(self._activity_info, handle)
class JournalPalette(BasePalette):
def __init__(self, home_activity):