Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSantiago Collazo <scollazo@activitycentral.com>2012-10-22 18:00:22 (GMT)
committer Santiago Collazo <scollazo@activitycentral.com>2012-10-22 18:00:22 (GMT)
commitead5abcab3c163ef4e26d254e68cffbc6e164e76 (patch)
tree6f117e0b74b6fa98a7ed54b33af04d3c1812843b
parent19fb32c44022f3d2acda9de587d8c1014748cd95 (diff)
parent2d75ff7d59d35a1db274ef36b29448ebcd4dddf3 (diff)
Merge remote-tracking branch 'aklis/integration' into devel
-rwxr-xr-xbin/sugar-session6
-rw-r--r--bin/sugar.in44
-rw-r--r--configure.ac1
-rw-r--r--data/Makefile.am14
-rw-r--r--data/gtkrc-contrast.em11
-rw-r--r--data/gtkrc.em1
-rw-r--r--data/icons/Makefile.am1
-rw-r--r--data/icons/module-accessibility.svg92
-rw-r--r--data/sugar.schemas.in12
-rw-r--r--extensions/cpsection/Makefile.am2
-rw-r--r--extensions/cpsection/accessibility/Makefile.am6
-rw-r--r--extensions/cpsection/accessibility/__init__.py25
-rw-r--r--extensions/cpsection/accessibility/model.py89
-rw-r--r--extensions/cpsection/accessibility/view.py302
-rw-r--r--extensions/globalkey/Makefile.am1
-rw-r--r--extensions/globalkey/magnifier.py96
-rw-r--r--src/jarabe/controlpanel/gui.py4
-rw-r--r--src/jarabe/controlpanel/sectionview.py4
-rw-r--r--src/jarabe/desktop/activitieslist.py9
-rw-r--r--src/jarabe/desktop/favoritesview.py29
-rw-r--r--src/jarabe/desktop/homewindow.py36
-rw-r--r--src/jarabe/desktop/schoolserver.py4
-rw-r--r--src/jarabe/journal/model.py50
-rw-r--r--src/jarabe/model/Makefile.am1
-rw-r--r--src/jarabe/model/accessibility.py160
-rw-r--r--src/jarabe/model/bundleregistry.py156
26 files changed, 1088 insertions, 68 deletions
diff --git a/bin/sugar-session b/bin/sugar-session
index 8475b9b..05493f2 100755
--- a/bin/sugar-session
+++ b/bin/sugar-session
@@ -263,6 +263,7 @@ def bootstrap():
GObject.idle_add(setup_notification_service_cb)
GObject.idle_add(setup_file_transfer_cb)
GObject.idle_add(show_software_updates_cb)
+ GObject.idle_add(setup_accessibility_cb)
if _USE_XKL:
GObject.idle_add(setup_keyboard_cb)
@@ -283,6 +284,11 @@ def set_theme():
settings.set_property('gtk-theme-name', sugar_theme)
settings.set_property('gtk-icon-theme-name', 'sugar')
+def setup_accessibility_cb():
+ from jarabe.model import accessibility
+ accessibility_manager = accessibility.AccessibilityManager()
+ accessibility_manager.setup_accessibility()
+
def main():
try:
diff --git a/bin/sugar.in b/bin/sugar.in
index 428cbf2..c889d6a 100644
--- a/bin/sugar.in
+++ b/bin/sugar.in
@@ -45,7 +45,18 @@ if test -z "$SUGAR_SCALING"; then
export SUGAR_SCALING=100
fi
-export GTK2_RC_FILES="@prefix@/share/sugar/data/sugar-$SUGAR_SCALING.gtkrc"
+if gconftool-2 --dir-exists=/desktop/sugar/interface ; then
+ THEME="$(gconftool-2 --get /desktop/sugar/interface/gtk_theme)"
+ if [ "$THEME" = 'sugar-contrast' ]; then
+ GTK2_THEME="sugar-$SUGAR_SCALING-contrast"
+ else
+ GTK2_THEME="sugar-$SUGAR_SCALING"
+ fi
+else
+ GTK2_THEME="sugar-$SUGAR_SCALING"
+fi
+
+export GTK2_RC_FILES="@prefix@/share/sugar/data/$GTK2_THEME.gtkrc"
# Needed for executing wpa_passphrase
export PATH="$PATH":/sbin:/usr/sbin
@@ -62,6 +73,27 @@ export LANGUAGE="${LANGUAGE:-${LANG}}"
# Set Sugar's telepathy accounts directory
export MC_ACCOUNT_DIR=$HOME/.sugar/$SUGAR_PROFILE/accounts
+# Check if the keyring exists and create a default
+# unencrypted keyring (OLPC #10290)
+keyring_path=$HOME/.gnome2/keyrings
+if [ ! -e $keyring_path/login.keyring ] &&
+ [ ! -e $keyring_path/default.keyring ]; then
+ mkdir -p $keyring_path
+ echo 'default' > $keyring_path/default
+ TIME=$(/bin/date +%s)
+ cat >> $keyring_path/default.keyring.tmp <<EOF
+[keyring]
+display-name=default
+ctime=$TIME
+mtime=$TIME
+lock-on-idle=false
+lock-timeout=0
+EOF
+
+mv $keyring_path/default.keyring{.tmp,}
+fi
+
+
# Source language settings and debug definitions
if [ -f ~/.i18n ]; then
. ~/.i18n
@@ -70,7 +102,15 @@ if [ -f ~/.sugar/debug ]; then
. ~/.sugar/debug
fi
-echo Xcursor.theme: sugar | xrdb -merge
+
+if gconftool-2 --dir-exists=/desktop/sugar/peripherals/mouse ; then
+ CURSOR_THEME="$(gconftool-2 --get /desktop/sugar/peripherals/mouse/cursor_theme)"
+else
+ CURSOR_THEME="sugar"
+fi
+
+
+echo Xcursor.theme: $CURSOR_THEME | xrdb -merge
metacity --no-force-fullscreen -d $DISPLAY &
exec sugar-session
diff --git a/configure.ac b/configure.ac
index 2052586..494445d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,7 @@ data/Makefile
data/sugar-emulator.desktop
extensions/cpsection/aboutcomputer/Makefile
extensions/cpsection/aboutme/Makefile
+extensions/cpsection/accessibility/Makefile
extensions/cpsection/datetime/Makefile
extensions/cpsection/frame/Makefile
extensions/cpsection/keyboard/Makefile
diff --git a/data/Makefile.am b/data/Makefile.am
index 6a62d23..2a92ce1 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -8,6 +8,14 @@ sugar-100.gtkrc: gtkrc.em
$(srcdir)/em.py -D scaling=\'100\' $(srcdir)/gtkrc.em > \
$(top_builddir)/data/sugar-100.gtkrc
+sugar-100-contrast.gtkrc: gtkrc-contrast.em
+ $(srcdir)/em.py -D scaling=\'100\' $(srcdir)/gtkrc-contrast.em > \
+ $(top_builddir)/data/sugar-100-contrast.gtkrc
+
+sugar-72-contrast.gtkrc: gtkrc-contrast.em
+ $(srcdir)/em.py -D scaling=\'100\' $(srcdir)/gtkrc-contrast.em > \
+ $(top_builddir)/data/sugar-72-contrast.gtkrc
+
sugardir = $(pkgdatadir)/data
sugar_DATA = \
activities.defaults \
@@ -18,7 +26,9 @@ sugar_DATA = \
GTKRC_FILES = \
sugar-72.gtkrc \
- sugar-100.gtkrc
+ sugar-100.gtkrc \
+ sugar-100-contrast.gtkrc \
+ sugar-72-contrast.gtkrc
xsessionsdir = $(datadir)/xsessions
xsessions_DATA = sugar.desktop
@@ -64,5 +74,5 @@ endif
icondir = $(datadir)/icons/hicolor/scalable/apps
icon_DATA = sugar-xo.svg
-EXTRA_DIST = $(sugar_DATA) $(xsessions_DATA) $(nmservice_DATA) $(mime_xml_in_files) em.py gtkrc.em $(schema_in_files) $(icon_DATA)
+EXTRA_DIST = $(sugar_DATA) $(xsessions_DATA) $(nmservice_DATA) $(mime_xml_in_files) em.py gtkrc.em gtkrc-contrast.em $(schema_in_files) $(icon_DATA)
CLEANFILES = $(GTKRC_FILES) $(mime_xml_files) $(schema_DATA)
diff --git a/data/gtkrc-contrast.em b/data/gtkrc-contrast.em
new file mode 100644
index 0000000..3ed4717
--- /dev/null
+++ b/data/gtkrc-contrast.em
@@ -0,0 +1,11 @@
+@{
+if scaling == '72':
+ icon_sizes = 'gtk-large-toolbar=40,40'
+else:
+ icon_sizes = 'gtk-large-toolbar=55,55'
+}@
+gtk-theme-name = "sugar-@scaling-contrast"
+gtk-icon-theme-name = "sugar"
+gtk-toolbar-style = GTK_TOOLBAR_ICONS
+gtk-icon-sizes = "@icon_sizes"
+gtk-cursor-blink-timeout = 3
diff --git a/data/gtkrc.em b/data/gtkrc.em
index d4e1a7c..4459c42 100644
--- a/data/gtkrc.em
+++ b/data/gtkrc.em
@@ -6,7 +6,6 @@ else:
}@
gtk-theme-name = "sugar-@scaling"
gtk-icon-theme-name = "sugar"
-gtk-cursor-theme-name = "sugar"
gtk-toolbar-style = GTK_TOOLBAR_ICONS
gtk-icon-sizes = "@icon_sizes"
gtk-cursor-blink-timeout = 3
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
index a35643a..2497c4a 100644
--- a/data/icons/Makefile.am
+++ b/data/icons/Makefile.am
@@ -3,6 +3,7 @@ sugardir = $(pkgdatadir)/data/icons
sugar_DATA = \
module-about_me.svg \
module-about_my_computer.svg \
+ module-accessibility.svg \
module-date_and_time.svg \
module-frame.svg \
module-keyboard.svg \
diff --git a/data/icons/module-accessibility.svg b/data/icons/module-accessibility.svg
new file mode 100644
index 0000000..245a47e
--- /dev/null
+++ b/data/icons/module-accessibility.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg795">
+ <defs
+ id="defs797" />
+ <g
+ id="layer1">
+ <path
+ d="m -23.738584,30.440666 a 8.901969,11.301082 0 1 1 -17.803937,0 8.901969,11.301082 0 1 1 17.803937,0 z"
+ transform="matrix(1.2765958,0,0,1.0558659,19.192898,-13.127946)"
+ id="path12"
+ style="fill:none;stroke:#ffffff" />
+ <path
+ d="m 30.145644,37.823484 c -0.583589,6.659344 -6.7511,11.52268 -13.775517,10.862567 C 9.3457054,48.02594 4.1243814,42.092348 4.7079694,35.433004 5.2915594,28.773661 20.67887,20.597949 14.885997,24.011638 -12.777062,40.313232 24.43362,62.068174 30.145644,37.823484 z"
+ id="path1405-0"
+ style="fill:none;stroke:#ffffff;stroke-width:1.44962609;stroke-opacity:1" />
+ <rect
+ width="2.916333"
+ height="21.188446"
+ ry="1.3939767"
+ x="18.332197"
+ y="13.76814"
+ id="rect1438-8"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.50547338;stroke-opacity:1" />
+ <rect
+ width="2.8716421"
+ height="19.140917"
+ ry="1.259271"
+ x="-35.091366"
+ y="-38.207035"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="rect1438-1-87"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.41987956;stroke-opacity:1" />
+ <rect
+ width="2.5733202"
+ height="15.766372"
+ ry="1.0372615"
+ x="-5.4068875"
+ y="-65.997124"
+ transform="matrix(-0.55477829,0.83199823,-0.68762037,-0.72607041,0,0)"
+ id="rect1438-0-4"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.21988189;stroke-opacity:1" />
+ <rect
+ width="2.0588"
+ height="12.916228"
+ ry="0.84975183"
+ x="6.7647386"
+ y="-38.278324"
+ transform="matrix(-0.21998034,0.97550431,-0.88967727,-0.45658992,0,0)"
+ id="rect1438-0-1-8"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.98759735;stroke-opacity:1" />
+ <rect
+ width="0.98179615"
+ height="13.275604"
+ ry="0.87339497"
+ x="-30.243309"
+ y="-41.495876"
+ transform="matrix(0,-1,-1,0,0,0)"
+ id="rect1438-1-8-5"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.69142139;stroke-opacity:1" />
+ <rect
+ width="0.97070193"
+ height="11.568416"
+ ry="0.76108003"
+ x="50.822704"
+ y="-8.6089849"
+ transform="matrix(0.89791652,0.44016579,0.56230073,-0.82693282,0,0)"
+ id="rect1438-1-8-0-0"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.64177793;stroke-opacity:1" />
+ <rect
+ width="1.6299056"
+ height="7.7739253"
+ ry="0.51144254"
+ x="72.759727"
+ y="35.364716"
+ transform="matrix(0.9888174,0.14913131,-0.60877492,0.79334299,0,0)"
+ id="rect1438-0-1-7-7"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.68172067;stroke-opacity:1" />
+ <path
+ d="m 79.67578,18.634508 a 3.2829957,3.0304577 0 1 1 -6.565992,0 3.2829957,3.0304577 0 1 1 6.565992,0 z"
+ transform="matrix(1.5397454,0,0,1.4719643,-97.932275,-17.750188)"
+ id="path1603-2"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in
index 579d24b..82d2565 100644
--- a/data/sugar.schemas.in
+++ b/data/sugar.schemas.in
@@ -204,6 +204,18 @@
</schema>
<schema>
+ <key>/schemas/desktop/sugar/show_register</key>
+ <applyto>/desktop/sugar/show_register</applyto>
+ <owner>sugar</owner>
+ <type>bool</type>
+ <default>true</default>
+ <locale name="C">
+ <short>Show Register</short>
+ <long>If TRUE, Sugar will show a "Register" option.</long>
+ </locale>
+ </schema>
+
+ <schema>
<key>/schemas/desktop/sugar/peripherals/keyboard/layouts</key>
<applyto>/desktop/sugar/peripherals/keyboard/layouts</applyto>
<owner>sugar</owner>
diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am
index a92b5dd..ee5897f 100644
--- a/extensions/cpsection/Makefile.am
+++ b/extensions/cpsection/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = aboutme aboutcomputer datetime frame keyboard language \
+SUBDIRS = aboutme aboutcomputer accessibility datetime frame keyboard language \
modemconfiguration network power updater
sugardir = $(pkgdatadir)/extensions/cpsection
diff --git a/extensions/cpsection/accessibility/Makefile.am b/extensions/cpsection/accessibility/Makefile.am
new file mode 100644
index 0000000..70d26f4
--- /dev/null
+++ b/extensions/cpsection/accessibility/Makefile.am
@@ -0,0 +1,6 @@
+sugardir = $(pkgdatadir)/extensions/cpsection/accessibility
+
+sugar_PYTHON = \
+ __init__.py \
+ model.py \
+ view.py
diff --git a/extensions/cpsection/accessibility/__init__.py b/extensions/cpsection/accessibility/__init__.py
new file mode 100644
index 0000000..c69acfc
--- /dev/null
+++ b/extensions/cpsection/accessibility/__init__.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2010 Plan Ceibal
+#
+# Author: Esteban Arias <earias@plan.ceibal.edu.uy>
+# Contact information: comunidad@plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+from gettext import gettext as _
+
+CLASS = 'accessibility'
+ICON = 'module-accessibility'
+TITLE = _('Accessibility')
+
diff --git a/extensions/cpsection/accessibility/model.py b/extensions/cpsection/accessibility/model.py
new file mode 100644
index 0000000..48b1636
--- /dev/null
+++ b/extensions/cpsection/accessibility/model.py
@@ -0,0 +1,89 @@
+# Copyright (C) 2010 Plan Ceibal
+#
+# Author: Esteban Arias <earias@plan.ceibal.edu.uy>
+# Contact information: comunidad@plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+from jarabe.model import accessibility
+
+keyboard = accessibility.Keyboard()
+screen = accessibility.Screen()
+mouse = accessibility.Mouse()
+
+KEYWORDS = ['mouse_keys', 'sticky_keys', 'bounce_keys', 'contrast', 'white_mouse', 'accel_mouse', 'capital_letters']
+
+def get_mouse_keys():
+ return keyboard.get_mouse_keys()
+
+def set_mouse_keys(activar):
+ keyboard.set_mouse_keys(activar)
+
+def print_mouse_keys():
+ print str(get_mouse_keys())
+
+def get_sticky_keys():
+ return keyboard.get_sticky_keys()
+
+def set_sticky_keys(activar):
+ keyboard.set_sticky_keys(activar)
+
+def print_sticky_keys():
+ print str(get_sticky_keys())
+
+def get_bounce_keys():
+ return keyboard.get_bounce_keys()
+
+def set_bounce_keys(activar):
+ keyboard.set_bounce_keys(activar)
+
+def print_bounce_keys():
+ print str(get_bounce_keys())
+
+def get_contrast():
+ return screen.get_contrast()
+
+def set_contrast(activar):
+ screen.set_contrast(activar)
+
+def print_contrast():
+ print str(get_contrast())
+
+def get_capital_letters():
+ return screen.get_capital_letters()
+
+def set_capital_letters(activar):
+ screen.set_capital_letters(activar)
+
+def print_capital_letters():
+ print str(get_capital_letters())
+
+def get_white_mouse():
+ return mouse.get_white_mouse()
+
+def set_white_mouse(activar):
+ mouse.set_white_mouse(activar)
+
+def print_white_mouse():
+ print str(get_white_mouse())
+
+def get_accel_mouse():
+ return mouse.get_accel_mouse()
+
+def set_accel_mouse(valor):
+ mouse.set_accel_mouse(valor)
+
+def print_accel_mouse():
+ print str(get_accel_mouse())
diff --git a/extensions/cpsection/accessibility/view.py b/extensions/cpsection/accessibility/view.py
new file mode 100644
index 0000000..e443cb4
--- /dev/null
+++ b/extensions/cpsection/accessibility/view.py
@@ -0,0 +1,302 @@
+# Copyright (C) 2010 Plan Ceibal
+#
+# Author: Esteban Arias <earias@plan.ceibal.edu.uy>
+# Contact information: comunidad@plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+from gi.repository import Gtk
+from gettext import gettext as _
+
+from sugar3.graphics import style
+
+from jarabe.controlpanel.sectionview import SectionView
+from jarabe.controlpanel.inlinealert import InlineAlert
+
+class accessibility(SectionView):
+ def __init__(self, model, alerts=None):
+ SectionView.__init__(self)
+
+ self._model = model
+ self.restart_alerts = alerts
+ self.set_border_width(style.DEFAULT_SPACING * 2)
+ self.set_spacing(style.DEFAULT_SPACING)
+ scrollwindow = Gtk.ScrolledWindow()
+ scrollwindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ self.pack_start(scrollwindow, True, True, 0)
+ scrollwindow.show()
+
+ self._vbox_section = Gtk.VBox()
+ scrollwindow.add_with_viewport(self._vbox_section)
+ self._vbox_section.show()
+
+ self._zone_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self.pack_start(self._zone_alert_box, False, False, 0)
+
+ self._zone_alert = InlineAlert()
+ self._zone_alert_box.pack_start(self._zone_alert, True, True, 0)
+ if 'zone' in self.restart_alerts:
+ self._zone_alert.props.msg = self.restart_msg
+ self._zone_alert.show()
+ self._zone_alert_box.show()
+
+ self.needs_restart = False
+
+ self._view_keyboard_options()
+ self._view_screen_options()
+ self._view_mouse_options()
+
+
+ def _view_keyboard_options(self):
+ separator_pm_keyboard = Gtk.HSeparator()
+ self._vbox_section.pack_start(separator_pm_keyboard, False, False, 0)
+ separator_pm_keyboard.show()
+
+ label_pm_keyboard = Gtk.Label(_('Keyboard'))
+ label_pm_keyboard.set_alignment(0, 0)
+ self._vbox_section.pack_start(label_pm_keyboard, False, False, 0)
+ label_pm_keyboard.show()
+
+ self.box_pm_keyboard = Gtk.VBox()
+ self.box_pm_keyboard.set_border_width(style.DEFAULT_SPACING * 2)
+ self.box_pm_keyboard.set_spacing(style.DEFAULT_SPACING)
+
+ self._view_mouse_keys()
+ self._view_sticky_keys()
+ self._view_bounce_keys()
+
+ self._vbox_section.pack_start(self.box_pm_keyboard, False, False, 0)
+ self.box_pm_keyboard.show()
+
+ def _view_screen_options(self):
+ separator_pm_screen = Gtk.HSeparator()
+ self._vbox_section.pack_start(separator_pm_screen, False, False, 0)
+ separator_pm_screen.show()
+
+ label_pm_screen = Gtk.Label(_('Screen'))
+ label_pm_screen.set_alignment(0, 0)
+ self._vbox_section.pack_start(label_pm_screen, False, False, 0)
+ label_pm_screen.show()
+
+ self.box_pm_screen = Gtk.VBox()
+ self.box_pm_screen.set_border_width(style.DEFAULT_SPACING * 2)
+ self.box_pm_screen.set_spacing(style.DEFAULT_SPACING)
+
+ self._view_contrast()
+ self._view_letters()
+
+ self._vbox_section.pack_start(self.box_pm_screen, False, False, 0)
+ self.box_pm_screen.show()
+
+ def _view_mouse_options(self):
+ separator_pm_mouse = Gtk.HSeparator()
+ self._vbox_section.pack_start(separator_pm_mouse, False, False, 0)
+ separator_pm_mouse.show()
+
+ label_pm_mouse = Gtk.Label(_('Mouse'))
+ label_pm_mouse.set_alignment(0, 0)
+ self._vbox_section.pack_start(label_pm_mouse, False, False, 0)
+ label_pm_mouse.show()
+
+ self.box_pm_mouse = Gtk.VBox()
+ self.box_pm_mouse.set_border_width(style.DEFAULT_SPACING * 2)
+ self.box_pm_mouse.set_spacing(style.DEFAULT_SPACING)
+
+ self._view_white_mouse()
+ self._view_acceleration_mouse()
+
+ self._vbox_section.pack_start(self.box_pm_mouse, False, False, 0)
+ self.box_pm_mouse.show()
+
+ def _set_mouse_keys(self, widget):
+ state = widget.get_active()
+ self._model.set_mouse_keys(state)
+
+ def _set_sticky_keys(self, widget):
+ state = widget.get_active()
+ self._model.set_sticky_keys(state)
+
+ def _set_bounce_keys(self, widget):
+ state = widget.get_active()
+ self._model.set_bounce_keys(state)
+
+ def _set_contrast(self, widget):
+ state = widget.get_active()
+ self._model.set_contrast(state)
+ self.restart_alerts.append('zone')
+ self.needs_restart = True
+ self._zone_alert.props.msg = self.restart_msg
+ self._zone_alert.show()
+
+ def _set_capital_letters(self, widget):
+ state = widget.get_active()
+ self._model.set_capital_letters(state)
+ self.restart_alerts.append('zone')
+ self.needs_restart = True
+ self._zone_alert.props.msg = self.restart_msg
+ self._zone_alert.show()
+
+ def _set_white_mouse(self, widget):
+ state = widget.get_active()
+ self._model.set_white_mouse(state)
+ self.restart_alerts.append('zone')
+ self.needs_restart = True
+ self._zone_alert.props.msg = self.restart_msg
+ self._zone_alert.show()
+
+ def cb_digits_scale_accel_mouse(self, adj):
+ self._model.set_accel_mouse(adj.get_value())
+
+ def undo(self):
+ self._model.set_mouse_keys(self.init_state_mouse_keys)
+ self._model.set_sticky_keys(self.init_state_sticky_keys)
+ self._model.set_bounce_keys(self.init_state_bounce_keys)
+
+
+ self._model.set_contrast(self.init_state_contrast)
+ self.btn_contrast.set_active(self.init_state_contrast)
+
+ self._model.set_capital_letters(self.init_state_capital_letters)
+ self.btn_capital_letters.set_active(self.init_state_capital_letters)
+
+ self._model.set_white_mouse(self.init_state_white_mouse)
+ self.btn_white_mouse.set_active(self.init_state_white_mouse)
+
+ self.adj_accel_mouse.set_value(self.init_state_accel_mouse)
+
+ self.needs_restart = False
+ self._zone_alert.hide()
+
+ def _view_mouse_keys(self):
+ self.btn_mouse_keys = Gtk.CheckButton(_('Mouse Keys'))
+ self._mouse_pm_change_handler = self.btn_mouse_keys.connect("toggled", self._set_mouse_keys)
+ self.init_state_mouse_keys = self._model.get_mouse_keys()
+ self.btn_mouse_keys.set_active(self.init_state_mouse_keys)
+ self.box_pm_keyboard.pack_start(self.btn_mouse_keys, True, True, 2)
+ self.btn_mouse_keys.show()
+
+ lbl_mouse = Gtk.Label(_('Move the mouse pointer with keyboard number.'))
+ lbl_mouse.set_alignment(0, 0)
+ self.box_pm_keyboard.pack_start(lbl_mouse, True, True, 2)
+ lbl_mouse.show()
+
+ def _view_sticky_keys(self):
+ self.btn_sticky_keys = Gtk.CheckButton(_('Sticky Keys'))
+ self._sticky_pm_change_handler = self.btn_sticky_keys.connect("toggled", self._set_sticky_keys)
+ self.init_state_sticky_keys = self._model.get_sticky_keys()
+ self.btn_sticky_keys.set_active(self.init_state_sticky_keys)
+ self.box_pm_keyboard.pack_start(self.btn_sticky_keys, True, True, 2)
+ self.btn_sticky_keys.show()
+
+ lbl_sticky = Gtk.Label(_('Instead of having to press two keys at once (such as CTRL + Q), you can press one key at a time.'))
+ lbl_sticky.set_line_wrap(True)
+ lbl_sticky.set_alignment(0, 0)
+ self.box_pm_keyboard.pack_start(lbl_sticky, True, True, 2)
+ lbl_sticky.show()
+
+ def _view_bounce_keys(self):
+ self.btn_bounce_keys = Gtk.CheckButton(_('Bounce Keys'))
+ self._bounce_pm_change_handler = self.btn_bounce_keys.connect("toggled", self._set_bounce_keys)
+ self.init_state_bounce_keys = self._model.get_bounce_keys()
+ self.btn_bounce_keys.set_active(self.init_state_bounce_keys)
+ self.box_pm_keyboard.pack_start(self.btn_bounce_keys, True, True, 2)
+ self.btn_bounce_keys.show()
+
+ lbl_bounce = Gtk.Label(_('Ignore rapid, repeated keypresses of the same key.'))
+ lbl_bounce.set_alignment(0, 0)
+ self.box_pm_keyboard.pack_start(lbl_bounce, True, True, 2)
+ lbl_bounce.show()
+
+ def _view_contrast(self):
+ self.btn_contrast = Gtk.CheckButton(_('Contrast'))
+ self._contrast_pm_change_handler = self.btn_contrast.connect("toggled", self._set_contrast)
+ self.init_state_contrast = self._model.get_contrast()
+ if self.init_state_contrast:
+ self.btn_contrast.handler_block(self._contrast_pm_change_handler)
+ self.btn_contrast.set_active(True)
+ self.btn_contrast.handler_unblock(self._contrast_pm_change_handler)
+ else:
+ self.btn_contrast.set_active(False)
+ self.box_pm_screen.pack_start(self.btn_contrast, True, True, 2)
+ self.btn_contrast.show()
+
+ lbl_contrast = Gtk.Label(_('Enables the color contrast of the graphic interface.'))
+ lbl_contrast.set_alignment(0, 0)
+ self.box_pm_screen.pack_start(lbl_contrast, True, True, 2)
+ lbl_contrast.show()
+
+ def _view_letters(self):
+ self.btn_capital_letters = Gtk.CheckButton(_('Capital letters'))
+ self._capital_letters_pm_change_handler = self.btn_capital_letters.connect("toggled", self._set_capital_letters)
+ self.init_state_capital_letters = self._model.get_capital_letters()
+ if self.init_state_capital_letters:
+ self.btn_capital_letters.handler_block(self._capital_letters_pm_change_handler)
+ self.btn_capital_letters.set_active(True)
+ self.btn_capital_letters.handler_unblock(self._capital_letters_pm_change_handler)
+ else:
+ self.btn_capital_letters.set_active(False)
+ self.box_pm_screen.pack_start(self.btn_capital_letters, True, True, 2)
+ self.btn_capital_letters.show()
+
+ lbl_capital_letters = Gtk.Label(_('Shows capital letters in the user interface.'))
+ lbl_capital_letters.set_alignment(0, 0)
+ self.box_pm_screen.pack_start(lbl_capital_letters, True, True, 2)
+ lbl_capital_letters.show()
+
+ def _view_white_mouse(self):
+ self.btn_white_mouse = Gtk.CheckButton(_('White Mouse'))
+ self._white_mouse_pm_change_handler = self.btn_white_mouse.connect("toggled", self._set_white_mouse)
+ self.init_state_white_mouse = self._model.get_white_mouse()
+ if self.init_state_white_mouse:
+ self.btn_white_mouse.handler_block(self._white_mouse_pm_change_handler)
+ self.btn_white_mouse.set_active(True)
+ self.btn_white_mouse.handler_unblock(self._white_mouse_pm_change_handler)
+ else:
+ self.btn_white_mouse.set_active(False)
+ self.box_pm_mouse.pack_start(self.btn_white_mouse, True, True, 2)
+ self.btn_white_mouse.show()
+
+ lbl_white_mouse = Gtk.Label(_('Show the mouse cursor white.'))
+ lbl_white_mouse.set_alignment(0, 0)
+ self.box_pm_mouse.pack_start(lbl_white_mouse, True, True, 2)
+ lbl_white_mouse.show()
+
+ def _view_acceleration_mouse(self):
+ box_accel_mouse = Gtk.HBox(False, 0)
+ box_accel_mouse.set_border_width(0)
+ lbl_accel_mouse = Gtk.Label(_('Acceleration: '))
+ lbl_accel_mouse.show()
+ box_accel_mouse.pack_start(lbl_accel_mouse, False, False, 0)
+
+ self.init_state_accel_mouse = self._model.get_accel_mouse();
+ self.adj_accel_mouse = Gtk.Adjustment(self.init_state_accel_mouse, 0.0, 5.0, 1.0, 1.0, 0.0)
+ self.adj_accel_mouse.connect("value_changed", self.cb_digits_scale_accel_mouse)
+ self.scale_accel_mouse = Gtk.HScale(adjustment=self.adj_accel_mouse)
+ self.scale_accel_mouse.set_digits(0)
+ self.scale_accel_mouse.show()
+
+ box_accel_mouse.pack_start(self.scale_accel_mouse, True, True, 0)
+ box_accel_mouse.show()
+
+ self.box_pm_mouse.pack_start(box_accel_mouse, True, True, 2)
+
+ desc_accel_mouse = Gtk.Label(_('Controller acceleration mouse.'))
+ desc_accel_mouse.set_alignment(0, 0)
+ self.box_pm_mouse.pack_start(desc_accel_mouse, True, True, 2)
+ desc_accel_mouse.show()
+
+ def setup(self):
+ pass
diff --git a/extensions/globalkey/Makefile.am b/extensions/globalkey/Makefile.am
index b6cbbd6..cab05ba 100644
--- a/extensions/globalkey/Makefile.am
+++ b/extensions/globalkey/Makefile.am
@@ -3,5 +3,6 @@ sugardir = $(pkgdatadir)/extensions/globalkey
sugar_PYTHON = \
__init__.py \
screenshot.py \
+ magnifier.py \
speech.py \
viewsource.py
diff --git a/extensions/globalkey/magnifier.py b/extensions/globalkey/magnifier.py
new file mode 100644
index 0000000..122e99b
--- /dev/null
+++ b/extensions/globalkey/magnifier.py
@@ -0,0 +1,96 @@
+# Copyright (C) 2010 Plan Ceibal <comunidad@plan.ceibal.edu.uy>
+
+import os
+
+import ConfigParser
+import logging
+from gi.repository import GObject
+
+from jarabe.view.viewsource import setup_view_source
+from sugar import env
+
+
+PATH_VMG_CONFIG = os.environ['HOME'] + '/.magnifier.ini'
+BOUND_KEYS = ['<shift>F13', 'F13', '<control>F13']
+
+def handle_key_press(key):
+ logger = logging.getLogger('magnifier')
+ logger.setLevel(logging.DEBUG)
+ logger.debug("Ejecutando magnifier......" + key)
+ if (key=='<shift>F13'):
+ set_ruta_archivo()
+ set_GraphicsTools()
+ if (key=='<control>F13'):
+ set_ruta_archivo()
+ set_InvertColors()
+ _run_cmd_async('launchVmg')
+
+def get_GraphicsTools():
+ return leer_config('General', 'GraphicsTools')
+
+def set_GraphicsTools():
+ grabar_config_GraphicsTools('General', 'GraphicsTools')
+
+def grabar_config_GraphicsTools(encabezado, etiqueta):
+ parser = ConfigParser.ConfigParser()
+ parser.read(PATH_VMG_CONFIG)
+ val_old = get_GraphicsTools()
+ if val_old == '1':
+ parser.set(encabezado, etiqueta, '0')
+ arch = open(PATH_VMG_CONFIG, 'w')
+ parser.write(arch)
+ arch.close()
+ else:
+ parser.set(encabezado, etiqueta, '1')
+ arch = open(PATH_VMG_CONFIG, 'w')
+ parser.write(arch)
+ arch.close()
+
+def get_InvertColors():
+ return leer_config('General', 'InvertColors')
+
+def set_InvertColors():
+ grabar_config_InvertColors('General', 'InvertColors')
+
+def grabar_config_InvertColors(encabezado, etiqueta):
+ parser = ConfigParser.ConfigParser()
+ parser.read(PATH_VMG_CONFIG)
+ val_old = get_InvertColors()
+ if val_old == '1':
+ parser.set(encabezado, etiqueta, '0')
+ arch = open(PATH_VMG_CONFIG, 'w')
+ parser.write(arch)
+ arch.close()
+ else:
+ parser.set(encabezado, etiqueta, '1')
+ arch = open(PATH_VMG_CONFIG, 'w')
+ parser.write(arch)
+ arch.close()
+
+def leer_config(encabezado, etiqueta):
+ parser = ConfigParser.ConfigParser()
+ parser.read(PATH_VMG_CONFIG)
+ return parser.get(encabezado, etiqueta)
+
+def set_ruta_archivo():
+ try:
+ f = file(PATH_VMG_CONFIG)
+ except:
+ PATH_VMG_CONFIG = "/root/.magnifier.ini"
+
+def _run_cmd_async(cmd):
+ logger = logging.getLogger('magnifier')
+ logger.setLevel(logging.DEBUG)
+ try:
+ GObject.spawn_async([find_and_absolutize('launchVmg')])
+ logger.debug("Ejecuto magnifier")
+ except Exception, e:
+ logger.debug("Error ejecutando magnifier" + str(e))
+
+def find_and_absolutize(script_name):
+ paths = env.os.environ['PATH'].split(':')
+ for path in paths:
+ looking_path = path + '/' + script_name
+ if env.os.path.isfile(looking_path):
+ return looking_path
+ return None
diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
index daa58e4..19dca00 100644
--- a/src/jarabe/controlpanel/gui.py
+++ b/src/jarabe/controlpanel/gui.py
@@ -1,4 +1,5 @@
# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2010 Plan Ceibal <comunidad@plan.ceibal.edu.uy>
#
# 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
@@ -247,7 +248,7 @@ class ControlPanel(Gtk.Window):
self._section_view.connect('request-close',
self.__close_request_cb)
self._main_view.modify_bg(Gtk.StateType.NORMAL,
- style.COLOR_WHITE.get_gdk_color())
+ style.COLOR_BG_CP.get_gdk_color())
def set_section_view_auto_close(self):
"""Automatically close the control panel if there is "nothing to do"
@@ -289,6 +290,7 @@ class ControlPanel(Gtk.Window):
return options
def __cancel_clicked_cb(self, widget):
+ self._section_view.perform_cancel_actions()
self._section_view.undo()
self._options[self._current_option]['alerts'] = []
self._section_toolbar.accept_button.set_sensitive(True)
diff --git a/src/jarabe/controlpanel/sectionview.py b/src/jarabe/controlpanel/sectionview.py
index 074a8ae..bee64e0 100644
--- a/src/jarabe/controlpanel/sectionview.py
+++ b/src/jarabe/controlpanel/sectionview.py
@@ -53,6 +53,10 @@ class SectionView(Gtk.VBox):
"""Undo here the changes that have been made in this section."""
pass
+ def perform_cancel_actions(self):
+ """Perform additional actions, when the "Cancel" button is clicked."""
+ pass
+
def perform_accept_actions(self):
"""Perform additional actions, when the "Ok" button is clicked."""
pass
diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
index 4adaf18..0194ec8 100644
--- a/src/jarabe/desktop/activitieslist.py
+++ b/src/jarabe/desktop/activitieslist.py
@@ -1,5 +1,6 @@
# Copyright (C) 2008 One Laptop Per Child
# Copyright (C) 2009 Tomeu Vizoso
+# Copyright (C) 2010 Plan Ceibal <comunidad@plan.ceibal.edu.uy>
#
# 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
@@ -139,7 +140,8 @@ class ActivitiesTreeView(Gtk.TreeView):
registry = bundleregistry.get_registry()
registry.set_bundle_favorite(row[ListModel.COLUMN_BUNDLE_ID],
row[ListModel.COLUMN_VERSION],
- not row[ListModel.COLUMN_FAVORITE])
+ not row[ListModel.COLUMN_FAVORITE],
+ True)
def __icon_clicked_cb(self, cell, path):
row = self.get_model()[path]
@@ -297,7 +299,7 @@ class CellRendererActivityIcon(CellRendererIcon):
self.props.width = style.GRID_CELL_SIZE
self.props.height = style.GRID_CELL_SIZE
self.props.size = style.STANDARD_ICON_SIZE
- self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.stroke_color = style.COLOR_DESKTOP_ICON.get_svg()
self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
self.props.mode = Gtk.CellRendererMode.ACTIVATABLE
@@ -558,7 +560,8 @@ class ActivityListPalette(ActivityPalette):
registry = bundleregistry.get_registry()
registry.set_bundle_favorite(self._bundle_id,
self._version,
- not self._favorite)
+ not self._favorite,
+ True)
def __activity_changed_cb(self, activity_registry, activity_info):
if activity_info.get_bundle_id() == self._bundle_id and \
diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py
index 9c7a76e..0c5e202 100644
--- a/src/jarabe/desktop/favoritesview.py
+++ b/src/jarabe/desktop/favoritesview.py
@@ -1,5 +1,6 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2010 Plan Ceibal <comunidad@plan.ceibal.edu.uy>
#
# 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
@@ -271,11 +272,17 @@ class FavoritesView(ViewContainer):
def _add_activity(self, activity_info):
if activity_info.get_bundle_id() == 'org.laptop.JournalActivity':
return
- icon = ActivityIcon(activity_info)
- icon.props.pixel_size = style.STANDARD_ICON_SIZE
- #icon.set_resume_mode(self._resume_mode)
- self.add(icon)
- icon.show()
+
+ # Add icon, if not already present (for the same combination of
+ # activity-id and activity-version)
+ icon = self._find_activity_icon(activity_info.get_bundle_id(),
+ activity_info.get_activity_version())
+ if icon is None:
+ icon = ActivityIcon(activity_info)
+ icon.props.pixel_size = style.STANDARD_ICON_SIZE
+ #icon.set_resume_mode(self._resume_mode)
+ self.add(icon)
+ icon.show()
def __activity_added_cb(self, activity_registry, activity_info):
registry = bundleregistry.get_registry()
@@ -325,7 +332,7 @@ class FavoritesView(ViewContainer):
try:
schoolserver.register_laptop()
except RegisterError, e:
- alert.props.title = _('Registration Failed')
+ alert.props.title = _('No School Server found on the network')
alert.props.msg = '%s' % e
else:
alert.props.title = _('Registration Successful')
@@ -499,7 +506,7 @@ class FavoritePalette(ActivityPalette):
ActivityPalette.__init__(self, activity_info)
if not journal_entries:
- xo_color = XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
style.COLOR_TRANSPARENT.get_svg()))
else:
xo_color = misc.get_icon_color(journal_entries[0])
@@ -605,6 +612,14 @@ class OwnerIcon(BuddyIcon):
palette = BuddyMenu(get_owner_instance())
client = GConf.Client.get_default()
+ show_register = client.get_bool('/desktop/sugar/show_register')
+
+ if show_register:
+ self._create_register_menu(palette, client)
+
+ return palette
+
+ def _create_register_menu(self, palette, client):
backup_url = client.get_string('/desktop/sugar/backup_url')
if not backup_url:
diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py
index a412b99..51873e9 100644
--- a/src/jarabe/desktop/homewindow.py
+++ b/src/jarabe/desktop/homewindow.py
@@ -16,6 +16,7 @@
from gettext import gettext as _
import logging
+import dbus
from gi.repository import GObject
from gi.repository import Gtk
@@ -32,6 +33,7 @@ from jarabe.desktop.transitionbox import TransitionBox
from jarabe.desktop.viewtoolbar import ViewToolbar
from jarabe.model.shell import ShellModel
from jarabe.model import shell
+from jarabe.model import notifications
_HOME_PAGE = 0
@@ -39,6 +41,13 @@ _GROUP_PAGE = 1
_MESH_PAGE = 2
_TRANSITION_PAGE = 3
+_DBUS_SYSTEM_IFACE = 'org.sugarlabs.system'
+_DBUS_SYSTEM_PATH = '/org/sugarlabs/system'
+_SYSTEM_REBOOT_ID = -1
+_SYSTEM_RELOGIN_ID = -2
+_SYSTEM_TIMEOUT = 5
+
+
_instance = None
@@ -99,6 +108,33 @@ class HomeWindow(Gtk.Window):
shell.get_model().zoom_level_changed.connect(
self.__zoom_level_changed_cb)
+ try:
+ systembus = dbus.SystemBus()
+ except dbus.DBusException:
+ logging.error('DBus SystemBus is not available')
+ else:
+ systembus.add_signal_receiver(self.__reboot_cb, 'Reboot',
+ _DBUS_SYSTEM_IFACE)
+ systembus.add_signal_receiver(self.__relogin_cb, 'Relogin',
+ _DBUS_SYSTEM_IFACE)
+
+ def _system_alert(self, replaces_id, app_icon, message):
+ service = notifications.get_service()
+ service.notification_received.send(self,app_name='system',
+ replaces_id=replaces_id, app_icon=app_icon,
+ summary=_('System alert'), body=message,
+ actions=[], hints={})
+
+ def __reboot_cb(self):
+ self._system_alert(_SYSTEM_REBOOT_ID, 'system-restart',
+ _('Please, reboot your computer to take into account ' \
+ 'new updates'))
+
+ def __relogin_cb(self):
+ self._system_alert(_SYSTEM_RELOGIN_ID, 'system-logout',
+ _('Please, restart Sugar to take into account ' \
+ 'new updates'))
+
def _deactivate_view(self, level):
group = palettegroup.get_group('default')
group.popdown()
diff --git a/src/jarabe/desktop/schoolserver.py b/src/jarabe/desktop/schoolserver.py
index cc26e56..2b8fa9a 100644
--- a/src/jarabe/desktop/schoolserver.py
+++ b/src/jarabe/desktop/schoolserver.py
@@ -140,7 +140,9 @@ def register_laptop(url=_REGISTER_URL):
data = server.register(sn, nick, uuid_, profile.pubkey)
except (xmlrpclib.Error, TypeError, socket.error):
logging.exception('Registration: cannot connect to server')
- raise RegisterError(_('Cannot connect to the server.'))
+ raise RegisterError(_('Please ensure that you are connected ' \
+ 'to the correct network and that the ' \
+ 'server is available.'))
finally:
socket.setdefaulttimeout(None)
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index c9e08ec..9b31ac0 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -289,6 +289,14 @@ class BaseResultSet(object):
return self._cache[self._position - self._offset]
+ def is_favorite_compatible(self, metadata):
+ if self._favorite == '0':
+ return True
+
+ return ((metadata is not None) and \
+ ('keep' in metadata.keys()) and \
+ (str(metadata['keep']) == '1'))
+
class DatastoreResultSet(BaseResultSet):
"""Encapsulates the result of a query on the datastore
@@ -352,6 +360,8 @@ class InplaceResultSet(BaseResultSet):
self._sort = query.get('order_by', ['+timestamp'])[0]
+ self._favorite = str(query.get('keep', 0))
+
def setup(self):
self._file_list = []
self._pending_directories = [self._mount_point]
@@ -459,10 +469,13 @@ class InplaceResultSet(BaseResultSet):
if S_IFMT(stat.st_mode) != S_IFREG:
return
+ metadata = _get_file_metadata(full_path, stat,
+ fetch_preview=False)
+
+ if not self.is_favorite_compatible(metadata):
+ return
if self._regex is not None and \
not self._regex.match(full_path):
- metadata = _get_file_metadata(full_path, stat,
- fetch_preview=False)
if not metadata:
return
add_to_list = False
@@ -510,7 +523,7 @@ class InplaceResultSet(BaseResultSet):
return
-class RemoteShareResultSet(object):
+class RemoteShareResultSet(BaseResultSet):
def __init__(self, ip_address_or_dns_name, query):
self._ip_address_or_dns_name = ip_address_or_dns_name
self._file_list = []
@@ -544,6 +557,8 @@ class RemoteShareResultSet(object):
self._sort = query.get('order_by', ['+timestamp'])[0]
+ self._favorite = str(query.get('keep', 0))
+
def setup(self):
try:
metadata_list_complete = webdavmanager.get_remote_webdav_share_metadata(self._ip_address_or_dns_name)
@@ -552,6 +567,9 @@ class RemoteShareResultSet(object):
for metadata in metadata_list_complete:
+ if not self.is_favorite_compatible(metadata):
+ continue
+
add_to_list = False
if self._regex is not None:
for f in ['fulltext', 'title',
@@ -1111,8 +1129,7 @@ def _write_entry_on_external_device(metadata, file_path,
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['title'] = file_name
_write_metadata_and_preview_files_and_return_file_paths(metadata,
file_name)
@@ -1131,28 +1148,7 @@ def _write_entry_on_external_device(metadata, file_path,
def get_file_name(title, mime_type):
- file_name = title
-
- extension = mime.get_primary_extension(mime_type)
- if extension is not None and extension:
- extension = '.' + extension
- if not file_name.endswith(extension):
- file_name += extension
-
- # Invalid characters in VFAT filenames. From
- # http://en.wikipedia.org/wiki/File_Allocation_Table
- invalid_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|', '\x7F']
- invalid_chars.extend([chr(x) for x in range(0, 32)])
- for char in invalid_chars:
- file_name = file_name.replace(char, '_')
-
- # FAT limit is 255, leave some space for uniqueness
- max_len = 250
- if len(file_name) > max_len:
- name, extension = os.path.splitext(file_name)
- file_name = name[0:max_len - len(extension)] + extension
-
- return file_name
+ return title
def get_unique_file_name(mount_point, file_name):
diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
index d40fb8d..8b37ee7 100644
--- a/src/jarabe/model/Makefile.am
+++ b/src/jarabe/model/Makefile.am
@@ -1,6 +1,7 @@
sugardir = $(pythondir)/jarabe/model
sugar_PYTHON = \
adhoc.py \
+ accessibility.py \
__init__.py \
buddy.py \
bundleregistry.py \
diff --git a/src/jarabe/model/accessibility.py b/src/jarabe/model/accessibility.py
new file mode 100644
index 0000000..6910b2b
--- /dev/null
+++ b/src/jarabe/model/accessibility.py
@@ -0,0 +1,160 @@
+# Copyright (C) 2010 Plan Ceibal
+#
+# Author: Esteban Arias <earias@plan.ceibal.edu.uy>
+# Contact information: comunidad@plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+from gi.repository import Gtk
+from gi.repository import GConf
+import subprocess
+
+class Keyboard:
+
+ def get_mouse_keys(self):
+ client = GConf.Client.get_default()
+ return client.get_bool("/desktop/sugar/accessibility/keyboard/mousekeys_enable")
+
+ def set_mouse_keys(self, activar):
+ client = GConf.Client.get_default()
+ client.set_bool("/desktop/sugar/accessibility/keyboard/mousekeys_enable", activar)
+ self.run_config_keyboard()
+
+ def get_sticky_keys(self):
+ client = GConf.Client.get_default()
+ return client.get_bool("/desktop/sugar/accessibility/keyboard/stickykeys_enable")
+
+ def set_sticky_keys(self, activar):
+ client = GConf.Client.get_default()
+ client.set_bool("/desktop/sugar/accessibility/keyboard/stickykeys_enable", activar)
+ self.run_config_keyboard()
+
+ def get_bounce_keys(self):
+ client = GConf.Client.get_default()
+ return client.get_bool("/desktop/sugar/accessibility/keyboard/bouncekeys_enable")
+
+ def set_bounce_keys(self, activar):
+ client = GConf.Client.get_default()
+ client.set_bool("/desktop/sugar/accessibility/keyboard/bouncekeys_enable", activar)
+ self.run_config_keyboard()
+
+ def run_config_keyboard(self):
+ cmd = ['ax']
+ if self.get_sticky_keys():
+ cmd.append('+stickykeys')
+ else:
+ cmd.append('-stickykeys')
+ if self.get_bounce_keys():
+ cmd.append('+bouncekeys')
+ else:
+ cmd.append('-bouncekeys')
+ if self.get_mouse_keys():
+ cmd += ['+mousekeys', 'mousemaxspeed', '3000', 'mousetimetomax', '1000', '-timeout', '-repeatkeys']
+ else:
+ cmd += ['-mousekeys', 'mousemaxspeed', '3000', 'mousetimetomax', '1000', '+timeout', '+repeatkeys']
+ subprocess.call(cmd)
+
+class Screen:
+
+ DEFAULT_THEME = "sugar"
+ DEFAULT_FONT_SIZE = 7
+ DEFAULT_FONT_FACE = "Sans Serif"
+ CONTRAST_THEME = "sugar-contrast"
+ CONTRAST_FONT_SIZE = 9.5
+ CAPITAL_LETTERS_FONT_FACE = "Oracle"
+
+ def get_contrast(self):
+ client = GConf.Client.get_default()
+ value = client.get_string("/desktop/sugar/interface/Gtk_theme")
+ return value==self.CONTRAST_THEME
+
+ def set_contrast(self, activar):
+ client = GConf.Client.get_default()
+ if (activar):
+ client.set_string("/desktop/sugar/interface/Gtk_theme", self.CONTRAST_THEME)
+ client.set_float('/desktop/sugar/font/default_size', self.CONTRAST_FONT_SIZE)
+ else:
+ client.set_string("/desktop/sugar/interface/Gtk_theme", self.DEFAULT_THEME)
+ client.set_float('/desktop/sugar/font/default_size', self.DEFAULT_FONT_SIZE)
+
+ def get_capital_letters(self):
+ client = GConf.Client.get_default()
+ value = client.get_string("/desktop/sugar/font/default_face")
+ return value==self.CAPITAL_LETTERS_FONT_FACE
+
+ def set_capital_letters(self, activar):
+ client = GConf.Client.get_default()
+ if (activar):
+ client.set_string('/desktop/sugar/font/default_face', self.CAPITAL_LETTERS_FONT_FACE)
+ else:
+ client.set_string('/desktop/sugar/font/default_face', self.DEFAULT_FONT_FACE)
+
+
+class Mouse:
+
+ WHITE_CURSOR_THEME="FlatbedCursors.White.Huge"
+ DEFAULT_CURSOR_THEME="sugar"
+ DEFAULT_ACCEL_MOUSE=3
+
+ def get_white_mouse(self):
+ client = GConf.Client.get_default()
+ value = client.get_string("/desktop/sugar/peripherals/mouse/cursor_theme")
+ return value==self.WHITE_CURSOR_THEME
+
+ def set_white_mouse(self, activar):
+ client = GConf.Client.get_default()
+ if (activar):
+ client.set_string("/desktop/sugar/peripherals/mouse/cursor_theme", self.WHITE_CURSOR_THEME)
+ else:
+ client.set_string("/desktop/sugar/peripherals/mouse/cursor_theme", self.DEFAULT_CURSOR_THEME)
+
+ def _set_white_mouse_setting(self):
+ cursor_theme = self.DEFAULT_CURSOR_THEME
+ if (self.get_white_mouse()):
+ cursor_theme = self.WHITE_CURSOR_THEME
+ settings = Gtk.Settings.get_default()
+ settings.set_property("gtk-cursor-theme-name", "%s" % (cursor_theme))
+
+ def get_accel_mouse(self):
+ client = GConf.Client.get_default()
+ value = client.get_float("/desktop/sugar/peripherals/mouse/motion_acceleration")
+ return value
+
+ def set_accel_mouse(self, value):
+ client = GConf.Client.get_default()
+ client.set_float("/desktop/sugar/peripherals/mouse/motion_acceleration", value)
+ self.run_config_mouse()
+
+ def _set_accel_mouse_setting(self):
+ cmd = ['xset', 'm' , str(self.get_accel_mouse())]
+ subprocess.call(cmd)
+
+ def run_config_mouse(self):
+ self._set_accel_mouse_setting()
+ self._set_white_mouse_setting()
+
+class AccessibilityManager:
+ def setup_accessibility(self):
+ client = GConf.Client.get_default()
+ is_accessibility = client.dir_exists("/desktop/sugar/accessibility")
+ mouse = Mouse()
+ if is_accessibility:
+ keyboard = Keyboard()
+ keyboard.run_config_keyboard()
+ mouse.run_config_mouse()
+ else:
+ mouse.set_accel_mouse(mouse.DEFAULT_ACCEL_MOUSE)
+ mouse.set_white_mouse(False)
+ mouse._set_accel_mouse_setting()
diff --git a/src/jarabe/model/bundleregistry.py b/src/jarabe/model/bundleregistry.py
index e441122..6fd4563 100644
--- a/src/jarabe/model/bundleregistry.py
+++ b/src/jarabe/model/bundleregistry.py
@@ -59,15 +59,18 @@ class BundleRegistry(GObject.GObject):
self._bundles = []
# hold a reference to the monitors so they don't get disposed
self._gio_monitors = []
+ self._monitor = {}
+ self._handler_id = {}
+ self._allow_file_monitoring = True
user_path = env.get_user_activities_path()
for activity_dir in [user_path, config.activities_path]:
self._scan_directory(activity_dir)
directory = Gio.File.new_for_path(activity_dir)
- monitor = directory.monitor_directory( \
- flags=Gio.FileMonitorFlags.NONE, cancellable=None)
- monitor.connect('changed', self.__file_monitor_changed_cb)
- self._gio_monitors.append(monitor)
+ self._monitor = directory.monitor_directory( \
+ flags=Gio.FileMonitorFlags.NONE, cancellable=None)
+ self._handler_id = self._monitor.connect('changed', self.__file_monitor_changed_cb)
+ self._gio_monitors.append(self._monitor)
self._last_defaults_mtime = -1
self._favorite_bundles = {}
@@ -94,12 +97,31 @@ class BundleRegistry(GObject.GObject):
def __file_monitor_changed_cb(self, monitor, one_file, other_file,
event_type):
- if not one_file.get_path().endswith('.activity'):
- return
- if event_type == Gio.FileMonitorEvent.CREATED:
- self.add_bundle(one_file.get_path(), install_mime_type=True)
- elif event_type == Gio.FileMonitorEvent.DELETED:
- self.remove_bundle(one_file.get_path())
+ if self._allow_file_monitoring:
+ if not one_file.get_path().endswith('.activity'):
+ return
+ if event_type == Gio.FileMonitorEvent.CREATED:
+ if self.get_bundle_by_path(one_file.get_path()) is None:
+ self.add_bundle(one_file.get_path(),install_mime_type=True)
+ elif event_type == Gio.FileMonitorEvent.DELETED:
+ self.remove_bundle(one_file.get_path())
+
+ # I tried 2 hours, trying to make the following mechanisms for
+ # event-blocking, work ::
+ #
+ # a. disconnect
+ # b. handler_disconnect
+ # c. handler_block/handler_unblock
+ # d. handler_block_by_func/handler_unblock_by_func
+ #
+ # I could not.
+ #
+ # In the end, I had to revert to using the oldy-goldy boolean flag.
+ def disable_directory_monitoring(self):
+ self._allow_file_monitoring = False
+
+ def enable_directory_monitoring(self):
+ self._allow_file_monitoring = True
def _load_mime_defaults(self):
defaults = {}
@@ -186,6 +208,12 @@ class BundleRegistry(GObject.GObject):
return bundle
return None
+ def get_bundle_by_path(self, bundle_path):
+ for bundle in self._bundles:
+ if bundle.get_path() == bundle_path:
+ return bundle
+ return None
+
def __iter__(self):
return self._bundles.__iter__()
@@ -213,7 +241,13 @@ class BundleRegistry(GObject.GObject):
bundle_dirs.sort(lambda d1, d2: cmp(bundles[d1], bundles[d2]))
for folder in bundle_dirs:
try:
- self._add_bundle(folder)
+ # sl#2818
+ #
+ # Add meta-info, to indicate that this is a startup
+ # operation.
+ #
+ # See 'elif not startup' notes in 'self._add_bundle()'
+ self._add_bundle(folder, False, True)
except:
# pylint: disable=W0702
logging.exception('Error while processing installed activity'
@@ -222,15 +256,13 @@ class BundleRegistry(GObject.GObject):
def add_bundle(self, bundle_path, install_mime_type=False):
bundle = self._add_bundle(bundle_path, install_mime_type)
if bundle is not None:
- self._set_bundle_favorite(bundle.get_bundle_id(),
- bundle.get_activity_version(),
- True)
self.emit('bundle-added', bundle)
return True
else:
return False
- def _add_bundle(self, bundle_path, install_mime_type=False):
+ def _add_bundle(self, bundle_path, install_mime_type=False,
+ startup=False):
logging.debug('STARTUP: Adding bundle %r', bundle_path)
try:
bundle = ActivityBundle(bundle_path)
@@ -250,17 +282,90 @@ class BundleRegistry(GObject.GObject):
return None
else:
logging.debug('Upgrade %s', bundle_id)
+
+ # Query if the bundle is a favorite...
+ self._is_bundle_favorite = \
+ self.is_bundle_favorite(installed.get_bundle_id(),
+ installed.get_activity_version())
+
+ # ...and then, remove the old bundle (we have the new
+ # one !!)
self.remove_bundle(installed.get_path())
- self._bundles.append(bundle)
+ # Check, if this bundle-id is a favorite.
+ if self._is_bundle_favorite:
+
+ # Mark the (new) bundle with this bundle-id, as
+ # favorite.
+ self.set_bundle_favorite(bundle.get_bundle_id(),
+ bundle.get_activity_version(),
+ True,
+ True)
+
+ # This (new) bundle is new (!!), and is also a
+ # favorite. Add this to Favorites-View.
+ self.emit('bundle-added', bundle)
+
+ elif not startup:
+
+ # Ticket sl#2818
+
+ # Sub-case
+ # --------
+ # The bundle is newly added; so set it as favorite as
+ # default, so that it is promptly available to the
+ # user.
+ # Note that, any newly added bundles during system
+ # startup, are not new-bundles-as-such. They were
+ # new, when they were first added, and their
+ # favorite-status set at that time.
+ # However, from code point of view, control reaches
+ # here, only if the bundle IS newly added by
+ # user-discretion.
+ self.set_bundle_favorite(bundle.get_bundle_id(),
+ bundle.get_activity_version(),
+ True,
+ True)
+
+ # Emit 'bundle-added' (of course, this bundle is new !!),
+ # so that it is added in Favorites-View.
+ self.emit('bundle-added', bundle)
+
+
+ # In either case,
+ # a. Startup
+ # b. Upgrade of bundle
+ # c, Addition of new bundle, by user-discretion.
+ # add the bundle to bundles-list.
+ self.add_bundle_to_bundlelist(bundle)
return bundle
+ def add_bundle_to_bundlelist(self, bundle):
+ for bundle_in_list in self._bundles:
+ if bundle_in_list.get_path() == \
+ bundle.get_path():
+ return False
+
+ self._bundles.append(bundle)
+ return True
+
def remove_bundle(self, bundle_path):
for bundle in self._bundles:
if bundle.get_path() == bundle_path:
+ # This bundle is going.
+ # Remove it from bundles list...
self._bundles.remove(bundle)
+
+ # ... and remove it from Favorites-List...
+ self.set_bundle_favorite(bundle.get_bundle_id(),
+ bundle.get_activity_version(),
+ False,
+ True)
+
+ # ...and remove its trace from Favorites-View.
self.emit('bundle-removed', bundle)
return True
+
return False
def get_activities_for_type(self, mime_type):
@@ -293,14 +398,22 @@ class BundleRegistry(GObject.GObject):
if bundle.get_bundle_id() == bundle_id and \
bundle.get_activity_version() == version:
return bundle
- raise ValueError('No bundle %r with version %r exists.' % \
- (bundle_id, version))
+ return None
+
+ def set_bundle_favorite(self, bundle_id, version, favorite,
+ force=False):
+ # Return if file monitoring is not allowed, and the previous
+ # bundle is not a favorite.
+ if not force:
+ if not self._allow_file_monitoring:
+ if not self._is_bundle_favorite:
+ return
- def set_bundle_favorite(self, bundle_id, version, favorite):
changed = self._set_bundle_favorite(bundle_id, version, favorite)
if changed:
bundle = self._find_bundle(bundle_id, version)
- self.emit('bundle-changed', bundle)
+ if bundle is not None:
+ self.emit('bundle-changed', bundle)
def _set_bundle_favorite(self, bundle_id, version, favorite):
key = self._get_favorite_key(bundle_id, version)
@@ -426,9 +539,6 @@ class BundleRegistry(GObject.GObject):
bundle.uninstall(install_path, force, delete_profile)
- if not self.remove_bundle(install_path):
- raise RegistrationException
-
def upgrade(self, bundle):
act = self.get_bundle(bundle.get_bundle_id())
if act is None: