Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/extensions/deviceicon
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/deviceicon')
-rw-r--r--extensions/deviceicon/Makefile.am10
-rw-r--r--extensions/deviceicon/Makefile.in446
-rw-r--r--extensions/deviceicon/__init__.py0
-rw-r--r--extensions/deviceicon/battery.py260
-rw-r--r--extensions/deviceicon/network.py1054
-rw-r--r--extensions/deviceicon/speaker.py223
-rw-r--r--extensions/deviceicon/speech.py148
-rw-r--r--extensions/deviceicon/touchpad.py146
-rw-r--r--extensions/deviceicon/volume.py130
9 files changed, 2417 insertions, 0 deletions
diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am
new file mode 100644
index 0000000..7ed1f77
--- /dev/null
+++ b/extensions/deviceicon/Makefile.am
@@ -0,0 +1,10 @@
+sugardir = $(pkgdatadir)/extensions/deviceicon
+
+sugar_PYTHON = \
+ __init__.py \
+ battery.py \
+ network.py \
+ speaker.py \
+ speech.py \
+ touchpad.py \
+ volume.py
diff --git a/extensions/deviceicon/Makefile.in b/extensions/deviceicon/Makefile.in
new file mode 100644
index 0000000..86ecc0b
--- /dev/null
+++ b/extensions/deviceicon/Makefile.in
@@ -0,0 +1,446 @@
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = extensions/deviceicon
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(sugar_PYTHON)
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__py_compile = PYTHON=$(PYTHON) $(SHELL) $(py_compile)
+am__installdirs = "$(DESTDIR)$(sugardir)"
+py_compile = $(top_srcdir)/py-compile
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GCONFTOOL = @GCONFTOOL@
+GCONF_SCHEMA_CONFIG_SOURCE = @GCONF_SCHEMA_CONFIG_SOURCE@
+GCONF_SCHEMA_FILE_DIR = @GCONF_SCHEMA_FILE_DIR@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SHELL_CFLAGS = @SHELL_CFLAGS@
+SHELL_LIBS = @SHELL_LIBS@
+STRIP = @STRIP@
+SUCROSE_VERSION = @SUCROSE_VERSION@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+sugardir = $(pkgdatadir)/extensions/deviceicon
+sugar_PYTHON = \
+ __init__.py \
+ battery.py \
+ network.py \
+ speaker.py \
+ speech.py \
+ touchpad.py \
+ volume.py
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign extensions/deviceicon/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign extensions/deviceicon/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sugarPYTHON: $(sugar_PYTHON)
+ @$(NORMAL_INSTALL)
+ test -z "$(sugardir)" || $(MKDIR_P) "$(DESTDIR)$(sugardir)"
+ @list='$(sugar_PYTHON)'; dlist=; list2=; test -n "$(sugardir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then b=; else b="$(srcdir)/"; fi; \
+ if test -f $$b$$p; then \
+ $(am__strip_dir) \
+ dlist="$$dlist $$f"; \
+ list2="$$list2 $$b$$p"; \
+ else :; fi; \
+ done; \
+ for file in $$list2; do echo $$file; done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sugardir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(sugardir)" || exit $$?; \
+ done || exit $$?; \
+ if test -n "$$dlist"; then \
+ $(am__py_compile) --destdir "$(DESTDIR)" \
+ --basedir "$(sugardir)" $$dlist; \
+ else :; fi
+
+uninstall-sugarPYTHON:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sugar_PYTHON)'; test -n "$(sugardir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ dir='$(DESTDIR)$(sugardir)'; \
+ filesc=`echo "$$files" | sed 's|$$|c|'`; \
+ fileso=`echo "$$files" | sed 's|$$|o|'`; \
+ st=0; \
+ for files in "$$files" "$$filesc" "$$fileso"; do \
+ $(am__uninstall_files_from_dir) || st=$$?; \
+ done; \
+ exit $$st
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+ for dir in "$(DESTDIR)$(sugardir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-sugarPYTHON
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-sugarPYTHON
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+ distclean-generic distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip install-sugarPYTHON installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \
+ uninstall-am uninstall-sugarPYTHON
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/extensions/deviceicon/__init__.py b/extensions/deviceicon/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extensions/deviceicon/__init__.py
diff --git a/extensions/deviceicon/battery.py b/extensions/deviceicon/battery.py
new file mode 100644
index 0000000..a70458a
--- /dev/null
+++ b/extensions/deviceicon/battery.py
@@ -0,0 +1,260 @@
+# Copyright (C) 2006-2007, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+from gettext import gettext as _
+import sys
+
+import gconf
+import glib
+import gobject
+import gtk
+import dbus
+
+from sugar.graphics import style
+from sugar.graphics.icon import get_icon_state
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.palette import Palette
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
+
+_ICON_NAME = 'battery'
+
+_STATUS_CHARGING = 0
+_STATUS_DISCHARGING = 1
+_STATUS_FULLY_CHARGED = 2
+_STATUS_NOT_PRESENT = 3
+
+_UP_DEVICE_IFACE = 'org.freedesktop.UPower.Device'
+
+_UP_TYPE_BATTERY = 2
+
+_UP_STATE_UNKNOWN = 0
+_UP_STATE_CHARGING = 1
+_UP_STATE_DISCHARGING = 2
+_UP_STATE_EMPTY = 3
+_UP_STATE_FULL = 4
+_UP_STATE_CHARGE_PENDING = 5
+_UP_STATE_DISCHARGE_PENDING = 6
+
+_WARN_MIN_PERCENTAGE = 15
+
+
+class DeviceView(TrayIcon):
+
+ FRAME_POSITION_RELATIVE = 102
+
+ def __init__(self, battery):
+ client = gconf.client_get_default()
+ self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+
+ self._model = DeviceModel(battery)
+ self.palette = BatteryPalette(glib.markup_escape_text(_('My Battery')))
+ self.palette.set_group_id('frame')
+ self._model.connect('updated',
+ self.__battery_status_changed_cb)
+ self._update_info()
+
+ def _update_info(self):
+ name = _ICON_NAME
+ current_level = self._model.props.level
+ xo_color = self._color
+ badge_name = None
+
+ if not self._model.props.present:
+ status = _STATUS_NOT_PRESENT
+ badge_name = None
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_WHITE.get_svg()))
+ elif self._model.props.charging:
+ status = _STATUS_CHARGING
+ name += '-charging'
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_WHITE.get_svg()))
+ elif self._model.props.discharging:
+ status = _STATUS_DISCHARGING
+ if current_level <= _WARN_MIN_PERCENTAGE:
+ badge_name = 'emblem-warning'
+ else:
+ status = _STATUS_FULLY_CHARGED
+
+ self.icon.props.icon_name = get_icon_state(name, current_level,
+ step=-5)
+ self.icon.props.xo_color = xo_color
+ self.icon.props.badge_name = badge_name
+
+ self.palette.set_info(current_level, self._model.props.time_remaining,
+ status)
+
+ def __battery_status_changed_cb(self, model):
+ self._update_info()
+
+
+class BatteryPalette(Palette):
+
+ def __init__(self, primary_text):
+ Palette.__init__(self, primary_text)
+ self._level = 0
+ self._time = 0
+ self._status = _STATUS_NOT_PRESENT
+ self._progress_bar = gtk.ProgressBar()
+ self._progress_bar.set_size_request(
+ style.zoom(style.GRID_CELL_SIZE * 4), -1)
+ self._progress_bar.show()
+ self._status_label = gtk.Label()
+ self._status_label.show()
+
+ vbox = gtk.VBox()
+ vbox.pack_start(self._progress_bar)
+ vbox.pack_start(self._status_label)
+ vbox.show()
+
+ self._progress_widget = vbox
+ self.set_content(self._progress_widget)
+
+ def set_info(self, percentage, seconds, status):
+ self._level = percentage
+ self._time = seconds
+ self._status = status
+ self._progress_bar.set_fraction(percentage / 100.0)
+ self._update_secondary()
+
+ def _update_secondary(self):
+ secondary_text = ''
+ status_text = '%s%%' % (self._level, )
+
+ progress_widget = self._progress_widget
+ if self._status == _STATUS_NOT_PRESENT:
+ secondary_text = _('Removed')
+ progress_widget = None
+ elif self._status == _STATUS_CHARGING:
+ secondary_text = _('Charging')
+ elif self._status == _STATUS_DISCHARGING:
+ if self._level <= _WARN_MIN_PERCENTAGE:
+ secondary_text = _('Very little power remaining')
+ else:
+ minutes_remaining = self._time // 60
+ remaining_hourpart = minutes_remaining // 60
+ remaining_minpart = minutes_remaining % 60
+ # TRANS: do not translate %(hour)d:%(min).2d it is a variable,
+ # only translate the word "remaining"
+ secondary_text = _('%(hour)d:%(min).2d remaining') % \
+ {'hour': remaining_hourpart, 'min': remaining_minpart}
+ else:
+ secondary_text = _('Charged')
+
+ self.set_content(progress_widget)
+
+ self.props.secondary_text = glib.markup_escape_text(secondary_text)
+ self._status_label.set_text(status_text)
+
+
+class DeviceModel(gobject.GObject):
+ __gproperties__ = {
+ 'level': (int, None, None, 0, 100, 0, gobject.PARAM_READABLE),
+ 'time-remaining': (int, None, None, 0, sys.maxint, 0,
+ gobject.PARAM_READABLE), # unit: seconds
+ 'charging': (bool, None, None, False, gobject.PARAM_READABLE),
+ 'discharging': (bool, None, None, False, gobject.PARAM_READABLE),
+ 'present': (bool, None, None, False, gobject.PARAM_READABLE),
+ }
+
+ __gsignals__ = {
+ 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ }
+
+ def __init__(self, battery):
+ gobject.GObject.__init__(self)
+ self._battery = battery
+ self._battery_props_iface = dbus.Interface(self._battery,
+ dbus.PROPERTIES_IFACE)
+ self._battery.connect_to_signal('Changed',
+ self.__battery_properties_changed_cb,
+ dbus_interface=_UP_DEVICE_IFACE)
+ self._fetch_properties_from_upower()
+
+ def _fetch_properties_from_upower(self):
+ """Get current values from UPower."""
+ # pylint: disable=W0201
+ try:
+ dbus_props = self._battery_props_iface.GetAll(_UP_DEVICE_IFACE)
+ except dbus.DBusException:
+ logging.error('Cannot access battery properties')
+ dbus_props = {}
+
+ self._level = dbus_props.get('Percentage', 0)
+ self._state = dbus_props.get('State', _UP_STATE_UNKNOWN)
+ self._present = dbus_props.get('IsPresent', False)
+ self._time_to_empty = dbus_props.get('TimeToEmpty', 0)
+ self._time_to_full = dbus_props.get('TimeToFull', 0)
+
+ def do_get_property(self, pspec):
+ """Return current value of given GObject property."""
+ if pspec.name == 'level':
+ return self._level
+ if pspec.name == 'charging':
+ return self._state == _UP_STATE_CHARGING
+ if pspec.name == 'discharging':
+ return self._state == _UP_STATE_DISCHARGING
+ if pspec.name == 'present':
+ return self._present
+ if pspec.name == 'time-remaining':
+ if self._state == _UP_STATE_CHARGING:
+ return self._time_to_full
+ if self._state == _UP_STATE_DISCHARGING:
+ return self._time_to_empty
+ return 0
+
+ def get_type(self):
+ return 'battery'
+
+ def __battery_properties_changed_cb(self):
+ old_level = self._level
+ old_state = self._state
+ old_present = self._present
+ old_time = self.props.time_remaining
+ self._fetch_properties_from_upower()
+ if self._level != old_level:
+ self.notify('level')
+ if self._state != old_state:
+ self.notify('charging')
+ self.notify('discharging')
+ if self._present != old_present:
+ self.notify('present')
+ if self.props.time_remaining != old_time:
+ self.notify('time-remaining')
+
+ self.emit('updated')
+
+
+def setup(tray):
+ bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
+ up_proxy = bus.get_object('org.freedesktop.UPower',
+ '/org/freedesktop/UPower')
+ upower = dbus.Interface(up_proxy, 'org.freedesktop.UPower')
+
+ for device_path in upower.EnumerateDevices():
+ device = bus.get_object('org.freedesktop.UPower', device_path)
+ device_prop_iface = dbus.Interface(device, dbus.PROPERTIES_IFACE)
+ device_type = device_prop_iface.Get(_UP_DEVICE_IFACE, 'Type')
+ if device_type == _UP_TYPE_BATTERY:
+ tray.add_device(DeviceView(device))
diff --git a/extensions/deviceicon/network.py b/extensions/deviceicon/network.py
new file mode 100644
index 0000000..96713fb
--- /dev/null
+++ b/extensions/deviceicon/network.py
@@ -0,0 +1,1054 @@
+#
+# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer
+# Copyright (C) 2009 Paraguay Educa, Martin Abente
+# Copyright (C) 2010 Plan Ceibal, Daniel Castelo
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from gettext import gettext as _
+import logging
+import hashlib
+import socket
+import struct
+import datetime
+import time
+import gtk
+import glib
+import gobject
+import gconf
+import dbus
+
+from sugar.graphics.icon import get_icon_state
+from sugar.graphics import style
+from sugar.graphics.palette import Palette
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.icon import Icon
+from sugar.graphics import xocolor
+from sugar import profile
+
+from jarabe.model import network
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+from jarabe.view.pulsingicon import PulsingIcon
+
+
+IP_ADDRESS_TEXT_TEMPLATE = _('IP address: %s')
+
+_GSM_STATE_NOT_READY = 0
+_GSM_STATE_DISCONNECTED = 1
+_GSM_STATE_CONNECTING = 2
+_GSM_STATE_CONNECTED = 3
+_GSM_STATE_FAILED = 4
+
+
+class WirelessPalette(Palette):
+ __gtype_name__ = 'SugarWirelessPalette'
+
+ __gsignals__ = {
+ 'deactivate-connection': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([])),
+ }
+
+ def __init__(self, primary_text):
+ Palette.__init__(self, label=primary_text)
+
+ self._disconnect_item = None
+
+ self._channel_label = gtk.Label()
+ self._channel_label.props.xalign = 0.0
+ self._channel_label.show()
+
+ self._ip_address_label = gtk.Label()
+
+ self._info = gtk.VBox()
+
+ def _padded(child, xalign=0, yalign=0.5):
+ padder = gtk.Alignment(xalign=xalign, yalign=yalign,
+ xscale=1, yscale=0.33)
+ padder.set_padding(style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING)
+ padder.add(child)
+ return padder
+
+ self._info.pack_start(_padded(self._channel_label))
+ self._info.pack_start(_padded(self._ip_address_label))
+ self._info.show_all()
+
+ self._disconnect_item = MenuItem(_('Disconnect'))
+ icon = Icon(icon_size=gtk.ICON_SIZE_MENU, icon_name='media-eject')
+ self._disconnect_item.set_image(icon)
+ self._disconnect_item.connect('activate',
+ self.__disconnect_activate_cb)
+ self.menu.append(self._disconnect_item)
+
+ def set_connecting(self):
+ label = glib.markup_escape_text(_('Connecting...'))
+ self.props.secondary_text = label
+
+ def _set_connected(self, iaddress):
+ self.set_content(self._info)
+ self.props.secondary_text = glib.markup_escape_text(_('Connected'))
+ 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):
+ label = glib.markup_escape_text(_('No wireless connection'))
+ self.props.primary_text = label
+ self.props.secondary_text = ''
+ self._disconnect_item.hide()
+ self.set_content(None)
+
+ def __disconnect_activate_cb(self, menuitem):
+ self.emit('deactivate-connection')
+
+ def _set_frequency(self, frequency):
+ channel = network.frequency_to_channel(frequency)
+ 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):
+ if ip_address is not None:
+ ip_address_text = IP_ADDRESS_TEXT_TEMPLATE % \
+ socket.inet_ntoa(struct.pack('I', ip_address))
+ else:
+ ip_address_text = ""
+ self._ip_address_label.set_text(ip_address_text)
+
+
+class WiredPalette(Palette):
+ __gtype_name__ = 'SugarWiredPalette'
+
+ def __init__(self):
+ label = glib.markup_escape_text(_('Wired Network'))
+ Palette.__init__(self, primary_text=label)
+
+ self._speed_label = gtk.Label()
+ self._speed_label.props.xalign = 0.0
+ self._speed_label.show()
+
+ self._ip_address_label = gtk.Label()
+
+ self._info = gtk.VBox()
+
+ def _padded(child, xalign=0, yalign=0.5):
+ padder = gtk.Alignment(xalign=xalign, yalign=yalign,
+ xscale=1, yscale=0.33)
+ padder.set_padding(style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING)
+ padder.add(child)
+ return padder
+
+ self._info.pack_start(_padded(self._speed_label))
+ self._info.pack_start(_padded(self._ip_address_label))
+ self._info.show_all()
+
+ self.set_content(self._info)
+ self.props.secondary_text = glib.markup_escape_text(_('Connected'))
+
+ def set_connected(self, speed, iaddress):
+ self._speed_label.set_text('%s: %d Mb/s' % (_('Speed'), speed))
+ self._set_ip_address(iaddress)
+
+ def _inet_ntoa(self, iaddress):
+ address = ['%s' % ((iaddress >> i) % 256) for i in [0, 8, 16, 24]]
+ return '.'.join(address)
+
+ def _set_ip_address(self, ip_address):
+ if ip_address is not None:
+ ip_address_text = IP_ADDRESS_TEXT_TEMPLATE % \
+ socket.inet_ntoa(struct.pack('I', ip_address))
+ else:
+ 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):
+ label = glib.markup_escape_text(_('Wireless modem'))
+ Palette.__init__(self, primary_text=label)
+
+ self._current_state = None
+ self._failed_connection = False
+
+ self._toggle_state_item = MenuItem('')
+ self._toggle_state_item.connect('activate', self.__toggle_state_cb)
+ self.menu.append(self._toggle_state_item)
+ self._toggle_state_item.show()
+
+ self.info_box = gtk.VBox()
+
+ self.error_title_label = gtk.Label("")
+ self.error_title_label.set_alignment(0, 0.5)
+ self.error_title_label.set_line_wrap(True)
+ self.info_box.pack_start(self.error_title_label)
+ self.error_description_label = gtk.Label("")
+ self.error_description_label.set_alignment(0, 0.5)
+ self.error_description_label.set_line_wrap(True)
+ self.info_box.pack_start(self.error_description_label)
+
+ self.connection_info_box = gtk.HBox()
+ icon = Icon(icon_name='data-upload', icon_size=gtk.ICON_SIZE_MENU)
+ self.connection_info_box.pack_start(icon)
+ icon.show()
+
+ self._data_label_up = gtk.Label()
+ self._data_label_up.props.xalign = 0.0
+ label_alignment = self._add_widget_with_padding(self._data_label_up)
+ self.connection_info_box.pack_start(label_alignment)
+ self._data_label_up.show()
+ label_alignment.show()
+
+ icon = Icon(icon_name='data-download', icon_size=gtk.ICON_SIZE_MENU)
+ self.connection_info_box.pack_start(icon)
+ icon.show()
+ self._data_label_down = gtk.Label()
+ self._data_label_down.props.xalign = 0.0
+ label_alignment = self._add_widget_with_padding(self._data_label_down)
+ self.connection_info_box.pack_start(label_alignment)
+ self._data_label_down.show()
+ label_alignment.show()
+
+ self.info_box.pack_start(self.connection_info_box)
+
+ self.info_box.show()
+ self.set_content(self.info_box)
+
+ self.set_state(_GSM_STATE_NOT_READY)
+
+ def _add_widget_with_padding(self, child, xalign=0, yalign=0.5):
+ alignment = gtk.Alignment(xalign=xalign, yalign=yalign,
+ xscale=1, yscale=0.33)
+ alignment.set_padding(style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING,
+ style.DEFAULT_SPACING)
+ alignment.add(child)
+ return alignment
+
+ def update_state(self, state, reason=0):
+ self._current_state = state
+ self._update_label_and_text(reason)
+
+ def _update_label_and_text(self, reason=0):
+ if self._current_state == _GSM_STATE_NOT_READY:
+ self._toggle_state_item.get_child().set_label('...')
+ label = glib.markup_escape_text(_('Please wait...'))
+ self.props.secondary_text = label
+
+ elif self._current_state == _GSM_STATE_DISCONNECTED:
+ if not self._failed_connection:
+ self._toggle_state_item.get_child().set_label(_('Connect'))
+ label = glib.markup_escape_text(_('Disconnected'))
+ self.props.secondary_text = label
+ icon = Icon(icon_name='dialog-ok', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
+
+ elif self._current_state == _GSM_STATE_CONNECTING:
+ self._toggle_state_item.get_child().set_label(_('Cancel'))
+ label = glib.markup_escape_text(_('Connecting...'))
+ self.props.secondary_text = label
+ icon = Icon(icon_name='dialog-cancel', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
+
+ elif self._current_state == _GSM_STATE_CONNECTED:
+ self._failed_connection = False
+ self._toggle_state_item.get_child().set_label(_('Disconnect'))
+ self.update_connection_time()
+ icon = Icon(icon_name='media-eject', \
+ icon_size=gtk.ICON_SIZE_MENU)
+ self._toggle_state_item.set_image(icon)
+
+ elif self._current_state == _GSM_STATE_FAILED:
+ message_error = self._get_error_by_nm_reason(reason)
+ self.add_alert(message_error[0], message_error[1])
+ else:
+ raise ValueError('Invalid GSM state while updating label and ' \
+ 'text, %s' % str(self._current_state))
+
+ def __toggle_state_cb(self, menuitem):
+ if self._current_state == _GSM_STATE_NOT_READY:
+ pass
+ elif self._current_state == _GSM_STATE_DISCONNECTED:
+ self.error_title_label.hide()
+ self.error_description_label.hide()
+ self.emit('gsm-connect')
+ elif self._current_state == _GSM_STATE_CONNECTING:
+ self.emit('gsm-disconnect')
+ elif self._current_state == _GSM_STATE_CONNECTED:
+ self.emit('gsm-disconnect')
+ else:
+ raise ValueError('Invalid GSM state while emitting signal, %s' % \
+ str(self._current_state))
+
+ def add_alert(self, error, suggestion):
+ self._failed_connection = True
+ action = _('Try connection again')
+ self._toggle_state_item.get_child().set_label(action)
+
+ title = _('Error: %s') % error
+ self.error_title_label.set_markup('<b>%s</b>' % title)
+ self.error_title_label.show()
+
+ message = _('Suggestion: %s') % suggestion
+ self.error_description_label.set_text(message)
+ self.error_description_label.show()
+
+ def update_connection_time(self, connection_time=None):
+ if connection_time is not None:
+ formatted_time = connection_time.strftime('%H:%M:%S')
+ else:
+ formatted_time = '00:00:00'
+ text = _('Connected for %s') % (formatted_time, )
+ self.props.secondary_text = glib.markup_escape_text(text)
+
+ def update_stats(self, in_bytes, out_bytes):
+ in_KBytes = in_bytes / 1024
+ out_KBytes = out_bytes / 1024
+ self._data_label_up.set_text(_('%d KB') % (out_KBytes))
+ self._data_label_down.set_text(_('%d KB') % (in_KBytes))
+
+ def _get_error_by_nm_reason(self, reason):
+ if reason in [network.NM_DEVICE_STATE_REASON_NO_SECRETS,
+ network.NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED]:
+ message = _('Check your PIN/PUK configuration.')
+ elif reason in [network.NM_DEVICE_STATE_REASON_PPP_DISCONNECT,
+ network.NM_DEVICE_STATE_REASON_PPP_FAILED]:
+ message = _('Check your Access Point Name ' \
+ '(APN) configuration')
+ elif reason in [network.NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER,
+ network.NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT]:
+ message = _('Check the Number configuration.')
+ elif reason == network.NM_DEVICE_STATE_REASON_CONFIG_FAILED:
+ message = _('Check your configuration.')
+ else:
+ message = ''
+ message_tuple = (network.get_error_by_reason(reason), message)
+ return message_tuple
+
+
+class WirelessDeviceView(ToolButton):
+
+ FRAME_POSITION_RELATIVE = 302
+
+ def __init__(self, device):
+ ToolButton.__init__(self)
+
+ self._bus = dbus.SystemBus()
+ self._device = device
+ self._device_props = None
+ self._flags = 0
+ self._ssid = ''
+ self._display_name = ''
+ self._mode = network.NM_802_11_MODE_UNKNOWN
+ self._strength = 0
+ self._frequency = 0
+ self._device_state = None
+ self._color = None
+ self._active_ap_op = None
+
+ self._icon = PulsingIcon()
+ self._icon.props.icon_name = get_icon_state('network-wireless', 0)
+ self._inactive_color = xocolor.XoColor(
+ '%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ self._icon.props.pulse_color = self._inactive_color
+ self._icon.props.base_color = self._inactive_color
+
+ self.set_icon_widget(self._icon)
+ self._icon.show()
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+ self._palette = WirelessPalette(self._display_name)
+ self._palette.connect('deactivate-connection',
+ self.__deactivate_connection_cb)
+ self.set_palette(self._palette)
+ self._palette.set_group_id('frame')
+
+ self._device_props = dbus.Interface(self._device,
+ dbus.PROPERTIES_IFACE)
+ self._device_props.GetAll(network.NM_DEVICE_IFACE, byte_arrays=True,
+ reply_handler=self.__get_device_props_reply_cb,
+ error_handler=self.__get_device_props_error_cb)
+
+ self._device_props.Get(network.NM_WIRELESS_IFACE, 'ActiveAccessPoint',
+ reply_handler=self.__get_active_ap_reply_cb,
+ error_handler=self.__get_active_ap_error_cb)
+
+ self._bus.add_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=network.NM_DEVICE_IFACE)
+
+ def disconnect(self):
+ self._bus.remove_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=network.NM_DEVICE_IFACE)
+
+ def __get_device_props_reply_cb(self, properties):
+ if 'State' in properties:
+ self._device_state = properties['State']
+ self._update_state()
+
+ def __get_device_props_error_cb(self, err):
+ logging.error('Error getting the device properties: %s', err)
+
+ def __get_active_ap_reply_cb(self, active_ap_op):
+ if self._active_ap_op != active_ap_op:
+ if self._active_ap_op is not None:
+ self._bus.remove_signal_receiver(
+ self.__ap_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._active_ap_op,
+ dbus_interface=network.NM_ACCESSPOINT_IFACE)
+ if active_ap_op == '/':
+ self._active_ap_op = None
+ return
+ self._active_ap_op = active_ap_op
+ active_ap = self._bus.get_object(network.NM_SERVICE, active_ap_op)
+ props = dbus.Interface(active_ap, dbus.PROPERTIES_IFACE)
+
+ props.GetAll(network.NM_ACCESSPOINT_IFACE, byte_arrays=True,
+ reply_handler=self.__get_all_ap_props_reply_cb,
+ error_handler=self.__get_all_ap_props_error_cb)
+
+ self._bus.add_signal_receiver(self.__ap_properties_changed_cb,
+ signal_name='PropertiesChanged',
+ path=self._active_ap_op,
+ dbus_interface=network.NM_ACCESSPOINT_IFACE)
+
+ def __get_active_ap_error_cb(self, err):
+ logging.error('Error getting the active access point: %s', err)
+
+ def __state_changed_cb(self, new_state, old_state, reason):
+ self._device_state = new_state
+ self._update_state()
+ self._device_props.Get(network.NM_WIRELESS_IFACE, 'ActiveAccessPoint',
+ reply_handler=self.__get_active_ap_reply_cb,
+ error_handler=self.__get_active_ap_error_cb)
+
+ def __ap_properties_changed_cb(self, properties):
+ self._update_properties(properties)
+
+ def _update_properties(self, properties):
+ if 'Mode' in properties:
+ self._mode = properties['Mode']
+ self._color = None
+ if 'Ssid' in properties:
+ self._ssid = properties['Ssid']
+ self._display_name = network.ssid_to_display_name(self._ssid)
+ self._color = None
+ if 'Strength' in properties:
+ self._strength = properties['Strength']
+ if 'Flags' in properties:
+ self._flags = properties['Flags']
+ if 'Frequency' in properties:
+ self._frequency = properties['Frequency']
+
+ if self._color == None:
+ if self._mode == network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._ssid):
+ self._color = profile.get_color()
+ else:
+ sha_hash = hashlib.sha1()
+ data = self._ssid + hex(self._flags)
+ sha_hash.update(data)
+ digest = hash(sha_hash.digest())
+ index = digest % len(xocolor.colors)
+
+ self._color = xocolor.XoColor('%s,%s' %
+ (xocolor.colors[index][0],
+ xocolor.colors[index][1]))
+ self._update()
+
+ def __get_all_ap_props_reply_cb(self, properties):
+ self._update_properties(properties)
+
+ def __get_all_ap_props_error_cb(self, err):
+ logging.error('Error getting the access point properties: %s', err)
+
+ def _update(self):
+ if self._flags == network.NM_802_11_AP_FLAGS_PRIVACY:
+ self._icon.props.badge_name = 'emblem-locked'
+ else:
+ self._icon.props.badge_name = None
+
+ label = glib.markup_escape_text(self._display_name)
+ self._palette.props.primary_text = label
+
+ self._update_state()
+ self._update_color()
+
+ def _update_state(self):
+ if self._active_ap_op is not None:
+ state = self._device_state
+ else:
+ state = network.NM_DEVICE_STATE_UNKNOWN
+
+ if self._mode != network.NM_802_11_MODE_ADHOC and \
+ network.is_sugar_adhoc_network(self._ssid) == False:
+ if state == network.NM_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
+ else:
+ channel = network.frequency_to_channel(self._frequency)
+ if state == network.NM_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.NM_DEVICE_STATE_PREPARE) and \
+ (state <= network.NM_DEVICE_STATE_IP_CONFIG):
+ self._palette.set_connecting()
+ self._icon.props.pulsing = True
+ elif state == network.NM_DEVICE_STATE_ACTIVATED:
+ address = self._device_props.Get(network.NM_DEVICE_IFACE, 'Ip4Address')
+ self._palette.set_connected_with_frequency(self._frequency,
+ address)
+ self._icon.props.pulsing = False
+ else:
+ self._icon.props.badge_name = None
+ self._icon.props.pulsing = False
+ self._icon.props.pulse_color = self._inactive_color
+ self._icon.props.base_color = self._inactive_color
+ self._palette.set_disconnected()
+
+ def _update_color(self):
+ self._icon.props.base_color = self._color
+
+ def __deactivate_connection_cb(self, palette, data=None):
+ network.disconnect_access_points([self._active_ap_op])
+
+ 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))
+ title = _('Mesh Network')
+ self._palette = WirelessPalette(glib.markup_escape_text(title))
+ 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,
+ dbus.PROPERTIES_IFACE)
+ self._device_props.Get(network.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=network.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=network.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):
+ channel = str(self._channel)
+ text = glib.markup_escape_text(_('Mesh Network %s') % (channel, ))
+ self._palette.props.primary_text = text
+
+ def _update(self):
+ state = self._device_state
+
+ if (state >= network.NM_DEVICE_STATE_PREPARE) and \
+ (state <= network.NM_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.NM_DEVICE_STATE_ACTIVATED:
+ address = self._device_props.Get(network.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(network.NM_SERVICE, network.NM_PATH)
+ netmgr = dbus.Interface(obj, network.NM_IFACE)
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
+ active_connections_o = netmgr_props.Get(network.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(network.NM_IFACE, conn_o)
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ ap_op = props.Get(network.NM_ACTIVE_CONN_IFACE, 'SpecificObject')
+
+ try:
+ obj = self._bus.get_object(network.NM_IFACE, ap_op)
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ device_type = props.Get(network.NM_DEVICE_IFACE, 'DeviceType')
+ if device_type == network.NM_DEVICE_TYPE_OLPC_MESH:
+ netmgr.DeactivateConnection(conn_o)
+ break
+ except dbus.exceptions.DBusException:
+ pass
+
+
+class WiredDeviceView(TrayIcon):
+
+ _ICON_NAME = 'network-wired'
+ FRAME_POSITION_RELATIVE = 301
+
+ def __init__(self, speed, address):
+ 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.set_palette_invoker(FrameWidgetInvoker(self))
+ self._palette = WiredPalette()
+ self.set_palette(self._palette)
+ self._palette.set_group_id('frame')
+ 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._palette = None
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+
+ self._bus.add_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=network.NM_DEVICE_IFACE)
+ self._bus.add_signal_receiver(self.__ppp_stats_changed_cb,
+ signal_name='PppStats',
+ path=self._device.object_path,
+ dbus_interface=network.NM_MODEM_IFACE)
+
+ def create_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)
+
+ self._palette = palette
+
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
+ props.GetAll(network.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:
+ connection.activate(self._device.object_path,
+ reply_handler=self.__connect_cb,
+ error_handler=self.__connect_error_cb)
+ else:
+ self._palette.add_alert(_('No GSM connection available.'), \
+ _('Create a connection in the ' \
+ 'control panel.'))
+
+ def __connect_cb(self, active_connection):
+ logging.debug('Connected successfully to gsm device, %s',
+ 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(network.NM_SERVICE, network.NM_PATH)
+ netmgr = dbus.Interface(obj, network.NM_IFACE)
+ netmgr_props = dbus.Interface(netmgr, dbus.PROPERTIES_IFACE)
+ active_connections_o = netmgr_props.Get(network.NM_IFACE, 'ActiveConnections')
+
+ for conn_o in active_connections_o:
+ obj = self._bus.get_object(network.NM_IFACE, conn_o)
+ props = dbus.Interface(obj, dbus.PROPERTIES_IFACE)
+ devices = props.Get(network.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('State: %s to %s, reason %s', old_state,
+ new_state, reason)
+ self._update_state(int(new_state), int(old_state), int(reason))
+
+ def __current_state_check_cb(self, properties):
+ self._update_state(int(properties['State']), 0, 0)
+
+ def __current_state_check_error_cb(self, error):
+ raise RuntimeError('Error when checking gsm device state, %s' % error)
+
+ def _update_state(self, state, old_state, reason):
+ gsm_state = None
+
+ if state is network.NM_DEVICE_STATE_ACTIVATED:
+ gsm_state = _GSM_STATE_CONNECTED
+ connection = network.find_gsm_connection()
+ if connection is not None:
+ self._connection_timestamp = time.time() - \
+ connection.get_settings('connection')['timestamp']
+ self._connection_time_handler = gobject.timeout_add_seconds( \
+ 1, self.__connection_timecount_cb)
+ self._palette.update_connection_time()
+ self._palette.update_stats(0, 0)
+ if self._palette is not None:
+ self._palette.connection_info_box.show()
+
+ elif state is network.NM_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)
+ if self._palette is not None:
+ self._palette.connection_info_box.hide()
+
+ elif state in [network.NM_DEVICE_STATE_UNMANAGED,
+ network.NM_DEVICE_STATE_UNAVAILABLE,
+ network.NM_DEVICE_STATE_UNKNOWN]:
+ gsm_state = _GSM_STATE_NOT_READY
+
+ elif (state >= network.NM_DEVICE_STATE_PREPARE) and \
+ (state <= network.NM_DEVICE_STATE_IP_CONFIG):
+ gsm_state = _GSM_STATE_CONNECTING
+
+ elif state == network.NM_DEVICE_STATE_FAILED:
+ gsm_state = _GSM_STATE_FAILED
+
+ if self._palette is not None:
+ self._palette.update_state(gsm_state, reason)
+
+ def disconnect(self):
+ self._bus.remove_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=network.NM_DEVICE_IFACE)
+
+ def __ppp_stats_changed_cb(self, in_bytes, out_bytes):
+ self._palette.update_stats(in_bytes, out_bytes)
+
+ def __connection_timecount_cb(self):
+ self._connection_timestamp = self._connection_timestamp + 1
+ connection_time = \
+ datetime.datetime.fromtimestamp(self._connection_timestamp)
+ self._palette.update_connection_time(connection_time)
+ return True
+
+
+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)
+
+ def disconnect(self):
+ self._device_view.disconnect()
+ self._tray.remove_device(self._device_view)
+ del self._device_view
+ 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(network.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=network.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=network.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 >= network.NM_DEVICE_STATE_PREPARE) and \
+ (state <= network.NM_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()
+ self._device = device
+ self._device_state = None
+ self._device_view = None
+ self._tray = tray
+
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
+ props.GetAll(network.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=network.NM_DEVICE_IFACE)
+
+ def disconnect(self):
+ self._bus.remove_signal_receiver(self.__state_changed_cb,
+ signal_name='StateChanged',
+ path=self._device.object_path,
+ dbus_interface=network.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 == network.NM_DEVICE_STATE_ACTIVATED:
+ props = dbus.Interface(self._device, dbus.PROPERTIES_IFACE)
+ address = props.Get(network.NM_DEVICE_IFACE, 'Ip4Address')
+ speed = props.Get(network.NM_WIRED_IFACE, 'Speed')
+ self._device_view = WiredDeviceView(speed, address)
+ self._tray.add_device(self._device_view)
+ else:
+ if self._device_view is not None:
+ self._tray.remove_device(self._device_view)
+ 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):
+ self._bus = dbus.SystemBus()
+ self._devices = {}
+ self._netmgr = None
+ self._tray = tray
+
+ try:
+ obj = self._bus.get_object(network.NM_SERVICE, network.NM_PATH)
+ self._netmgr = dbus.Interface(obj, network.NM_IFACE)
+ except dbus.DBusException:
+ logging.error('%s service not available', network.NM_SERVICE)
+ return
+
+ self._netmgr.GetDevices(reply_handler=self.__get_devices_reply_cb,
+ error_handler=self.__get_devices_error_cb)
+
+ self._bus.add_signal_receiver(self.__device_added_cb,
+ signal_name='DeviceAdded',
+ dbus_interface=network.NM_IFACE)
+ self._bus.add_signal_receiver(self.__device_removed_cb,
+ signal_name='DeviceRemoved',
+ dbus_interface=network.NM_IFACE)
+
+ def __get_devices_reply_cb(self, devices):
+ for device_op in devices:
+ self._check_device(device_op)
+
+ def __get_devices_error_cb(self, err):
+ logging.error('Failed to get devices: %s', err)
+
+ def _check_device(self, device_op):
+ if device_op in self._devices:
+ return
+
+ nm_device = self._bus.get_object(network.NM_SERVICE, device_op)
+ props = dbus.Interface(nm_device, dbus.PROPERTIES_IFACE)
+
+ device_type = props.Get(network.NM_DEVICE_IFACE, 'DeviceType')
+ if device_type == network.NM_DEVICE_TYPE_ETHERNET:
+ device = WiredDeviceObserver(nm_device, self._tray)
+ self._devices[device_op] = device
+ elif device_type == network.NM_DEVICE_TYPE_WIFI:
+ device = WirelessDeviceObserver(nm_device, self._tray)
+ self._devices[device_op] = device
+ elif device_type == network.NM_DEVICE_TYPE_OLPC_MESH:
+ device = MeshDeviceObserver(nm_device, self._tray)
+ self._devices[device_op] = device
+ elif device_type == network.NM_DEVICE_TYPE_MODEM:
+ device = GsmDeviceObserver(nm_device, self._tray)
+ self._devices[device_op] = device
+
+ def __device_added_cb(self, device_op):
+ self._check_device(device_op)
+
+ def __device_removed_cb(self, device_op):
+ if device_op in self._devices:
+ device = self._devices[device_op]
+ device.disconnect()
+ del self._devices[device_op]
+
+
+def setup(tray):
+ device_observer = NetworkManagerObserver(tray)
diff --git a/extensions/deviceicon/speaker.py b/extensions/deviceicon/speaker.py
new file mode 100644
index 0000000..d8b26be
--- /dev/null
+++ b/extensions/deviceicon/speaker.py
@@ -0,0 +1,223 @@
+# Copyright (C) 2008 Martin Dengler
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from gettext import gettext as _
+import gconf
+
+import glib
+import gobject
+import gtk
+
+from sugar.graphics import style
+from sugar.graphics.icon import get_icon_state, Icon
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.palette import Palette
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+from jarabe.model import sound
+
+_ICON_NAME = 'speaker'
+
+
+class DeviceView(TrayIcon):
+
+ FRAME_POSITION_RELATIVE = 103
+
+ def __init__(self):
+ client = gconf.client_get_default()
+ self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+
+ self._model = DeviceModel()
+ self._model.connect('notify::level', self.__speaker_status_changed_cb)
+ self._model.connect('notify::muted', self.__speaker_status_changed_cb)
+
+ self.connect('expose-event', self.__expose_event_cb)
+
+ self._icon_widget.connect('button-release-event',
+ self.__button_release_event_cb)
+
+ self._update_info()
+
+ def create_palette(self):
+ label = glib.markup_escape_text(_('My Speakers'))
+ palette = SpeakerPalette(label, model=self._model)
+ palette.set_group_id('frame')
+ return palette
+
+ def _update_info(self):
+ name = _ICON_NAME
+ current_level = self._model.props.level
+ xo_color = self._color
+
+ if self._model.props.muted:
+ name += '-muted'
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_WHITE.get_svg()))
+
+ self.icon.props.icon_name = get_icon_state(name, current_level,
+ step=-1)
+ self.icon.props.xo_color = xo_color
+
+ def __button_release_event_cb(self, widget, event):
+ if event.button != 1:
+ return False
+
+ self.palette_invoker.notify_right_click()
+ return True
+
+ def __expose_event_cb(self, *args):
+ self._update_info()
+
+ def __speaker_status_changed_cb(self, pspec_, param_):
+ self._update_info()
+
+
+class SpeakerPalette(Palette):
+
+ def __init__(self, primary_text, model):
+ Palette.__init__(self, label=primary_text)
+
+ self._model = model
+
+ vbox = gtk.VBox()
+ self.set_content(vbox)
+ vbox.show()
+
+ vol_step = sound.VOLUME_STEP
+ self._adjustment = gtk.Adjustment(value=self._model.props.level,
+ lower=0,
+ upper=100 + vol_step,
+ step_incr=vol_step,
+ page_incr=vol_step,
+ page_size=vol_step)
+ self._hscale = gtk.HScale(self._adjustment)
+ self._hscale.set_digits(0)
+ self._hscale.set_draw_value(False)
+ vbox.add(self._hscale)
+ self._hscale.show()
+
+ self._mute_item = MenuItem('')
+ self._mute_icon = Icon(icon_size=gtk.ICON_SIZE_MENU)
+ self._mute_item.set_image(self._mute_icon)
+ self.menu.append(self._mute_item)
+ self._mute_item.show()
+
+ self._adjustment_handler_id = \
+ self._adjustment.connect('value_changed',
+ self.__adjustment_changed_cb)
+
+ self._model_notify_level_handler_id = \
+ self._model.connect('notify::level', self.__level_changed_cb)
+ self._model.connect('notify::muted', self.__muted_changed_cb)
+
+ self._mute_item.connect('activate', self.__mute_activate_cb)
+
+ self.connect('popup', self.__popup_cb)
+
+ def _update_muted(self):
+ if self._model.props.muted:
+ mute_item_text = _('Unmute')
+ mute_item_icon_name = 'dialog-ok'
+ else:
+ mute_item_text = _('Mute')
+ mute_item_icon_name = 'dialog-cancel'
+ self._mute_item.get_child().set_text(mute_item_text)
+ self._mute_icon.props.icon_name = mute_item_icon_name
+
+ def _update_level(self):
+ if self._adjustment.value != self._model.props.level:
+ self._adjustment.handler_block(self._adjustment_handler_id)
+ try:
+ self._adjustment.value = self._model.props.level
+ finally:
+ self._adjustment.handler_unblock(self._adjustment_handler_id)
+
+ def __adjustment_changed_cb(self, adj_):
+ self._model.handler_block(self._model_notify_level_handler_id)
+ try:
+ self._model.props.level = self._adjustment.value
+ finally:
+ self._model.handler_unblock(self._model_notify_level_handler_id)
+ self._model.props.muted = self._adjustment.value == 0
+
+ def __level_changed_cb(self, pspec_, param_):
+ self._update_level()
+
+ def __mute_activate_cb(self, menuitem_):
+ self._model.props.muted = not self._model.props.muted
+
+ def __muted_changed_cb(self, pspec_, param_):
+ self._update_muted()
+
+ def __popup_cb(self, palette_):
+ self._update_level()
+ self._update_muted()
+
+
+class DeviceModel(gobject.GObject):
+ __gproperties__ = {
+ 'level': (int, None, None, 0, 100, 0, gobject.PARAM_READWRITE),
+ 'muted': (bool, None, None, False, gobject.PARAM_READWRITE),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ sound.muted_changed.connect(self.__muted_changed_cb)
+ sound.volume_changed.connect(self.__volume_changed_cb)
+
+ def __muted_changed_cb(self, **kwargs):
+ self.notify('muted')
+
+ def __volume_changed_cb(self, **kwargs):
+ self.notify('level')
+
+ def _get_level(self):
+ return sound.get_volume()
+
+ def _set_level(self, new_volume):
+ sound.set_volume(new_volume)
+
+ def _get_muted(self):
+ return sound.get_muted()
+
+ def _set_muted(self, mute):
+ sound.set_muted(mute)
+
+ def get_type(self):
+ return 'speaker'
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'level':
+ return self._get_level()
+ elif pspec.name == 'muted':
+ return self._get_muted()
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'level':
+ self._set_level(value)
+ elif pspec.name == 'muted':
+ self._set_muted(value)
+
+
+def setup(tray):
+ tray.add_device(DeviceView())
diff --git a/extensions/deviceicon/speech.py b/extensions/deviceicon/speech.py
new file mode 100644
index 0000000..6b8f915
--- /dev/null
+++ b/extensions/deviceicon/speech.py
@@ -0,0 +1,148 @@
+# Copyright (C) 2011 One Laptop Per Child
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from gettext import gettext as _
+
+import glib
+import gtk
+import gconf
+import gobject
+
+from sugar.graphics.icon import Icon
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.palette import Palette
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics import style
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+from jarabe.model import speech
+
+
+_ICON_NAME = 'microphone'
+
+
+class SpeechDeviceView(TrayIcon):
+
+ FRAME_POSITION_RELATIVE = 150
+
+ def __init__(self):
+ client = gconf.client_get_default()
+ self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
+ TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+ self._manager = speech.get_speech_manager()
+ self._icon_widget.connect('button-release-event',
+ self.__button_release_event_cb)
+
+ def create_palette(self):
+ label = glib.markup_escape_text(_('Speech'))
+ palette = SpeechPalette(label, manager=self._manager)
+ palette.set_group_id('frame')
+ return palette
+
+ def __button_release_event_cb(self, widget, event):
+ self.palette_invoker.notify_right_click()
+ return True
+
+
+class SpeechPalette(Palette):
+
+ def __init__(self, primary_text, manager):
+ Palette.__init__(self, label=primary_text)
+
+ self._manager = manager
+ self._manager.connect('play', self._set_menu_state, 'play')
+ self._manager.connect('stop', self._set_menu_state, 'stop')
+ self._manager.connect('pause', self._set_menu_state, 'pause')
+
+ vbox = gtk.VBox()
+ self.set_content(vbox)
+
+ self._play_icon = Icon(icon_name='player_play')
+ self._pause_icon = Icon(icon_name='player_pause')
+ self._play_pause_menu = MenuItem(text_label=_('Say selected text'))
+ self._play_pause_menu.set_image(self._play_icon)
+ self._play_pause_menu.connect('activate', self.__play_activated_cb)
+ self._play_pause_menu.show()
+
+ self._stop_menu = MenuItem(icon_name='player_stop',
+ text_label=_('Stop playback'))
+ self._stop_menu.connect('activate', self.__stop_activated_cb)
+ self._stop_menu.set_sensitive(False)
+ self._stop_menu.show()
+
+ self.menu.append(self._play_pause_menu)
+ self.menu.append(self._stop_menu)
+
+ self._adj_pitch = gtk.Adjustment(value=self._manager.get_pitch(),
+ lower=self._manager.MIN_PITCH,
+ upper=self._manager.MAX_PITCH)
+ self._hscale_pitch = gtk.HScale(self._adj_pitch)
+ self._hscale_pitch.set_draw_value(False)
+
+ vbox.pack_start(gtk.Label(_('Pitch')), padding=style.DEFAULT_PADDING)
+ vbox.pack_start(self._hscale_pitch)
+
+ self._adj_rate = gtk.Adjustment(value=self._manager.get_rate(),
+ lower=self._manager.MIN_RATE,
+ upper=self._manager.MAX_RATE)
+ self._hscale_rate = gtk.HScale(self._adj_rate)
+ self._hscale_rate.set_draw_value(False)
+
+ vbox.pack_start(gtk.Label(_('Rate')), padding=style.DEFAULT_PADDING)
+ vbox.pack_start(self._hscale_rate)
+ vbox.show_all()
+
+ self._adj_pitch.connect('value_changed', self.__adj_pitch_changed_cb)
+ self._adj_rate.connect('value_changed', self.__adj_rate_changed_cb)
+
+ def __adj_pitch_changed_cb(self, adjustement):
+ self._manager.set_pitch(int(adjustement.value))
+
+ def __adj_rate_changed_cb(self, adjustement):
+ self._manager.set_rate(int(adjustement.value))
+
+ def __play_activated_cb(self, widget):
+ if self._manager.is_paused:
+ self._manager.restart()
+ elif not self._manager.is_playing:
+ self._manager.say_selected_text()
+ else:
+ self._manager.pause()
+
+ def __stop_activated_cb(self, widget):
+ self._manager.stop()
+
+ def _set_menu_state(self, manager, signal):
+ if signal == 'play':
+ self._play_pause_menu.set_image(self._pause_icon)
+ self._play_pause_menu.set_label(_('Pause playback'))
+ self._stop_menu.set_sensitive(True)
+
+ elif signal == 'pause':
+ self._play_pause_menu.set_image(self._play_icon)
+ self._play_pause_menu.set_label(_('Say selected text'))
+ self._stop_menu.set_sensitive(True)
+
+ elif signal == 'stop':
+ self._play_pause_menu.set_image(self._play_icon)
+ self._play_pause_menu.set_label(_('Say selected text'))
+ self._stop_menu.set_sensitive(False)
+
+
+def setup(tray):
+ tray.add_device(SpeechDeviceView())
diff --git a/extensions/deviceicon/touchpad.py b/extensions/deviceicon/touchpad.py
new file mode 100644
index 0000000..150e8dc
--- /dev/null
+++ b/extensions/deviceicon/touchpad.py
@@ -0,0 +1,146 @@
+# Copyright (C) 2010, Walter Bender, Sugar Labs
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+from gettext import gettext as _
+import os
+
+import gtk
+import gconf
+import glib
+
+import logging
+
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.palette import Palette
+from sugar.graphics import style
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
+TOUCHPAD_MODE_MOUSE = 'mouse'
+TOUCHPAD_MODE_PENTABLET = 'pentablet'
+
+TOUCHPAD_MODES = (TOUCHPAD_MODE_MOUSE, TOUCHPAD_MODE_PENTABLET)
+STATUS_TEXT = (_('finger'), _('stylus'))
+STATUS_ICON = ('touchpad-capacitive', 'touchpad-resistive')
+
+# NODE_PATH is used to communicate with the touchpad device.
+NODE_PATH = '/sys/devices/platform/i8042/serio1/hgpk_mode'
+
+
+class DeviceView(TrayIcon):
+ """ Manage the touchpad mode from the device palette on the Frame. """
+
+ FRAME_POSITION_RELATIVE = 500
+
+ def __init__(self):
+ """ Create the icon that represents the touchpad. """
+ icon_name = STATUS_ICON[_read_touchpad_mode()]
+
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+ TrayIcon.__init__(self, icon_name=icon_name, xo_color=color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ def create_palette(self):
+ """ Create a palette for this icon; called by the Sugar framework
+ when a palette needs to be displayed. """
+ label = glib.markup_escape_text(_('My touchpad'))
+ self.palette = ResourcePalette(label, self.icon)
+ self.palette.set_group_id('frame')
+ return self.palette
+
+ def __button_release_event_cb(self, widget, event):
+ """ Callback for button release event; used to invoke touchpad-mode
+ change. """
+ self.palette.toggle_mode()
+ return True
+
+
+class ResourcePalette(Palette):
+ """ Palette attached to the decive icon that represents the touchpas. """
+
+ def __init__(self, primary_text, icon):
+ """ Create the palette and initilize with current touchpad status. """
+ Palette.__init__(self, label=primary_text)
+
+ self._icon = icon
+
+ vbox = gtk.VBox()
+ self.set_content(vbox)
+
+ self._status_text = gtk.Label()
+ vbox.pack_start(self._status_text, padding=style.DEFAULT_PADDING)
+ self._status_text.show()
+
+ vbox.show()
+
+ self._mode = _read_touchpad_mode()
+ self._update()
+
+ def _update(self):
+ """ Update the label and icon based on the current mode. """
+ self._status_text.set_label(STATUS_TEXT[self._mode])
+ self._icon.props.icon_name = STATUS_ICON[self._mode]
+
+ def toggle_mode(self):
+ """ Toggle the touchpad mode. """
+ self._mode = 1 - self._mode
+ _write_touchpad_mode(self._mode)
+ self._update()
+
+
+def setup(tray):
+ """ Initialize the devic icon; called by the shell when initializing the
+ Frame. """
+ if os.path.exists(NODE_PATH):
+ tray.add_device(DeviceView())
+ _write_touchpad_mode_str(TOUCHPAD_MODE_MOUSE)
+
+
+def _read_touchpad_mode_str():
+ """ Read the touchpad mode string from the node path. """
+ node_file_handle = open(NODE_PATH, 'r')
+ text = node_file_handle.read().strip().lower()
+ node_file_handle.close()
+ return text
+
+
+def _read_touchpad_mode():
+ """ Read the touchpad mode and return the mode index. """
+ mode_str = _read_touchpad_mode_str()
+ if mode_str not in TOUCHPAD_MODES:
+ return None
+ return TOUCHPAD_MODES.index(mode_str)
+
+
+def _write_touchpad_mode_str(mode_str):
+ """ Write the touchpad mode to the node path. """
+ try:
+ node_file_handle = open(NODE_PATH, 'w')
+ except IOError, e:
+ logging.error('Error opening %s for writing: %s', NODE_PATH, e)
+ return
+ node_file_handle.write(mode_str)
+ node_file_handle.close()
+
+
+def _write_touchpad_mode(mode_num):
+ """ Look up the mode (by index) and write to node path. """
+ return _write_touchpad_mode_str(TOUCHPAD_MODES[mode_num])
diff --git a/extensions/deviceicon/volume.py b/extensions/deviceicon/volume.py
new file mode 100644
index 0000000..ea2377d
--- /dev/null
+++ b/extensions/deviceicon/volume.py
@@ -0,0 +1,130 @@
+# Copyright (C) 2008 One Laptop Per Child
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+
+import gobject
+import gio
+import gtk
+import gconf
+
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.journal import journalactivity
+from jarabe.view.palettes import VolumePalette
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
+
+_icons = {}
+
+
+class DeviceView(TrayIcon):
+
+ FRAME_POSITION_RELATIVE = 500
+
+ def __init__(self, mount):
+
+ self._mount = mount
+
+ icon_name = None
+ icon_theme = gtk.icon_theme_get_default()
+ for icon_name in self._mount.get_icon().props.names:
+ icon_info = icon_theme.lookup_icon(icon_name,
+ gtk.ICON_SIZE_LARGE_TOOLBAR, 0)
+ if icon_info is not None:
+ break
+
+ if icon_name is None:
+ icon_name = 'drive'
+
+ # TODO: retrieve the colors from the owner of the device
+ client = gconf.client_get_default()
+ color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ TrayIcon.__init__(self, icon_name=icon_name, xo_color=color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+
+ self.connect('button-release-event', self.__button_release_event_cb)
+
+ def create_palette(self):
+ palette = VolumePalette(self._mount)
+ palette.set_group_id('frame')
+ return palette
+
+ def __button_release_event_cb(self, widget, event):
+ journal = journalactivity.get_journal()
+ journal.set_active_volume(self._mount)
+ journal.reveal()
+ return True
+
+
+def setup(tray):
+ gobject.idle_add(_setup_volumes, tray)
+
+
+def _setup_volumes(tray):
+ volume_monitor = gio.volume_monitor_get()
+
+ for volume in volume_monitor.get_volumes():
+ _mount(volume, tray)
+
+ for mount in volume_monitor.get_mounts():
+ _add_device(mount, tray)
+
+ volume_monitor.connect('volume-added', _volume_added_cb, tray)
+ volume_monitor.connect('mount-added', _mount_added_cb, tray)
+ volume_monitor.connect('mount-removed', _mount_removed_cb, tray)
+
+
+def _volume_added_cb(volume_monitor, volume, tray):
+ _mount(volume, tray)
+
+
+def _mount(volume, tray):
+ # Follow Nautilus behaviour here
+ # since it has the same issue with removable device
+ # and it would be good to not invent our own workflow
+ if hasattr(volume, 'should_automount') and not volume.should_automount():
+ return
+
+ #TODO: should be done by some other process, like gvfs-hal-volume-monitor
+ #TODO: use volume.should_automount() when it gets into pygtk
+ if volume.get_mount() is None and volume.can_mount():
+ #TODO: pass None as mount_operation, or better, SugarMountOperation
+ volume.mount(gtk.MountOperation(tray.get_toplevel()), _mount_cb)
+
+
+def _mount_cb(volume, result):
+ logging.debug('_mount_cb %r %r', volume, result)
+ volume.mount_finish(result)
+
+
+def _mount_added_cb(volume_monitor, mount, tray):
+ _add_device(mount, tray)
+
+
+def _mount_removed_cb(volume_monitor, mount, tray):
+ icon = _icons[mount]
+ tray.remove_device(icon)
+ del _icons[mount]
+
+
+def _add_device(mount, tray):
+ icon = DeviceView(mount)
+ _icons[mount] = icon
+ tray.add_device(icon)