Split sugar-toolkit out of sugar shell.

This commit is contained in:
Marco Pesenti Gritti
2008-02-06 10:20:33 +01:00
parent 44efc2a131
commit 488402df7d
263 changed files with 31 additions and 35736 deletions
-1
View File
@@ -1 +0,0 @@
config.py
-11
View File
@@ -1,11 +0,0 @@
SUBDIRS = controlpanel hardware model view intro
sugardir = $(pkgdatadir)/shell
sugar_PYTHON = \
config.py \
emulator.py \
logsmanager.py \
main.py \
shellservice.py
EXTRA_DIST = $(bin_SCRIPTS) $(conf_DATA)
-26
View File
@@ -1,26 +0,0 @@
"""OLPC Sugar Graphical "Shell" Interface
Provides the shell-level operations for managing
the OLPC laptop computers. It interacts heavily
with (and depends upon) the Sugar UI libraries.
This is a "graphical" shell, the name does not
refer to a command-line "shell" interface.
"""
# 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
-18
View File
@@ -1,18 +0,0 @@
# Copyright (C) 2008 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
prefix = '@prefix@'
data_path = '@prefix@/share/sugar/data'
-5
View File
@@ -1,5 +0,0 @@
sugardir = $(pkgdatadir)/shell/controlpanel
sugar_PYTHON = \
__init__.py \
cmd.py \
control.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-80
View File
@@ -1,80 +0,0 @@
# Copyright (C) 2007, One Laptop Per Child
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import sys
import getopt
from gettext import gettext as _
from sugar import env
from controlpanel import control
def cmd_help():
print _('Usage: sugar-control-panel [ option ] key [ args ... ] \n\
Control for the sugar environment. \n\
Options: \n\
-h show this help message and exit \n\
-l list all the available options \n\
-h key show information about this key \n\
-g key get the current value of the key \n\
-s key set the current value for the key \n\
')
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], "h:s:g:l", [])
except getopt.GetoptError:
cmd_help()
sys.exit(2)
output = None
verbose = False
if not opts:
cmd_help()
sys.exit()
for opt, key in opts:
if opt in ("-h"):
method = getattr(control, 'set_' + key, None)
if method is None:
print _("sugar-control-panel: key=%s not an available option"% key)
sys.exit()
else:
print method.__doc__
if opt in ("-l"):
elems = dir(control)
for elem in elems:
if elem.startswith('set_'):
print elem[4:]
if opt in ("-g"):
method = getattr(control, 'print_' + key, None)
if method is None:
print _("sugar-control-panel: key=%s not an available option"% key)
sys.exit()
else:
method()
if opt in ("-s"):
method = getattr(control, 'set_' + key, None)
if method is None:
print _("sugar-control-panel: key=%s not an available option"% key)
sys.exit()
else:
try:
method(*args)
except Exception, e:
print _("sugar-control-panel: %s"% e)
-481
View File
@@ -1,481 +0,0 @@
# Copyright (C) 2007, One Laptop Per Child
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
#
# The language config is based on the system-config-language
# (http://fedoraproject.org/wiki/SystemConfig/language) tool
# and the timezone config on the system-config-date
# (http://fedoraproject.org/wiki/SystemConfig/date) tool.
# Parts of the code were reused.
#
import os
import string
import shutil
from gettext import gettext as _
import dbus
from sugar import profile
from sugar.graphics.xocolor import XoColor
NM_SERVICE_NAME = 'org.freedesktop.NetworkManager'
NM_SERVICE_PATH = '/org/freedesktop/NetworkManager'
NM_SERVICE_IFACE = 'org.freedesktop.NetworkManager'
NM_ASLEEP = 1
_COLORS = {'red': {'dark':'#b20008', 'medium':'#e6000a', 'light':'#ffadce'},
'orange': {'dark':'#9a5200', 'medium':'#c97e00', 'light':'#ffc169'},
'yellow': {'dark':'#807500', 'medium':'#be9e00', 'light':'#fffa00'},
'green': {'dark':'#008009', 'medium':'#00b20d', 'light':'#8bff7a'},
'blue': {'dark':'#00588c', 'medium':'#005fe4', 'light':'#bccdff'},
'purple': {'dark':'#5e008c', 'medium':'#7f00bf', 'light':'#d1a3ff'}
}
_MODIFIERS = ('dark', 'medium', 'light')
_TIMEZONE_CONFIG = '/etc/sysconfig/clock'
_LANGUAGES = {
'Afrikaans/South_Africa': 'af_ZA',
'Albanian': 'sq_AL.UTF-8',
'Amharic/Ethiopian': 'am_ET.UTF-8',
'Arabic/Algeria': 'ar_DZ.UTF-8',
'Arabic/Bahrain': 'ar_BH.UTF-8',
'Arabic/Egypt': 'ar_EG.UTF-8',
'Arabic/India': 'ar_IN.UTF-8',
'Arabic/Iraq': 'ar_IQ.UTF-8',
'Arabic/Jordan': 'ar_JO.UTF-8',
'Arabic/Kuwait': 'ar_KW.UTF-8',
'Arabic/Lebanon': 'ar_LB.UTF-8',
'Arabic/Libyan_Arab_Jamahiriya': 'ar_LY.UTF-8',
'Arabic/Morocco': 'ar_MA.UTF-8',
'Arabic/Oman': 'ar_OM.UTF-8',
'Arabic/Qatar': 'ar_QA.UTF-8',
'Arabic/Saudi_Arabia': 'ar_SA.UTF-8',
'Arabic/Sudan': 'ar_SD.UTF-8',
'Arabic/Syrian_Arab_Republic': 'ar_SY.UTF-8',
'Arabic/Tunisia': 'ar_TN.UTF-8',
'Arabic/United_Arab_Emirates': 'ar_AE.UTF-8',
'Arabic/Yemen': 'ar_YE.UTF-8',
'Basque/Spain': 'eu_ES.UTF-8',
'Belarusian': 'be_BY.UTF-8',
'Bengali/BD': 'bn_BD.UTF-8',
'Bengali/India': 'bn_IN.UTF-8',
'Bosnian/Bosnia_and_Herzegowina': 'bs_BA',
'Breton/France': 'br_FR',
'Bulgarian': 'bg_BG.UTF-8',
'Catalan/Spain': 'ca_ES.UTF-8',
'Chinese/Hong_Kong': 'zh_HK.UTF-8',
'Chinese/P.R._of_China': 'zh_CN.UTF-8',
'Chinese/Taiwan': 'zh_TW.UTF-8',
'Cornish/Britain': 'kw_GB.UTF-8',
'Croatian': 'hr_HR.UTF-8',
'Czech': 'cs_CZ.UTF-8',
'Danish': 'da_DK.UTF-8',
'Dutch/Belgium': 'nl_BE.UTF-8',
'Dutch/Netherlands': 'nl_NL.UTF-8',
'English/Australia': 'en_AU.UTF-8',
'English/Botswana': 'en_BW.UTF-8',
'English/Canada': 'en_CA.UTF-8',
'English/Denmark': 'en_DK.UTF-8',
'English/Great_Britain': 'en_GB.UTF-8',
'English/Hong_Kong': 'en_HK.UTF-8',
'English/India': 'en_IN.UTF-8',
'English/Ireland': 'en_IE.UTF-8',
'English/New_Zealand': 'en_NZ.UTF-8',
'English/Philippines': 'en_PH.UTF-8',
'English/Singapore': 'en_SG.UTF-8',
'English/South_Africa': 'en_ZA.UTF-8',
'English/USA': 'en_US.UTF-8',
'English/Zimbabwe': 'en_ZW.UTF-8',
'Estonian': 'et_EE.UTF-8',
'Faroese/Faroe_Islands': 'fo_FO.UTF-8',
'Finnish': 'fi_FI.UTF-8',
'French/Belgium': 'fr_BE.UTF-8',
'French/Canada': 'fr_CA.UTF-8',
'French/France': 'fr_FR.UTF-8',
'French/Luxemburg': 'fr_LU.UTF-8',
'French/Switzerland': 'fr_CH.UTF-8',
'Galician/Spain': 'gl_ES.UTF-8',
'German/Austria': 'de_AT.UTF-8',
'German/Belgium': 'de_BE.UTF-8',
'German/Germany': 'de_DE.UTF-8',
'German/Luxemburg': 'de_LU.UTF-8',
'German/Switzerland': 'de_CH.UTF-8',
'Greek': 'el_GR.UTF-8',
'Greenlandic/Greenland': 'kl_GL.UTF-8',
'Gujarati/India': 'gu_IN.UTF-8',
'Hausa/Nigeria': 'ha_NG.UTF-8',
'Hebrew/Israel': 'he_IL.UTF-8',
'Hindi/India': 'hi_IN.UTF-8',
'Hungarian': 'hu_HU.UTF-8',
'Icelandic': 'is_IS.UTF-8',
'Igbo/Nigeria': 'ig_NG.UTF-8',
'Indonesian': 'id_ID.UTF-8',
'Irish': 'ga_IE.UTF-8',
'Italian/Italy': 'it_IT.UTF-8',
'Italian/Switzerland': 'it_CH.UTF-8',
'Japanese': 'ja_JP.UTF-8',
'Korean/Republic_of_Korea': 'ko_KR.UTF-8',
'Lao/Laos': 'lo_LA.UTF-8',
'Latvian/Latvia': 'lv_LV.UTF-8',
'Lithuanian': 'lt_LT.UTF-8',
'Macedonian': 'mk_MK.UTF-8',
'Malay/Malaysia': 'ms_MY.UTF-8',
'Maltese/malta': 'mt_MT.UTF-8',
'Manx/Britain': 'gv_GB.UTF-8',
'Marathi/India': 'mr_IN.UTF-8',
'Mongolian': 'mn_MN.UTF-8',
'Nepali': 'ne_NP.UTF-8',
'Northern/Norway': 'se_NO',
'Norwegian': 'nb_NO.UTF-8',
'Norwegian,/Norway': 'nn_NO.UTF-8',
'Occitan/France': 'oc_FR',
'Oriya/India': 'or_IN.UTF-8',
'Persian/Iran': 'fa_IR.UTF-8',
'Polish': 'pl_PL.UTF-8',
'Portuguese/Brasil': 'pt_BR.UTF-8',
'Portuguese/Portugal': 'pt_PT.UTF-8',
'Punjabi/India': 'pa_IN.UTF-8',
'Romanian': 'ro_RO.UTF-8',
'Russian': 'ru_RU.UTF-8',
'Russian/Ukraine': 'ru_UA.UTF-8',
'Serbian': 'sr_CS.UTF-8',
'Serbian/Latin': 'sr_CS.UTF-8@Latn',
'Slovak': 'sk_SK.UTF-8',
'Slovenian/Slovenia': 'sl_SI.UTF-8',
'Spanish/Argentina': 'es_AR.UTF-8',
'Spanish/Bolivia': 'es_BO.UTF-8',
'Spanish/Chile': 'es_CL.UTF-8',
'Spanish/Colombia': 'es_CO.UTF-8',
'Spanish/Costa_Rica': 'es_CR.UTF-8',
'Spanish/Dominican_Republic': 'es_DO.UTF-8',
'Spanish/El_Salvador': 'es_SV.UTF-8',
'Spanish/Equador': 'es_EC.UTF-8',
'Spanish/Guatemala': 'es_GT.UTF-8',
'Spanish/Honduras': 'es_HN.UTF-8',
'Spanish/Mexico': 'es_MX.UTF-8',
'Spanish/Nicaragua': 'es_NI.UTF-8',
'Spanish/Panama': 'es_PA.UTF-8',
'Spanish/Paraguay': 'es_PY.UTF-8',
'Spanish/Peru': 'es_PE.UTF-8',
'Spanish/Puerto_Rico': 'es_PR.UTF-8',
'Spanish/Spain': 'es_ES.UTF-8',
'Spanish/USA': 'es_US.UTF-8',
'Spanish/Uruguay': 'es_UY.UTF-8',
'Spanish/Venezuela': 'es_VE.UTF-8',
'Swedish/Finland': 'sv_FI.UTF-8',
'Swedish/Sweden': 'sv_SE.UTF-8',
'Tagalog/Philippines': 'tl_PH',
'Tamil/India': 'ta_IN.UTF-8',
'Telugu/India': 'te_IN.UTF-8',
'Thai': 'th_TH.UTF-8',
'Turkish': 'tr_TR.UTF-8',
'Ukrainian': 'uk_UA.UTF-8',
'Urdu/Pakistan': 'ur_PK',
'Uzbek/Uzbekistan': 'uz_UZ',
'Walloon/Belgium': 'wa_BE@euro',
'Welsh/Great_Britain': 'cy_GB.UTF-8',
'Xhosa/South_Africa': 'xh_ZA.UTF-8',
'Yoruba/Nigeria': 'yo_NG.UTF-8',
'Zulu/South_Africa': 'zu_ZA.UTF-8'
}
def _initialize():
timezones = _read_zonetab()
j=0
for timezone in timezones:
set_timezone.__doc__ += timezone+', '
j+=1
if j%3 == 0:
set_timezone.__doc__ += '\n'
keys = _LANGUAGES.keys()
keys.sort()
i = 0
for key in keys:
set_language.__doc__ += key+', '
i+=1
if i%3 == 0:
set_language.__doc__ += '\n'
def _note_restart():
print _('To apply your changes you have to restart sugar.\n' +
'Hit at the same time ctrl+alt+erase on the keyboard to do this.')
def get_jabber():
pro = profile.get_profile()
return pro.jabber_server
def print_jabber():
print get_jabber()
def set_jabber(server):
"""Set the jabber server
server : e.g. 'olpc.collabora.co.uk'
"""
pro = profile.get_profile()
pro.jabber_server = server
pro.jabber_registered = False
pro.save()
_note_restart()
def get_color():
return profile.get_color()
def print_color():
color = get_color().to_string()
str = color.split(',')
stroke = None
fill = None
for color in _COLORS:
for hue in _COLORS[color]:
if _COLORS[color][hue] == str[0]:
stroke = (color, hue)
if _COLORS[color][hue] == str[1]:
fill = (color, hue)
if stroke is not None:
print 'stroke: color=%s hue=%s'%(stroke[0], stroke[1])
else:
print 'stroke: %s'%(str[0])
if fill is not None:
print 'fill: color=%s hue=%s'%(fill[0], fill[1])
else:
print 'fill: %s'%(str[1])
def set_color(stroke, fill, modstroke='medium', modfill='medium'):
"""Set the system color by setting a fill and stroke color.
fill : [red, orange, yellow, blue, purple]
stroke : [red, orange, yellow, blue, purple]
hue stroke : [dark, medium, light] (optional)
hue fill : [dark, medium, light] (optional)
"""
if modstroke not in _MODIFIERS or modfill not in _MODIFIERS:
print (_("Error in specified color modifiers."))
return
if stroke not in _COLORS or fill not in _COLORS:
print (_("Error in specified colors."))
return
if modstroke == modfill:
if modfill == 'medium':
modfill = 'light'
else:
modfill = 'medium'
color = _COLORS[stroke][modstroke] + ',' + _COLORS[fill][modfill]
pro = profile.get_profile()
pro.color = XoColor(color)
pro.save()
_note_restart()
def get_nick():
return profile.get_nick_name()
def print_nick():
print get_nick()
def set_nick(nick):
"""Set the nickname.
nick : e.g. 'walter'
"""
pro = profile.get_profile()
pro.nick_name = nick
pro.save()
_note_restart()
def get_radio():
bus = dbus.SystemBus()
proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
state = nm.getWirelessEnabled()
if state == 0:
return _('off')
elif state == 1:
return _('on')
else:
return _('State is unknown.')
def print_radio():
print get_radio()
def set_radio(state):
"""Turn Radio 'on' or 'off'
state : 'on/off'
"""
if state == 'on':
bus = dbus.SystemBus()
proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
nm.setWirelessEnabled(True)
elif state == 'off':
bus = dbus.SystemBus()
proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
nm.setWirelessEnabled(False)
else:
print (_("Error in specified radio argument use on/off."))
def _check_for_superuser():
if os.getuid():
print _("Permission denied. You need to be root to run this method.")
return False
return True
def get_timezone():
if not os.access(_TIMEZONE_CONFIG, os.R_OK):
# this is what the default is for the /etc/localtime
return "America/New_York"
fd = open(_TIMEZONE_CONFIG, "r")
lines = fd.readlines()
fd.close()
try:
for line in lines:
line = string.strip(line)
if len (line) and line[0] == '#':
continue
try:
tokens = string.split(line, "=")
if tokens[0] == "ZONE":
timezone = string.replace(tokens[1], '"', '')
return timezone
except Exception, e:
print "get_timezone: %s" % e
except Exception, e:
print "get_timezone: %s" % e
return None
def print_timezone():
timezone = get_timezone()
if timezone is None:
print (_("Error in reading timezone"))
else:
print timezone
def _read_zonetab(fn='/usr/share/zoneinfo/zone.tab'):
fd = open (fn, 'r')
lines = fd.readlines()
fd.close()
timezones = []
for line in lines:
if line.startswith('#'):
continue
line = line.split()
if len(line) > 1:
timezones.append(line[2])
timezones.sort()
return timezones
def set_timezone(timezone):
"""Set the system timezone
timezone :
"""
if not _check_for_superuser():
return
timezones = _read_zonetab()
if timezone in timezones:
fromfile = os.path.join("/usr/share/zoneinfo/", timezone)
try:
shutil.copyfile(fromfile, "/etc/localtime")
except OSError, (errno, msg):
print (_("Error copying timezone (from %s): %s") % (fromfile, msg))
return
try:
os.chmod("/etc/localtime", 0644)
except OSError, (errno, msg):
print (_("Changing permission of timezone: %s") % (msg))
return
# Write info to the /etc/sysconfig/clock file
fd = open(_TIMEZONE_CONFIG, "w")
fd.write('# use sugar-control-panel to change this\n')
fd.write('ZONE="%s"\n' % timezone)
fd.write('UTC=true\n')
fd.close()
else:
print (_("Error timezone does not exist."))
def _writeI18N(lang):
path = os.path.join(os.environ.get("HOME"), '.i18n')
if os.access(path, os.W_OK) == 0:
print(_("Could not access %s. Create standard settings.") % path)
fd = open(path, 'w')
fd.write('LANG="en_US.UTF-8"\n')
fd.close()
else:
fd = open(path, 'r')
lines = fd.readlines()
fd.close()
for i in range(len(lines)):
if lines[i][:5] == "LANG=":
lines[i] = 'LANG="' + lang + '"\n'
fd = open(path, 'w')
fd.writelines(lines)
fd.close()
def get_language():
originalFile = None
path = os.path.join(os.environ.get("HOME"), '.i18n')
if os.access(path, os.R_OK) == 0:
print(_("Could not access %s. Create standard settings.") % path)
fd = open(path, 'w')
default = 'en_US.UTF-8'
fd.write('LANG="%s"\n'%default)
fd.close()
return default
fd = open(path, "r")
lines = fd.readlines()
fd.close()
lang = None
for line in lines:
if line[:5] == "LANG=":
lang = line[5:].replace('"', '')
lang = lang.strip()
return lang
def print_language():
code = get_language()
for lang in _LANGUAGES:
if _LANGUAGES[lang] == code:
print lang
return
print (_("Language for code=%s could not be determined.") % code)
def set_language(language):
"""Set the system language.
languages :
"""
if language in _LANGUAGES:
_writeI18N(_LANGUAGES[language])
_note_restart()
else:
print (_("Sorry I do not speak \'%s\'.") % language)
# inilialize the docstrings for the timezone and language
_initialize()
-156
View File
@@ -1,156 +0,0 @@
# Copyright (C) 2006, 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 os
import sys
import socket
import logging
from optparse import OptionParser
log = logging.getLogger( 'sugar-emulator' )
log.setLevel( logging.DEBUG )
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from sugar import env
import config
def _get_display_number():
"""Find a free display number trying to connect to 6000+ ports"""
log.info( "Attempting to find free port for X11 (Xephyr)" )
retries = 20
display_number = 1
display_is_free = False
while not display_is_free and retries > 0:
lockstr = "/tmp/.X%d-lock" % display_number
if not os.path.exists(lockstr):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('127.0.0.1', 6000 + display_number))
s.close()
except:
display_is_free = True
break
display_number += 1
retries -= 1
if display_is_free:
log.info(
' Found free port: #%s (%s)',
display_number, display_number+6000
)
return display_number
else:
logging.error('Cannot find a free display.')
sys.exit(0)
def _start_xephyr(dpi=None):
display = _get_display_number()
log.info( 'Starting the Xephyr nested X display on display %s', display )
cmd = [ 'Xephyr' ]
cmd.append(':%d' % display)
cmd.append('-ac')
if gtk.gdk.screen_width() < 1200 or gtk.gdk.screen_height() < 900:
cmd.append('-fullscreen')
else:
cmd.append('-screen')
cmd.append('%dx%d' % (1200, 900))
if not dpi:
dpi = gtk.settings_get_default().get_property('gtk-xft-dpi') / 1024
if dpi > 0:
cmd.append('-dpi')
cmd.append('%d' % dpi)
log.debug( 'Xephyr command: %s', " ".join( cmd ) )
result = gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
pid = result[0]
os.environ['DISPLAY'] = ":%d" % (display)
os.environ['SUGAR_EMULATOR_PID'] = str(pid)
def _start_matchbox():
log.info( 'Starting the matchbox window manager' )
cmd = ['matchbox-window-manager']
cmd.extend(['-use_titlebar', 'no'])
cmd.extend(['-theme', 'sugar'])
log.debug( 'Matchbox command: %s', " ".join( cmd) )
gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
def _setup_env():
os.environ['SUGAR_EMULATOR'] = 'yes'
os.environ['GABBLE_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-gabble.log')
os.environ['SALUT_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-salut.log')
os.environ['STREAM_ENGINE_LOGFILE'] = os.path.join(
env.get_profile_path(), 'logs', 'telepathy-stream-engine.log')
def main():
"""Script-level operations"""
parser = OptionParser()
parser.add_option('-x', '--xo-style', dest='xo_style',
action='store_true', help='use the XO style')
(options, args) = parser.parse_args()
logging.basicConfig()
_setup_env()
if options.xo_style:
_start_xephyr(dpi=201)
else:
_start_xephyr()
if options.xo_style:
os.environ['SUGAR_XO_STYLE'] = 'yes'
else:
os.environ['SUGAR_XO_STYLE'] = 'no'
if options.xo_style:
gtkrc_filename = 'sugar-xo.gtkrc'
else:
gtkrc_filename = 'sugar.gtkrc'
os.environ['SUGAR_XO_STYLE'] = 'no'
gtkrc_path = os.path.join(config.data_path, gtkrc_filename)
os.environ['GTK2_RC_FILES'] = gtkrc_path
command = ['dbus-launch', 'dbus-launch', '--exit-with-session']
if not args:
command.append('sugar-shell')
else:
_start_matchbox()
if args[0].endswith('.py'):
command.append('python')
command.append(args[0])
log.info( "Attempting to launch sugar to replace this process: %s", " ".join(command))
os.execlp( *command )
-13
View File
@@ -1,13 +0,0 @@
sugardir = $(pkgdatadir)/shell/hardware
sugar_PYTHON = \
__init__.py \
hardwaremanager.py \
keydialog.py \
nmclient.py \
nminfo.py \
schoolserver.py
dbusservicedir = $(sysconfdir)/dbus-1/system.d/
dbusservice_DATA = NetworkManagerInfo.conf
EXTRA_DIST = $(dbusservice_DATA)
-26
View File
@@ -1,26 +0,0 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.freedesktop.NetworkManagerInfo"/>
<allow send_destination="org.freedesktop.NetworkManagerInfo"/>
<allow send_interface="org.freedesktop.NetworkManagerInfo"/>
</policy>
<policy at_console="true">
<allow own="org.freedesktop.NetworkManagerInfo"/>
<allow send_destination="org.freedesktop.NetworkManagerInfo"/>
<allow send_interface="org.freedesktop.NetworkManagerInfo"/>
</policy>
<policy context="default">
<deny own="org.freedesktop.NetworkManagerInfo"/>
<deny send_destination="org.freedesktop.NetworkManagerInfo"/>
<deny send_interface="org.freedesktop.NetworkManagerInfo"/>
</policy>
<limit name="max_replies_per_connection">512</limit>
</busconfig>
-16
View File
@@ -1,16 +0,0 @@
# 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
-143
View File
@@ -1,143 +0,0 @@
# 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
import dbus
import gst
import gst.interfaces
from hardware.nmclient import NMClient
from sugar.profile import get_profile
from sugar import env
_HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager'
_HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager'
_HARDWARE_MANAGER_OBJECT_PATH = '/org/laptop/HardwareManager'
COLOR_MODE = 0
B_AND_W_MODE = 1
class HardwareManager(object):
def __init__(self):
try:
bus = dbus.SystemBus()
proxy = bus.get_object(_HARDWARE_MANAGER_SERVICE,
_HARDWARE_MANAGER_OBJECT_PATH)
self._service = dbus.Interface(proxy, _HARDWARE_MANAGER_INTERFACE)
except dbus.DBusException, e:
self._service = None
logging.info('Hardware manager service not found.')
self._mixer = gst.element_factory_make('alsamixer')
self._mixer.set_state(gst.STATE_PAUSED)
self._master = None
for track in self._mixer.list_tracks():
if track.flags & gst.interfaces.MIXER_TRACK_MASTER:
self._master = track
def get_volume(self):
if not self._mixer or not self._master:
logging.error('Cannot get the volume')
return self._convert_volume(0)
max_volume = self._master.max_volume
min_volume = self._master.min_volume
volume = self._mixer.get_volume(self._master)[0]
return volume * 100.0 / (max_volume - min_volume) + min_volume
def set_volume(self, volume):
if not self._mixer or not self._master:
logging.error('Cannot set the volume')
if volume < 0 or volume > 100:
logging.error('Trying to set an invalid volume value.')
return
max_volume = self._master.max_volume
min_volume = self._master.min_volume
volume = volume * (max_volume - min_volume) / 100.0 + min_volume
volume_list = [ volume ] * self._master.num_channels
self._mixer.set_volume(self._master, tuple(volume_list))
def set_mute(self, mute):
if not self._mixer or not self._master:
logging.error('Cannot mute the audio channel')
self._mixer.set_mute(self._master, mute)
def startup(self):
if env.is_emulator() is False:
profile = get_profile()
self.set_volume(profile.sound_volume)
def shutdown(self):
if env.is_emulator() is False:
profile = get_profile()
profile.sound_volume = self.get_volume()
profile.save()
def set_dcon_freeze(self, frozen):
if not self._service:
return
self._service.set_dcon_freeze(frozen)
def set_display_mode(self, mode):
if not self._service:
return
self._service.set_display_mode(mode)
def set_display_brightness(self, level):
if not self._service:
logging.error('Cannot set display brightness')
return
self._service.set_display_brightness(level)
def get_display_brightness(self):
if not self._service:
logging.error('Cannot get display brightness')
return
return self._service.get_display_brightness()
def toggle_keyboard_brightness(self):
if not self._service:
return
if self._service.get_keyboard_brightness():
self._service.set_keyboard_brightness(False)
else:
self._service.set_keyboard_brightness(True)
def get_manager():
return _manager
def get_network_manager():
return _network_manager
_manager = HardwareManager()
try:
_network_manager = NMClient()
except dbus.DBusException, e:
_network_manager = None
logging.info('Network manager service not found.')
-351
View File
@@ -1,351 +0,0 @@
# vi: ts=4 ai noet
#
# 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 md5
from gettext import gettext as _
import gobject, gtk
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
IW_AUTH_ALG_SHARED_KEY = 0x00000002
IW_AUTH_WPA_VERSION_DISABLED = 0x00000001
IW_AUTH_WPA_VERSION_WPA = 0x00000002
IW_AUTH_WPA_VERSION_WPA2 = 0x00000004
NM_802_11_CAP_NONE = 0x00000000
NM_802_11_CAP_PROTO_NONE = 0x00000001
NM_802_11_CAP_PROTO_WEP = 0x00000002
NM_802_11_CAP_PROTO_WPA = 0x00000004
NM_802_11_CAP_PROTO_WPA2 = 0x00000008
NM_802_11_CAP_KEY_MGMT_PSK = 0x00000040
NM_802_11_CAP_KEY_MGMT_802_1X = 0x00000080
NM_802_11_CAP_CIPHER_WEP40 = 0x00001000
NM_802_11_CAP_CIPHER_WEP104 = 0x00002000
NM_802_11_CAP_CIPHER_TKIP = 0x00004000
NM_802_11_CAP_CIPHER_CCMP = 0x00008000
NM_AUTH_TYPE_WPA_PSK_AUTO = 0x00000000
IW_AUTH_CIPHER_NONE = 0x00000001
IW_AUTH_CIPHER_WEP40 = 0x00000002
IW_AUTH_CIPHER_TKIP = 0x00000004
IW_AUTH_CIPHER_CCMP = 0x00000008
IW_AUTH_CIPHER_WEP104 = 0x00000010
IW_AUTH_KEY_MGMT_802_1X = 0x1
IW_AUTH_KEY_MGMT_PSK = 0x2
def string_is_hex(key):
is_hex = True
for c in key:
if not 'a' <= c.lower() <= 'f' and not '0' <= c <= '9':
is_hex = False
return is_hex
def string_is_ascii(string):
try:
string.encode('ascii')
return True
except:
return False
def string_to_hex(passphrase):
key = ''
for c in passphrase:
key += '%02x' % ord(c)
return key
def hash_passphrase(passphrase):
# passphrase must have a length of 64
if len(passphrase) > 64:
passphrase = passphrase[:64]
elif len(passphrase) < 64:
while len(passphrase) < 64:
passphrase += passphrase[:64 - len(passphrase)]
passphrase = md5.new(passphrase).digest()
return string_to_hex(passphrase)[:26]
class KeyDialog(gtk.Dialog):
def __init__(self, net, async_cb, async_err_cb):
gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL)
self.set_title("Wireless Key Required")
self._net = net
self._async_cb = async_cb
self._async_err_cb = async_err_cb
self.set_has_separator(False)
label = gtk.Label("A wireless encryption key is required for\n" \
" the wireless network '%s'." % net.get_ssid())
self.vbox.pack_start(label)
self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK)
self.set_default_response(gtk.RESPONSE_OK)
self.set_has_separator(True)
def add_key_entry(self):
self._entry = gtk.Entry()
#self._entry.props.visibility = False
self._entry.connect('changed', self._update_response_sensitivity)
self._entry.connect('activate', self._entry_activate_cb)
self.vbox.pack_start(self._entry)
self.vbox.set_spacing(6)
self.vbox.show_all()
self._update_response_sensitivity()
self._entry.grab_focus()
def _entry_activate_cb(self, entry):
self.response(gtk.RESPONSE_OK)
def create_security(self):
raise NotImplementedError
def get_network(self):
return self._net
def get_callbacks(self):
return (self._async_cb, self._async_err_cb)
WEP_PASSPHRASE = 1
WEP_HEX = 2
WEP_ASCII = 3
class WEPKeyDialog(KeyDialog):
def __init__(self, net, async_cb, async_err_cb):
KeyDialog.__init__(self, net, async_cb, async_err_cb)
# WEP key type
self.key_store = gtk.ListStore(str, int)
self.key_store.append(["Passphrase (128-bit)", WEP_PASSPHRASE])
self.key_store.append(["Hex (40/128-bit)", WEP_HEX])
self.key_store.append(["ASCII (40/128-bit)", WEP_ASCII])
self.key_combo = gtk.ComboBox(self.key_store)
cell = gtk.CellRendererText()
self.key_combo.pack_start(cell, True)
self.key_combo.add_attribute(cell, 'text', 0)
self.key_combo.set_active(0)
self.key_combo.connect('changed', self._key_combo_changed_cb)
hbox = gtk.HBox()
hbox.pack_start(gtk.Label(_("Key Type:")))
hbox.pack_start(self.key_combo)
hbox.show_all()
self.vbox.pack_start(hbox)
# Key entry field
self.add_key_entry()
# WEP authentication mode
self.auth_store = gtk.ListStore(str, int)
self.auth_store.append(["Open System", IW_AUTH_ALG_OPEN_SYSTEM])
self.auth_store.append(["Shared Key", IW_AUTH_ALG_SHARED_KEY])
self.auth_combo = gtk.ComboBox(self.auth_store)
cell = gtk.CellRendererText()
self.auth_combo.pack_start(cell, True)
self.auth_combo.add_attribute(cell, 'text', 0)
self.auth_combo.set_active(0)
hbox = gtk.HBox()
hbox.pack_start(gtk.Label(_("Authentication Type:")))
hbox.pack_start(self.auth_combo)
hbox.show_all()
self.vbox.pack_start(hbox)
def _key_combo_changed_cb(self, widget):
self._update_response_sensitivity()
def _get_security(self):
key = self._entry.get_text()
it = self.key_combo.get_active_iter()
(key_type, ) = self.key_store.get(it, 1)
if key_type == WEP_PASSPHRASE:
key = hash_passphrase(key)
elif key_type == WEP_ASCII:
key = string_to_hex(key)
it = self.auth_combo.get_active_iter()
(auth_alg, ) = self.auth_store.get(it, 1)
we_cipher = None
if len(key) == 26:
we_cipher = IW_AUTH_CIPHER_WEP104
elif len(key) == 10:
we_cipher = IW_AUTH_CIPHER_WEP40
return (we_cipher, key, auth_alg)
def print_security(self):
(we_cipher, key, auth_alg) = self._get_security()
print "Cipher: %d" % we_cipher
print "Key: %s" % key
print "Auth: %d" % auth_alg
def create_security(self):
(we_cipher, key, auth_alg) = self._get_security()
from nminfo import Security
return Security.new_from_args(we_cipher, (key, auth_alg))
def _update_response_sensitivity(self, ignored=None):
key = self._entry.get_text()
it = self.key_combo.get_active_iter()
(key_type, ) = self.key_store.get(it, 1)
valid = False
if key_type == WEP_PASSPHRASE:
# As the md5 passphrase can be of any length and has no indicator,
# we cannot check for the validity of the input.
if len(key) > 0:
valid = True
elif key_type == WEP_ASCII:
if len(key) == 5 or len(key) == 13:
valid = string_is_ascii(key)
elif key_type == WEP_HEX:
if len(key) == 10 or len(key) == 26:
valid = string_is_hex(key)
self.set_response_sensitive(gtk.RESPONSE_OK, valid)
class WPAKeyDialog(KeyDialog):
def __init__(self, net, async_cb, async_err_cb):
KeyDialog.__init__(self, net, async_cb, async_err_cb)
self.add_key_entry()
self.store = gtk.ListStore(str, int)
self.store.append(["Automatic", NM_AUTH_TYPE_WPA_PSK_AUTO])
if net.get_caps() & NM_802_11_CAP_CIPHER_CCMP:
self.store.append(["AES-CCMP", IW_AUTH_CIPHER_CCMP])
if net.get_caps() & NM_802_11_CAP_CIPHER_TKIP:
self.store.append(["TKIP", IW_AUTH_CIPHER_TKIP])
self.combo = gtk.ComboBox(self.store)
cell = gtk.CellRendererText()
self.combo.pack_start(cell, True)
self.combo.add_attribute(cell, 'text', 0)
self.combo.set_active(0)
self.hbox = gtk.HBox()
self.hbox.pack_start(gtk.Label(_("Encryption Type:")))
self.hbox.pack_start(self.combo)
self.hbox.show_all()
self.vbox.pack_start(self.hbox)
def _get_security(self):
ssid = self.get_network().get_ssid()
key = self._entry.get_text()
is_hex = string_is_hex(key)
real_key = None
if len(key) == 64 and is_hex:
# Hex key
real_key = key
elif len(key) >= 8 and len(key) <= 63:
# passphrase
import commands
(s, o) = commands.getstatusoutput("/usr/sbin/wpa_passphrase '%s' '%s'" % (ssid, key))
if s != 0:
raise RuntimeError("Error hashing passphrase: %s" % o)
lines = o.split("\n")
for line in lines:
if line.strip().startswith("psk="):
real_key = line.strip()[4:]
if real_key and len(real_key) != 64:
real_key = None
if not real_key:
raise RuntimeError("Invalid key")
it = self.combo.get_active_iter()
(we_cipher, ) = self.store.get(it, 1)
wpa_ver = IW_AUTH_WPA_VERSION_WPA
caps = self.get_network().get_caps()
if caps & NM_802_11_CAP_PROTO_WPA2:
wpa_ver = IW_AUTH_WPA_VERSION_WPA2
return (we_cipher, real_key, wpa_ver)
def print_security(self):
(we_cipher, key, wpa_ver) = self._get_security()
print "Cipher: %d" % we_cipher
print "Key: %s" % key
print "WPA Ver: %d" % wpa_ver
def create_security(self):
(we_cipher, key, wpa_ver) = self._get_security()
from nminfo import Security
return Security.new_from_args(we_cipher, (key, wpa_ver, IW_AUTH_KEY_MGMT_PSK))
def _update_response_sensitivity(self, ignored=None):
key = self._entry.get_text()
is_hex = string_is_hex(key)
valid = False
if len(key) == 64 and is_hex:
# hex key
valid = True
elif len(key) >= 8 and len(key) <= 63:
# passphrase
valid = True
self.set_response_sensitive(gtk.RESPONSE_OK, valid)
return False
def new_key_dialog(net, async_cb, async_err_cb):
caps = net.get_caps()
if (caps & NM_802_11_CAP_CIPHER_TKIP or caps & NM_802_11_CAP_CIPHER_CCMP) and \
(caps & NM_802_11_CAP_PROTO_WPA or caps & NM_802_11_CAP_PROTO_WPA2):
return WPAKeyDialog(net, async_cb, async_err_cb)
elif (caps & NM_802_11_CAP_CIPHER_WEP40 or caps & NM_802_11_CAP_CIPHER_WEP104) and \
(caps & NM_802_11_CAP_PROTO_WEP):
return WEPKeyDialog(net, async_cb, async_err_cb)
else:
raise RuntimeError("Unhandled network capabilities %x" % caps)
class FakeNet(object):
def get_ssid(self):
return "olpcwpa"
def get_caps(self):
# return NM_802_11_CAP_CIPHER_WEP104 | NM_802_11_CAP_PROTO_WEP
return NM_802_11_CAP_CIPHER_CCMP | NM_802_11_CAP_CIPHER_TKIP | NM_802_11_CAP_PROTO_WPA
def response_cb(widget, response_id):
if response_id == gtk.RESPONSE_OK:
print dialog.print_security()
else:
print "canceled"
widget.hide()
widget.destroy()
if __name__ == "__main__":
net = FakeNet()
dialog = new_key_dialog(net, None, None)
dialog.connect("response", response_cb)
dialog.run()
-721
View File
@@ -1,721 +0,0 @@
#
# 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
import os
import dbus
import dbus.glib
import dbus.decorators
import gobject
import gtk
from hardware import nminfo
from sugar.graphics import xocolor
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
IW_AUTH_ALG_SHARED_KEY = 0x00000002
NM_DEVICE_STAGE_STRINGS=("Unknown",
"Prepare",
"Config",
"Need Users Key",
"IP Config",
"IP Config Get",
"IP Config Commit",
"Activated",
"Failed",
"Canceled"
)
NM_SERVICE = 'org.freedesktop.NetworkManager'
NM_IFACE = 'org.freedesktop.NetworkManager'
NM_IFACE_DEVICES = 'org.freedesktop.NetworkManager.Devices'
NM_PATH = '/org/freedesktop/NetworkManager'
DEVICE_TYPE_UNKNOWN = 0
DEVICE_TYPE_802_3_ETHERNET = 1
DEVICE_TYPE_802_11_WIRELESS = 2
DEVICE_TYPE_802_11_MESH_OLPC = 3
NM_DEVICE_CAP_NONE = 0x00000000
NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001
NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002
NM_DEVICE_CAP_WIRELESS_SCAN = 0x00000004
sys_bus = dbus.SystemBus()
NM_802_11_CAP_NONE = 0x00000000
NM_802_11_CAP_PROTO_NONE = 0x00000001
NM_802_11_CAP_PROTO_WEP = 0x00000002
NM_802_11_CAP_PROTO_WPA = 0x00000004
NM_802_11_CAP_PROTO_WPA2 = 0x00000008
NM_802_11_CAP_KEY_MGMT_PSK = 0x00000040
NM_802_11_CAP_KEY_MGMT_802_1X = 0x00000080
NM_802_11_CAP_CIPHER_WEP40 = 0x00001000
NM_802_11_CAP_CIPHER_WEP104 = 0x00002000
NM_802_11_CAP_CIPHER_TKIP = 0x00004000
NM_802_11_CAP_CIPHER_CCMP = 0x00008000
NETWORK_STATE_CONNECTING = 0
NETWORK_STATE_CONNECTED = 1
NETWORK_STATE_NOTCONNECTED = 2
DEVICE_STATE_ACTIVATING = 0
DEVICE_STATE_ACTIVATED = 1
DEVICE_STATE_INACTIVE = 2
IW_MODE_ADHOC = 1
IW_MODE_INFRA = 2
class Network(gobject.GObject):
__gsignals__ = {
'initialized' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_BOOLEAN])),
'strength-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'state-changed' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([]))
}
def __init__(self, client, op):
gobject.GObject.__init__(self)
self._client = client
self._op = op
self._ssid = None
self._mode = None
self._strength = 0
self._caps = 0
self._valid = False
self._favorite = False
self._state = NETWORK_STATE_NOTCONNECTED
obj = sys_bus.get_object(NM_SERVICE, self._op)
net = dbus.Interface(obj, NM_IFACE_DEVICES)
net.getProperties(reply_handler=self._update_reply_cb,
error_handler=self._update_error_cb)
def _update_reply_cb(self, *props):
self._ssid = props[1]
self._strength = props[3]
self._mode = props[6]
self._caps = props[7]
if self._caps & NM_802_11_CAP_PROTO_WPA or self._caps & NM_802_11_CAP_PROTO_WPA2:
if not (self._caps & NM_802_11_CAP_KEY_MGMT_PSK):
# 802.1x is not supported at this time
logging.debug("Net(%s): ssid '%s' dropping because 802.1x is unsupported" % (self._op,
self._ssid))
self._valid = False
self.emit('initialized', self._valid)
return
if self._mode != IW_MODE_INFRA:
# Don't show Ad-Hoc networks; they usually don't DHCP and therefore
# won't work well here. This also works around the bug where we show
# our own mesh SSID on the Mesh view when in mesh mode
logging.debug("Net(%s): ssid '%s' is adhoc; not showing" % (self._op,
self._ssid))
self._valid = False
self.emit('initialized', self._valid)
return
fav_nets = []
if self._client.nminfo:
fav_nets = self._client.nminfo.get_networks(nminfo.NETWORK_TYPE_ALLOWED)
if self._ssid in fav_nets:
self._favorite = True
self._valid = True
logging.debug("Net(%s): caps 0x%X" % (self._ssid, self._caps))
self.emit('initialized', self._valid)
def _update_error_cb(self, err):
logging.debug("Net(%s): failed to update. (%s)" % (self._op, err))
self._valid = False
self.emit('initialized', self._valid)
def get_colors(self):
import sha
sh = sha.new()
data = self._ssid + hex(self._caps) + hex(self._mode)
sh.update(data)
h = hash(sh.digest())
idx = h % len(xocolor._colors)
# stroke, fill
return (xocolor._colors[idx][0], xocolor._colors[idx][1])
def get_ssid(self):
return self._ssid
def get_caps(self):
return self._caps
def get_mode(self):
return self._mode
def get_state(self):
return self._state
def set_state(self, state):
if state == self._state:
return
self._state = state
if self._valid:
self.emit('state-changed')
def get_op(self):
return self._op
def get_strength(self):
return self._strength
def set_strength(self, strength):
if strength == self._strength:
return
self._strength = strength
if self._valid:
self.emit('strength-changed')
def is_valid(self):
return self._valid
def is_favorite(self):
return self._favorite
class Device(gobject.GObject):
__gsignals__ = {
'initialized': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'init-failed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'ssid-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'strength-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'state-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'activation-stage-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'network-appeared': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'network-disappeared': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self, client, op):
gobject.GObject.__init__(self)
self._client = client
self._op = op
self._iface = None
self._type = DEVICE_TYPE_UNKNOWN
self._udi = None
self._active = False
self._act_stage = 0
self._strength = 0
self._freq = 0.0
self._link = False
self._valid = False
self._networks = {}
self._caps = 0
self._state = DEVICE_STATE_INACTIVE
self._active_network = None
self._active_net_sigid = 0
obj = sys_bus.get_object(NM_SERVICE, self._op)
self.dev = dbus.Interface(obj, NM_IFACE_DEVICES)
self.dev.getProperties(reply_handler=self._update_reply_cb,
error_handler=self._update_error_cb)
def _is_activating(self):
if self._active and self._act_stage >= 1 and self._act_stage <= 6:
return True
return False
def _is_activated(self):
if self._active and self._act_stage == 7:
return True
return False
def _update_reply_cb(self, *props):
self._iface = props[1]
self._type = props[2]
self._udi = props[3]
self._active = props[4]
self._act_stage = props[5]
self._link = props[15]
self._caps = props[17]
if self._type == DEVICE_TYPE_802_11_WIRELESS:
old_strength = self._strength
self._strength = props[14]
if self._strength != old_strength:
if self._valid:
self.emit('strength-changed')
self._update_networks(props[20], props[19])
elif self._type == DEVICE_TYPE_802_11_MESH_OLPC:
old_strength = self._strength
self._strength = props[14]
if self._strength != old_strength:
if self._valid:
self.emit('strength-changed')
self._valid = True
if self._is_activating():
self.set_state(DEVICE_STATE_ACTIVATING)
elif self._is_activated():
self.set_state(DEVICE_STATE_ACTIVATED)
else:
self.set_state(DEVICE_STATE_INACTIVE)
self.emit('initialized')
def _update_networks(self, net_ops, active_op):
for op in net_ops:
net = Network(self._client, op)
self._networks[op] = net
net.connect('initialized', lambda *args: self._net_initialized_cb(active_op, *args))
def _update_error_cb(self, err):
logging.debug("Device(%s): failed to update. (%s)" % (self._op, err))
self._valid = False
self.emit('init-failed')
def _net_initialized_cb(self, active_op, net, valid):
net_op = net.get_op()
if not self._networks.has_key(net_op):
return
if not valid:
# init failure
del self._networks[net_op]
return
# init success
if self._valid:
self.emit('network-appeared', net)
if active_op and net_op == active_op:
self.set_active_network(net)
def get_op(self):
return self._op
def get_networks(self):
ret = []
for net in self._networks.values():
if net.is_valid():
ret.append(net)
return ret
def get_network(self, op):
if self._networks.has_key(op) and self._networks[op].is_valid():
return self._networks[op]
return None
def get_network_ops(self):
ret = []
for net in self._networks.values():
if net.is_valid():
ret.append(net.get_op())
return ret
def get_mesh_step(self):
if self._type != DEVICE_TYPE_802_11_MESH_OLPC:
raise RuntimeError("Only valid for mesh devices")
try:
step = self.dev.getMeshStep(timeout=3)
except dbus.DBusException, e:
step = 0
return step
def get_frequency(self):
freq = 0.0
try:
freq = self.dev.getFrequency(timeout=3)
except dbus.DBusException, e:
pass
# Hz -> GHz
self._freq = freq / 1000000000.0
return self._freq
def get_strength(self):
return self._strength
def set_strength(self, strength):
if strength == self._strength:
return False
if strength >= 0 and strength <= 100:
self._strength = strength
else:
self._strength = 0
if self._valid:
self.emit('strength-changed')
def network_appeared(self, network):
if self._networks.has_key(network):
return
net = Network(self._client, network)
self._networks[network] = net
net.connect('initialized', lambda *args: self._net_initialized_cb(None, *args))
def network_disappeared(self, network):
if not self._networks.has_key(network):
return
if self._valid:
self.emit('network-disappeared', self._networks[network])
del self._networks[network]
def set_active_network(self, network):
if self._active_network == network:
return
# Make sure the old one doesn't get a stuck state
if self._active_network:
self._active_network.set_state(NETWORK_STATE_NOTCONNECTED)
self._active_network.disconnect(self._active_net_sigid)
self._active_network = network
if self._active_network:
self._active_net_sigid = self._active_network.connect("initialized",
self._active_net_initialized);
# don't emit ssid-changed for networks that are not yet valid
if self._valid:
if self._active_network and self._active_network.is_valid():
self.emit('ssid-changed')
elif not self._active_network:
self.emit('ssid-changed')
def _active_net_initialized(self, net, user_data=None):
if self._active_network and self._active_network.is_valid():
self.emit('ssid-changed')
def _get_active_net_cb(self, state, net_op):
if not self._networks.has_key(net_op):
self.set_active_network(None)
return
self.set_active_network(self._networks[net_op])
_device_to_network_state = {
DEVICE_STATE_ACTIVATING : NETWORK_STATE_CONNECTING,
DEVICE_STATE_ACTIVATED : NETWORK_STATE_CONNECTED,
DEVICE_STATE_INACTIVE : NETWORK_STATE_NOTCONNECTED
}
network_state = _device_to_network_state[state]
self._active_network.set_state(network_state)
def _get_active_net_error_cb(self, err):
logging.debug("Couldn't get active network: %s" % err)
self.set_active_network(None)
def get_state(self):
return self._state
def set_state(self, state):
if state == self._state:
return
if state == DEVICE_STATE_INACTIVE:
self._act_stage = 0
self._state = state
if self._valid:
self.emit('state-changed')
if self._type == DEVICE_TYPE_802_11_WIRELESS:
if state == DEVICE_STATE_INACTIVE:
self.set_active_network(None)
else:
self.dev.getActiveNetwork(reply_handler=lambda *args: self._get_active_net_cb(state, *args),
error_handler=self._get_active_net_error_cb)
def set_activation_stage(self, stage):
if stage == self._act_stage:
return
self._act_stage = stage
if self._valid:
self.emit('activation-stage-changed')
def get_activation_stage(self):
return self._act_stage
def get_ssid(self):
if self._active_network and self._active_network.is_valid():
return self._active_network.get_ssid()
elif not self._active_network:
return None
def get_active_network(self):
return self._active_network
def get_type(self):
return self._type
def is_valid(self):
return self._valid
def set_carrier(self, on):
self._link = on
def get_capabilities(self):
return self._caps
class NMClient(gobject.GObject):
__gsignals__ = {
'device-added' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'device-activated' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'device-activating': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'device-removed' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self):
gobject.GObject.__init__(self)
self.nminfo = None
self._nm_present = False
self._update_timer = 0
self._devices = {}
try:
self.nminfo = nminfo.NMInfo(self)
except RuntimeError:
pass
self._setup_dbus()
if self._nm_present:
self._get_initial_devices()
def get_devices(self):
return self._devices.values()
def _get_initial_devices_reply_cb(self, ops):
for op in ops:
self._add_device(op)
def _dev_initialized_cb(self, dev):
self.emit('device-added', dev)
def _dev_init_failed_cb(self, dev):
# Device failed to initialize, likely due to dbus errors or something
op = dev.get_op()
self._remove_device(op)
def _get_initial_devices_error_cb(self, err):
logging.debug("Error updating devices (%s)" % err)
def _get_initial_devices(self):
self._nm_obj.getDevices(reply_handler=self._get_initial_devices_reply_cb, \
error_handler=self._get_initial_devices_error_cb)
def _add_device(self, dev_op):
if self._devices.has_key(dev_op):
return
dev = Device(self, dev_op)
self._devices[dev_op] = dev
dev.connect('init-failed', self._dev_init_failed_cb)
dev.connect('initialized', self._dev_initialized_cb)
dev.connect('state-changed', self._dev_state_changed_cb)
def _remove_device(self, dev_op):
if not self._devices.has_key(dev_op):
return
dev = self._devices[dev_op]
if dev.is_valid():
self.emit('device-removed', dev)
del self._devices[dev_op]
def _dev_state_changed_cb(self, dev):
op = dev.get_op()
if not self._devices.has_key(op) or not dev.is_valid():
return
if dev.get_state() == DEVICE_STATE_ACTIVATING:
self.emit('device-activating', dev)
elif dev.get_state() == DEVICE_STATE_ACTIVATED:
self.emit('device-activated', dev)
def get_device(self, dev_op):
if not self._devices.has_key(dev_op):
return None
return self._devices[dev_op]
def _setup_dbus(self):
self._sig_handlers = {
'StateChange': self.state_changed_sig_handler,
'DeviceAdded': self.device_added_sig_handler,
'DeviceRemoved': self.device_removed_sig_handler,
'DeviceActivationStage': self.device_activation_stage_sig_handler,
'DeviceActivating': self.device_activating_sig_handler,
'DeviceNowActive': self.device_now_active_sig_handler,
'DeviceNoLongerActive': self.device_no_longer_active_sig_handler,
'DeviceActivationFailed': self.device_activation_failed_sig_handler,
'DeviceCarrierOn': self.device_carrier_on_sig_handler,
'DeviceCarrierOff': self.device_carrier_off_sig_handler,
'DeviceStrengthChanged': self.wireless_device_strength_changed_sig_handler,
'WirelessNetworkAppeared': self.wireless_network_appeared_sig_handler,
'WirelessNetworkDisappeared': self.wireless_network_disappeared_sig_handler,
'WirelessNetworkStrengthChanged': self.wireless_network_strength_changed_sig_handler
}
try:
self._nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH)
self._nm_obj = dbus.Interface(self._nm_proxy, NM_IFACE)
except dbus.DBusException, e:
logging.debug("Could not connect to NetworkManager!")
self._nm_present = False
return
sys_bus.add_signal_receiver(self.name_owner_changed_sig_handler,
signal_name="NameOwnerChanged",
dbus_interface="org.freedesktop.DBus")
for (signal, handler) in self._sig_handlers.items():
sys_bus.add_signal_receiver(handler, signal_name=signal, dbus_interface=NM_IFACE)
# Find out whether or not NMI is running
try:
bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \
dbus_interface='org.freedesktop.DBus')
if name:
self._nm_present = True
except dbus.DBusException:
pass
def set_active_device(self, device, network=None, mesh_freq=None, mesh_start=None):
ssid = ""
if network:
ssid = network.get_ssid()
if device.get_type() == DEVICE_TYPE_802_11_MESH_OLPC:
if mesh_freq or mesh_start:
if mesh_freq and not mesh_start:
self._nm_obj.setActiveDevice(device.get_op(), dbus.Double(mesh_freq))
elif mesh_start and not mesh_freq:
self._nm_obj.setActiveDevice(device.get_op(), dbus.Double(0.0), dbus.UInt32(mesh_start))
else:
self._nm_obj.setActiveDevice(device.get_op(), dbus.Double(mesh_freq), dbus.UInt32(mesh_start))
else:
self._nm_obj.setActiveDevice(device.get_op())
else:
self._nm_obj.setActiveDevice(device.get_op(), ssid)
def state_changed_sig_handler(self, new_state):
logging.debug('NM State Changed to %d' % new_state)
def device_activation_stage_sig_handler(self, device, stage):
logging.debug('Device Activation Stage "%s" for device %s' % (NM_DEVICE_STAGE_STRINGS[stage], device))
if not self._devices.has_key(device):
logging.debug('DeviceActivationStage, device %s does not exist' % (device))
return
self._devices[device].set_activation_stage(stage)
def device_activating_sig_handler(self, device):
logging.debug('DeviceActivating for %s' % (device))
if not self._devices.has_key(device):
logging.debug('DeviceActivating, device %s does not exist' % (device))
return
self._devices[device].set_state(DEVICE_STATE_ACTIVATING)
def device_now_active_sig_handler(self, device, ssid=None):
logging.debug('DeviceNowActive for %s' % (device))
if not self._devices.has_key(device):
logging.debug('DeviceNowActive, device %s does not exist' % (device))
return
self._devices[device].set_state(DEVICE_STATE_ACTIVATED)
def device_no_longer_active_sig_handler(self, device):
logging.debug('DeviceNoLongerActive for %s' % (device))
if not self._devices.has_key(device):
logging.debug('DeviceNoLongerActive, device %s does not exist' % (device))
return
self._devices[device].set_state(DEVICE_STATE_INACTIVE)
def device_activation_failed_sig_handler(self, device, ssid=None):
logging.debug('DeviceActivationFailed for %s' % (device))
if not self._devices.has_key(device):
logging.debug('DeviceActivationFailed, device %s does not exist' % (device))
return
self._devices[device].set_state(DEVICE_STATE_INACTIVE)
def name_owner_changed_sig_handler(self, name, old, new):
if name != NM_SERVICE:
return
if (old and len(old)) and (not new and not len(new)):
# NM went away
self._nm_present = False
devs = self._devices.keys()
for op in devs:
self._remove_device(op)
self._devices = {}
elif (not old and not len(old)) and (new and len(new)):
# NM started up
self._nm_present = True
self._get_initial_devices()
def device_added_sig_handler(self, device):
logging.debug('DeviceAdded for %s' % (device))
self._add_device(device)
def device_removed_sig_handler(self, device):
logging.debug('DeviceRemoved for %s' % (device))
self._remove_device(device)
def wireless_network_appeared_sig_handler(self, device, network):
if not self._devices.has_key(device):
return
self._devices[device].network_appeared(network)
def wireless_network_disappeared_sig_handler(self, device, network):
if not self._devices.has_key(device):
return
self._devices[device].network_disappeared(network)
def wireless_device_strength_changed_sig_handler(self, device, strength):
if not self._devices.has_key(device):
return
self._devices[device].set_strength(strength)
def wireless_network_strength_changed_sig_handler(self, device, network, strength):
if not self._devices.has_key(device):
return
net = self._devices[device].get_network(network)
if net:
net.set_strength(strength)
def device_carrier_on_sig_handler(self, device):
if not self._devices.has_key(device):
return
self._devices[device].set_carrier(True)
def device_carrier_off_sig_handler(self, device):
if not self._devices.has_key(device):
return
self._devices[device].set_carrier(False)
-525
View File
@@ -1,525 +0,0 @@
# vi: ts=4 ai noet
#
# 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 dbus
import dbus.service
import time
import os
import binascii
import ConfigParser
import logging
import nmclient
import keydialog
import gtk
from sugar import env
IW_AUTH_KEY_MGMT_802_1X = 0x1
IW_AUTH_KEY_MGMT_PSK = 0x2
IW_AUTH_WPA_VERSION_DISABLED = 0x00000001
IW_AUTH_WPA_VERSION_WPA = 0x00000002
IW_AUTH_WPA_VERSION_WPA2 = 0x00000004
NM_AUTH_TYPE_WPA_PSK_AUTO = 0x00000000
IW_AUTH_CIPHER_NONE = 0x00000001
IW_AUTH_CIPHER_WEP40 = 0x00000002
IW_AUTH_CIPHER_TKIP = 0x00000004
IW_AUTH_CIPHER_CCMP = 0x00000008
IW_AUTH_CIPHER_WEP104 = 0x00000010
IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001
IW_AUTH_ALG_SHARED_KEY = 0x00000002
NM_INFO_IFACE='org.freedesktop.NetworkManagerInfo'
NM_INFO_PATH='/org/freedesktop/NetworkManagerInfo'
class NoNetworks(dbus.DBusException):
def __init__(self):
dbus.DBusException.__init__(self)
self._dbus_error_name = NM_INFO_IFACE + '.NoNetworks'
class CanceledKeyRequestError(dbus.DBusException):
def __init__(self):
dbus.DBusException.__init__(self)
self._dbus_error_name = NM_INFO_IFACE + '.CanceledError'
class NetworkInvalidError(Exception):
pass
class NMConfig(ConfigParser.ConfigParser):
def get_bool(self, section, name):
opt = self.get(section, name)
if type(opt) == type(""):
if opt.lower() == 'yes' or opt.lower() == 'true':
return True
elif opt.lower() == 'no' or opt.lower() == 'false':
return False
raise ValueError("Invalid format for %s/%s. Should be one of [yes, no, true, false]." % (section, name))
def get_list(self, section, name):
opt = self.get(section, name)
if type(opt) == type(""):
if not len(opt):
return []
try:
return opt.split()
except Exception:
pass
raise ValueError("Invalid format for %s/%s. Should be a space-separate list." % (section, name))
def get_int(self, section, name):
opt = self.get(section, name)
try:
return int(opt)
except Exception:
pass
raise ValueError("Invalid format for %s/%s. Should be a valid integer." % (section, name))
def get_float(self, section, name):
opt = self.get(section, name)
try:
return float(opt)
except Exception:
pass
raise ValueError("Invalid format for %s/%s. Should be a valid float." % (section, name))
NETWORK_TYPE_UNKNOWN = 0
NETWORK_TYPE_ALLOWED = 1
NETWORK_TYPE_INVALID = 2
class Security(object):
def __init__(self, we_cipher):
self._we_cipher = we_cipher
def read_from_config(self, cfg, name):
pass
def read_from_args(self, args):
pass
def new_from_config(cfg, name):
security = None
we_cipher = cfg.get_int(name, "we_cipher")
if we_cipher == IW_AUTH_CIPHER_NONE:
security = Security(we_cipher)
elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104:
security = WEPSecurity(we_cipher)
elif we_cipher == NM_AUTH_TYPE_WPA_PSK_AUTO or we_cipher == IW_AUTH_CIPHER_CCMP or we_cipher == IW_AUTH_CIPHER_TKIP:
security = WPASecurity(we_cipher)
else:
raise ValueError("Unsupported security combo")
security.read_from_config(cfg, name)
return security
new_from_config = staticmethod(new_from_config)
def new_from_args(we_cipher, args):
security = None
try:
if we_cipher == IW_AUTH_CIPHER_NONE:
security = Security(we_cipher)
elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104:
security = WEPSecurity(we_cipher)
elif we_cipher == NM_AUTH_TYPE_WPA_PSK_AUTO or we_cipher == IW_AUTH_CIPHER_CCMP or we_cipher == IW_AUTH_CIPHER_TKIP:
security = WPASecurity(we_cipher)
else:
raise ValueError("Unsupported security combo")
security.read_from_args(args)
except ValueError, e:
logging.debug("Error reading security information: %s" % e)
del security
return None
return security
new_from_args = staticmethod(new_from_args)
def get_properties(self):
return [dbus.Int32(self._we_cipher)]
def write_to_config(self, section, config):
config.set(section, "we_cipher", self._we_cipher)
class WEPSecurity(Security):
def read_from_args(self, args):
if len(args) != 2:
raise ValueError("not enough arguments")
key = args[0]
auth_alg = args[1]
if isinstance(key, unicode):
key = key.encode()
if not isinstance(key, str):
raise ValueError("wrong argument type for key")
if not isinstance(auth_alg, int):
raise ValueError("wrong argument type for auth_alg")
self._key = key
self._auth_alg = auth_alg
def read_from_config(self, cfg, name):
# Key should be a hex encoded string
self._key = cfg.get(name, "key")
if self._we_cipher == IW_AUTH_CIPHER_WEP40 and len(self._key) != 10:
raise ValueError("Key length not right for 40-bit WEP")
if self._we_cipher == IW_AUTH_CIPHER_WEP104 and len(self._key) != 26:
raise ValueError("Key length not right for 104-bit WEP")
try:
a = binascii.a2b_hex(self._key)
except TypeError:
raise ValueError("Key was not a hexadecimal string.")
self._auth_alg = cfg.get_int(name, "auth_alg")
if self._auth_alg != IW_AUTH_ALG_OPEN_SYSTEM and self._auth_alg != IW_AUTH_ALG_SHARED_KEY:
raise ValueError("Invalid authentication algorithm %d" % self._auth_alg)
def get_properties(self):
args = Security.get_properties(self)
args.append(dbus.String(self._key))
args.append(dbus.Int32(self._auth_alg))
return args
def write_to_config(self, section, config):
Security.write_to_config(self, section, config)
config.set(section, "key", self._key)
config.set(section, "auth_alg", self._auth_alg)
class WPASecurity(Security):
def read_from_args(self, args):
if len(args) != 3:
raise ValueError("not enough arguments")
key = args[0]
if isinstance(key, unicode):
key = key.encode()
if not isinstance(key, str):
raise ValueError("wrong argument type for key")
wpa_ver = args[1]
if not isinstance(wpa_ver, int):
raise ValueError("wrong argument type for WPA version")
key_mgmt = args[2]
if not isinstance(key_mgmt, int):
raise ValueError("wrong argument type for WPA key management")
if not key_mgmt & IW_AUTH_KEY_MGMT_PSK:
raise ValueError("Key management types other than PSK are not supported")
self._key = key
self._wpa_ver = wpa_ver
self._key_mgmt = key_mgmt
def read_from_config(self, cfg, name):
# Key should be a hex encoded string
self._key = cfg.get(name, "key")
if len(self._key) != 64:
raise ValueError("Key length not right for WPA-PSK")
try:
a = binascii.a2b_hex(self._key)
except TypeError:
raise ValueError("Key was not a hexadecimal string.")
self._wpa_ver = cfg.get_int(name, "wpa_ver")
if self._wpa_ver != IW_AUTH_WPA_VERSION_WPA and self._wpa_ver != IW_AUTH_WPA_VERSION_WPA2:
raise ValueError("Invalid WPA version %d" % self._wpa_ver)
self._key_mgmt = cfg.get_int(name, "key_mgmt")
if not self._key_mgmt & IW_AUTH_KEY_MGMT_PSK:
raise ValueError("Invalid WPA key management option %d" % self._key_mgmt)
def get_properties(self):
args = Security.get_properties(self)
args.append(dbus.String(self._key))
args.append(dbus.Int32(self._wpa_ver))
args.append(dbus.Int32(self._key_mgmt))
return args
def write_to_config(self, section, config):
Security.write_to_config(self, section, config)
config.set(section, "key", self._key)
config.set(section, "wpa_ver", self._wpa_ver)
config.set(section, "key_mgmt", self._key_mgmt)
class Network:
def __init__(self, ssid):
self.ssid = ssid
self.timestamp = int(time.time())
self.bssids = []
self.we_cipher = 0
self._security = None
def get_properties(self):
bssid_list = dbus.Array([], signature="s")
for item in self.bssids:
bssid_list.append(dbus.String(item))
args = [dbus.String(self.ssid), dbus.Int32(self.timestamp), dbus.Boolean(True), bssid_list]
args += self._security.get_properties()
return tuple(args)
def get_security(self):
return self._security.get_properties()
def set_security(self, security):
self._security = security
def read_from_args(self, auto, bssid, we_cipher, args):
if auto == False:
self.timestamp = int(time.time())
if not bssid in self.bssids:
self.bssids.append(bssid)
self._security = Security.new_from_args(we_cipher, args)
if not self._security:
raise NetworkInvalidError("Invalid security information")
def read_from_config(self, config):
try:
self.timestamp = config.get_int(self.ssid, "timestamp")
except (ConfigParser.NoOptionError, ValueError), e:
raise NetworkInvalidError(e)
try:
self._security = Security.new_from_config(config, self.ssid)
except Exception, e:
raise NetworkInvalidError(e)
# The following don't need to be present
try:
self.bssids = config.get_list(self.ssid, "bssids")
except (ConfigParser.NoOptionError, ValueError), e:
pass
def write_to_config(self, config):
try:
config.add_section(self.ssid)
config.set(self.ssid, "timestamp", self.timestamp)
if len(self.bssids) > 0:
opt = " "
opt.join(self.bssids)
config.set(self.ssid, "bssids", opt)
self._security.write_to_config(self.ssid, config)
except Exception, e:
logging.debug("Error writing '%s': %s" % (self.ssid, e))
class NotFoundError(dbus.DBusException):
pass
class UnsupportedError(dbus.DBusException):
pass
class NMInfoDBusServiceHelper(dbus.service.Object):
def __init__(self, parent):
self._parent = parent
bus = dbus.SystemBus()
# If NMI is already around, don't grab the NMI service
bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
name = None
try:
name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \
dbus_interface='org.freedesktop.DBus')
except dbus.DBusException:
pass
if name:
logging.debug("NMI service already owned by %s, won't claim it." % name)
raise RuntimeError
bus_name = dbus.service.BusName(NM_INFO_IFACE, bus=bus)
dbus.service.Object.__init__(self, bus_name, NM_INFO_PATH)
@dbus.service.method(NM_INFO_IFACE, in_signature='i', out_signature='as')
def getNetworks(self, net_type):
ssids = self._parent.get_networks(net_type)
if len(ssids) > 0:
return dbus.Array(ssids)
raise NoNetworks()
@dbus.service.method(NM_INFO_IFACE, in_signature='si', async_callbacks=('async_cb', 'async_err_cb'))
def getNetworkProperties(self, ssid, net_type, async_cb, async_err_cb):
self._parent.get_network_properties(ssid, net_type, async_cb, async_err_cb)
@dbus.service.method(NM_INFO_IFACE)
def updateNetworkInfo(self, ssid, bauto, bssid, cipher, *args):
self._parent.update_network_info(ssid, bauto, bssid, cipher, args)
@dbus.service.method(NM_INFO_IFACE, async_callbacks=('async_cb', 'async_err_cb'))
def getKeyForNetwork(self, dev_path, net_path, ssid, attempt, new_key, async_cb, async_err_cb):
self._parent.get_key_for_network(dev_path, net_path, ssid,
attempt, new_key, async_cb, async_err_cb)
@dbus.service.method(NM_INFO_IFACE)
def cancelGetKeyForNetwork(self):
self._parent.cancel_get_key_for_network()
class NMInfo(object):
def __init__(self, client):
profile_path = env.get_profile_path()
self._cfg_file = os.path.join(profile_path, "nm", "networks.cfg")
self._nmclient = client
self._allowed_networks = self._read_config()
self._dbus_helper = NMInfoDBusServiceHelper(self)
self._key_dialog = None
def save_config(self):
self._write_config(self._allowed_networks)
def _read_config(self):
if not os.path.exists(os.path.dirname(self._cfg_file)):
os.makedirs(os.path.dirname(self._cfg_file), 0755)
if not os.path.exists(self._cfg_file):
self._write_config({})
return {}
config = NMConfig()
config.read(self._cfg_file)
networks = {}
for name in config.sections():
try:
net = Network(name)
net.read_from_config(config)
networks[name] = net
except Exception, e:
logging.error("Error when processing config for the network %s: %r" % (name, e))
del config
return networks
def _write_config(self, networks):
fp = open(self._cfg_file, 'w')
config = NMConfig()
for net in networks.values():
net.write_to_config(config)
config.write(fp)
fp.close()
del config
def get_networks(self, net_type):
if net_type != NETWORK_TYPE_ALLOWED:
raise ValueError("Bad network type")
nets = []
for net in self._allowed_networks.values():
nets.append(net.ssid)
logging.debug("Returning networks: %s" % nets)
return nets
def get_network_properties(self, ssid, net_type, async_cb, async_err_cb):
if not isinstance(ssid, unicode):
async_err_cb(ValueError("Invalid arguments; ssid must be unicode."))
if net_type != NETWORK_TYPE_ALLOWED:
async_err_cb(ValueError("Bad network type"))
if not self._allowed_networks.has_key(ssid):
async_err_cb(NotFoundError("Network '%s' not found." % ssid))
network = self._allowed_networks[ssid]
props = network.get_properties()
# DBus workaround: the normal method return handler wraps
# the returned arguments in a tuple and then converts that to a
# struct, but NetworkManager expects a plain list of arguments.
# It turns out that the async callback method return code _doesn't_
# wrap the returned arguments in a tuple, so as a workaround use
# the async callback stuff here even though we're not doing it
# asynchronously.
async_cb(*props)
def update_network_info(self, ssid, auto, bssid, we_cipher, args):
if not isinstance(ssid, unicode):
raise ValueError("Invalid arguments; ssid must be unicode.")
if self._allowed_networks.has_key(ssid):
del self._allowed_networks[ssid]
net = Network(ssid)
try:
net.read_from_args(auto, bssid, we_cipher, args)
logging.debug("Updated network information for '%s'." % ssid)
self._allowed_networks[ssid] = net
self.save_config()
except NetworkInvalidError, e:
logging.debug("Error updating network information: %s" % e)
del net
def get_key_for_network(self, dev_op, net_op, ssid, attempt, new_key, async_cb, async_err_cb):
if not isinstance(ssid, unicode):
raise ValueError("Invalid arguments; ssid must be unicode.")
if self._allowed_networks.has_key(ssid) and not new_key:
# We've got the info already
net = self._allowed_networks[ssid]
async_cb(tuple(net.get_security()))
return
# Otherwise, ask the user for it
net = None
dev = self._nmclient.get_device(dev_op)
if not dev:
async_err_cb(NotFoundError("Device was unknown."))
return
if dev.get_type() == nmclient.DEVICE_TYPE_802_3_ETHERNET:
# We don't support wired 802.1x yet...
async_err_cb(UnsupportedError("Device type is unsupported by NMI."))
return
net = dev.get_network(net_op)
if not net:
async_err_cb(NotFoundError("Network was unknown."))
return
self._key_dialog = keydialog.new_key_dialog(net, async_cb, async_err_cb)
self._key_dialog.connect("response", self._key_dialog_response_cb)
self._key_dialog.connect("destroy", self._key_dialog_destroy_cb)
self._key_dialog.show_all()
def _key_dialog_destroy_cb(self, widget, foo=None):
if widget != self._key_dialog:
return
self._key_dialog_response_cb(widget, gtk.RESPONSE_CANCEL)
def _key_dialog_response_cb(self, widget, response_id):
if widget != self._key_dialog:
return
(async_cb, async_err_cb) = self._key_dialog.get_callbacks()
net = self._key_dialog.get_network()
security = None
if response_id == gtk.RESPONSE_OK:
security = self._key_dialog.create_security()
self._key_dialog = None
widget.destroy()
if response_id in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_NONE]:
# key dialog dialog was canceled; send the error back to NM
async_err_cb(CanceledKeyRequestError())
elif response_id == gtk.RESPONSE_OK:
if not security:
raise RuntimeError("Invalid security arguments.")
props = security.get_properties()
a = tuple(props)
async_cb(*a)
else:
raise RuntimeError("Unhandled key dialog response %d" % response_id)
def cancel_get_key_for_network(self):
# Close the wireless key dialog and just have it return
# with the 'canceled' argument set to true
if not self._key_dialog:
return
self._key_dialog_destroy_cb(self._key_dialog)
-45
View File
@@ -1,45 +0,0 @@
from sugar.profile import get_profile
from xmlrpclib import ServerProxy, Error
import sys
import os
REGISTER_URL = 'http://schoolserver:8080/'
def register_laptop(url=REGISTER_URL):
if not have_ofw_tree():
return False
sn = read_ofw('mfg-data/SN')
uuid = read_ofw('mfg-data/U#')
sn = sn or 'SHF00000000'
uuid = uuid or '00000000-0000-0000-0000-000000000000'
profile = get_profile()
try:
server = ServerProxy(url)
data = server.register(sn, profile.nick_name, uuid, profile.pubkey)
if data['success'] != 'OK':
print >> sys.stderr, "Error registering laptop: " + data['error']
return False
profile.jabber_server = data['jabberserver']
profile.backup1 = data['backupurl']
profile.save()
except Error, e:
print >> sys.stderr, "Error registering laptop: " + str(e)
return False
return True
def have_ofw_tree():
return os.path.exists('/ofw')
def read_ofw(path):
path = os.path.join('/ofw', path)
if not os.path.exists(path):
return None
fh = open(path, 'r')
data = fh.read().rstrip('\0\n')
fh.close()
return data
-10
View File
@@ -1,10 +0,0 @@
imagedir = $(pkgdatadir)/shell/intro
image_DATA = default-picture.png
EXTRA_DIST = $(conf_DATA) $(image_DATA)
sugardir = $(pkgdatadir)/shell/intro
sugar_PYTHON = \
__init__.py \
colorpicker.py \
intro.py \
glive.py
View File
-42
View File
@@ -1,42 +0,0 @@
# Copyright (C) 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 hippo
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.graphics.xocolor import XoColor
class ColorPicker(hippo.CanvasBox, hippo.CanvasItem):
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._xo = CanvasIcon(size=style.XLARGE_ICON_SIZE,
icon_name='computer-xo')
self._set_random_colors()
self._xo.connect('activated', self._xo_activated_cb)
self.append(self._xo)
def _xo_activated_cb(self, item):
self._set_random_colors()
def get_color(self):
return self._xo_color
def _set_random_colors(self):
self._xo_color = XoColor()
self._xo.props.xo_color = self._xo_color
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

-196
View File
@@ -1,196 +0,0 @@
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
import gtk
import pygtk
pygtk.require('2.0')
import sys
import pygst
pygst.require('0.10')
import gst
import gst.interfaces
import gobject
gobject.threads_init()
class Glive(gobject.GObject):
__gsignals__ = {
'new-picture': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'sink': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
}
def __init__(self, parent, width, height):
gobject.GObject.__init__(self)
self._parent = parent
#check out the halfpipe, d00d.
self.pipeline = gst.Pipeline()
self.v4l2src = gst.element_factory_make("v4l2src", "v4l2src")
self.t = gst.element_factory_make("tee", "tee")
self.t_src_pad = self.t.get_request_pad( "src%d" )
self.vscale = gst.element_factory_make("videoscale", "videoscale")
self.ximagesink = gst.element_factory_make("ximagesink", "ximagesink")
self.pipeline.add(self.v4l2src)
self.pipeline.add(self.t)
self.pipeline.add(self.vscale)
self.pipeline.add(self.ximagesink)
self.v4l2src.link(self.t)
videoscale_structure = gst.Structure("video/x-raw-rgb")
videoscale_structure['width'] = width
videoscale_structure['height'] = height
videoscale_structure['bpp'] = 16
videoscale_structure['depth'] = 16
videoscale_caps = gst.Caps(videoscale_structure)
self.t_src_pad.link(self.vscale.get_pad("sink"))
self.vscale.link(self.ximagesink, videoscale_caps)
#self.vscale.link(self.ximagesink)
self.queue = gst.element_factory_make("queue", "queue")
self.queue.set_property("leaky", True)
self.queue.set_property("max-size-buffers", 1)
self.qsrc = self.queue.get_pad( "src" )
self.qsink = self.queue.get_pad("sink")
self.ffmpeg = gst.element_factory_make("ffmpegcolorspace", "ffmpegcolorspace")
self.jpgenc = gst.element_factory_make("jpegenc", "jpegenc")
self.filesink = gst.element_factory_make("fakesink", "fakesink")
self.filesink.connect( "handoff", self.copyframe )
self.filesink.set_property("signal-handoffs", True)
self.pipeline.add(self.queue, self.ffmpeg, self.jpgenc, self.filesink)
#only link at snapshot time
#self.t.link(self.queue)
self.queue.link(self.ffmpeg)
self.ffmpeg.link(self.jpgenc)
self.jpgenc.link(self.filesink)
self.exposureOpen = False
self._bus = self.pipeline.get_bus()
self._CONNECT_SYNC = -1
self._CONNECT_MSG = -1
self.doPostBusStuff()
def copyframe(self, fsink, buffer, pad, user_data=None):
#for some reason, we get two back to back buffers, even though we
#ask for only one.
if (self.exposureOpen):
self.exposureOpen = False
piccy = gtk.gdk.pixbuf_loader_new_with_mime_type("image/jpeg")
piccy.write( buffer )
piccy.close()
pixbuf = piccy.get_pixbuf()
del piccy
self.t.unlink(self.queue)
self.queue.set_property("leaky", True)
gobject.idle_add(self.loadPic, pixbuf)
def loadPic( self, pixbuf ):
self.emit('new-picture', pixbuf)
def takeSnapshot( self ):
if (self.exposureOpen):
return
else:
self.exposureOpen = True
self.t.link(self.queue)
def doPostBusStuff(self):
self._bus.enable_sync_message_emission()
self._bus.add_signal_watch()
self._CONNECT_SYNC = self._bus.connect('sync-message::element', self.on_sync_message)
self._CONNECT_MSG = self._bus.connect('message', self.on_message)
def on_sync_message(self, bus, message):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
self.emit('sink', message.src)
message.src.set_property('force-aspect-ratio', True)
def on_message(self, bus, message):
t = message.type
if (t == gst.MESSAGE_ERROR):
err, debug = message.parse_error()
if (self.on_eos):
self.on_eos()
self._playing = False
elif (t == gst.MESSAGE_EOS):
if (self.on_eos):
self.on_eos()
self._playing = False
def on_eos( self ):
pass
def stop(self):
self.pipeline.set_state(gst.STATE_NULL)
def play(self):
self.pipeline.set_state(gst.STATE_PLAYING)
def pause(self):
self.pipeline.set_state(gst.STATE_PAUSED)
class LiveVideoSlot(gtk.EventBox):
__gsignals__ = {
'pixbuf': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
}
def __init__(self, width, height):
gtk.EventBox.__init__(self)
self.imagesink = None
self.playa = None
self._width = width
self._height = height
self.unset_flags(gtk.DOUBLE_BUFFERED)
self.connect('focus-in-event', self.focus_in)
self.connect('focus-out-event', self.focus_out)
self.connect("button-press-event", self._button_press_event_cb)
self.connect("expose-event", self._expose_event_cb)
def _expose_event_cb(self, widget, event):
if not self.playa:
self.playa = Glive(self, self._width, self._height)
self.playa.connect('new-picture', self._new_picture_cb)
self.playa.connect('sink', self._new_sink_cb)
def _new_picture_cb(self, playa, pixbuf):
self.emit('pixbuf', pixbuf)
def _new_sink_cb(self, playa, sink):
if (self.imagesink != None):
assert self.window.xid
self.imagesink = None
del self.imagesink
self.imagesink = sink
self.imagesink.set_xwindow_id(self.window.xid)
def _button_press_event_cb(self, widget, event):
self.takeSnapshot()
def focus_in(self, widget, event, args=None):
self.play()
def focus_out(self, widget, event, args=None):
self.stop()
def play( self ):
self.playa.play()
def pause( self ):
self.playa.pause()
def stop( self ):
self.playa.stop()
def takeSnapshot( self ):
self.playa.takeSnapshot()
-267
View File
@@ -1,267 +0,0 @@
# Copyright (C) 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 os
from ConfigParser import ConfigParser
from gettext import gettext as _
import gtk
import gobject
import dbus
import hippo
import logging
from sugar import env
from sugar.graphics import style
from sugar.graphics.icon import Icon
from sugar.graphics.entry import CanvasEntry
from sugar.profile import get_profile
import colorpicker
_BACKGROUND_COLOR = style.COLOR_PANEL_GREY
class _Page(hippo.CanvasBox):
__gproperties__ = {
'valid' : (bool, None, None, False,
gobject.PARAM_READABLE)
}
def __init__(self, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self.valid = False
def set_valid(self, valid):
self.valid = valid
self.notify('valid')
def do_get_property(self, pspec):
if pspec.name == 'valid':
return self.valid
def activate(self):
pass
class _NamePage(_Page):
def __init__(self, intro):
_Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER,
background_color=_BACKGROUND_COLOR.get_int(),
spacing=style.DEFAULT_SPACING,
orientation=hippo.ORIENTATION_HORIZONTAL,)
self._intro = intro
label = hippo.CanvasText(text=_("Name:"))
self.append(label)
self._entry = CanvasEntry(box_width=style.zoom(300))
self._entry.set_background(_BACKGROUND_COLOR.get_html())
self._entry.connect('notify::text', self._text_changed_cb)
widget = self._entry.props.widget
widget.set_max_length(45)
self.append(self._entry)
def _text_changed_cb(self, entry, pspec):
valid = len(entry.props.text.strip()) > 0
self.set_valid(valid)
def get_name(self):
return self._entry.props.text
def activate(self):
self._entry.props.widget.grab_focus()
class _ColorPage(_Page):
def __init__(self, **kwargs):
_Page.__init__(self, xalign=hippo.ALIGNMENT_CENTER,
background_color=_BACKGROUND_COLOR.get_int(),
spacing=style.DEFAULT_SPACING,
yalign=hippo.ALIGNMENT_CENTER, **kwargs)
self._label = hippo.CanvasText(text=_("Click to change color:"),
xalign=hippo.ALIGNMENT_CENTER)
self.append(self._label)
self._cp = colorpicker.ColorPicker(xalign=hippo.ALIGNMENT_CENTER)
self.append(self._cp)
self._color = self._cp.get_color()
self.set_valid(True)
def get_color(self):
return self._cp.get_color()
class _IntroBox(hippo.CanvasBox):
__gsignals__ = {
'done': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
gobject.TYPE_PYOBJECT]))
}
PAGE_NAME = 0
PAGE_COLOR = 1
PAGE_FIRST = PAGE_NAME
PAGE_LAST = PAGE_COLOR
def __init__(self):
hippo.CanvasBox.__init__(self, padding=style.zoom(30),
background_color=_BACKGROUND_COLOR.get_int())
self._page = self.PAGE_NAME
self._name_page = _NamePage(self)
self._color_page = _ColorPage()
self._current_page = None
self._setup_page()
def _setup_page(self):
self.remove_all()
if self._page == self.PAGE_NAME:
self._current_page = self._name_page
elif self._page == self.PAGE_COLOR:
self._current_page = self._color_page
self.append(self._current_page, hippo.PACK_EXPAND)
button_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
if self._page != self.PAGE_FIRST:
back_button = hippo.CanvasButton(text=_('Back'))
image = Icon(icon_name='go-left')
back_button.props.widget.set_image(image)
back_button.connect('activated', self._back_activated_cb)
button_box.append(back_button)
spacer = hippo.CanvasBox()
button_box.append(spacer, hippo.PACK_EXPAND)
self._next_button = hippo.CanvasButton()
image = Icon(icon_name='go-right')
self._next_button.props.widget.set_image(image)
if self._page == self.PAGE_LAST:
self._next_button.props.text = _('Done')
self._next_button.connect('activated', self._done_activated_cb)
else:
self._next_button.props.text = _('Next')
self._next_button.connect('activated', self._next_activated_cb)
self._current_page.activate()
self._update_next_button()
button_box.append(self._next_button)
self._current_page.connect('notify::valid',
self._page_valid_changed_cb)
self.append(button_box)
def _update_next_button(self):
widget = self._next_button.props.widget
widget.props.sensitive = self._current_page.props.valid
def _page_valid_changed_cb(self, page, pspec):
self._update_next_button()
def _back_activated_cb(self, item):
self.back()
def back(self):
if self._page != self.PAGE_FIRST:
self._page -= 1
self._setup_page()
def _next_activated_cb(self, item):
self.next()
def next(self):
if self._page == self.PAGE_LAST:
self.done()
if self._current_page.props.valid:
self._page += 1
self._setup_page()
def _done_activated_cb(self, item):
self.done()
def done(self):
path = os.path.join(os.path.dirname(__file__), 'default-picture.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(path)
name = self._name_page.get_name()
color = self._color_page.get_color()
self.emit('done', pixbuf, name, color)
def _key_press_cb(self, widget, event):
if gtk.gdk.keyval_name(event.keyval) == "Return":
self.next()
return True
elif gtk.gdk.keyval_name(event.keyval) == "Escape":
self.back()
return True
return False
class IntroWindow(gtk.Window):
def __init__(self):
gtk.Window.__init__(self)
self._canvas = hippo.Canvas()
self._intro_box = _IntroBox()
self._intro_box.connect('done', self._done_cb)
self._canvas.set_root(self._intro_box)
self.add(self._canvas)
self._canvas.show()
self.connect('key-press-event', self._intro_box._key_press_cb)
def _done_cb(self, box, pixbuf, name, color):
self.hide()
gobject.idle_add(self._create_profile, pixbuf, name, color)
def _create_profile(self, pixbuf, name, color):
# Save the buddy icon
icon_path = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
scaled = pixbuf.scale_simple(200, 200, gtk.gdk.INTERP_BILINEAR)
pixbuf.save(icon_path, "jpeg", {"quality":"85"})
profile = get_profile()
profile.nick_name = name
profile.color = color
profile.save()
# Generate keypair
import commands
keypath = os.path.join(env.get_profile_path(), "owner.key")
if not os.path.isfile(keypath):
cmd = "ssh-keygen -q -t dsa -f %s -C '' -N ''" % keypath
(s, o) = commands.getstatusoutput(cmd)
if s != 0:
logging.error("Could not generate key pair: %d" % s)
else:
logging.error("Keypair exists, skip generation.")
gtk.main_quit()
return False
if __name__ == "__main__":
w = IntroWindow()
w.show()
w.connect('destroy', gtk.main_quit)
gtk.main()
-54
View File
@@ -1,54 +0,0 @@
# Copyright (C) 2007 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import os
import time
from sugar import env
_MAX_BACKUP_DIRS = 3
def setup():
logs_dir = env.get_logs_path()
if not os.path.isdir(logs_dir):
os.makedirs(logs_dir)
backup_logs = []
backup_dirs = []
for f in os.listdir(logs_dir):
path = os.path.join(logs_dir, f)
if os.path.isfile(path):
backup_logs.append(f)
elif os.path.isdir(path):
backup_dirs.append(path)
if len(backup_dirs) > _MAX_BACKUP_DIRS:
backup_dirs.sort()
root = backup_dirs[0]
for f in os.listdir(root):
os.remove(os.path.join(root, f))
os.rmdir(root)
if len(backup_logs) > 0:
name = str(int(time.time()))
backup_dir = os.path.join(logs_dir, name)
os.mkdir(backup_dir)
for log in backup_logs:
source_path = os.path.join(logs_dir, log)
dest_path = os.path.join(backup_dir, log)
os.rename(source_path, dest_path)
-153
View File
@@ -1,153 +0,0 @@
# Copyright (C) 2006, 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 sys
import os
from ConfigParser import ConfigParser
import gettext
# HACK we need to import numpy before gtk otherwise we traceback in
# some locales. See http://dev.laptop.org/ticket/5559.
import numpy
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from sugar import env
from sugar import logger
from sugar.profile import get_profile
from view.Shell import Shell
from model.shellmodel import ShellModel
from shellservice import ShellService
from hardware import hardwaremanager
from intro import intro
import logsmanager
import config
def _start_matchbox():
cmd = ['matchbox-window-manager']
cmd.extend(['-use_titlebar', 'no'])
cmd.extend(['-theme', 'sugar'])
cmd.extend(['-kbdconfig', os.path.join(config.data_path, 'kbdconfig')])
gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
def _save_session_info():
# Save our DBus Session Bus address somewhere it can be found
#
# WARNING!!! this is going away at some near future point, do not rely on it
#
session_info_file = os.path.join(env.get_profile_path(), "session.info")
f = open(session_info_file, "w")
cp = ConfigParser()
cp.add_section('Session')
cp.set('Session', 'dbus_address', os.environ['DBUS_SESSION_BUS_ADDRESS'])
cp.set('Session', 'display', gtk.gdk.display_get_default().get_name())
cp.write(f)
f.close()
def _setup_translations():
locale_path = os.path.join(config.prefix, 'share', 'locale')
domain = 'sugar'
gettext.bindtextdomain(domain, locale_path)
gettext.textdomain(domain)
def check_cm(bus_name):
try:
import dbus
bus = dbus.SessionBus()
bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
name = bus_object.GetNameOwner(bus_name, dbus_interface='org.freedesktop.DBus')
if name:
return True
except dbus.DBusException:
pass
return False
def _shell_started_cb():
# Unfreeze the display
hw_manager = hardwaremanager.get_manager()
hw_manager.set_dcon_freeze(0)
def main():
gobject.idle_add(_shell_started_cb)
logsmanager.setup()
logger.start('shell')
_save_session_info()
_start_matchbox()
_setup_translations()
hw_manager = hardwaremanager.get_manager()
hw_manager.startup()
icons_path = os.path.join(config.data_path, 'icons')
gtk.icon_theme_get_default().append_search_path(icons_path)
# Do initial setup if needed
if not get_profile().is_valid():
win = intro.IntroWindow()
win.show_all()
gtk.main()
if os.environ.has_key("SUGAR_TP_DEBUG"):
# Allow the user time to start up telepathy connection managers
# using the Sugar DBus bus address
import time
from telepathy.client import ManagerRegistry
registry = ManagerRegistry()
registry.LoadManagers()
debug_flags = os.environ["SUGAR_TP_DEBUG"].split(',')
for cm_name in debug_flags:
if cm_name not in ["gabble", "salut"]:
continue
try:
cm = registry.services[cm_name]
except KeyError:
print RuntimeError("%s connection manager not found!" % cm_name)
while not check_cm(cm['busname']):
print "Waiting for %s on: DBUS_SESSION_BUS_ADDRESS=%s" % \
(cm_name, os.environ["DBUS_SESSION_BUS_ADDRESS"])
try:
time.sleep(5)
except KeyboardInterrupt:
print "Got Ctrl+C, continuing..."
break
model = ShellModel()
shell = Shell(model)
service = ShellService(shell)
try:
gtk.main()
except KeyboardInterrupt:
print 'Ctrl+C pressed, exiting...'
session_info_file = os.path.join(env.get_profile_path(), "session.info")
os.remove(session_info_file)
-164
View File
@@ -1,164 +0,0 @@
# 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 sugar.presence import presenceservice
from sugar.graphics.xocolor import XoColor
import gobject
_NOT_PRESENT_COLOR = "#888888,#BBBBBB"
class BuddyModel(gobject.GObject):
__gsignals__ = {
'appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
'nick-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'color-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([])),
'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self, key=None, buddy=None, nick=None):
if (key and buddy) or (not key and not buddy):
raise RuntimeError("Must specify only _one_ of key or buddy.")
gobject.GObject.__init__(self)
self._ba_handler = None
self._pc_handler = None
self._dis_handler = None
self._bic_handler = None
self._cac_handler = None
self._pservice = presenceservice.get_instance()
self._buddy = None
if not buddy:
self._key = key
# connect to the PS's buddy-appeared signal and
# wait for the buddy to appear
self._ba_handler = self._pservice.connect('buddy-appeared',
self._buddy_appeared_cb)
# Set color to 'inactive'/'disconnected'
self._set_color_from_string(_NOT_PRESENT_COLOR)
self._nick = nick
self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
else:
self._update_buddy(buddy)
def _get_buddies_cb(self, list):
buddy = None
for iter_buddy in list:
if iter_buddy.props.key == self._key:
buddy = iter_buddy
break
if buddy:
if self._ba_handler:
# Once we have the buddy, we no longer need to
# monitor buddy-appeared events
self._pservice.disconnect(self._ba_handler)
self._ba_handler = None
self._update_buddy(buddy)
def _set_color_from_string(self, color_string):
self._color = XoColor(color_string)
def get_key(self):
return self._key
def get_nick(self):
return self._nick
def get_color(self):
return self._color
def get_buddy(self):
return self._buddy
def is_owner(self):
if not self._buddy:
return False
return self._buddy.props.owner
def is_present(self):
if self._buddy:
return True
return False
def get_current_activity(self):
if self._buddy:
return self._buddy.props.current_activity
return None
def _update_buddy(self, buddy):
if not buddy:
raise ValueError("Buddy cannot be None.")
self._buddy = buddy
self._key = self._buddy.props.key
self._nick = self._buddy.props.nick
self._set_color_from_string(self._buddy.props.color)
self._pc_handler = self._buddy.connect('property-changed', self._buddy_property_changed_cb)
self._bic_handler = self._buddy.connect('icon-changed', self._buddy_icon_changed_cb)
def _buddy_appeared_cb(self, pservice, buddy):
if self._buddy or buddy.props.key != self._key:
return
if self._ba_handler:
# Once we have the buddy, we no longer need to
# monitor buddy-appeared events
self._pservice.disconnect(self._ba_handler)
self._ba_handler = None
self._update_buddy(buddy)
self.emit('appeared')
def _buddy_property_changed_cb(self, buddy, keys):
if not self._buddy:
return
if 'color' in keys:
self._set_color_from_string(self._buddy.props.color)
self.emit('color-changed', self.get_color())
if 'current-activity' in keys:
self.emit('current-activity-changed', buddy.props.current_activity)
if 'nick' in keys:
self._nick = self._buddy.props.nick
self.emit('nick-changed', self.get_nick())
def _buddy_disappeared_cb(self, buddy):
if buddy != self._buddy:
return
self._buddy.disconnect(self._pc_handler)
self._buddy.disconnect(self._dis_handler)
self._buddy.disconnect(self._bic_handler)
self._buddy.disconnect(self._cac_handler)
self._set_color_from_string(_NOT_PRESENT_COLOR)
self.emit('disappeared')
self._buddy = None
def _buddy_icon_changed_cb(self, buddy):
self.emit('icon-changed')
-114
View File
@@ -1,114 +0,0 @@
# 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 dbus
import os
from ConfigParser import ConfigParser
import gobject
from model.BuddyModel import BuddyModel
from sugar import env
import logging
class Friends(gobject.GObject):
__gsignals__ = {
'friend-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([object])),
'friend-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([str])),
}
def __init__(self):
gobject.GObject.__init__(self)
self._friends = {}
self._path = os.path.join(env.get_profile_path(), 'friends')
self.load()
def has_buddy(self, buddy):
return self._friends.has_key(buddy.get_key())
def add_friend(self, buddy_info):
self._friends[buddy_info.get_key()] = buddy_info
self.emit('friend-added', buddy_info)
def make_friend(self, buddy):
if not self.has_buddy(buddy):
self.add_friend(buddy)
self.save()
def remove(self, buddy_info):
del self._friends[buddy_info.get_key()]
self.save()
self.emit('friend-removed', buddy_info.get_key())
def __iter__(self):
return self._friends.values().__iter__()
def load(self):
cp = ConfigParser()
try:
success = cp.read([self._path])
if success:
for key in cp.sections():
# HACK: don't screw up on old friends files
if len(key) < 20:
continue
buddy = BuddyModel(key=key, nick=cp.get(key, 'nick'))
self.add_friend(buddy)
except Exception, exc:
logging.error("Error parsing friends file: %s" % exc)
def save(self):
cp = ConfigParser()
for friend in self:
section = friend.get_key()
cp.add_section(section)
cp.set(section, 'nick', friend.get_nick())
cp.set(section, 'color', friend.get_color().to_string())
fileobject = open(self._path, 'w')
cp.write(fileobject)
fileobject.close()
self._sync_friends()
def _sync_friends(self):
# XXX: temporary hack
# remove this when the shell service has a D-Bus API for buddies
def friends_synced():
pass
def friends_synced_error(e):
logging.error("Error asking presence service to sync friends: %s"
% e)
keys = []
for friend in self:
keys.append(friend.get_key())
bus = dbus.SessionBus()
ps = bus.get_object('org.laptop.Sugar.Presence',
'/org/laptop/Sugar/Presence')
psi = dbus.Interface(ps, 'org.laptop.Sugar.Presence')
psi.SyncFriends(keys,
reply_handler=friends_synced,
error_handler=friends_synced_error)
-72
View File
@@ -1,72 +0,0 @@
# 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 gobject
from sugar.presence import presenceservice
class Invite:
def __init__(self, issuer, bundle_id, activity_id):
self._issuer = issuer
self._activity_id = activity_id
self._bundle_id = bundle_id
def get_activity_id(self):
return self._activity_id
def get_bundle_id(self):
return self._bundle_id
class Invites(gobject.GObject):
__gsignals__ = {
'invite-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([object])),
'invite-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([object])),
}
def __init__(self):
gobject.GObject.__init__(self)
self._dict = {}
ps = presenceservice.get_instance()
owner = ps.get_owner()
owner.connect('joined-activity', self._owner_joined_cb)
def add_invite(self, issuer, bundle_id, activity_id):
if activity_id in self._dict:
# there is no point to add more than one time
# an invite for the same activity
return
invite = Invite(issuer, bundle_id, activity_id)
self._dict[activity_id] = invite
self.emit('invite-added', invite)
def remove_invite(self, invite):
self._dict.pop(invite.get_activity_id())
self.emit('invite-removed', invite)
def remove_activity(self, activity_id):
invite = self._dict.get(activity_id)
if invite is not None:
self.remove_invite(invite)
def _owner_joined_cb(self, owner, activity):
self.remove_activity(activity.props.id)
def __iter__(self):
return self._dict.values().__iter__()
-14
View File
@@ -1,14 +0,0 @@
SUBDIRS = devices
sugardir = $(pkgdatadir)/shell/model
sugar_PYTHON = \
__init__.py \
accesspointmodel.py \
BuddyModel.py \
Friends.py \
Invites.py \
Owner.py \
MeshModel.py \
shellmodel.py \
homeactivity.py \
homemodel.py
-235
View File
@@ -1,235 +0,0 @@
# 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 gobject
from sugar.graphics.xocolor import XoColor
from sugar.presence import presenceservice
from sugar import activity
from model.BuddyModel import BuddyModel
from model.accesspointmodel import AccessPointModel
from hardware import hardwaremanager
from hardware import nmclient
class ActivityModel:
def __init__(self, activity, bundle):
self.activity = activity
self.bundle = bundle
def get_id(self):
return self.activity.props.id
def get_icon_name(self):
return self.bundle.icon
def get_color(self):
return XoColor(self.activity.props.color)
def get_bundle_id(self):
return self.bundle.bundle_id
class MeshModel(gobject.GObject):
__gsignals__ = {
'activity-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'activity-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'buddy-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'buddy-moved': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT,
gobject.TYPE_PYOBJECT])),
'buddy-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'access-point-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'access-point-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'mesh-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'mesh-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._activities = {}
self._buddies = {}
self._access_points = {}
self._mesh = None
self._pservice = presenceservice.get_instance()
self._pservice.connect("activity-appeared",
self._activity_appeared_cb)
self._pservice.connect('activity-disappeared',
self._activity_disappeared_cb)
self._pservice.connect("buddy-appeared",
self._buddy_appeared_cb)
self._pservice.connect("buddy-disappeared",
self._buddy_disappeared_cb)
# Add any buddies the PS knows about already
self._pservice.get_buddies_async(reply_handler=self._get_buddies_cb)
self._pservice.get_activities_async(reply_handler=self._get_activities_cb)
network_manager = hardwaremanager.get_network_manager()
if network_manager:
for nm_device in network_manager.get_devices():
self._add_network_device(nm_device)
network_manager.connect('device-added',
self._nm_device_added_cb)
network_manager.connect('device-removed',
self._nm_device_removed_cb)
def _get_buddies_cb(self, list):
for buddy in list:
self._buddy_appeared_cb(self._pservice, buddy)
def _get_activities_cb(self, list):
for activity in list:
self._check_activity(activity)
def _nm_device_added_cb(self, manager, nm_device):
self._add_network_device(nm_device)
def _nm_device_removed_cb(self, manager, nm_device):
self._remove_network_device(nm_device)
def _nm_network_appeared_cb(self, nm_device, nm_network):
self._add_access_point(nm_device, nm_network)
def _nm_network_disappeared_cb(self, nm_device, nm_network):
if self._access_points.has_key(nm_network.get_op()):
ap = self._access_points[nm_network.get_op()]
self._remove_access_point(ap)
def _add_network_device(self, nm_device):
dtype = nm_device.get_type()
if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS:
for nm_network in nm_device.get_networks():
self._add_access_point(nm_device, nm_network)
nm_device.connect('network-appeared',
self._nm_network_appeared_cb)
nm_device.connect('network-disappeared',
self._nm_network_disappeared_cb)
elif dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
self._mesh = nm_device
self.emit('mesh-added', self._mesh)
def _remove_network_device(self, nm_device):
if nm_device == self._mesh:
self._mesh = None
self.emit('mesh-removed')
elif nm_device.get_type() == nmclient.DEVICE_TYPE_802_11_WIRELESS:
aplist = self._access_points.values()
for ap in aplist:
if ap.get_nm_device() == nm_device:
self._remove_access_point(ap)
def _add_access_point(self, nm_device, nm_network):
model = AccessPointModel(nm_device, nm_network)
self._access_points[model.get_id()] = model
self.emit('access-point-added', model)
def _remove_access_point(self, ap):
if not self._access_points.has_key(ap.get_id()):
return
self.emit('access-point-removed', ap)
del self._access_points[ap.get_id()]
def get_mesh(self):
return self._mesh
def get_access_points(self):
return self._access_points.values()
def get_activities(self):
return self._activities.values()
def get_buddies(self):
return self._buddies.values()
def _buddy_activity_changed_cb(self, model, cur_activity):
if not self._buddies.has_key(model.get_key()):
return
if cur_activity and self._activities.has_key(cur_activity.props.id):
activity_model = self._activities[cur_activity.props.id]
self.emit('buddy-moved', model, activity_model)
else:
self.emit('buddy-moved', model, None)
def _buddy_appeared_cb(self, pservice, buddy):
if self._buddies.has_key(buddy.props.key):
return
model = BuddyModel(buddy=buddy)
model.connect('current-activity-changed',
self._buddy_activity_changed_cb)
self._buddies[buddy.props.key] = model
self.emit('buddy-added', model)
cur_activity = buddy.props.current_activity
if cur_activity:
self._buddy_activity_changed_cb(model, cur_activity)
def _buddy_disappeared_cb(self, pservice, buddy):
if not self._buddies.has_key(buddy.props.key):
return
self.emit('buddy-removed', self._buddies[buddy.props.key])
del self._buddies[buddy.props.key]
def _activity_appeared_cb(self, pservice, activity):
self._check_activity(activity)
def _check_activity(self, presence_activity):
registry = activity.get_registry()
bundle = registry.get_activity(presence_activity.props.type)
if not bundle:
return
if self.has_activity(presence_activity.props.id):
return
self.add_activity(bundle, presence_activity)
def has_activity(self, activity_id):
return self._activities.has_key(activity_id)
def get_activity(self, activity_id):
if self.has_activity(activity_id):
return self._activities[activity_id]
else:
return None
def add_activity(self, bundle, activity):
model = ActivityModel(activity, bundle)
self._activities[model.get_id()] = model
self.emit('activity-added', model)
for buddy in self._pservice.get_buddies():
cur_activity = buddy.props.current_activity
key = buddy.props.key
if cur_activity == activity and self._buddies.has_key(key):
buddy_model = self._buddies[key]
self.emit('buddy-moved', buddy_model, model)
def _activity_disappeared_cb(self, pservice, activity):
if self._activities.has_key(activity.props.id):
activity_model = self._activities[activity.props.id]
self.emit('activity-removed', activity_model)
del self._activities[activity.props.id]
-87
View File
@@ -1,87 +0,0 @@
# 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 gobject
import os
import random
import base64
import time
import logging
import dbus
from sugar import env
from sugar import profile
from sugar.presence import presenceservice
from sugar import util
from model.Invites import Invites
class ShellOwner(gobject.GObject):
__gtype_name__ = "ShellOwner"
__gsignals__ = {
'nick-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_STRING])),
'color-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'icon-changed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
"""Class representing the owner of this machine/instance. This class
runs in the shell and serves up the buddy icon and other stuff. It's the
server portion of the Owner, paired with the client portion in Buddy.py."""
def __init__(self):
gobject.GObject.__init__(self)
self._nick = profile.get_nick_name()
self._icon = None
self._icon_hash = ""
icon = os.path.join(env.get_profile_path(), "buddy-icon.jpg")
if not os.path.exists(icon):
raise RuntimeError("missing buddy icon")
fd = open(icon, "r")
self._icon = fd.read()
fd.close()
if not self._icon:
raise RuntimeError("invalid buddy icon")
# Get the icon's hash
import md5
digest = md5.new(self._icon).digest()
self._icon_hash = util.printable_hash(digest)
self._pservice = presenceservice.get_instance()
self._pservice.connect('activity-invitation',
self._activity_invitation_cb)
self._pservice.connect('activity-disappeared',
self._activity_disappeared_cb)
self._invites = Invites()
def get_invites(self):
return self._invites
def get_nick(self):
return self._nick
def _activity_invitation_cb(self, pservice, activity, buddy, message):
self._invites.add_invite(buddy, activity.props.type,
activity.props.id)
def _activity_disappeared_cb(self, pservice, activity):
self._invites.remove_activity(activity.props.id)
-16
View File
@@ -1,16 +0,0 @@
# 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
-81
View File
@@ -1,81 +0,0 @@
# 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 gobject
import sys
from hardware import nmclient
STATE_CONNECTING = 0
STATE_CONNECTED = 1
STATE_NOTCONNECTED = 2
_nm_state_to_state = {
nmclient.NETWORK_STATE_CONNECTED : STATE_CONNECTED,
nmclient.NETWORK_STATE_CONNECTING : STATE_CONNECTING,
nmclient.NETWORK_STATE_NOTCONNECTED : STATE_NOTCONNECTED
}
class AccessPointModel(gobject.GObject):
__gproperties__ = {
'name' : (str, None, None, None,
gobject.PARAM_READABLE),
'strength' : (int, None, None, 0, 100, 0,
gobject.PARAM_READABLE),
'state' : (int, None, None, STATE_CONNECTING,
STATE_NOTCONNECTED, 0, gobject.PARAM_READABLE),
'capabilities' : (int, None, None, 0, 0x7FFFFFFF, 0,
gobject.PARAM_READABLE),
'mode' : (int, None, None, 0, 6, 0, gobject.PARAM_READABLE)
}
def __init__(self, nm_device, nm_network):
gobject.GObject.__init__(self)
self._nm_network = nm_network
self._nm_device = nm_device
self._nm_network.connect('strength-changed',
self._strength_changed_cb)
self._nm_network.connect('state-changed',
self._state_changed_cb)
def _strength_changed_cb(self, nm_network):
self.notify('strength')
def _state_changed_cb(self, nm_network):
self.notify('state')
def get_id(self):
return self._nm_network.get_op()
def get_nm_device(self):
return self._nm_device
def get_nm_network(self):
return self._nm_network
def do_get_property(self, pspec):
if pspec.name == 'strength':
return self._nm_network.get_strength()
elif pspec.name == 'name':
return self._nm_network.get_ssid()
elif pspec.name == 'state':
nm_state = self._nm_network.get_state()
return _nm_state_to_state[nm_state]
elif pspec.name == 'capabilities':
return self._nm_network.get_caps()
elif pspec.name == 'mode':
return self._nm_network.get_mode()
-8
View File
@@ -1,8 +0,0 @@
SUBDIRS = network
sugardir = $(pkgdatadir)/shell/model/devices
sugar_PYTHON = \
__init__.py \
device.py \
devicesmodel.py \
battery.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-96
View File
@@ -1,96 +0,0 @@
# 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
import gobject
import dbus
from model.devices import device
_LEVEL_PROP = 'battery.charge_level.percentage'
_CHARGING_PROP = 'battery.rechargeable.is_charging'
_DISCHARGING_PROP = 'battery.rechargeable.is_discharging'
class Device(device.Device):
__gproperties__ = {
'level' : (int, None, None, 0, 100, 0,
gobject.PARAM_READABLE),
'charging' : (bool, None, None, False,
gobject.PARAM_READABLE),
'discharging' : (bool, None, None, False,
gobject.PARAM_READABLE)
}
def __init__(self, udi):
device.Device.__init__(self, udi)
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
proxy = bus.get_object('org.freedesktop.Hal', udi)
self._battery = dbus.Interface(proxy, 'org.freedesktop.Hal.Device')
bus.add_signal_receiver(self._battery_changed,
'PropertyModified',
'org.freedesktop.Hal.Device',
'org.freedesktop.Hal',
udi)
self._level = self._get_level()
self._charging = self._get_charging()
self._discharging = self._get_discharging()
def _get_level(self):
try:
return self._battery.GetProperty(_LEVEL_PROP)
except dbus.DBusException:
logging.error('Cannot access %s' % _LEVEL_PROP)
return 0
def _get_charging(self):
try:
return self._battery.GetProperty(_CHARGING_PROP)
except dbus.DBusException:
logging.error('Cannot access %s' % _CHARGING_PROP)
return False
def _get_discharging(self):
try:
return self._battery.GetProperty(_DISCHARGING_PROP)
except dbus.DBusException:
logging.error('Cannot access %s' % _DISCHARGING_PROP)
return False
def do_get_property(self, pspec):
if pspec.name == 'level':
return self._level
if pspec.name == 'charging':
return self._charging
if pspec.name == 'discharging':
return self._discharging
def get_type(self):
return 'battery'
def _battery_changed(self, num_changes, changes_list):
for change in changes_list:
if change[0] == _LEVEL_PROP:
self._level = self._get_level()
self.notify('level')
elif change[0] == _CHARGING_PROP:
self._charging = self._get_charging()
self.notify('charging')
elif change[0] == _DISCHARGING_PROP:
self._discharging = self._get_discharging()
self.notify('discharging')
-45
View File
@@ -1,45 +0,0 @@
#
# Copyright (C) 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 gobject
from hardware import nmclient
from sugar import util
STATE_ACTIVATING = 0
STATE_ACTIVATED = 1
STATE_INACTIVE = 2
_nm_state_to_state = {
nmclient.DEVICE_STATE_ACTIVATING : STATE_ACTIVATING,
nmclient.DEVICE_STATE_ACTIVATED : STATE_ACTIVATED,
nmclient.DEVICE_STATE_INACTIVE : STATE_INACTIVE
}
class Device(gobject.GObject):
def __init__(self, device_id=None):
gobject.GObject.__init__(self)
if device_id:
self._id = device_id
else:
self._id = util.unique_id()
def get_type(self):
return 'unknown'
def get_id(self):
return self._id
-136
View File
@@ -1,136 +0,0 @@
#
# Copyright (C) 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
import gobject
import dbus
from model.devices import device
from model.devices.network import wired
from model.devices.network import wireless
from model.devices.network import mesh
from model.devices import battery
from hardware import hardwaremanager
from hardware import nmclient
class DevicesModel(gobject.GObject):
__gsignals__ = {
'device-appeared' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'device-disappeared': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._devices = {}
self._sigids = {}
self._observe_hal_manager()
self._observe_network_manager()
def _observe_hal_manager(self):
bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
proxy = bus.get_object('org.freedesktop.Hal',
'/org/freedesktop/Hal/Manager')
hal_manager = dbus.Interface(proxy, 'org.freedesktop.Hal.Manager')
for udi in hal_manager.FindDeviceByCapability('battery'):
self.add_device(battery.Device(udi))
def _observe_network_manager(self):
network_manager = hardwaremanager.get_network_manager()
if not network_manager:
return
for device in network_manager.get_devices():
self._check_network_device(device)
network_manager.connect('device-added',
self._network_device_added_cb)
network_manager.connect('device-activating',
self._network_device_activating_cb)
network_manager.connect('device-activated',
self._network_device_activated_cb)
network_manager.connect('device-removed',
self._network_device_removed_cb)
def _network_device_added_cb(self, network_manager, nm_device):
state = nm_device.get_state()
if state == nmclient.DEVICE_STATE_ACTIVATING \
or state == nmclient.DEVICE_STATE_ACTIVATED:
self._check_network_device(nm_device)
def _network_device_activating_cb(self, network_manager, nm_device):
self._check_network_device(nm_device)
def _network_device_activated_cb(self, network_manager, nm_device):
pass
def _network_device_removed_cb(self, network_manager, nm_device):
if self._devices.has_key(str(nm_device.get_op())):
self.remove_device(self._get_network_device(nm_device))
def _check_network_device(self, nm_device):
if not nm_device.is_valid():
logging.debug("Device %s not valid" % nm_device.get_op())
return
dtype = nm_device.get_type()
if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS \
or dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
self._add_network_device(nm_device)
def _get_network_device(self, nm_device):
return self._devices[str(nm_device.get_op())]
def _network_device_state_changed_cb(self, dev, param):
if dev.props.state == device.STATE_INACTIVE:
self.remove_device(dev)
def _add_network_device(self, nm_device):
if self._devices.has_key(str(nm_device.get_op())):
logging.debug("Tried to add device %s twice" % nm_device.get_op())
return
dtype = nm_device.get_type()
if dtype == nmclient.DEVICE_TYPE_802_11_WIRELESS:
dev = wireless.Device(nm_device)
self.add_device(dev)
sigid = dev.connect('notify::state', self._network_device_state_changed_cb)
self._sigids[dev] = sigid
if dtype == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
dev = mesh.Device(nm_device)
self.add_device(dev)
sigid = dev.connect('notify::state', self._network_device_state_changed_cb)
self._sigids[dev] = sigid
def __iter__(self):
return iter(self._devices.values())
def add_device(self, device):
self._devices[device.get_id()] = device
self.emit('device-appeared', device)
def remove_device(self, device):
self.emit('device-disappeared', self._devices[device.get_id()])
device.disconnect(self._sigids[device])
del self._sigids[device]
del self._devices[device.get_id()]
-6
View File
@@ -1,6 +0,0 @@
sugardir = $(pkgdatadir)/shell/model/devices/network
sugar_PYTHON = \
__init__.py \
mesh.py \
wired.py \
wireless.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-75
View File
@@ -1,75 +0,0 @@
#
# 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 gobject
from model.devices import device
from hardware import nmclient
class Device(device.Device):
__gproperties__ = {
'strength' : (int, None, None, 0, 100, 0,
gobject.PARAM_READABLE),
'state' : (int, None, None, device.STATE_ACTIVATING,
device.STATE_INACTIVE, 0, gobject.PARAM_READABLE),
'activation-stage': (int, None, None, 0, 7, 0, gobject.PARAM_READABLE),
'frequency': (float, None, None, 0, 2.72, 0, gobject.PARAM_READABLE),
'mesh-step': (int, None, None, 0, 4, 0, gobject.PARAM_READABLE),
}
def __init__(self, nm_device):
device.Device.__init__(self)
self._nm_device = nm_device
self._nm_device.connect('strength-changed',
self._strength_changed_cb)
self._nm_device.connect('state-changed',
self._state_changed_cb)
self._nm_device.connect('activation-stage-changed',
self._activation_stage_changed_cb)
def _strength_changed_cb(self, nm_device):
self.notify('strength')
def _state_changed_cb(self, nm_device):
self.notify('state')
def _activation_stage_changed_cb(self, nm_device):
self.notify('activation-stage')
def do_get_property(self, pspec):
if pspec.name == 'strength':
return self._nm_device.get_strength()
elif pspec.name == 'state':
nm_state = self._nm_device.get_state()
return device._nm_state_to_state[nm_state]
elif pspec.name == 'activation-stage':
return self._nm_device.get_activation_stage()
elif pspec.name == 'frequency':
return self._nm_device.get_frequency()
elif pspec.name == 'mesh-step':
return self._nm_device.get_mesh_step()
def get_type(self):
return 'network.mesh'
def get_id(self):
return str(self._nm_device.get_op())
def get_nm_device(self):
return self._nm_device
-28
View File
@@ -1,28 +0,0 @@
# 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
from model.devices import device
class Device(device.Device):
def __init__(self, nm_device):
device.Device.__init__(self)
self._nm_device = device
def get_id(self):
return str(self._nm_device.get_op())
def get_type(self):
return 'network.wired'
-96
View File
@@ -1,96 +0,0 @@
#
# 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 gobject
from model.devices import device
from hardware import nmclient
def freq_to_channel(freq):
ftoc = { 2.412: 1, 2.417: 2, 2.422: 3, 2.427: 4,
2.432: 5, 2.437: 6, 2.442: 7, 2.447: 8,
2.452: 9, 2.457: 10, 2.462: 11, 2.467: 12,
2.472: 13
}
return ftoc[freq]
def channel_to_freq(channel):
ctof = { 1: 2.412, 2: 2.417, 3: 2.422, 4: 2.427,
5: 2.432, 6: 2.437, 7: 2.442, 8: 2.447,
9: 2.452, 10: 2.457, 11: 2.462, 12: 2.467,
13: 2.472
}
return ctof[channel]
class Device(device.Device):
__gproperties__ = {
'name' : (str, None, None, None,
gobject.PARAM_READABLE),
'strength' : (int, None, None, 0, 100, 0,
gobject.PARAM_READABLE),
'state' : (int, None, None, device.STATE_ACTIVATING,
device.STATE_INACTIVE, 0, gobject.PARAM_READABLE),
'frequency': (float, None, None, 0.0, 9999.99, 0.0,
gobject.PARAM_READABLE)
}
def __init__(self, nm_device):
device.Device.__init__(self)
self._nm_device = nm_device
self._nm_device.connect('strength-changed',
self._strength_changed_cb)
self._nm_device.connect('ssid-changed',
self._ssid_changed_cb)
self._nm_device.connect('state-changed',
self._state_changed_cb)
def _strength_changed_cb(self, nm_device):
self.notify('strength')
def _ssid_changed_cb(self, nm_device):
self.notify('name')
def _state_changed_cb(self, nm_device):
self.notify('state')
def do_get_property(self, pspec):
if pspec.name == 'strength':
return self._nm_device.get_strength()
elif pspec.name == 'name':
import logging
logging.debug('wireless.Device.props.name: %s' % self._nm_device.get_ssid())
return self._nm_device.get_ssid()
elif pspec.name == 'state':
nm_state = self._nm_device.get_state()
return device._nm_state_to_state[nm_state]
elif pspec.name == 'frequency':
return self._nm_device.get_frequency()
def get_type(self):
return 'network.wireless'
def get_id(self):
return str(self._nm_device.get_op())
def get_active_network_colors(self):
net = self._nm_device.get_active_network()
if not net:
return (None, None)
return net.get_colors()
-215
View File
@@ -1,215 +0,0 @@
# Copyright (C) 2006-2007 Owen Williams.
#
# 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 time
import logging
import gobject
import dbus
from sugar.graphics.xocolor import XoColor
from sugar.presence import presenceservice
from sugar import profile
_SERVICE_NAME = "org.laptop.Activity"
_SERVICE_PATH = "/org/laptop/Activity"
_SERVICE_INTERFACE = "org.laptop.Activity"
class HomeActivity(gobject.GObject):
"""Activity which appears in the "Home View" of the Sugar shell
This class stores the Sugar Shell's metadata regarding a
given activity/application in the system. It interacts with
the sugar.activity.* modules extensively in order to
accomplish its tasks.
"""
__gtype_name__ = 'SugarHomeActivity'
__gproperties__ = {
'launching' : (bool, None, None, False,
gobject.PARAM_READWRITE),
}
def __init__(self, activity_info, activity_id):
"""Initialise the HomeActivity
activity_info -- sugar.activity.registry.ActivityInfo instance,
provides the information required to actually
create the new instance. This is, in effect,
the "type" of activity being created.
activity_id -- unique identifier for this instance
of the activity type
"""
gobject.GObject.__init__(self)
self._window = None
self._xid = None
self._pid = None
self._service = None
self._activity_id = activity_id
self._activity_info = activity_info
self._launch_time = time.time()
self._launching = False
self._retrieve_service()
if not self._service:
bus = dbus.SessionBus()
bus.add_signal_receiver(self._name_owner_changed_cb,
signal_name="NameOwnerChanged",
dbus_interface="org.freedesktop.DBus")
def set_window(self, window):
"""An activity is 'launched' once we get its window."""
if self._window or self._xid:
raise RuntimeError("Activity is already launched!")
if not window:
raise ValueError("window must be valid")
self._window = window
self._xid = window.get_xid()
self._pid = window.get_pid()
def get_service(self):
"""Get the activity service
Note that non-native Sugar applications will not have
such a service, so the return value will be None in
those cases.
"""
return self._service
def get_title(self):
"""Retrieve the application's root window's suggested title"""
if self._window:
return self._window.get_name()
else:
return ''
def get_icon_path(self):
"""Retrieve the activity's icon (file) name"""
if self._activity_info:
return self._activity_info.icon
else:
return None
def get_icon_color(self):
"""Retrieve the appropriate icon colour for this activity
Uses activity_id to index into the PresenceService's
set of activity colours, if the PresenceService does not
have an entry (implying that this is not a Sugar-shared application)
uses the local user's profile.get_color() to determine the
colour for the icon.
"""
pservice = presenceservice.get_instance()
# HACK to suppress warning in logs when activity isn't found
# (if it's locally launched and not shared yet)
activity = None
for act in pservice.get_activities():
if self._activity_id == act.props.id:
activity = act
break
if activity != None:
return XoColor(activity.props.color)
else:
return profile.get_color()
def get_activity_id(self):
"""Retrieve the "activity_id" passed in to our constructor
This is a "globally likely unique" identifier generated by
sugar.util.unique_id
"""
return self._activity_id
def get_xid(self):
"""Retrieve the X-windows ID of our root window"""
return self._xid
def get_window(self):
"""Retrieve the X-windows root window of this application
This was stored by the set_window method, which was
called by HomeModel._add_activity, which was called
via a callback that looks for all 'window-opened'
events.
HomeModel currently uses a dbus service query on the
activity to determine to which HomeActivity the newly
launched window belongs.
"""
return self._window
def get_type(self):
"""Retrieve the activity bundle id for future reference"""
if self._activity_info:
return self._activity_info.bundle_id
else:
return None
def get_launch_time(self):
"""Return the time at which the activity was first launched
Format is floating-point time.time() value
(seconds since the epoch)
"""
return self._launch_time
def get_pid(self):
"""Returns the activity's PID"""
return self._pid
def equals(self, activity):
if self._activity_id and activity.get_activity_id():
return self._activity_id == activity.get_activity_id()
if self._xid and activity.get_xid():
return self._xid == activity.get_xid()
return False
def do_set_property(self, pspec, value):
if pspec.name == 'launching':
self._launching = value
def do_get_property(self, pspec):
if pspec.name == 'launching':
return self._launching
def _get_service_name(self):
if self._activity_id:
return _SERVICE_NAME + self._activity_id
else:
return None
def _retrieve_service(self):
if not self._activity_id:
return
try:
bus = dbus.SessionBus()
proxy = bus.get_object(self._get_service_name(),
_SERVICE_PATH + "/" + self._activity_id)
self._service = dbus.Interface(proxy, _SERVICE_INTERFACE)
except dbus.DBusException:
self._service = None
def _name_owner_changed_cb(self, name, old, new):
if name == self._get_service_name():
self._retrieve_service()
-283
View File
@@ -1,283 +0,0 @@
# Copyright (C) 2006-2007 Owen Williams.
#
# 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 wnck
import dbus
from sugar import wm
from sugar import activity
from model.homeactivity import HomeActivity
class HomeModel(gobject.GObject):
"""Model of the "Home" view (activity management)
The HomeModel is basically the point of registration
for all running activities within Sugar. It traps
events that tell the system there is a new activity
being created (generated by the activity factories),
or removed, as well as those which tell us that the
currently focussed activity has changed.
The HomeModel tracks a set of HomeActivity instances,
which are tracking the window to activity mappings
the activity factories have set up.
"""
__gsignals__ = {
'activity-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'activity-started': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'activity-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'active-activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'pending-activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._activities = []
self._active_activity = None
self._pending_activity = None
screen = wnck.screen_get_default()
screen.connect('window-opened', self._window_opened_cb)
screen.connect('window-closed', self._window_closed_cb)
screen.connect('active-window-changed',
self._active_window_changed_cb)
def _get_activities_with_window(self):
ret = []
for i in self._activities:
if i.get_window() is not None:
ret.append(i)
return ret
def get_previous_activity(self):
activities = self._get_activities_with_window()
i = activities.index(self._pending_activity)
if len(activities) == 0:
return None
elif i - 1 >= 0:
return activities[i - 1]
else:
return activities[len(activities) - 1]
def get_next_activity(self):
activities = self._get_activities_with_window()
i = activities.index(self._pending_activity)
if len(activities) == 0:
return None
elif i + 1 < len(activities):
return activities[i + 1]
else:
return activities[0]
def get_pending_activity(self):
"""Returns the activity that would be seen in the Activity zoom level
In the Home (or Neighborhood or Groups) zoom level, this
indicates the activity that would become active if the user
switched to the Activity zoom level. (In the Activity zoom
level, this just returns the currently-active activity.)
Unlike get_active_activity(), this never returns None as long
as there is any activity running.
"""
return self._pending_activity
def _set_pending_activity(self, home_activity):
if self._pending_activity == home_activity:
return
self._pending_activity = home_activity
self.emit('pending-activity-changed', self._pending_activity)
def get_active_activity(self):
"""Returns the activity that the user is currently working in
In the Activity zoom level, this returns the currently-active
activity. In the other zoom levels, it returns the activity
that was most-recently active in the Activity zoom level, or
None if the most-recently-active activity is no longer
running.
"""
return self._active_activity
def _set_active_activity(self, home_activity):
if self._active_activity == home_activity:
return
if self._active_activity:
service = self._active_activity.get_service()
if service:
service.SetActive(False,
reply_handler=self._set_active_success,
error_handler=self._set_active_error)
if home_activity:
service = home_activity.get_service()
if service:
service.SetActive(True,
reply_handler=self._set_active_success,
error_handler=self._set_active_error)
self._active_activity = home_activity
self.emit('active-activity-changed', self._active_activity)
def __iter__(self):
return iter(self._activities)
def __len__(self):
return len(self._activities)
def __getitem__(self, i):
return self._activities[i]
def index(self, obj):
return self._activities.index(obj)
def _window_opened_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
home_activity = None
activity_id = wm.get_activity_id(window)
service_name = wm.get_bundle_id(window)
if service_name:
registry = activity.get_registry()
activity_info = registry.get_activity(service_name)
else:
activity_info = None
if activity_id:
home_activity = self._get_activity_by_id(activity_id)
if not home_activity:
home_activity = HomeActivity(activity_info, activity_id)
self._add_activity(home_activity)
home_activity.set_window(window)
home_activity.props.launching = False
self.emit('activity-started', home_activity)
if self._pending_activity is None:
self._set_pending_activity(home_activity)
def _window_closed_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
self._remove_activity_by_xid(window.get_xid())
def _get_activity_by_xid(self, xid):
for home_activity in self._activities:
if home_activity.get_xid() == xid:
return home_activity
return None
def _get_activity_by_id(self, activity_id):
for home_activity in self._activities:
if home_activity.get_activity_id() == activity_id:
return home_activity
return None
def _set_active_success(self):
pass
def _set_active_error(self, err):
logging.error("set_active() failed: %s" % err)
def _active_window_changed_cb(self, screen, previous_window=None):
window = screen.get_active_window()
if window is None:
return
if window.get_window_type() != wnck.WINDOW_DIALOG:
while window.get_transient() is not None:
window = window.get_transient()
activity = self._get_activity_by_xid(window.get_xid())
if activity is not None:
self._set_pending_activity(activity)
self._set_active_activity(activity)
def _add_activity(self, home_activity):
self._activities.append(home_activity)
self.emit('activity-added', home_activity)
def _remove_activity(self, home_activity):
if home_activity == self._active_activity:
self._set_active_activity(None)
if home_activity == self._pending_activity:
# Figure out the new _pending_activity
windows = wnck.screen_get_default().get_windows_stacked()
windows.reverse()
for window in windows:
new_activity = self._get_activity_by_xid(window.get_xid())
if new_activity is not None:
self._set_pending_activity(new_activity)
break
else:
logging.error('No activities are running')
self._set_pending_activity(None)
self.emit('activity-removed', home_activity)
self._activities.remove(home_activity)
def _remove_activity_by_xid(self, xid):
home_activity = self._get_activity_by_xid(xid)
if home_activity:
self._remove_activity(home_activity)
else:
logging.error('Model for window %d does not exist.' % xid)
def notify_activity_launch(self, activity_id, service_name):
registry = activity.get_registry()
activity_info = registry.get_activity(service_name)
if not activity_info:
raise ValueError("Activity service name '%s' was not found in the bundle registry." % service_name)
home_activity = HomeActivity(activity_info, activity_id)
home_activity.props.launching = True
self._add_activity(home_activity)
# FIXME: better learn about finishing processes by receiving a signal.
# Now just check whether an activity has a window after ~90sec
gobject.timeout_add(90000, self._check_activity_launched, activity_id)
def notify_activity_launch_failed(self, activity_id):
home_activity = self._get_activity_by_id(activity_id)
if home_activity:
logging.debug("Activity %s (%s) launch failed" % (activity_id, home_activity.get_type()))
self._remove_activity(home_activity)
else:
logging.error('Model for activity id %s does not exist.' % activity_id)
def _check_activity_launched(self, activity_id):
home_activity = self._get_activity_by_id(activity_id)
if home_activity and home_activity.props.launching:
logging.debug('Activity %s still launching, assuming it failed...', activity_id)
self.notify_activity_launch_failed(activity_id)
return False
-112
View File
@@ -1,112 +0,0 @@
# 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 os
import wnck
import gobject
from sugar.presence import presenceservice
from model.Friends import Friends
from model.MeshModel import MeshModel
from model.homemodel import HomeModel
from model.Owner import ShellOwner
from model.devices.devicesmodel import DevicesModel
from sugar import env
class ShellModel(gobject.GObject):
STATE_STARTUP = 0
STATE_RUNNING = 1
STATE_SHUTDOWN = 2
ZOOM_MESH = 0
ZOOM_FRIENDS = 1
ZOOM_HOME = 2
ZOOM_ACTIVITY = 3
__gproperties__ = {
'state' : (int, None, None,
0, 2, STATE_RUNNING,
gobject.PARAM_READWRITE),
'zoom-level' : (int, None, None,
0, 3, ZOOM_HOME,
gobject.PARAM_READABLE)
}
def __init__(self):
gobject.GObject.__init__(self)
self._current_activity = None
self._state = self.STATE_RUNNING
self._zoom_level = self.ZOOM_HOME
self._showing_desktop = True
self._pservice = presenceservice.get_instance()
self._owner = ShellOwner()
self._friends = Friends()
self._mesh = MeshModel()
self._home = HomeModel()
self._devices = DevicesModel()
self._screen = wnck.screen_get_default()
self._screen.connect('showing-desktop-changed',
self._showing_desktop_changed_cb)
def set_zoom_level(self, level):
self._zoom_level = level
self.notify('zoom-level')
def get_zoom_level(self):
if self._screen.get_showing_desktop():
return self._zoom_level
else:
return self.ZOOM_ACTIVITY
def do_set_property(self, pspec, value):
if pspec.name == 'state':
self._state = value
def do_get_property(self, pspec):
if pspec.name == 'state':
return self._state
elif pspec.name == 'zoom-level':
return self.get_zoom_level()
def get_mesh(self):
return self._mesh
def get_friends(self):
return self._friends
def get_invites(self):
return self._owner.get_invites()
def get_home(self):
return self._home
def get_owner(self):
return self._owner
def get_devices(self):
return self._devices
def _showing_desktop_changed_cb(self, screen):
showing_desktop = self._screen.get_showing_desktop()
if self._showing_desktop != showing_desktop:
self._showing_desktop = showing_desktop
self.notify('zoom-level')
-130
View File
@@ -1,130 +0,0 @@
# 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
"""D-bus service providing access to the shell's functionality"""
import dbus
import os
_DBUS_SERVICE = "org.laptop.Shell"
_DBUS_SHELL_IFACE = "org.laptop.Shell"
_DBUS_OWNER_IFACE = "org.laptop.Shell.Owner"
_DBUS_PATH = "/org/laptop/Shell"
_DBUS_RAINBOW_IFACE = "org.laptop.security.Rainbow"
class ShellService(dbus.service.Object):
"""Provides d-bus service to script the shell's operations
Uses a shell_model object to observe events such as changes to:
* nickname
* colour
* icon
* currently active activity
and pass the event off to the methods in the dbus signature.
Key method here at the moment is add_bundle, which is used to
do a run-time registration of a bundle using it's application path.
XXX At the moment the d-bus service methods do not appear to do
anything other than add_bundle
"""
_rainbow = None
def __init__(self, shell):
self._shell = shell
self._shell_model = shell.get_model()
self._owner = self._shell_model.get_owner()
self._owner.connect('nick-changed', self._owner_nick_changed_cb)
self._owner.connect('icon-changed', self._owner_icon_changed_cb)
self._owner.connect('color-changed', self._owner_color_changed_cb)
self._home_model = self._shell_model.get_home()
self._home_model.connect('active-activity-changed',
self._cur_activity_changed_cb)
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(_DBUS_SERVICE, bus=bus)
dbus.service.Object.__init__(self, bus_name, _DBUS_PATH)
@dbus.service.method(_DBUS_SHELL_IFACE,
in_signature="s", out_signature="b")
def ActivateActivity(self, activity_id):
host = self._shell.get_activity(activity_id)
if host:
host.present()
return True
return False
@dbus.service.method(_DBUS_SHELL_IFACE,
in_signature="ss", out_signature="")
def NotifyLaunch(self, bundle_id, activity_id):
self._shell.notify_launch(bundle_id, activity_id)
@dbus.service.method(_DBUS_SHELL_IFACE,
in_signature="s", out_signature="")
def NotifyLaunchFailure(self, activity_id):
self._shell.notify_launch_failure(activity_id)
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
def ColorChanged(self, color):
pass
def _owner_color_changed_cb(self, new_color):
self.ColorChanged(new_color.to_string())
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
def NickChanged(self, nick):
pass
def _owner_nick_changed_cb(self, new_nick):
self.NickChanged(new_nick)
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="ay")
def IconChanged(self, icon_data):
pass
def _owner_icon_changed_cb(self, new_icon):
self.IconChanged(dbus.ByteArray(new_icon))
def _get_rainbow_service(self):
"""Lazily initializes an interface to the Rainbow security daemon."""
if self._rainbow is None:
system_bus = dbus.SystemBus()
object = system_bus.get_object(_DBUS_RAINBOW_IFACE, '/',
follow_name_owner_changes=True)
self._rainbow = dbus.Interface(object,
dbus_interface=_DBUS_RAINBOW_IFACE)
return self._rainbow
@dbus.service.signal(_DBUS_OWNER_IFACE, signature="s")
def CurrentActivityChanged(self, activity_id):
if os.path.exists('/etc/olpc-security'):
self._get_rainbow_service().ChangeActivity(
activity_id,
dbus_interface=_DBUS_RAINBOW_IFACE)
def _cur_activity_changed_cb(self, owner, new_activity):
new_id = ""
if new_activity:
new_id = new_activity.get_activity_id()
if new_id:
self.CurrentActivityChanged(new_id)
-118
View File
@@ -1,118 +0,0 @@
# 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 gtk
import dbus
import logging
from sugar.presence import presenceservice
import OverlayWindow
class ActivityChatWindow(gtk.Window):
def __init__(self, gdk_window, chat_widget):
gtk.Window.__init__(self)
self.realize()
self.set_decorated(False)
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.window.set_accept_focus(True)
self.window.set_transient_for(gdk_window)
self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
self.set_default_size(600, 450)
self.add(chat_widget)
class ActivityHost:
def __init__(self, model):
self._model = model
self._window = model.get_window()
self._gdk_window = gtk.gdk.window_foreign_new(self.get_xid())
try:
self._overlay_window = OverlayWindow.OverlayWindow(self._gdk_window)
win = self._overlay_window.window
except RuntimeError:
self._overlay_window = None
win = self._gdk_window
#self._chat_widget = ActivityChat.ActivityChat(self)
self._chat_widget = gtk.HBox()
self._chat_window = ActivityChatWindow(win, self._chat_widget)
self._frame_was_visible = False
def get_id(self):
return self._model.get_activity_id()
def get_xid(self):
return self._window.get_xid()
def get_model(self):
return self._model
def invite(self, buddy_model):
service = self._model.get_service()
if service:
buddy = buddy_model.get_buddy()
service.Invite(buddy.props.key)
else:
logging.error('Invite failed, activity service not ')
def toggle_fullscreen(self):
fullscreen = not self._window.is_fullscreen()
self._window.set_fullscreen(fullscreen)
def present(self):
# wnck.Window.activate() expects a timestamp, but we don't
# always have one, and libwnck will complain if we pass "0",
# and matchbox doesn't look at the timestamp anyway. So we
# just always pass "1".
self._window.activate(1)
def close(self):
# The "1" is a fake timestamp as with present()
self._window.close(1)
def show_dialog(self, dialog):
dialog.show()
dialog.window.set_transient_for(self._gdk_window)
def chat_show(self, frame_was_visible):
if self._overlay_window:
self._overlay_window.appear()
self._chat_window.show_all()
self._frame_was_visible = frame_was_visible
def chat_hide(self):
self._chat_window.hide()
if self._overlay_window:
self._overlay_window.disappear()
wasvis = self._frame_was_visible
self._frame_was_visible = False
return wasvis
def is_chat_visible(self):
return self._chat_window.get_property('visible')
def set_active(self, active):
if not active:
self.chat_hide()
self._frame_was_visible = False
def destroy(self):
self._chat_window.destroy()
self._frame_was_visible = False
-54
View File
@@ -1,54 +0,0 @@
# 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
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.palette import Palette
from sugar.graphics import style
from view.BuddyMenu import BuddyMenu
class BuddyIcon(CanvasIcon):
def __init__(self, shell, buddy, size=style.STANDARD_ICON_SIZE):
CanvasIcon.__init__(self, icon_name='computer-xo', size=size)
self._greyed_out = False
self._shell = shell
self._buddy = buddy
self._buddy.connect('appeared', self._buddy_presence_change_cb)
self._buddy.connect('disappeared', self._buddy_presence_change_cb)
self._buddy.connect('color-changed', self._buddy_presence_change_cb)
palette = BuddyMenu(shell, buddy)
self.set_palette(palette)
self._update_color()
def _buddy_presence_change_cb(self, buddy, color=None):
# Update the icon's color when the buddy comes and goes
self._update_color()
def _update_color(self):
if self._greyed_out:
self.props.stroke_color = '#D5D5D5'
self.props.fill_color = '#E5E5E5'
else:
self.props.xo_color = self._buddy.get_color()
def set_filter(self, query):
self._greyed_out = (self._buddy.get_nick().lower().find(query) == -1) \
and not self._buddy.is_owner()
self._update_color()
-113
View File
@@ -1,113 +0,0 @@
# 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
from gettext import gettext as _
import logging
import gobject
import hippo
from sugar.graphics.palette import Palette
from sugar.graphics.menuitem import MenuItem
from sugar.graphics.icon import Icon
from sugar.presence import presenceservice
class BuddyMenu(Palette):
def __init__(self, shell, buddy):
self._buddy = buddy
self._shell = shell
Palette.__init__(self, buddy.get_nick())
self._active_activity_changed_hid = None
self.connect('destroy', self.__destroy_cb)
self._buddy.connect('icon-changed', self._buddy_icon_changed_cb)
self._buddy.connect('nick-changed', self._buddy_nick_changed_cb)
owner = self._get_shell_model().get_owner()
if not buddy.is_owner():
self._add_items()
def _get_shell_model(self):
return self._shell.get_model()
def _get_home_model(self):
return self._get_shell_model().get_home()
def __destroy_cb(self, menu):
if self._active_activity_changed_hid is not None:
home_model = self._get_home_model()
home_model.disconnect(self._active_activity_changed_hid)
def _add_items(self):
pservice = presenceservice.get_instance()
friends = self._get_shell_model().get_friends()
if friends.has_buddy(self._buddy):
menu_item = MenuItem(_('Remove friend'), 'list-remove')
menu_item.connect('activate', self._remove_friend_cb)
else:
menu_item = MenuItem(_('Make friend'), 'list-add')
menu_item.connect('activate', self._make_friend_cb)
self.menu.append(menu_item)
menu_item.show()
self._invite_menu = MenuItem('')
self._invite_menu.connect('activate', self._invite_friend_cb)
self.menu.append(self._invite_menu)
home_model = self._get_home_model()
self._active_activity_changed_hid = home_model.connect(
'active-activity-changed', self._cur_activity_changed_cb)
activity = home_model.get_active_activity()
self._update_invite_menu(activity)
def _update_invite_menu(self, activity):
if activity is None:
self._invite_menu.hide()
else:
title = activity.get_title()
label = self._invite_menu.get_children()[0]
label.set_text(_('Invite to %s') % title)
icon = Icon(file=activity.get_icon_path())
icon.props.xo_color = activity.get_icon_color()
self._invite_menu.set_image(icon)
icon.show()
self._invite_menu.show()
def _cur_activity_changed_cb(self, home_model, activity_model):
self._update_invite_menu(activity_model)
def _buddy_icon_changed_cb(self, buddy):
pass
def _buddy_nick_changed_cb(self, buddy, nick):
self.set_primary_text(nick)
def _make_friend_cb(self, menuitem):
friends = self._get_shell_model().get_friends()
friends.make_friend(self._buddy)
def _remove_friend_cb(self, menuitem):
friends = self._get_shell_model().get_friends()
friends.remove(self._buddy)
def _invite_friend_cb(self, menuitem):
activity = self._shell.get_current_activity()
activity.invite(self._buddy)
-14
View File
@@ -1,14 +0,0 @@
SUBDIRS = devices frame home
sugardir = $(pkgdatadir)/shell/view
sugar_PYTHON = \
__init__.py \
ActivityHost.py \
BuddyIcon.py \
BuddyMenu.py \
clipboardicon.py \
clipboardmenu.py \
keyhandler.py \
pulsingicon.py \
OverlayWindow.py \
Shell.py
-68
View File
@@ -1,68 +0,0 @@
# 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 gtk
import cairo
def _grab_pixbuf(window=None):
if not window:
screen = gtk.gdk.screen_get_default()
window = screen.get_root_window()
color_map = gtk.gdk.colormap_get_system()
(x, y, w, h, bpp) = window.get_geometry()
return gtk.gdk.pixbuf_get_from_drawable(None, window, color_map, x, y, 0, 0, w, h)
class OverlayWindow(gtk.Window):
def __init__(self, lower_window):
gtk.Window.__init__(self)
self._lower_window = lower_window
self._img = gtk.Image()
self.add(self._img)
self.realize()
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.window.set_accept_focus(False)
self.window.set_transient_for(lower_window)
self.set_decorated(False)
self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
self.set_default_size(gtk.gdk.screen_width(), gtk.gdk.screen_height())
self.set_app_paintable(True)
# self.connect('expose-event', self._expose_cb)
def appear(self):
pbuf = _grab_pixbuf(self._lower_window)
#pbuf.saturate_and_pixelate(pbuf, 0.5, False)
w = pbuf.get_width()
h = pbuf.get_height()
pbuf2 = pbuf.composite_color_simple(w, h, gtk.gdk.INTERP_NEAREST, 100, 1024, 0, 0)
self._img.set_from_pixbuf(pbuf2)
self.show_all()
def disappear(self):
self._img.set_from_pixbuf(None)
self.hide()
def _expose_cb(self, widget, event):
cr = widget.window.cairo_create()
cr.set_source_rgba(0.0, 0.0, 0.0, 0.4) # Transparent
cr.set_operator(cairo.OPERATOR_SOURCE)
cr.paint()
return False
-298
View File
@@ -1,298 +0,0 @@
# 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
from gettext import gettext as _
from sets import Set
import logging
import tempfile
import os
import time
import shutil
import gobject
import gtk
import wnck
import dbus
from sugar.activity.activityhandle import ActivityHandle
from sugar import activity
from sugar.activity import activityfactory
from sugar.datastore import datastore
from sugar import profile
from sugar import env
from view.ActivityHost import ActivityHost
from view.frame.frame import Frame
from view.keyhandler import KeyHandler
from view.home.HomeWindow import HomeWindow
from model.shellmodel import ShellModel
# #3903 - this constant can be removed and assumed to be 1 when dbus-python
# 0.82.3 is the only version used
if dbus.version >= (0, 82, 3):
DBUS_PYTHON_TIMEOUT_UNITS_PER_SECOND = 1
else:
DBUS_PYTHON_TIMEOUT_UNITS_PER_SECOND = 1000
class Shell(gobject.GObject):
def __init__(self, model):
gobject.GObject.__init__(self)
self._activities_starting = Set()
self._model = model
self._hosts = {}
self._screen = wnck.screen_get_default()
self._current_host = None
self._pending_host = None
self._screen_rotation = 0
self._zoom_level = ShellModel.ZOOM_HOME
self._key_handler = KeyHandler(self)
self._frame = Frame(self)
self._home_window = HomeWindow(self)
self._home_window.show()
home_model = self._model.get_home()
home_model.connect('activity-started', self._activity_started_cb)
home_model.connect('activity-removed', self._activity_removed_cb)
home_model.connect('active-activity-changed',
self._active_activity_changed_cb)
home_model.connect('pending-activity-changed',
self._pending_activity_changed_cb)
self._model.connect('notify::zoom-level', self._zoom_level_changed_cb)
gobject.idle_add(self._start_journal_idle)
def _start_journal_idle(self):
# Mount the datastore in internal flash
ds_path = env.get_profile_path('datastore')
try:
datastore.mount(ds_path, [], timeout=120 * \
DBUS_PYTHON_TIMEOUT_UNITS_PER_SECOND)
except:
# Don't explode if there's corruption; move the data out of the way
# and attempt to create a store from scratch.
shutil.move(ds_path, os.path.abspath(ds_path) + str(time.time()))
datastore.mount(ds_path, [], timeout=120 * \
DBUS_PYTHON_TIMEOUT_UNITS_PER_SECOND)
# Checking for the bundle existence will also ensure
# that the shell service is started up.
registry = activity.get_registry()
if registry.get_activity('org.laptop.JournalActivity'):
self.start_activity('org.laptop.JournalActivity')
def _activity_started_cb(self, home_model, home_activity):
activity_host = ActivityHost(home_activity)
self._hosts[activity_host.get_xid()] = activity_host
if home_activity.get_type() in self._activities_starting:
self._activities_starting.remove(home_activity.get_type())
def _activity_removed_cb(self, home_model, home_activity):
if home_activity.get_type() in self._activities_starting:
self._activities_starting.remove(home_activity.get_type())
xid = home_activity.get_xid()
if self._hosts.has_key(xid):
self._hosts[xid].destroy()
del self._hosts[xid]
def _active_activity_changed_cb(self, home_model, home_activity):
if home_activity:
host = self._hosts[home_activity.get_xid()]
else:
host = None
if self._current_host:
self._current_host.set_active(False)
self._current_host = host
def _pending_activity_changed_cb(self, home_model, home_activity):
if home_activity:
self._pending_host = self._hosts[home_activity.get_xid()]
else:
self._pending_host = None
def get_model(self):
return self._model
def get_frame(self):
return self._frame
def join_activity(self, bundle_id, activity_id):
activity_host = self.get_activity(activity_id)
if activity_host:
activity_host.present()
return
# Get the service name for this activity, if
# we have a bundle on the system capable of handling
# this activity type
registry = activity.get_registry()
bundle = registry.get_activity(bundle_id)
if not bundle:
logging.error("Couldn't find activity for type %s" % bundle_id)
return
handle = ActivityHandle(activity_id)
activityfactory.create(bundle_id, handle)
def notify_launch(self, bundle_id, activity_id):
# Zoom to Home for launch feedback
self.set_zoom_level(ShellModel.ZOOM_HOME)
home_model = self._model.get_home()
home_model.notify_activity_launch(activity_id, bundle_id)
def notify_launch_failure(self, activity_id):
home_model = self._model.get_home()
home_model.notify_activity_launch_failed(activity_id)
def start_activity(self, activity_type):
if activity_type in self._activities_starting:
logging.debug("This activity is still launching.")
return
self._activities_starting.add(activity_type)
activityfactory.create(activity_type)
def take_activity_screenshot(self):
if self._model.get_zoom_level() != ShellModel.ZOOM_ACTIVITY:
return
if self.get_frame().visible:
return
home_model = self._model.get_home()
activity = home_model.get_active_activity()
if activity is not None:
service = activity.get_service()
if service is not None:
try:
service.TakeScreenshot(timeout=2.0)
except dbus.DBusException, e:
logging.debug('Error raised by TakeScreenshot(): %s', e)
def set_zoom_level(self, level):
if level == self._zoom_level:
return
self.take_activity_screenshot()
if level == ShellModel.ZOOM_ACTIVITY:
if self._pending_host is not None:
self._pending_host.present()
self._screen.toggle_showing_desktop(False)
else:
self._model.set_zoom_level(level)
self._screen.toggle_showing_desktop(True)
self._home_window.set_zoom_level(level)
def _zoom_level_changed_cb(self, model, pspec):
new_level = model.props.zoom_level
if new_level == ShellModel.ZOOM_HOME:
self._frame.show(Frame.MODE_NON_INTERACTIVE)
if self._zoom_level == ShellModel.ZOOM_HOME:
self._frame.hide()
self._zoom_level = new_level
def toggle_activity_fullscreen(self):
if self._model.get_zoom_level() == ShellModel.ZOOM_ACTIVITY:
self.get_current_activity().toggle_fullscreen()
def activate_previous_activity(self):
home_model = self._model.get_home()
activity = home_model.get_previous_activity()
if activity:
self.take_activity_screenshot()
activity.get_window().activate(1)
def activate_next_activity(self):
home_model = self._model.get_home()
activity = home_model.get_next_activity()
if activity:
self.take_activity_screenshot()
activity.get_window().activate(1)
def close_current_activity(self):
if self._model.get_zoom_level() != ShellModel.ZOOM_ACTIVITY:
return
home_model = self._model.get_home()
activity = home_model.get_active_activity()
if activity.get_type() == 'org.laptop.JournalActivity':
return
self.take_activity_screenshot()
self.get_current_activity().close()
def get_current_activity(self):
return self._current_host
def get_activity(self, activity_id):
for host in self._hosts.values():
if host.get_id() == activity_id:
return host
return None
def toggle_chat_visibility(self):
act = self.get_current_activity()
if not act:
return
is_visible = self._frame.is_visible()
if act.is_chat_visible():
frame_was_visible = act.chat_hide()
if not frame_was_visible:
self._frame.do_slide_out()
else:
if not is_visible:
self._frame.do_slide_in()
act.chat_show(is_visible)
def take_screenshot(self):
file_path = os.path.join(tempfile.gettempdir(), '%i' % time.time())
window = gtk.gdk.get_default_root_window()
width, height = window.get_size()
x_orig, y_orig = window.get_origin()
screenshot = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, has_alpha=False,
bits_per_sample=8, width=width, height=height)
screenshot.get_from_drawable(window, window.get_colormap(), x_orig, y_orig, 0, 0,
width, height)
screenshot.save(file_path, "png")
try:
jobject = datastore.create()
try:
jobject.metadata['title'] = _('Screenshot')
jobject.metadata['keep'] = '0'
jobject.metadata['buddies'] = ''
jobject.metadata['preview'] = ''
jobject.metadata['icon-color'] = profile.get_color().to_string()
jobject.metadata['mime_type'] = 'image/png'
jobject.file_path = file_path
datastore.write(jobject)
finally:
jobject.destroy()
del jobject
finally:
os.remove(file_path)
-16
View File
@@ -1,16 +0,0 @@
# 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
-165
View File
@@ -1,165 +0,0 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, 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
from gettext import gettext as _
import gobject
import gtk
from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.xocolor import XoColor
from sugar.graphics.icon import Icon
from sugar.graphics import style
from sugar.clipboard import clipboardservice
from sugar.bundle.activitybundle import ActivityBundle
from sugar import util
from sugar import profile
from view.clipboardmenu import ClipboardMenu
from view.frame.frameinvoker import FrameWidgetInvoker
class ClipboardIcon(RadioToolButton):
__gtype_name__ = 'SugarClipboardIcon'
def __init__(self, object_id, name, group):
RadioToolButton.__init__(self, group=group)
self._object_id = object_id
self._name = name
self._percent = 0
self._preview = None
self._activity = None
self.owns_clipboard = False
self.props.sensitive = False
self.props.active = False
self._icon = Icon()
self._icon.props.xo_color = profile.get_color()
self.set_icon_widget(self._icon)
self._icon.show()
cb_service = clipboardservice.get_instance()
cb_service.connect('object-state-changed', self._object_state_changed_cb)
obj = cb_service.get_object(self._object_id)
self.palette = ClipboardMenu(self._object_id, self._name, self._percent,
self._preview, self._activity,
self._is_bundle(obj['FORMATS']))
self.palette.props.invoker = FrameWidgetInvoker(self)
self.child.connect('drag_data_get', self._drag_data_get_cb)
self.connect('notify::active', self._notify_active_cb)
def _is_bundle(self, formats):
# A bundle will have only one format.
return formats and formats[0] in [ActivityBundle.MIME_TYPE,
ActivityBundle.DEPRECATED_MIME_TYPE]
def get_object_id(self):
return self._object_id
def _drag_data_get_cb(self, widget, context, selection, targetType, eventTime):
logging.debug('_drag_data_get_cb: requested target ' + selection.target)
cb_service = clipboardservice.get_instance()
data = cb_service.get_object_data(self._object_id, selection.target)['DATA']
selection.set(selection.target, 8, data)
def _put_in_clipboard(self):
logging.debug('ClipboardIcon._put_in_clipboard')
if self._percent < 100:
raise ValueError('Object is not complete, cannot be put into the clipboard.')
targets = self._get_targets()
if targets:
clipboard = gtk.Clipboard()
if not clipboard.set_with_data(targets,
self._clipboard_data_get_cb,
self._clipboard_clear_cb,
targets):
logging.error('GtkClipboard.set_with_data failed!')
else:
self.owns_clipboard = True
def _clipboard_data_get_cb(self, clipboard, selection, info, targets):
if not selection.target in [target[0] for target in targets]:
logging.warning('ClipboardIcon._clipboard_data_get_cb: asked %s but' \
' only have %r.' % (selection.target, targets))
return
cb_service = clipboardservice.get_instance()
data = cb_service.get_object_data(self._object_id, selection.target)['DATA']
selection.set(selection.target, 8, data)
def _clipboard_clear_cb(self, clipboard, targets):
logging.debug('ClipboardIcon._clipboard_clear_cb')
self.owns_clipboard = False
def _object_state_changed_cb(self, cb_service, object_id, name, percent,
icon_name, preview, activity):
if object_id != self._object_id:
return
cb_service = clipboardservice.get_instance()
obj = cb_service.get_object(self._object_id)
if icon_name:
self._icon.props.icon_name = icon_name
else:
self._icon.props.icon_name = 'application-octet-stream'
self.child.drag_source_set(gtk.gdk.BUTTON1_MASK,
self._get_targets(),
gtk.gdk.ACTION_COPY)
self.child.drag_source_set_icon_name(self._icon.props.icon_name)
self._name = name
self._preview = preview
self._activity = activity
self.palette.set_state(name, percent, preview, activity,
self._is_bundle(obj['FORMATS']))
old_percent = self._percent
self._percent = percent
if self._percent == 100:
self.props.sensitive = True
# Clipboard object became complete. Make it the active one.
if old_percent < 100 and self._percent == 100:
self.props.active = True
def _notify_active_cb(self, widget, pspec):
if self.props.active:
self._put_in_clipboard()
else:
self.owns_clipboard = False
def _get_targets(self):
cb_service = clipboardservice.get_instance()
attrs = cb_service.get_object(self._object_id)
format_types = attrs[clipboardservice.FORMATS_KEY]
targets = []
for format_type in format_types:
targets.append((format_type, 0, 0))
return targets
-223
View File
@@ -1,223 +0,0 @@
# Copyright (C) 2007, 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 tempfile
import urlparse
import os
import logging
import gtk
import hippo
from sugar.graphics.palette import Palette
from sugar.graphics.menuitem import MenuItem
from sugar.graphics import style
from sugar.clipboard import clipboardservice
from sugar.datastore import datastore
from sugar import mime
from sugar import profile
from sugar import activity
class ClipboardMenu(Palette):
def __init__(self, object_id, name, percent, preview, activities, installable):
Palette.__init__(self, name)
self._object_id = object_id
self._percent = percent
self._activities = activities
self.set_group_id('frame')
self._progress_bar = None
"""
if preview:
self._preview_text = hippo.CanvasText(text=preview,
size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
self._preview_text.props.color = color.LABEL_TEXT.get_int()
self._preview_text.props.font_desc = \
style.FONT_NORMAL.get_pango_desc()
self.append(self._preview_text)
"""
self._remove_item = MenuItem(_('Remove'), 'list-remove')
self._remove_item.connect('activate', self._remove_item_activate_cb)
self.menu.append(self._remove_item)
self._remove_item.show()
self._open_item = MenuItem(_('Open'))
self._open_item.connect('activate', self._open_item_activate_cb)
self.menu.append(self._open_item)
self._open_item.show()
#self._stop_item = MenuItem(_('Stop download'), 'stock-close')
# TODO: Implement stopping downloads
#self._stop_item.connect('activate', self._stop_item_activate_cb)
#self.append_menu_item(self._stop_item)
self._journal_item = MenuItem(_('Add to journal'), 'document-save')
self._journal_item.connect('activate', self._journal_item_activate_cb)
self.menu.append(self._journal_item)
self._journal_item.show()
self._update_items_visibility(installable)
self._update_open_submenu()
def _update_open_submenu(self):
logging.debug('_update_open_submenu: %r' % self._activities)
if self._activities is None or len(self._activities) <= 1:
if self._open_item.get_submenu() is not None:
self._open_item.remove_submenu()
return
submenu = self._open_item.get_submenu()
if submenu is None:
submenu = gtk.Menu()
self._open_item.set_submenu(submenu)
submenu.show()
else:
for item in submenu.get_children():
submenu.remove(item)
for service_name in self._activities:
registry = activity.get_registry()
activity_info = registry.get_activity(service_name)
if not activity_info:
logging.warning('Activity %s is unknown.' % service_name)
item = gtk.MenuItem(activity_info.name)
item.connect('activate', self._open_submenu_item_activate_cb, service_name)
submenu.append(item)
item.show()
def _update_items_visibility(self, installable):
if self._percent == 100 and (self._activities or installable):
self._remove_item.props.sensitive = True
self._open_item.props.sensitive = True
#self._stop_item.props.sensitive = False
self._journal_item.props.sensitive = True
elif self._percent == 100 and (not self._activities and not installable):
self._remove_item.props.sensitive = True
self._open_item.props.sensitive = False
#self._stop_item.props.sensitive = False
self._journal_item.props.sensitive = True
else:
self._remove_item.props.sensitive = True
self._open_item.props.sensitive = False
# TODO: reenable the stop item when we implement stoping downloads.
#self._stop_item.props.sensitive = True
self._journal_item.props.sensitive = False
self._update_progress_bar()
def _update_progress_bar(self):
if self._percent == 100.0:
if self._progress_bar:
self._progress_bar = None
self.set_content(None)
else:
if self._progress_bar is None:
self._progress_bar = gtk.ProgressBar()
self._progress_bar.show()
self.set_content(self._progress_bar)
self._progress_bar.props.fraction = self._percent / 100.0
self._progress_bar.props.text = '%.2f %%' % self._percent
def set_state(self, name, percent, preview, activities, installable):
self.set_primary_text(name)
self._percent = percent
self._activities = activities
self._update_progress_bar()
self._update_items_visibility(installable)
self._update_open_submenu()
def _open_item_activate_cb(self, menu_item):
logging.debug('_open_item_activate_cb')
if self._percent < 100 or menu_item.get_submenu() is not None:
return
jobject = self._copy_to_journal()
jobject.resume(self._activities[0])
jobject.destroy()
def _open_submenu_item_activate_cb(self, menu_item, service_name):
logging.debug('_open_submenu_item_activate_cb')
if self._percent < 100:
return
jobject = self._copy_to_journal()
jobject.resume(service_name)
jobject.destroy()
def _remove_item_activate_cb(self, menu_item):
cb_service = clipboardservice.get_instance()
cb_service.delete_object(self._object_id)
def _journal_item_activate_cb(self, menu_item):
logging.debug('_journal_item_activate_cb')
jobject = self._copy_to_journal()
jobject.destroy()
def _write_to_temp_file(self, data):
f, file_path = tempfile.mkstemp()
try:
os.write(f, data)
finally:
os.close(f)
return file_path
def _copy_to_journal(self):
cb_service = clipboardservice.get_instance()
obj = cb_service.get_object(self._object_id)
format = mime.choose_most_significant(obj['FORMATS'])
data = cb_service.get_object_data(self._object_id, format)
transfer_ownership = False
if format == 'text/uri-list':
uris = mime.split_uri_list(data['DATA'])
if len(uris) == 1 and uris[0].startswith('file://'):
file_path = urlparse.urlparse(uris[0]).path
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
file_path = self._write_to_temp_file(data['DATA'])
transfer_ownership = True
mime_type = 'text/uri-list'
else:
if data['ON_DISK']:
file_path = urlparse.urlparse(data['DATA']).path
transfer_ownership = False
mime_type = mime.get_for_file(file_path)
else:
file_path = self._write_to_temp_file(data['DATA'])
transfer_ownership = True
mime_type = mime.get_for_file(file_path)
jobject = datastore.create()
jobject.metadata['title'] = _('Clipboard object: %s.') % obj['NAME']
jobject.metadata['keep'] = '0'
jobject.metadata['buddies'] = ''
jobject.metadata['preview'] = ''
jobject.metadata['icon-color'] = profile.get_color().to_string()
jobject.metadata['mime_type'] = mime_type
jobject.file_path = file_path
datastore.write(jobject, transfer_ownership=transfer_ownership)
return jobject
-7
View File
@@ -1,7 +0,0 @@
SUBDIRS = network
sugardir = $(pkgdatadir)/shell/view/devices
sugar_PYTHON = \
__init__.py \
battery.py \
deviceview.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-100
View File
@@ -1,100 +0,0 @@
# 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
from gettext import gettext as _
import gtk
from sugar import profile
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.icon import get_icon_state
from sugar.graphics import style
from sugar.graphics.palette import Palette
_ICON_NAME = 'battery'
_STATUS_CHARGING = 0
_STATUS_DISCHARGING = 1
_STATUS_FULLY_CHARGED = 2
class DeviceView(CanvasIcon):
def __init__(self, model):
CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE,
xo_color=profile.get_color())
self._model = model
self._palette = BatteryPalette(_('My Battery life'))
self.set_palette(self._palette)
model.connect('notify::level', self._battery_status_changed_cb)
model.connect('notify::charging', self._battery_status_changed_cb)
model.connect('notify::discharging', self._battery_status_changed_cb)
self._update_info()
def _update_info(self):
name = get_icon_state(_ICON_NAME, self._model.props.level)
self.props.icon_name = name
# Update palette
if self._model.props.charging:
status = _STATUS_CHARGING
self.props.badge_name = 'emblem-charging'
elif self._model.props.discharging:
status = _STATUS_DISCHARGING
self.props.badge_name = None
else:
status = _STATUS_FULLY_CHARGED
self.props.badge_name = None
self._palette.set_level(self._model.props.level)
self._palette.set_status(status)
def _battery_status_changed_cb(self, pspec, param):
self._update_info()
class BatteryPalette(Palette):
def __init__(self, primary_text):
Palette.__init__(self, primary_text)
self._level = 0
self._progress_bar = gtk.ProgressBar()
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.set_content(vbox)
def set_level(self, percent):
self._level = percent
fraction = percent/100.0
self._progress_bar.set_fraction(fraction)
def set_status(self, status):
percent_string = ' (%s%%)' % self._level
if status == _STATUS_CHARGING:
charge_text = _('Battery charging') + percent_string
elif status == _STATUS_DISCHARGING:
charge_text = _('Battery discharging') + percent_string
elif status == _STATUS_FULLY_CHARGED:
charge_text = _('Battery fully charged')
self._status_label.set_text(charge_text)
-27
View File
@@ -1,27 +0,0 @@
# 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
from sugar.graphics.icon import CanvasIcon
def create(model):
name = 'view.devices.' + model.get_type()
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod.DeviceView(model)
-6
View File
@@ -1,6 +0,0 @@
sugardir = $(pkgdatadir)/shell/view/devices/network
sugar_PYTHON = \
__init__.py \
mesh.py \
wired.py \
wireless.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-125
View File
@@ -1,125 +0,0 @@
#
# 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
from gettext import gettext as _
import gtk
from sugar import profile
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from model.devices import device
from sugar.graphics.palette import Palette
from model.devices.network import wireless
from hardware import hardwaremanager
class DeviceView(CanvasIcon):
def __init__(self, model):
CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE,
icon_name='network-mesh')
self._model = model
self._palette = MeshPalette(_("Mesh Network"), model)
self.set_palette(self._palette)
model.connect('notify::state', self._state_changed_cb)
model.connect('notify::activation-stage', self._state_changed_cb)
self._update_state()
def _state_changed_cb(self, model, pspec):
self._update_state()
def _update_state(self):
# FIXME Change icon colors once we have real icons
state = self._model.props.state
self._palette.update_state(state)
if state == device.STATE_ACTIVATING:
self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
elif state == device.STATE_ACTIVATED:
self.props.xo_color = profile.get_color()
elif state == device.STATE_INACTIVE:
self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
if state == device.STATE_INACTIVE:
self._palette.set_primary_text(_("Mesh Network"))
else:
chan = wireless.freq_to_channel(self._model.props.frequency)
if chan > 0:
self._palette.set_primary_text(_("Mesh Network") + " %d" % chan)
self._palette.set_mesh_step(self._model.props.mesh_step, state)
class MeshPalette(Palette):
def __init__(self, primary_text, model):
Palette.__init__(self, primary_text, menu_after_content=True)
self._model = model
self._step_label = gtk.Label()
self._step_label.show()
vbox = gtk.VBox()
vbox.pack_start(self._step_label)
vbox.show()
self.set_content(vbox)
self._disconnect_item = gtk.MenuItem(_('Disconnect...'))
self._disconnect_item.connect('activate', self._disconnect_activate_cb)
self.menu.append(self._disconnect_item)
def update_state(self, state):
if state == device.STATE_ACTIVATED:
self._disconnect_item.show()
else:
self._disconnect_item.hide()
def _disconnect_activate_cb(self, menuitem):
# Disconnection for an mesh means activating the default mesh device
# again without a channel
network_manager = hardwaremanager.get_network_manager()
nm_device = self._model.get_nm_device()
if network_manager and nm_device:
network_manager.set_active_device(nm_device)
def set_mesh_step(self, step, state):
label = ""
if step == 1:
if state == device.STATE_ACTIVATED:
label = _("Connected to a School Mesh Portal")
elif state == device.STATE_ACTIVATING:
label = _("Looking for a School Mesh Portal...")
elif step == 3:
if state == device.STATE_ACTIVATED:
label = _("Connected to an XO Mesh Portal")
elif state == device.STATE_ACTIVATING:
label = _("Looking for an XO Mesh Portal...")
elif step == 4:
if state == device.STATE_ACTIVATED:
label = _("Connected to a Simple Mesh")
elif state == device.STATE_ACTIVATING:
label = _("Starting a Simple Mesh")
if len(label):
self._step_label.set_text(label)
else:
import logging
logging.debug("Unhandled mesh step %d" % step)
self._step_label.set_text(_("Unknown Mesh"))
-22
View File
@@ -1,22 +0,0 @@
# 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
from view.devices import deviceview
class DeviceView(deviceview.DeviceView):
def __init__(self, model):
deviceview.DeviceView.__init__(self, model)
self.props.icon_name = 'network-wired'
-132
View File
@@ -1,132 +0,0 @@
#
# 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
from gettext import gettext as _
import gtk
from sugar.graphics.icon import get_icon_state
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.graphics.palette import Palette
from model.devices.network import wireless
from model.devices import device
from hardware import hardwaremanager
from hardware import nmclient
_ICON_NAME = 'network-wireless'
class DeviceView(CanvasIcon):
def __init__(self, model):
CanvasIcon.__init__(self, size=style.MEDIUM_ICON_SIZE)
self._model = model
meshdev = None
network_manager = hardwaremanager.get_network_manager()
for device in network_manager.get_devices():
if device.get_type() == nmclient.DEVICE_TYPE_802_11_MESH_OLPC:
meshdev = device
break
self._palette = WirelessPalette(self._get_palette_primary_text(), meshdev)
self.set_palette(self._palette)
self._counter = 0
self._palette.set_frequency(self._model.props.frequency)
model.connect('notify::name', self._name_changed_cb)
model.connect('notify::strength', self._strength_changed_cb)
model.connect('notify::state', self._state_changed_cb)
self._update_icon()
self._update_state()
def _get_palette_primary_text(self):
if self._model.props.state == device.STATE_INACTIVE:
return _("Disconnected")
return self._model.props.name
def _strength_changed_cb(self, model, pspec):
self._update_icon()
# Only update frequency periodically
if self._counter % 4 == 0:
self._palette.set_frequency(self._model.props.frequency)
self._counter += 1
def _name_changed_cb(self, model, pspec):
self.palette.set_primary_text(self._get_palette_primary_text())
def _state_changed_cb(self, model, pspec):
self._update_state()
self.palette.set_primary_text(self._get_palette_primary_text())
def _update_icon(self):
strength = self._model.props.strength
if self._model.props.state == device.STATE_INACTIVE:
strength = 0
icon_name = get_icon_state(_ICON_NAME, strength)
if icon_name:
self.props.icon_name = icon_name
def _update_state(self):
# FIXME Change icon colors once we have real icons
state = self._model.props.state
if state == device.STATE_ACTIVATING:
self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
elif state == device.STATE_ACTIVATED:
(stroke, fill) = self._model.get_active_network_colors()
self.props.stroke_color = stroke
self.props.fill_color = fill
elif state == device.STATE_INACTIVE:
self.props.fill_color = style.COLOR_INACTIVE_FILL.get_svg()
self.props.stroke_color = style.COLOR_INACTIVE_STROKE.get_svg()
class WirelessPalette(Palette):
def __init__(self, primary_text, meshdev):
Palette.__init__(self, primary_text, menu_after_content=True)
self._meshdev = meshdev
self._chan_label = gtk.Label()
self._chan_label.show()
vbox = gtk.VBox()
vbox.pack_start(self._chan_label)
vbox.show()
if meshdev:
disconnect_item = gtk.MenuItem(_('Disconnect...'))
disconnect_item.connect('activate', self._disconnect_activate_cb)
self.menu.append(disconnect_item)
disconnect_item.show()
self.set_content(vbox)
def _disconnect_activate_cb(self, menuitem):
# Disconnection for an AP means activating the default mesh device
network_manager = hardwaremanager.get_network_manager()
if network_manager and self._meshdev:
network_manager.set_active_device(self._meshdev)
def set_frequency(self, freq):
try:
chan = wireless.freq_to_channel(freq)
except KeyError:
chan = 0
self._chan_label.set_text("%s: %d" % (_("Channel"), chan))
-14
View File
@@ -1,14 +0,0 @@
sugardir = $(pkgdatadir)/shell/view/frame
sugar_PYTHON = \
__init__.py \
activitiestray.py \
activitybutton.py \
clipboardbox.py \
clipboardpanelwindow.py \
frameinvoker.py \
friendstray.py \
eventarea.py \
frame.py \
overlaybox.py \
framewindow.py \
zoomtoolbar.py
-16
View File
@@ -1,16 +0,0 @@
# 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
-159
View File
@@ -1,159 +0,0 @@
# 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 os
import logging
import hippo
from sugar.graphics.tray import TrayButton
from sugar.graphics.tray import HTray
from sugar.graphics.icon import Icon
from sugar.graphics import style
from sugar import profile
from sugar import activity
from sugar import env
from activitybutton import ActivityButton
import config
class InviteButton(TrayButton):
def __init__(self, activity_model, invite):
TrayButton.__init__(self)
icon = Icon(file=activity_model.get_icon_name(),
xo_color=activity_model.get_color())
self.set_icon_widget(icon)
icon.show()
self._invite = invite
def get_activity_id(self):
return self._invite.get_activity_id()
def get_bundle_id(self):
return self._invite.get_bundle_id()
def get_invite(self):
return self._invite
class ActivitiesTray(hippo.CanvasBox):
def __init__(self, shell):
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
self._shell = shell
self._shell_model = self._shell.get_model()
self._invite_to_item = {}
self._invites = self._shell_model.get_invites()
self._config = self._load_config()
self._tray = HTray()
self.append(hippo.CanvasWidget(widget=self._tray), hippo.PACK_EXPAND)
self._tray.show()
registry = activity.get_registry()
registry.get_activities_async(reply_handler=self._get_activities_cb)
registry.connect('activity-added', self._activity_added_cb)
registry.connect('activity-removed', self._activity_removed_cb)
for invite in self._invites:
self.add_invite(invite)
self._invites.connect('invite-added', self._invite_added_cb)
self._invites.connect('invite-removed', self._invite_removed_cb)
def _load_config(self):
cfg = []
f = open(os.path.join(config.data_path, 'activities.defaults'), 'r')
for line in f.readlines():
line = line.strip()
if line and not line.startswith('#'):
cfg.append(line)
f.close()
return cfg
def _get_activities_cb(self, activity_list):
known_activities = []
unknown_activities = []
name_to_activity = {}
while activity_list:
info = activity_list.pop()
name_to_activity[info.bundle_id] = info
if info.bundle_id in self._config:
known_activities.append(info)
else:
unknown_activities.append(info)
sorted_activities = []
for name in self._config:
if name in name_to_activity:
sorted_activities.append(name_to_activity[name])
for info in sorted_activities + unknown_activities:
if info.show_launcher:
self.add_activity(info)
def _activity_clicked_cb(self, icon):
self._shell.start_activity(icon.get_bundle_id())
def _invite_clicked_cb(self, icon):
self._invites.remove_invite(icon.get_invite())
self._shell.join_activity(icon.get_bundle_id(),
icon.get_activity_id())
def _invite_added_cb(self, invites, invite):
self.add_invite(invite)
def _invite_removed_cb(self, invites, invite):
self.remove_invite(invite)
def _remove_activity_cb(self, item):
self._tray.remove_item(item)
def _activity_added_cb(self, activity_registry, activity_info):
self.add_activity(activity_info)
def _activity_removed_cb(self, activity_registry, activity_info):
for item in self._tray.get_children():
if item.get_bundle_id() == activity_info.bundle_id:
self._tray.remove_item(item)
return
def add_activity(self, activity_info):
item = ActivityButton(activity_info)
item.connect('clicked', self._activity_clicked_cb)
item.connect('remove_activity', self._remove_activity_cb)
self._tray.add_item(item, -1)
item.show()
def add_invite(self, invite):
mesh = self._shell_model.get_mesh()
activity_model = mesh.get_activity(invite.get_activity_id())
if activity:
item = InviteButton(activity_model, invite)
item.connect('clicked', self._invite_clicked_cb)
self._tray.add_item(item, 0)
item.show()
self._invite_to_item[invite] = item
def remove_invite(self, invite):
self._tray.remove_item(self._invite_to_item[invite])
del self._invite_to_item[invite]
-65
View File
@@ -1,65 +0,0 @@
# Copyright (C) 2007, One Laptop Per Child
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gtk
import os
import gobject
from gettext import gettext as _
from sugar.graphics.palette import Palette
from sugar.graphics.tray import TrayButton
from sugar.graphics.icon import Icon
from sugar.graphics import style
from view.frame.frameinvoker import FrameWidgetInvoker
class ActivityButton(TrayButton, gobject.GObject):
__gtype_name__ = 'SugarActivityButton'
__gsignals__ = {
'remove_activity': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([]))
}
def __init__(self, activity_info):
TrayButton.__init__(self)
icon = Icon(file=activity_info.icon,
stroke_color=style.COLOR_WHITE.get_svg(),
fill_color=style.COLOR_TRANSPARENT.get_svg())
self.set_icon_widget(icon)
icon.show()
self._activity_info = activity_info
self.setup_rollover_options()
def get_bundle_id(self):
return self._activity_info.bundle_id
def setup_rollover_options(self):
palette = Palette(self._activity_info.name)
self.set_palette(palette)
palette.props.invoker = FrameWidgetInvoker(self)
#TODO: Disabled this until later, see #4967
# if os.path.dirname(self._activity_info.path) == os.path.expanduser('~/Activities'):
# menu_item = gtk.MenuItem(_('Remove'))
# menu_item.connect('activate', self.item_remove_cb)
# palette.menu.append(menu_item)
# menu_item.show()
def item_remove_cb(self, widget):
self.emit('remove_activity')
-193
View File
@@ -1,193 +0,0 @@
# Copyright (C) 2007, 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 os
import logging
import tempfile
import hippo
import gtk
from sugar import util
from sugar.clipboard import clipboardservice
from sugar.graphics.tray import VTray
from sugar.graphics import style
from view.clipboardicon import ClipboardIcon
class _ContextMap:
"""Maps a drag context to the clipboard object involved in the dragging."""
def __init__(self):
self._context_map = {}
def add_context(self, context, object_id, data_types):
"""Establishes the mapping. data_types will serve us for reference-
counting this mapping.
"""
self._context_map[context] = [object_id, data_types]
def get_object_id(self, context):
"""Retrieves the object_id associated with context.
Will release the association when this function was called as many times
as the number of data_types that this clipboard object contains.
"""
[object_id, data_types_left] = self._context_map[context]
data_types_left = data_types_left - 1
if data_types_left == 0:
del self._context_map[context]
else:
self._context_map[context] = [object_id, data_types_left]
return object_id
def has_context(self, context):
return context in self._context_map
class ClipboardBox(hippo.CanvasBox):
MAX_ITEMS = gtk.gdk.screen_height() / style.GRID_CELL_SIZE - 2
def __init__(self):
hippo.CanvasBox.__init__(self)
self._icons = {}
self._context_map = _ContextMap()
self._tray = VTray()
self.append(hippo.CanvasWidget(widget=self._tray), hippo.PACK_EXPAND)
self._tray.show()
cb_service = clipboardservice.get_instance()
cb_service.connect('object-added', self._object_added_cb)
cb_service.connect('object-deleted', self._object_deleted_cb)
def owns_clipboard(self):
for icon in self._icons.values():
if icon.owns_clipboard:
return True
return False
def _add_selection(self, object_id, selection):
if not selection.data:
return
logging.debug('ClipboardBox: adding type ' + selection.type)
cb_service = clipboardservice.get_instance()
if selection.type == 'text/uri-list':
uris = selection.data.split('\n')
if len(uris) > 1:
raise NotImplementedError('Multiple uris in text/uri-list still not supported.')
cb_service.add_object_format(object_id,
selection.type,
uris[0],
on_disk=True)
else:
cb_service.add_object_format(object_id,
selection.type,
selection.data,
on_disk=False)
def _object_added_cb(self, cb_service, object_id, name):
if self._icons:
group = self._icons.values()[0]
else:
group = None
icon = ClipboardIcon(object_id, name, group)
self._tray.add_item(icon, 0)
icon.show()
self._icons[object_id] = icon
objects_to_delete = self._tray.get_children()[ClipboardBox.MAX_ITEMS:]
for icon in objects_to_delete:
logging.debug('ClipboardBox: deleting surplus object')
cb_service = clipboardservice.get_instance()
cb_service.delete_object(icon.get_object_id())
logging.debug('ClipboardBox: ' + object_id + ' was added.')
def _object_deleted_cb(self, cb_service, object_id):
icon = self._icons[object_id]
self._tray.remove_item(icon)
del self._icons[object_id]
logging.debug('ClipboardBox: ' + object_id + ' was deleted.')
def drag_motion_cb(self, widget, context, x, y, time):
logging.debug('ClipboardBox._drag_motion_cb')
context.drag_status(gtk.gdk.ACTION_COPY, time)
return True;
def drag_drop_cb(self, widget, context, x, y, time):
logging.debug('ClipboardBox._drag_drop_cb')
cb_service = clipboardservice.get_instance()
object_id = cb_service.add_object(name="")
self._context_map.add_context(context, object_id, len(context.targets))
if 'XdndDirectSave0' in context.targets:
window = context.source_window
prop_type, format, filename = \
window.property_get('XdndDirectSave0','text/plain')
# FIXME query the clipboard service for a filename?
base_dir = tempfile.gettempdir()
dest_filename = util.unique_id()
name, dot, extension = filename.rpartition('.')
dest_filename += dot + extension
dest_uri = 'file://' + os.path.join(base_dir, dest_filename)
window.property_change('XdndDirectSave0', prop_type, format,
gtk.gdk.PROP_MODE_REPLACE, dest_uri)
widget.drag_get_data(context, 'XdndDirectSave0', time)
else:
for target in context.targets:
if str(target) not in ('TIMESTAMP', 'TARGETS', 'MULTIPLE'):
widget.drag_get_data(context, target, time)
cb_service.set_object_percent(object_id, percent=100)
return True
def drag_data_received_cb(self, widget, context, x, y, selection, targetType, time):
logging.debug('ClipboardBox: got data for target %r' % selection.target)
object_id = self._context_map.get_object_id(context)
try:
if selection is None:
logging.warn('ClipboardBox: empty selection for target ' + selection.target)
elif selection.target == 'XdndDirectSave0':
if selection.data == 'S':
window = context.source_window
prop_type, format, dest = \
window.property_get('XdndDirectSave0','text/plain')
clipboard = clipboardservice.get_instance()
clipboard.add_object_format(
object_id, 'XdndDirectSave0', dest, on_disk=True)
else:
self._add_selection(object_id, selection)
finally:
# If it's the last target to be processed, finish the dnd transaction
if not self._context_map.has_context(context):
context.drop_finish(True, gtk.get_current_event_time())
-99
View File
@@ -1,99 +0,0 @@
# Copyright (C) 2007, 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 urlparse
import gtk
import hippo
from view.frame.framewindow import FrameWindow
from view.frame.clipboardbox import ClipboardBox
from sugar.clipboard import clipboardservice
from sugar import util
class ClipboardPanelWindow(FrameWindow):
def __init__(self, frame, orientation):
FrameWindow.__init__(self, orientation)
self._frame = frame
# Listening for new clipboard objects
# NOTE: we need to keep a reference to gtk.Clipboard in order to keep
# listening to it.
self._clipboard = gtk.Clipboard()
self._clipboard.connect("owner-change", self._owner_change_cb)
self._clipboard_box = ClipboardBox()
self.append(self._clipboard_box, hippo.PACK_EXPAND)
# Receiving dnd drops
self.drag_dest_set(0, [], 0)
self.connect("drag_motion", self._clipboard_box.drag_motion_cb)
self.connect("drag_drop", self._clipboard_box.drag_drop_cb)
self.connect("drag_data_received",
self._clipboard_box.drag_data_received_cb)
def _owner_change_cb(self, clipboard, event):
logging.debug("owner_change_cb")
if self._clipboard_box.owns_clipboard():
return
cb_service = clipboardservice.get_instance()
key = cb_service.add_object(name="")
cb_service.set_object_percent(key, percent=0)
targets = clipboard.wait_for_targets()
for target in targets:
if target not in ('TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS'):
logging.debug('Asking for target %s.' % target)
selection = clipboard.wait_for_contents(target)
if not selection:
logging.warning('no data for selection target %s.' % target)
continue
self._add_selection(key, selection)
cb_service.set_object_percent(key, percent=100)
def _add_selection(self, key, selection):
if not selection.data:
logging.warning('no data for selection target %s.' % selection.type)
return
logging.debug('adding type ' + selection.type + '.')
cb_service = clipboardservice.get_instance()
if selection.type == 'text/uri-list':
uris = selection.get_uris()
if len(uris) > 1:
raise NotImplementedError('Multiple uris in text/uri-list still not supported.')
uri = uris[0]
scheme, netloc, path, parameters, query, fragment = urlparse.urlparse(uri)
on_disk = (scheme == 'file')
cb_service.add_object_format(key,
selection.type,
uri,
on_disk)
else:
cb_service.add_object_format(key,
selection.type,
selection.data,
on_disk=False)
-106
View File
@@ -1,106 +0,0 @@
# Copyright (C) 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 gtk
import gobject
import wnck
class EventArea(gobject.GObject):
__gsignals__ = {
'enter': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'leave': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._windows = []
self._hover = False
right = gtk.gdk.screen_width() - 1
bottom = gtk.gdk.screen_height() -1
invisible = self._create_invisible(0, 0, 1, 1)
self._windows.append(invisible)
invisible = self._create_invisible(right, 0, 1, 1)
self._windows.append(invisible)
invisible = self._create_invisible(0, bottom, 1, 1)
self._windows.append(invisible)
invisible = self._create_invisible(right, bottom, 1, 1)
self._windows.append(invisible)
screen = wnck.screen_get_default()
screen.connect('window-stacking-changed',
self._window_stacking_changed_cb)
def _create_invisible(self, x, y, width, height):
invisible = gtk.Invisible()
invisible.connect('enter-notify-event', self._enter_notify_cb)
invisible.connect('leave-notify-event', self._leave_notify_cb)
invisible.drag_dest_set(0, [], 0)
invisible.connect('drag_motion', self._drag_motion_cb)
invisible.connect('drag_leave', self._drag_leave_cb)
invisible.realize()
invisible.window.set_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.ENTER_NOTIFY_MASK |
gtk.gdk.LEAVE_NOTIFY_MASK)
invisible.window.move_resize(x, y, width, height)
return invisible
def _notify_enter(self):
if not self._hover:
self._hover = True
self.emit('enter')
def _notify_leave(self):
if self._hover:
self._hover = False
self.emit('leave')
def _enter_notify_cb(self, widget, event):
self._notify_enter()
def _leave_notify_cb(self, widget, event):
self._notify_leave()
def _drag_motion_cb(self, widget, drag_context, x, y, timestamp):
drag_context.drag_status(0, timestamp);
self._notify_enter()
return True
def _drag_leave_cb(self, widget, drag_context, timestamp):
self._notify_leave()
return True
def show(self):
for window in self._windows:
window.show()
def hide(self):
for window in self._windows:
window.hide()
def _window_stacking_changed_cb(self, screen):
for window in self._windows:
window.window.raise_()
-272
View File
@@ -1,272 +0,0 @@
# 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
import gtk
import gobject
import hippo
from sugar.graphics import animator
from sugar.graphics import style
from sugar.graphics import palettegroup
from sugar.clipboard import clipboardservice
from view.frame.eventarea import EventArea
from view.frame.activitiestray import ActivitiesTray
from view.frame.zoomtoolbar import ZoomToolbar
from view.frame.friendstray import FriendsTray
from view.frame.framewindow import FrameWindow
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
from model.shellmodel import ShellModel
_FRAME_HIDING_DELAY = 500
class _Animation(animator.Animation):
def __init__(self, frame, end):
start = frame.current_position
animator.Animation.__init__(self, start, end)
self._frame = frame
def next_frame(self, current):
self._frame.move(current)
class _MouseListener(object):
def __init__(self, frame):
self._frame = frame
self._hide_sid = 0
def mouse_enter(self):
self._show_frame()
def mouse_leave(self):
if self._frame.mode == Frame.MODE_MOUSE:
self._hide_frame()
def _show_frame(self):
if self._hide_sid != 0:
gobject.source_remove(self._hide_sid)
self._frame.show(Frame.MODE_MOUSE)
def _hide_frame_timeout_cb(self):
self._frame.hide()
return False
def _hide_frame(self):
if self._hide_sid != 0:
gobject.source_remove(self._hide_sid)
self._hide_sid = gobject.timeout_add(
_FRAME_HIDING_DELAY, self._hide_frame_timeout_cb)
class _KeyListener(object):
def __init__(self, frame):
self._frame = frame
def key_press(self):
if self._frame.visible:
if self._frame.mode == Frame.MODE_KEYBOARD:
self._frame.hide()
else:
self._frame.show(Frame.MODE_KEYBOARD)
class Frame(object):
MODE_MOUSE = 0
MODE_KEYBOARD = 1
MODE_NON_INTERACTIVE = 2
def __init__(self, shell):
self.mode = None
self._palette_group = palettegroup.get_group('frame')
self._palette_group.connect('popdown', self._palette_group_popdown_cb)
self._left_panel = None
self._right_panel = None
self._top_panel = None
self._bottom_panel = None
self._shell = shell
self.current_position = 0.0
self._animator = None
self._event_area = EventArea()
self._event_area.connect('enter', self._enter_corner_cb)
self._event_area.show()
self._top_panel = self._create_top_panel()
self._bottom_panel = self._create_bottom_panel()
self._left_panel = self._create_left_panel()
self._right_panel = self._create_right_panel()
screen = gtk.gdk.screen_get_default()
screen.connect('size-changed', self._size_changed_cb)
cb_service = clipboardservice.get_instance()
cb_service.connect_after('object-added', self._clipboard_object_added_cb)
self._key_listener = _KeyListener(self)
self._mouse_listener = _MouseListener(self)
self.move(1.0)
def is_visible(self):
return self.current_position != 0.0
def hide(self):
if self._animator:
self._animator.stop()
self._animator = animator.Animator(0.5)
self._animator.add(_Animation(self, 0.0))
self._animator.start()
self._event_area.show()
self.mode = None
def show(self, mode):
if self.visible:
return
if self._animator:
self._animator.stop()
self._shell.take_activity_screenshot()
self.mode = mode
self._animator = animator.Animator(0.5)
self._animator.add(_Animation(self, 1.0))
self._animator.start()
self._event_area.hide()
def move(self, pos):
self.current_position = pos
self._update_position()
def _is_hover(self):
return (self._top_panel.hover or \
self._bottom_panel.hover or \
self._left_panel.hover or \
self._right_panel.hover)
def _create_top_panel(self):
panel = self._create_panel(gtk.POS_TOP)
toolbar = ZoomToolbar(self._shell)
panel.append(hippo.CanvasWidget(widget=toolbar))
toolbar.show()
return panel
def _create_bottom_panel(self):
panel = self._create_panel(gtk.POS_BOTTOM)
box = ActivitiesTray(self._shell)
panel.append(box, hippo.PACK_EXPAND)
return panel
def _create_right_panel(self):
panel = self._create_panel(gtk.POS_RIGHT)
tray = FriendsTray(self._shell)
panel.append(hippo.CanvasWidget(widget=tray), hippo.PACK_EXPAND)
tray.show()
return panel
def _create_left_panel(self):
panel = ClipboardPanelWindow(self, gtk.POS_LEFT)
self._connect_to_panel(panel)
panel.connect('drag-motion', self._drag_motion_cb)
panel.connect('drag-leave', self._drag_leave_cb)
return panel
def _create_panel(self, orientation):
panel = FrameWindow(orientation)
self._connect_to_panel(panel)
return panel
def _move_panel(self, panel, pos, x1, y1, x2, y2):
x = (x2 - x1) * pos + x1
y = (y2 - y1) * pos + y1
panel.move(int(x), int(y))
# FIXME we should hide and show as necessary to free memory
if not panel.props.visible:
panel.show()
def _connect_to_panel(self, panel):
panel.connect('enter-notify-event', self._enter_notify_cb)
panel.connect('leave-notify-event', self._leave_notify_cb)
def _update_position(self):
screen_h = gtk.gdk.screen_height()
screen_w = gtk.gdk.screen_width()
self._move_panel(self._top_panel, self.current_position,
0, - self._top_panel.size, 0, 0)
self._move_panel(self._bottom_panel, self.current_position,
0, screen_h, 0, screen_h - self._bottom_panel.size)
self._move_panel(self._left_panel, self.current_position,
- self._left_panel.size, 0, 0, 0)
self._move_panel(self._right_panel, self.current_position,
screen_w, 0, screen_w - self._right_panel.size, 0)
def _size_changed_cb(self, screen):
self._update_position()
def _clipboard_object_added_cb(self, cb_service, object_id, name):
if not self.visible:
self.show(self.MODE_NON_INTERACTIVE)
gobject.timeout_add(2000, lambda: self.hide())
def _enter_notify_cb(self, window, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
self._mouse_listener.mouse_enter()
def _leave_notify_cb(self, window, event):
if event.detail == gtk.gdk.NOTIFY_INFERIOR:
return
if not self._is_hover() and not self._palette_group.is_up():
self._mouse_listener.mouse_leave()
def _palette_group_popdown_cb(self, group):
if not self._is_hover():
self._mouse_listener.mouse_leave()
def _drag_motion_cb(self, window, context, x, y, time):
self._mouse_listener.mouse_enter()
def _drag_leave_cb(self, window, drag_context, timestamp):
self._mouse_listener.mouse_leave()
def _enter_corner_cb(self, event_area):
self._mouse_listener.mouse_enter()
def notify_key_press(self):
self._key_listener.key_press()
visible = property(is_visible, None)
-39
View File
@@ -1,39 +0,0 @@
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import gtk
from sugar.graphics import style
from sugar.graphics.palette import Palette
from sugar.graphics.palette import CanvasInvoker
from sugar.graphics.palette import WidgetInvoker
def _get_screen_area():
frame_thickness = style.GRID_CELL_SIZE
x = y = frame_thickness
width = gtk.gdk.screen_width() - frame_thickness
height = gtk.gdk.screen_height() - frame_thickness
return gtk.gdk.Rectangle(x, y, width, height)
class FrameWidgetInvoker(WidgetInvoker):
def __init__(self, widget):
WidgetInvoker.__init__(self, widget.child)
self._position_hint = self.ANCHORED
self._screen_area = _get_screen_area()
-104
View File
@@ -1,104 +0,0 @@
# 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 gtk
import hippo
from sugar.graphics import style
class FrameWindow(gtk.Window):
__gtype_name__ = 'SugarFrameWindow'
def __init__(self, position):
gtk.Window.__init__(self)
self.hover = False
self.size = style.GRID_CELL_SIZE + style.LINE_WIDTH
self._position = position
self.set_decorated(False)
self.connect('realize', self._realize_cb)
self.connect('enter-notify-event', self._enter_notify_cb)
self.connect('leave-notify-event', self._leave_notify_cb)
self._canvas = hippo.Canvas()
self.add(self._canvas)
self._canvas.show()
box = hippo.CanvasBox()
self._canvas.set_root(box)
padding = style.GRID_CELL_SIZE
if self._position == gtk.POS_TOP or self._position == gtk.POS_BOTTOM:
box.props.orientation = hippo.ORIENTATION_HORIZONTAL
box.props.padding_left = padding
box.props.padding_right = padding
box.props.padding_top = 0
box.props.padding_bottom = 0
else:
box.props.orientation = hippo.ORIENTATION_VERTICAL
box.props.padding_left = 0
box.props.padding_right = 0
box.props.padding_top = padding
box.props.padding_bottom = padding
self._bg = hippo.CanvasBox(
border_color=style.COLOR_BUTTON_GREY.get_int())
border = style.LINE_WIDTH
if position == gtk.POS_TOP:
self._bg.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._bg.props.border_bottom = border
elif position == gtk.POS_BOTTOM:
self._bg.props.orientation = hippo.ORIENTATION_HORIZONTAL
self._bg.props.border_top = border
elif position == gtk.POS_LEFT:
self._bg.props.orientation = hippo.ORIENTATION_VERTICAL
self._bg.props.border_right = border
elif position == gtk.POS_RIGHT:
self._bg.props.orientation = hippo.ORIENTATION_VERTICAL
self._bg.props.border_left = border
box.append(self._bg, hippo.PACK_EXPAND)
self._update_size()
screen = gtk.gdk.screen_get_default()
screen.connect('size-changed', self._size_changed_cb)
def append(self, child, flags=0):
self._bg.append(child, flags)
def _update_size(self):
if self._position == gtk.POS_TOP or self._position == gtk.POS_BOTTOM:
self.resize(gtk.gdk.screen_width(), self.size)
else:
self.resize(self.size, gtk.gdk.screen_height())
def _realize_cb(self, widget):
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.window.set_accept_focus(False)
def _enter_notify_cb(self, window, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
self.hover = True
def _leave_notify_cb(self, window, event):
if event.detail != gtk.gdk.NOTIFY_INFERIOR:
self.hover = False
def _size_changed_cb(self, screen):
self._update_size()
-142
View File
@@ -1,142 +0,0 @@
# 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 hippo
from sugar.presence import presenceservice
from sugar.graphics.tray import VTray, TrayIcon
from view.BuddyMenu import BuddyMenu
from view.frame.frameinvoker import FrameWidgetInvoker
from model.BuddyModel import BuddyModel
class FriendIcon(TrayIcon):
def __init__(self, shell, buddy):
TrayIcon.__init__(self, icon_name='computer-xo',
xo_color=buddy.get_color())
palette = BuddyMenu(shell, buddy)
self.set_palette(palette)
palette.set_group_id('frame')
palette.props.invoker = FrameWidgetInvoker(self)
class FriendsTray(VTray):
def __init__(self, shell):
VTray.__init__(self)
self._shell = shell
self._activity_ps = None
self._joined_hid = -1
self._left_hid = -1
self._buddies = {}
self._pservice = presenceservice.get_instance()
self._pservice.connect('activity-appeared',
self.__activity_appeared_cb)
self._owner = self._pservice.get_owner()
# Add initial activities the PS knows about
self._pservice.get_activities_async(reply_handler=self._get_activities_cb)
home_model = shell.get_model().get_home()
home_model.connect('pending-activity-changed',
self._pending_activity_changed_cb)
def _get_activities_cb(self, list):
for activity in list:
self.__activity_appeared_cb(self._pservice, activity)
def add_buddy(self, buddy):
if self._buddies.has_key(buddy.props.key):
return
model = BuddyModel(buddy=buddy)
icon = FriendIcon(self._shell, model)
self.add_item(icon)
icon.show()
self._buddies[buddy.props.key] = icon
def remove_buddy(self, buddy):
if not self._buddies.has_key(buddy.props.key):
return
self.remove_item(self._buddies[buddy.props.key])
del self._buddies[buddy.props.key]
def clear(self):
for item in self.get_children():
self.remove_item(item)
self._buddies = {}
def __activity_appeared_cb(self, pservice, activity_ps):
activity = self._shell.get_current_activity()
if activity and activity_ps.props.id == activity.get_id():
self._set_activity_ps(activity_ps, True)
def _set_activity_ps(self, activity_ps, shared_activity):
if self._activity_ps == activity_ps:
return
if self._joined_hid > 0:
self._activity_ps.disconnect(self._joined_hid)
self._joined_hid = -1
if self._left_hid > 0:
self._activity_ps.disconnect(self._left_hid)
self._left_hid = -1
self._activity_ps = activity_ps
self.clear()
if shared_activity is True:
for buddy in activity_ps.get_joined_buddies():
self.add_buddy(buddy)
self._joined_hid = activity_ps.connect(
'buddy-joined', self.__buddy_joined_cb)
self._left_hid = activity_ps.connect(
'buddy-left', self.__buddy_left_cb)
else:
# only display myself if not shared
self.add_buddy(self._owner)
def _pending_activity_changed_cb(self, home_model, home_activity):
if home_activity is None:
return
activity_id = home_activity.get_activity_id()
if activity_id is None:
return
# check if activity is shared
activity = None
for act in self._pservice.get_activities():
if activity_id == act.props.id:
activity = act
break
if activity:
self._set_activity_ps(activity, True)
else:
self._set_activity_ps(home_activity, False)
def __buddy_joined_cb(self, activity, buddy):
self.add_buddy(buddy)
def __buddy_left_cb(self, activity, buddy):
self.remove_buddy(buddy)
-32
View File
@@ -1,32 +0,0 @@
# 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 hippo
from sugar.graphics.iconbutton import IconButton
class OverlayBox(hippo.CanvasBox):
def __init__(self, shell):
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
self._shell = shell
icon = IconButton(icon_name='stock-chat')
icon.connect('activated', self._overlay_clicked_cb)
self.append(icon)
def _overlay_clicked_cb(self, item):
self._shell.toggle_chat_visibility()
-84
View File
@@ -1,84 +0,0 @@
# 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
from gettext import gettext as _
import gtk
from sugar.graphics.palette import Palette
from sugar.graphics.toolbutton import ToolButton
from view.frame.frameinvoker import FrameWidgetInvoker
from model.shellmodel import ShellModel
class ZoomToolbar(gtk.Toolbar):
def __init__(self, shell):
gtk.Toolbar.__init__(self)
self._shell = shell
self.set_show_arrow(False)
button = ToolButton(icon_name='zoom-neighborhood')
button.connect('clicked',
self._level_clicked_cb,
ShellModel.ZOOM_MESH)
self.insert(button, -1)
button.show()
palette = Palette(_('Neighborhood'))
palette.props.invoker = FrameWidgetInvoker(button)
palette.set_group_id('frame')
button.set_palette(palette)
button = ToolButton(icon_name='zoom-groups')
button.connect('clicked',
self._level_clicked_cb,
ShellModel.ZOOM_FRIENDS)
self.insert(button, -1)
button.show()
palette = Palette(_('Group'))
palette.props.invoker = FrameWidgetInvoker(button)
palette.set_group_id('frame')
button.set_palette(palette)
button = ToolButton(icon_name='zoom-home')
button.connect('clicked',
self._level_clicked_cb,
ShellModel.ZOOM_HOME)
self.insert(button, -1)
button.show()
palette = Palette(_('Home'))
palette.props.invoker = FrameWidgetInvoker(button)
palette.set_group_id('frame')
button.set_palette(palette)
button = ToolButton(icon_name='zoom-activity')
button.connect('clicked',
self._level_clicked_cb,
ShellModel.ZOOM_ACTIVITY)
self.insert(button, -1)
button.show()
palette = Palette(_('Activity'))
palette.props.invoker = FrameWidgetInvoker(button)
palette.set_group_id('frame')
button.set_palette(palette)
def _level_clicked_cb(self, button, level):
self._shell.set_zoom_level(level)
-86
View File
@@ -1,86 +0,0 @@
# 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 hippo
import gobject
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.presence import presenceservice
from sugar import activity
from view.BuddyIcon import BuddyIcon
class FriendView(hippo.CanvasBox):
def __init__(self, shell, buddy, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self._pservice = presenceservice.get_instance()
self._buddy = buddy
self._buddy_icon = BuddyIcon(shell, buddy)
self._buddy_icon.props.size = style.LARGE_ICON_SIZE
self.append(self._buddy_icon)
self._activity_icon = CanvasIcon(size=style.LARGE_ICON_SIZE)
self._activity_icon_visible = False
if self._buddy.is_present():
self._buddy_appeared_cb(buddy)
self._buddy.connect('current-activity-changed', self._buddy_activity_changed_cb)
self._buddy.connect('appeared', self._buddy_appeared_cb)
self._buddy.connect('disappeared', self._buddy_disappeared_cb)
self._buddy.connect('color-changed', self._buddy_color_changed_cb)
def _get_new_icon_name(self, ps_activity):
registry = activity.get_registry()
activity_info = registry.get_activity(ps_activity.props.type)
if activity_info:
return activity_info.icon
return None
def _remove_activity_icon(self):
if self._activity_icon_visible:
self.remove(self._activity_icon)
self._activity_icon_visible = False
def _buddy_activity_changed_cb(self, buddy, ps_activity=None):
if not ps_activity:
self._remove_activity_icon()
return
# FIXME: use some sort of "unknown activity" icon rather
# than hiding the icon?
name = self._get_new_icon_name(ps_activity)
if name:
self._activity_icon.props.file_name = name
self._activity_icon.props.xo_color = buddy.get_color()
if not self._activity_icon_visible:
self.append(self._activity_icon, hippo.PACK_EXPAND)
self._activity_icon_visible = True
else:
self._remove_activity_icon()
def _buddy_appeared_cb(self, buddy):
home_activity = self._buddy.get_current_activity()
self._buddy_activity_changed_cb(buddy, home_activity)
def _buddy_disappeared_cb(self, buddy):
self._buddy_activity_changed_cb(buddy, None)
def _buddy_color_changed_cb(self, buddy, color):
self._activity_icon.props.xo_color = buddy.get_color()
-67
View File
@@ -1,67 +0,0 @@
# 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 random
import hippo
import gobject
from sugar import profile
from sugar.graphics import style
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.palette import Palette
from view.home.FriendView import FriendView
from view.home.spreadlayout import SpreadLayout
class FriendsBox(hippo.CanvasBox):
__gtype_name__ = 'SugarFriendsBox'
def __init__(self, shell):
hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff)
self._shell = shell
self._friends = {}
self._layout = SpreadLayout()
self.set_layout(self._layout)
self._owner_icon = CanvasIcon(icon_name='computer-xo', cache=True,
xo_color=profile.get_color())
self._owner_icon.props.size = style.LARGE_ICON_SIZE
palette = Palette(profile.get_nick_name())
self._owner_icon.set_palette(palette)
self._layout.add_center(self._owner_icon)
friends = self._shell.get_model().get_friends()
for friend in friends:
self.add_friend(friend)
friends.connect('friend-added', self._friend_added_cb)
friends.connect('friend-removed', self._friend_removed_cb)
def add_friend(self, buddy_info):
icon = FriendView(self._shell, buddy_info)
self._layout.add(icon)
self._friends[buddy_info.get_key()] = icon
def _friend_added_cb(self, data_model, buddy_info):
self.add_friend(buddy_info)
def _friend_removed_cb(self, data_model, key):
self._layout.remove(self._friends[key])
del self._friends[key]
-287
View File
@@ -1,287 +0,0 @@
# 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 os
import logging
import signal
from gettext import gettext as _
import re
import gobject
import gtk
import hippo
import dbus
from hardware import hardwaremanager
from sugar.graphics import style
from sugar.graphics.palette import Palette
from sugar.profile import get_profile
from sugar import env
from view.home.activitiesdonut import ActivitiesDonut
from view.devices import deviceview
from view.home.MyIcon import MyIcon
from model.shellmodel import ShellModel
from hardware import schoolserver
_logger = logging.getLogger('HomeBox')
class HomeBox(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarHomeBox'
def __init__(self, shell):
hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff)
self._redraw_id = None
shell_model = shell.get_model()
top_box = hippo.CanvasBox(box_height=style.GRID_CELL_SIZE * 2.5)
self.append(top_box)
center_box = hippo.CanvasBox(yalign=hippo.ALIGNMENT_CENTER)
self.append(center_box, hippo.PACK_EXPAND)
bottom_box = hippo.CanvasBox(box_height=style.GRID_CELL_SIZE * 2.5)
self.append(bottom_box)
self._donut = ActivitiesDonut(shell)
center_box.append(self._donut)
self._my_icon = _MyIcon(shell, style.XLARGE_ICON_SIZE)
self.append(self._my_icon, hippo.PACK_FIXED)
self._devices_box = _DevicesBox(shell_model.get_devices())
bottom_box.append(self._devices_box)
shell_model.connect('notify::state',
self._shell_state_changed_cb)
def _shell_state_changed_cb(self, model, pspec):
# FIXME implement this
if model.props.state == ShellModel.STATE_SHUTDOWN:
pass
def do_allocate(self, width, height, origin_changed):
hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
[icon_width, icon_height] = self._my_icon.get_allocation()
self.set_position(self._my_icon, (width - icon_width) / 2,
(height - icon_height) / 2)
_REDRAW_TIMEOUT = 5 * 60 * 1000 # 5 minutes
def resume(self):
if self._redraw_id is None:
self._redraw_id = gobject.timeout_add(self._REDRAW_TIMEOUT,
self._redraw_activity_ring)
self._redraw_activity_ring()
def suspend(self):
if self._redraw_id is not None:
gobject.source_remove(self._redraw_id)
self._redraw_id = None
def _redraw_activity_ring(self):
self._donut.redraw()
return True
def has_activities(self):
return self._donut.has_activities()
def enable_xo_palette(self):
self._my_icon.enable_palette()
def grab_and_rotate(self):
pass
def rotate(self):
pass
def release(self):
pass
class _DevicesBox(hippo.CanvasBox):
def __init__(self, devices_model):
gobject.GObject.__init__(self,
orientation=hippo.ORIENTATION_HORIZONTAL,
xalign=hippo.ALIGNMENT_CENTER)
self._device_icons = {}
for device in devices_model:
self._add_device(device)
devices_model.connect('device-appeared',
self._device_appeared_cb)
devices_model.connect('device-disappeared',
self._device_disappeared_cb)
def _add_device(self, device):
view = deviceview.create(device)
self.append(view)
self._device_icons[device.get_id()] = view
def _remove_device(self, device):
self.remove(self._device_icons[device.get_id()])
del self._device_icons[device.get_id()]
def _device_appeared_cb(self, model, device):
self._add_device(device)
def _device_disappeared_cb(self, model, device):
self._remove_device(device)
class _MyIcon(MyIcon):
def __init__(self, shell, scale):
MyIcon.__init__(self, scale)
self._power_manager = None
self._shell = shell
self._profile = get_profile()
def enable_palette(self):
palette = Palette(self._profile.nick_name)
item = gtk.MenuItem(_('Reboot'))
item.connect('activate', self._reboot_activate_cb)
palette.menu.append(item)
item.show()
item = gtk.MenuItem(_('Shutdown'))
item.connect('activate', self._shutdown_activate_cb)
palette.menu.append(item)
item.show()
if not self._profile.is_registered():
item = gtk.MenuItem(_('Register'))
item.connect('activate', self._register_activate_cb)
palette.menu.append(item)
item.show()
item = gtk.MenuItem(_('About this XO'))
item.connect('activate', self._about_activate_cb)
palette.menu.append(item)
item.show()
self.set_palette(palette)
def _reboot_activate_cb(self, menuitem):
model = self._shell.get_model()
model.props.state = ShellModel.STATE_SHUTDOWN
pm = self._get_power_manager()
hw_manager = hardwaremanager.get_manager()
hw_manager.shutdown()
if env.is_emulator():
self._close_emulator()
else:
pm.Reboot()
def _shutdown_activate_cb(self, menuitem):
model = self._shell.get_model()
model.props.state = ShellModel.STATE_SHUTDOWN
pm = self._get_power_manager()
hw_manager = hardwaremanager.get_manager()
hw_manager.shutdown()
if env.is_emulator():
self._close_emulator()
else:
pm.Shutdown()
def _register_activate_cb(self, menuitem):
schoolserver.register_laptop()
if self._profile.is_registered():
self.get_palette().menu.remove(menuitem)
def _about_activate_cb(self, menuitem):
dialog = gtk.Dialog(_('About this XO'),
self.palette,
gtk.DIALOG_MODAL |
gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_OK, gtk.RESPONSE_OK))
not_available = _('Not available')
build = self._read_file('/boot/olpc_build')
if build is None:
build = not_available
label_build = gtk.Label('Build: %s' % build)
label_build.set_alignment(0, 0.5)
label_build.show()
dialog.vbox.pack_start(label_build)
firmware = self._read_file('/ofw/openprom/model')
if firmware is None:
firmware = not_available
else:
firmware = re.split(" +", firmware)
if len(firmware) == 3:
firmware = firmware[1]
label_firmware = gtk.Label('Firmware: %s' % firmware)
label_firmware.set_alignment(0, 0.5)
label_firmware.show()
dialog.vbox.pack_start(label_firmware)
serial = self._read_file('/ofw/serial-number')
if serial is None:
serial = not_available
label_serial = gtk.Label('Serial Number: %s' % serial)
label_serial.set_alignment(0, 0.5)
label_serial.show()
dialog.vbox.pack_start(label_serial)
dialog.set_default_response(gtk.RESPONSE_OK)
dialog.connect('response', self._response_cb)
dialog.show()
def _read_file(self, path):
if os.access(path, os.R_OK) == 0:
_logger.error('read_file() No such file or directory: %s', path)
return None
fd = open(path, 'r')
value = fd.read()
fd.close()
if value:
value = value.strip('\n')
return value
else:
_logger.error('read_file() No information in file or directory: %s', path)
return None
def _response_cb(self, widget, response_id):
if response_id == gtk.RESPONSE_OK:
widget.destroy()
def _close_emulator(self):
if os.environ.has_key('SUGAR_EMULATOR_PID'):
pid = int(os.environ['SUGAR_EMULATOR_PID'])
os.kill(pid, signal.SIGTERM)
def _get_power_manager(self):
if self._power_manager is None:
bus = dbus.SystemBus()
proxy = bus.get_object('org.freedesktop.Hal',
'/org/freedesktop/Hal/devices/computer')
self._power_manager = dbus.Interface(proxy, \
'org.freedesktop.Hal.Device.SystemPowerManagement')
return self._power_manager
-141
View File
@@ -1,141 +0,0 @@
# 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 gtk
import hippo
import cairo
from sugar.graphics import style
from view.home.MeshBox import MeshBox
from view.home.HomeBox import HomeBox
from view.home.FriendsBox import FriendsBox
from view.home.transitionbox import TransitionBox
from model.shellmodel import ShellModel
_HOME_PAGE = 0
_FRIENDS_PAGE = 1
_MESH_PAGE = 2
_TRANSITION_PAGE = 3
class HomeWindow(gtk.Window):
def __init__(self, shell):
gtk.Window.__init__(self)
self._shell = shell
self._active = False
self._level = ShellModel.ZOOM_HOME
self._canvas = hippo.Canvas()
self.add(self._canvas)
self._canvas.show()
self.set_default_size(gtk.gdk.screen_width(),
gtk.gdk.screen_height())
self.realize()
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP)
self.connect("key-release-event", self._key_release_cb)
self.connect('focus-in-event', self._focus_in_cb)
self.connect('focus-out-event', self._focus_out_cb)
self._enter_sid = self.connect('enter-notify-event',
self._enter_notify_event_cb)
self._leave_sid = self.connect('leave-notify-event',
self._leave_notify_event_cb)
self._motion_sid = self.connect('motion-notify-event',
self._motion_notify_event_cb)
self._home_box = HomeBox(shell)
self._friends_box = FriendsBox(shell)
self._mesh_box = MeshBox(shell)
self._transition_box = TransitionBox()
self._activate_view()
self._canvas.set_root(self._home_box)
self._transition_box.connect('completed',
self._transition_completed_cb)
def _enter_notify_event_cb(self, window, event):
if event.x != gtk.gdk.screen_width() / 2 or \
event.y != gtk.gdk.screen_height() / 2:
self._mouse_moved()
def _leave_notify_event_cb(self, window, event):
self._mouse_moved()
def _motion_notify_event_cb(self, window, event):
self._mouse_moved()
# We want to enable the XO palette only when the user
# moved away from the default mouse position (screen center).
def _mouse_moved(self):
self._home_box.enable_xo_palette()
self.disconnect(self._leave_sid)
self.disconnect(self._motion_sid)
self.disconnect(self._enter_sid)
def _key_release_cb(self, widget, event):
keyname = gtk.gdk.keyval_name(event.keyval)
if keyname == "Alt_L":
self._home_box.release()
def _deactivate_view(self):
if self._level == ShellModel.ZOOM_HOME:
self._home_box.suspend()
elif self._level == ShellModel.ZOOM_MESH:
self._mesh_box.suspend()
def _activate_view(self):
if self._level == ShellModel.ZOOM_HOME:
self._home_box.resume()
elif self._level == ShellModel.ZOOM_MESH:
self._mesh_box.resume()
def _focus_in_cb(self, widget, event):
self._activate_view()
def _focus_out_cb(self, widget, event):
self._deactivate_view()
def set_zoom_level(self, level):
self._deactivate_view()
self._level = level
self._activate_view()
self._canvas.set_root(self._transition_box)
if level == ShellModel.ZOOM_HOME:
size = style.XLARGE_ICON_SIZE
elif level == ShellModel.ZOOM_FRIENDS:
size = style.LARGE_ICON_SIZE
elif level == ShellModel.ZOOM_MESH:
size = style.STANDARD_ICON_SIZE
self._transition_box.set_size(size)
def _transition_completed_cb(self, transition_box):
if self._level == ShellModel.ZOOM_HOME:
self._canvas.set_root(self._home_box)
elif self._level == ShellModel.ZOOM_FRIENDS:
self._canvas.set_root(self._friends_box)
elif self._level == ShellModel.ZOOM_MESH:
self._canvas.set_root(self._mesh_box)
self._mesh_box.focus_search_entry()
def get_home_box(self):
return self._home_box
-14
View File
@@ -1,14 +0,0 @@
sugardir = $(pkgdatadir)/shell/view/home
sugar_PYTHON = \
__init__.py \
activitiesdonut.py \
FriendView.py \
FriendsBox.py \
HomeBox.py \
HomeWindow.py \
MeshBox.py \
MyIcon.py \
proc_smaps.py \
snowflakelayout.py \
spreadlayout.py \
transitionbox.py
-615
View File
@@ -1,615 +0,0 @@
# 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 random
from gettext import gettext as _
import logging
import hippo
import gobject
import gtk
from sugar.graphics.icon import CanvasIcon
from sugar.graphics import style
from sugar.graphics.icon import get_icon_state
from sugar.graphics import style
from sugar.graphics import palette
from sugar.graphics import iconentry
from sugar.graphics.menuitem import MenuItem
from sugar import profile
from model import accesspointmodel
from model.devices.network import mesh
from model.devices.network import wireless
from hardware import hardwaremanager
from hardware import nmclient
from view.BuddyIcon import BuddyIcon
from view.pulsingicon import PulsingIcon
from view.home.snowflakelayout import SnowflakeLayout
from view.home.spreadlayout import SpreadLayout
from hardware.nmclient import NM_802_11_CAP_PROTO_WEP, NM_802_11_CAP_PROTO_WPA, NM_802_11_CAP_PROTO_WPA2
_ICON_NAME = 'network-wireless'
class AccessPointView(PulsingIcon):
def __init__(self, model, mesh_device=None):
PulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE, cache=True)
self._model = model
self._meshdev = mesh_device
self._disconnect_item = None
self._greyed_out = False
self.connect('activated', self._activate_cb)
model.connect('notify::strength', self._strength_changed_cb)
model.connect('notify::name', self._name_changed_cb)
model.connect('notify::state', self._state_changed_cb)
(stroke, fill) = model.get_nm_network().get_colors()
self._device_stroke = stroke
self._device_fill = fill
self._palette = self._create_palette()
self.set_palette(self._palette)
self._update_icon()
self._update_name()
self._update_state()
# Update badge
caps = model.props.capabilities
if model.get_nm_network().is_favorite():
self.props.badge_name = "emblem-favorite"
elif (caps & NM_802_11_CAP_PROTO_WEP) or (caps & NM_802_11_CAP_PROTO_WPA) or (caps & NM_802_11_CAP_PROTO_WPA2):
self.props.badge_name = "emblem-locked"
def _create_palette(self):
p = palette.Palette(self._model.props.name, menu_after_content=True)
if not self._meshdev:
return p
# Only show disconnect when there's a mesh device, because mesh takes
# priority over the normal wireless device. NM doesn't have a "disconnect"
# method for a device either (for various reasons) so this doesn't
# have a good mapping
self._disconnect_item = gtk.MenuItem(_('Disconnect...'))
self._disconnect_item.connect('activate', self._disconnect_activate_cb)
p.menu.append(self._disconnect_item)
if self._model.props.state == accesspointmodel.STATE_CONNECTED:
self._disconnect_item.show()
return p
def _disconnect_activate_cb(self, menuitem):
# Disconnection for an AP means activating the default mesh device
network_manager = hardwaremanager.get_network_manager()
if network_manager and self._meshdev:
network_manager.set_active_device(self._meshdev)
def _strength_changed_cb(self, model, pspec):
self._update_icon()
def _name_changed_cb(self, model, pspec):
self._update_name()
def _state_changed_cb(self, model, pspec):
self._update_state()
def _activate_cb(self, icon):
network_manager = hardwaremanager.get_network_manager()
if network_manager:
device = self._model.get_nm_device()
network = self._model.get_nm_network()
network_manager.set_active_device(device, network)
def _update_name(self):
self._palette.set_primary_text(self._model.props.name)
def _update_icon(self):
icon_name = get_icon_state(_ICON_NAME, self._model.props.strength)
if icon_name:
self.props.icon_name = icon_name
def _update_state(self):
if self._model.props.state == accesspointmodel.STATE_CONNECTING:
if self._disconnect_item:
self._disconnect_item.hide()
self.props.pulse_time = 1.0
self.props.colors = [
[ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill).get_svg() ],
[ style.Color(self._device_stroke).get_svg(),
'#e2e2e2' ]
]
elif self._model.props.state == accesspointmodel.STATE_CONNECTED:
if self._disconnect_item:
self._disconnect_item.show()
self.props.pulse_time = 0.0
self.props.colors = [
[ '#ffffff',
style.Color(self._device_fill).get_svg() ],
[ '#ffffff',
style.Color(self._device_fill).get_svg() ]
]
elif self._model.props.state == accesspointmodel.STATE_NOTCONNECTED:
if self._disconnect_item:
self._disconnect_item.hide()
self.props.pulse_time = 0.0
self.props.colors = [
[ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill).get_svg() ]
]
if self._greyed_out:
self.props.pulse_time = 0.0
self.props.colors = [['#D5D5D5', '#D5D5D5']]
def set_filter(self, query):
self._greyed_out = self._model.props.name.lower().find(query) == -1
self._update_state()
_MESH_ICON_NAME = 'network-mesh'
class MeshDeviceView(PulsingIcon):
def __init__(self, nm_device, channel):
if not channel in [1, 6, 11]:
raise ValueError("Invalid channel %d" % channel)
PulsingIcon.__init__(self, size=style.STANDARD_ICON_SIZE,
icon_name=_MESH_ICON_NAME, cache=True)
self._nm_device = nm_device
self.channel = channel
self.props.badge_name = "badge-channel-%d" % self.channel
self._greyed_out = False
self._disconnect_item = None
self._palette = self._create_palette()
self.set_palette(self._palette)
mycolor = profile.get_color()
self._device_fill = mycolor.get_fill_color()
self._device_stroke = mycolor.get_stroke_color()
self.connect('activated', self._activate_cb)
self._nm_device.connect('state-changed', self._state_changed_cb)
self._nm_device.connect('activation-stage-changed', self._state_changed_cb)
self._update_state()
def _create_palette(self):
p = palette.Palette(_("Mesh Network") + " " + str(self.channel), menu_after_content=True)
self._disconnect_item = gtk.MenuItem(_('Disconnect...'))
self._disconnect_item.connect('activate', self._disconnect_activate_cb)
p.menu.append(self._disconnect_item)
state = self._nm_device.get_state()
chan = wireless.freq_to_channel(self._nm_device.get_frequency())
if state == nmclient.DEVICE_STATE_ACTIVATED and chan == self.channel:
self._disconnect_item.show()
return p
def _disconnect_activate_cb(self, menuitem):
network_manager = hardwaremanager.get_network_manager()
if network_manager:
network_manager.set_active_device(self._nm_device)
def _activate_cb(self, icon):
network_manager = hardwaremanager.get_network_manager()
if network_manager:
freq = wireless.channel_to_freq(self.channel)
network_manager.set_active_device(self._nm_device, mesh_freq=freq)
def _state_changed_cb(self, model):
self._update_state()
def _update_state(self):
state = self._nm_device.get_state()
chan = wireless.freq_to_channel(self._nm_device.get_frequency())
if self._greyed_out:
self.props.colors = [['#D5D5D5', '#D5D5D5']]
elif state == nmclient.DEVICE_STATE_ACTIVATING and chan == self.channel:
self._disconnect_item.hide()
self.props.pulse_time = 0.75
self.props.colors = [
[ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill).get_svg() ],
[ style.Color(self._device_stroke).get_svg(),
'#e2e2e2' ]
]
elif state == nmclient.DEVICE_STATE_ACTIVATED and chan == self.channel:
self._disconnect_item.show()
self.props.pulse_time = 0.0
self.props.colors = [
[ '#ffffff',
style.Color(self._device_fill).get_svg() ],
[ '#ffffff',
style.Color(self._device_fill).get_svg() ]
]
elif state == nmclient.DEVICE_STATE_INACTIVE or chan != self.channel:
self._disconnect_item.hide()
self.props.pulse_time = 0.0
self.props.colors = [
[ style.Color(self._device_stroke).get_svg(),
style.Color(self._device_fill).get_svg() ]
]
else:
raise RuntimeError("Shouldn't get here")
def set_filter(self, query):
self._greyed_out = (query != '')
self._update_state()
class ActivityView(hippo.CanvasBox):
def __init__(self, shell, model):
hippo.CanvasBox.__init__(self)
self._shell = shell
self._model = model
self._icons = {}
self._layout = SnowflakeLayout()
self.set_layout(self._layout)
self._icon = self._create_icon()
self._layout.add(self._icon, center=True)
self._update_palette()
activity = self._model.activity
activity.connect('notify::name', self._name_changed_cb)
activity.connect('notify::color', self._color_changed_cb)
activity.connect('notify::private', self._private_changed_cb)
activity.connect('joined', self._joined_changed_cb)
#FIXME: 'joined' signal not working, see #5032
def _create_icon(self):
icon = CanvasIcon(file_name=self._model.get_icon_name(),
xo_color=self._model.get_color(), cache=True,
size=style.STANDARD_ICON_SIZE)
icon.connect('activated', self._clicked_cb)
return icon
def _create_palette(self):
p = palette.Palette(self._model.activity.props.name)
private = self._model.activity.props.private
joined = self._model.activity.props.joined
if joined:
item = MenuItem(_('Resume'), 'activity-start')
item.connect('activate', self._clicked_cb)
item.show()
p.menu.append(item)
elif not private:
item = MenuItem(_('Join'), 'activity-start')
item.connect('activate', self._clicked_cb)
item.show()
p.menu.append(item)
return p
def _update_palette(self):
self._palette = self._create_palette()
self._icon.set_palette(self._palette)
def has_buddy_icon(self, key):
return self._icons.has_key(key)
def add_buddy_icon(self, key, icon):
self._icons[key] = icon
self._layout.add(icon)
def remove_buddy_icon(self, key):
icon = self._icons[key]
del self._icons[key]
icon.destroy()
def _clicked_cb(self, item):
bundle_id = self._model.get_bundle_id()
self._shell.join_activity(bundle_id, self._model.get_id())
def set_filter(self, query):
text_to_check = self._model.activity.props.name.lower() + \
self._model.activity.props.type.lower()
if text_to_check.find(query) == -1:
self._icon.props.stroke_color = '#D5D5D5'
self._icon.props.fill_color = '#E5E5E5'
else:
self._icon.props.xo_color = self._model.get_color()
for key, icon in self._icons.iteritems():
if hasattr(icon, 'set_filter'):
icon.set_filter(query)
def _name_changed_cb(self, activity, pspec):
self._update_palette()
def _color_changed_cb(self, activity, pspec):
self._layout.remove(self._icon)
self._icon = self._create_icon()
self._layout.add(self._icon, center=True)
self._icon.set_palette(self._palette)
def _private_changed_cb(self, activity, pspec):
self._update_palette()
def _joined_changed_cb(self, widget, event):
logging.debug('ActivityView._joined_changed_cb: AAAA!!!!')
_AUTOSEARCH_TIMEOUT = 1000
class MeshToolbar(gtk.Toolbar):
__gtype_name__ = 'MeshToolbar'
__gsignals__ = {
'query-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([str]))
}
def __init__(self):
gtk.Toolbar.__init__(self)
self._query = None
self._autosearch_timer = None
self._add_separator()
tool_item = gtk.ToolItem()
tool_item.set_expand(True)
self.insert(tool_item, -1)
tool_item.show()
self._search_entry = iconentry.IconEntry()
self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY, 'system-search')
self._search_entry.add_clear_button()
self._search_entry.connect('activate', self._entry_activated_cb)
self._search_entry.connect('changed', self._entry_changed_cb)
tool_item.add(self._search_entry)
self._search_entry.show()
self._add_separator()
def _add_separator(self):
separator = gtk.SeparatorToolItem()
separator.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
separator.props.draw = False
self.insert(separator, -1)
separator.show()
def _entry_activated_cb(self, entry):
if self._autosearch_timer:
gobject.source_remove(self._autosearch_timer)
new_query = entry.props.text
if self._query != new_query:
self._query = new_query
self.emit('query-changed', self._query)
def _entry_changed_cb(self, entry):
if not entry.props.text:
entry.activate()
return
if self._autosearch_timer:
gobject.source_remove(self._autosearch_timer)
self._autosearch_timer = gobject.timeout_add(_AUTOSEARCH_TIMEOUT,
self._autosearch_timer_cb)
def _autosearch_timer_cb(self):
logging.debug('_autosearch_timer_cb')
self._autosearch_timer = None
self._search_entry.activate()
return False
class MeshBox(hippo.CanvasBox):
def __init__(self, shell):
hippo.CanvasBox.__init__(self)
self._shell = shell
self._model = shell.get_model().get_mesh()
self._buddies = {}
self._activities = {}
self._access_points = {}
self._mesh = {}
self._buddy_to_activity = {}
self._suspended = True
self._query = ''
self._toolbar = MeshToolbar()
self._toolbar.connect('query-changed', self._toolbar_query_changed_cb)
self.append(hippo.CanvasWidget(widget=self._toolbar))
self._layout_box = hippo.CanvasBox(background_color=0xe2e2e2ff)
self.append(self._layout_box, hippo.PACK_EXPAND)
self._layout = SpreadLayout()
self._layout_box.set_layout(self._layout)
for buddy_model in self._model.get_buddies():
self._add_alone_buddy(buddy_model)
self._model.connect('buddy-added', self._buddy_added_cb)
self._model.connect('buddy-removed', self._buddy_removed_cb)
self._model.connect('buddy-moved', self._buddy_moved_cb)
for activity_model in self._model.get_activities():
self._add_activity(activity_model)
self._model.connect('activity-added', self._activity_added_cb)
self._model.connect('activity-removed', self._activity_removed_cb)
for ap_model in self._model.get_access_points():
self._add_access_point(ap_model)
self._model.connect('access-point-added',
self._access_point_added_cb)
self._model.connect('access-point-removed',
self._access_point_removed_cb)
if self._model.get_mesh():
self._mesh_added_cb(self._model, self._model.get_mesh())
self._model.connect('mesh-added',
self._mesh_added_cb)
self._model.connect('mesh-removed',
self._mesh_removed_cb)
def _mesh_added_cb(self, model, meshdev):
self._add_mesh_icon(meshdev, 1)
self._add_mesh_icon(meshdev, 6)
self._add_mesh_icon(meshdev, 11)
def _mesh_removed_cb(self, model):
self._remove_mesh_icon(1)
self._remove_mesh_icon(6)
self._remove_mesh_icon(11)
def _buddy_added_cb(self, model, buddy_model):
self._add_alone_buddy(buddy_model)
def _buddy_removed_cb(self, model, buddy_model):
self._remove_buddy(buddy_model)
def _buddy_moved_cb(self, model, buddy_model, activity_model):
# Owner doesn't move from the center
if buddy_model.is_owner():
return
self._move_buddy(buddy_model, activity_model)
def _activity_added_cb(self, model, activity_model):
self._add_activity(activity_model)
def _activity_removed_cb(self, model, activity_model):
self._remove_activity(activity_model)
def _access_point_added_cb(self, model, ap_model):
self._add_access_point(ap_model)
def _access_point_removed_cb(self, model, ap_model):
self._remove_access_point(ap_model)
def _add_mesh_icon(self, meshdev, channel):
if self._mesh.has_key(channel):
self._remove_mesh_icon(channel)
if not meshdev:
return
self._mesh[channel] = MeshDeviceView(meshdev, channel)
self._layout.add(self._mesh[channel])
def _remove_mesh_icon(self, channel):
if not self._mesh.has_key(channel):
return
self._layout.remove(self._mesh[channel])
del self._mesh[channel]
def _add_alone_buddy(self, buddy_model):
icon = BuddyIcon(self._shell, buddy_model)
if buddy_model.is_owner():
vertical_offset = - style.GRID_CELL_SIZE
self._layout.add_center(icon, vertical_offset)
else:
self._layout.add(icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
self._buddies[buddy_model.get_key()] = icon
def _remove_alone_buddy(self, buddy_model):
icon = self._buddies[buddy_model.get_key()]
self._layout.remove(icon)
del self._buddies[buddy_model.get_key()]
icon.destroy()
def _remove_buddy(self, buddy_model):
key = buddy_model.get_key()
if self._buddies.has_key(key):
self._remove_alone_buddy(buddy_model)
else:
for activity in self._activities.values():
if activity.has_buddy_icon(key):
activity.remove_buddy_icon(key)
def _move_buddy(self, buddy_model, activity_model):
key = buddy_model.get_key()
self._remove_buddy(buddy_model)
if activity_model == None:
self._add_alone_buddy(buddy_model)
elif activity_model.get_id() in self._activities:
activity = self._activities[activity_model.get_id()]
icon = BuddyIcon(self._shell, buddy_model,
style.SMALL_ICON_SIZE)
activity.add_buddy_icon(buddy_model.get_key(), icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
def _add_activity(self, activity_model):
icon = ActivityView(self._shell, activity_model)
self._layout.add(icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
self._activities[activity_model.get_id()] = icon
def _remove_activity(self, activity_model):
icon = self._activities[activity_model.get_id()]
self._layout.remove(icon)
del self._activities[activity_model.get_id()]
icon.destroy()
def _add_access_point(self, ap_model):
meshdev = self._model.get_mesh()
icon = AccessPointView(ap_model, meshdev)
self._layout.add(icon)
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
self._access_points[ap_model.get_id()] = icon
def _remove_access_point(self, ap_model):
icon = self._access_points[ap_model.get_id()]
self._layout.remove(icon)
del self._access_points[ap_model.get_id()]
def suspend(self):
if not self._suspended:
self._suspended = True
for ap in self._access_points.values():
ap.props.paused = True
def resume(self):
if self._suspended:
self._suspended = False
for ap in self._access_points.values():
ap.props.paused = False
def _toolbar_query_changed_cb(self, toolbar, query):
self._query = query.lower()
for icon in self._layout_box.get_children():
if hasattr(icon, 'set_filter'):
icon.set_filter(self._query)
def focus_search_entry(self):
self._toolbar._search_entry.grab_focus()
-24
View File
@@ -1,24 +0,0 @@
# 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
from sugar.graphics.icon import CanvasIcon
from sugar import profile
class MyIcon(CanvasIcon):
def __init__(self, size):
CanvasIcon.__init__(self, size=size,
icon_name='computer-xo',
xo_color=profile.get_color())
-16
View File
@@ -1,16 +0,0 @@
# 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
-556
View File
@@ -1,556 +0,0 @@
# 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 colorsys
from gettext import gettext as _
import logging
import math
import os
import hippo
import gobject
import gtk
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.menuitem import MenuItem
from sugar.graphics.palette import Palette
from sugar.graphics import style
from sugar.graphics import xocolor
from sugar import profile
import proc_smaps
_MAX_ACTIVITIES = 6
_MIN_WEDGE_SIZE = 1.0 / _MAX_ACTIVITIES
_DONUT_SIZE = style.zoom(450)
# TODO: rgb_to_html and html_to_rgb are useful elsewhere
# we should put this in a common module
def rgb_to_html(r, g, b):
""" (r, g, b) tuple (in float format) -> #RRGGBB """
return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))
def html_to_rgb(html_color):
""" #RRGGBB -> (r, g, b) tuple (in float format) """
html_color = html_color.strip()
if html_color[0] == '#':
html_color = html_color[1:]
if len(html_color) != 6:
raise ValueError, "input #%s is not in #RRGGBB format" % html_color
r, g, b = html_color[:2], html_color[2:4], html_color[4:]
r, g, b = [int(n, 16) for n in (r, g, b)]
r, g, b = (r / 255.0, g / 255.0, b / 255.0)
return (r, g, b)
class ActivityIcon(CanvasIcon):
_INTERVAL = 200
__gsignals__ = {
'resume': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
'stop': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([]))
}
def __init__(self, activity):
self._orig_color = activity.get_icon_color()
self._icon_colors = self._compute_icon_colors()
self._direction = 0
self._level_max = len(self._icon_colors) - 1
self._level = self._level_max
color = self._icon_colors[self._level]
CanvasIcon.__init__(self, xo_color=color, cache=True,
size=style.MEDIUM_ICON_SIZE)
icon_path = activity.get_icon_path()
if icon_path:
self.props.file_name = icon_path
else:
self.props.icon_name = 'image-missing'
self._activity = activity
self._pulse_id = 0
self.size = _MIN_WEDGE_SIZE
palette = Palette(_('Starting...'))
self.set_palette(palette)
if activity.props.launching:
self._start_pulsing()
activity.connect('notify::launching', self._launching_changed_cb)
else:
self._setup_palette()
def _setup_palette(self):
palette = self.get_palette()
palette.set_primary_text(self._activity.get_title())
resume_menu_item = MenuItem(_('Resume'), 'activity-start')
resume_menu_item.connect('activate', self._resume_activate_cb)
palette.menu.append(resume_menu_item)
resume_menu_item.show()
# FIXME: kludge
if self._activity.get_type() != "org.laptop.JournalActivity":
stop_menu_item = MenuItem(_('Stop'), 'activity-stop')
stop_menu_item.connect('activate', self._stop_activate_cb)
palette.menu.append(stop_menu_item)
stop_menu_item.show()
def _launching_changed_cb(self, activity, pspec):
if not activity.props.launching:
self._stop_pulsing()
self._setup_palette()
def __del__(self):
self._cleanup()
def _cleanup(self):
if self._pulse_id:
gobject.source_remove(self._pulse_id)
self._pulse_id = 0
def _compute_icon_colors(self):
_LEVEL_MAX = 1.6
_LEVEL_STEP = 0.16
_LEVEL_MIN = 0.0
icon_colors = {}
level = _LEVEL_MIN
for i in range(0, int(_LEVEL_MAX / _LEVEL_STEP)):
icon_colors[i] = self._get_icon_color_for_level(level)
level += _LEVEL_STEP
return icon_colors
def _get_icon_color_for_level(self, level):
factor = math.sin(level)
h, s, v = colorsys.rgb_to_hsv(*html_to_rgb(self._orig_color.get_fill_color()))
new_fill = rgb_to_html(*colorsys.hsv_to_rgb(h, s * factor, v))
h, s, v = colorsys.rgb_to_hsv(*html_to_rgb(self._orig_color.get_stroke_color()))
new_stroke = rgb_to_html(*colorsys.hsv_to_rgb(h, s * factor, v))
return xocolor.XoColor("%s,%s" % (new_stroke, new_fill))
def _pulse_cb(self):
if self._direction == 1:
self._level += 1
if self._level > self._level_max:
self._direction = 0
self._level = self._level_max
elif self._direction == 0:
self._level -= 1
if self._level <= 0:
self._direction = 1
self._level = 0
self.props.xo_color = self._icon_colors[self._level]
self.emit_paint_needed(0, 0, -1, -1)
return True
def _start_pulsing(self):
if self._pulse_id:
return
self._pulse_id = gobject.timeout_add(self._INTERVAL, self._pulse_cb)
def _stop_pulsing(self):
if not self._pulse_id:
return
self._cleanup()
self._level = 100.0
self.props.xo_color = self._orig_color
def _resume_activate_cb(self, menuitem):
self.emit('resume')
def _stop_activate_cb(self, menuitem):
self.emit('stop')
def get_activity(self):
return self._activity
class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarActivitiesDonut'
def __init__(self, shell, **kwargs):
hippo.CanvasBox.__init__(self, **kwargs)
self._activities = []
self._shell = shell
self._angles = []
self._shell_mappings = proc_smaps.get_shared_mapping_names(os.getpid())
self._layout = _Layout()
self.set_layout(self._layout)
self._model = shell.get_model().get_home()
self._model.connect('activity-added', self._activity_added_cb)
self._model.connect('activity-removed', self._activity_removed_cb)
self._model.connect('pending-activity-changed', self._activity_changed_cb)
self.connect('button-release-event', self._button_release_event_cb)
def _get_icon_from_activity(self, activity):
for icon in self._activities:
if icon.get_activity().equals(activity):
return icon
def _activity_added_cb(self, model, activity):
self._add_activity(activity)
def _activity_removed_cb(self, model, activity):
self._remove_activity(activity)
def _activity_changed_cb(self, model, activity):
self.emit_paint_needed(0, 0, -1, -1)
def _remove_activity(self, activity):
icon = self._get_icon_from_activity(activity)
if icon:
self.remove(icon)
icon._cleanup()
self._activities.remove(icon)
self._compute_angles()
def _add_activity(self, activity):
icon = ActivityIcon(activity)
icon.connect('resume', self._activity_icon_resumed_cb)
icon.connect('stop', self._activity_icon_stop_cb)
self.append(icon, hippo.PACK_FIXED)
self._activities.append(icon)
self._compute_angles()
def _activity_icon_resumed_cb(self, icon):
activity = icon.get_activity()
activity_host = self._shell.get_activity(activity.get_activity_id())
if activity_host:
activity_host.present()
else:
logging.error("Could not find ActivityHost for activity %s" %
activity.get_activity_id())
def _activity_icon_stop_cb(self, icon):
activity = icon.get_activity()
activity_host = self._shell.get_activity(activity.get_activity_id())
if activity_host:
activity_host.close()
else:
logging.error("Could not find ActivityHost for activity %s" %
activity.get_activity_id())
def _get_activity(self, x, y):
# Compute the distance from the center.
[width, height] = self.get_allocation()
x -= width / 2
y -= height / 2
r = math.hypot(x, y)
# Ignore the click if it's not inside the donut
if r < self._get_inner_radius() or r > self._get_radius():
return None
# Now figure out where in the donut the click was.
angle = math.atan2(-y, -x) + math.pi
# Unfortunately, _get_angles() doesn't count from 0 to 2pi, it
# counts from roughly pi/2 to roughly 5pi/2. So we have to
# compare its return values against both angle and angle+2pi
high_angle = angle + 2 * math.pi
for index, activity in enumerate(self._model):
[angle_start, angle_end] = self._get_angles(index)
if angle_start < angle and angle_end > angle:
return activity
elif angle_start < high_angle and angle_end > high_angle:
return activity
return None
def _button_release_event_cb(self, item, event):
activity = self._get_activity(event.x, event.y)
if activity is None:
return False
activity_host = self._shell.get_activity(activity.get_activity_id())
if activity_host:
activity_host.present()
return True
def _set_fixed_arc_size(self):
"""Set fixed arc size"""
n = len(self._activities)
if n > _MAX_ACTIVITIES:
size = 1.0 / n
else:
size = 1.0 / _MAX_ACTIVITIES
for act in self._activities:
act.size = size
def _update_activity_sizes(self):
"""Currently the size of an activity on the donut does not
represent it's memory usage. This is disabled because it was
either not working perfectly or a little confusing. See #3605"""
self._set_fixed_arc_size()
return
# Get the memory mappings of each process that hosts an
# activity, and count how many activity instances each
# activity process hosts, and how many processes are mapping
# each shared library, etc
process_mappings = {}
num_activities = {}
num_mappings = {}
unknown_size_activities = 0
for activity in self._model:
pid = activity.get_pid()
if not pid:
# Still starting up, hasn't opened a window yet
unknown_size_activities += 1
continue
if num_activities.has_key(pid):
num_activities[pid] += 1
continue
try:
mappings = proc_smaps.get_mappings(pid, self._shell_mappings)
for mapping in mappings:
if mapping.shared > 0:
if num_mappings.has_key(mapping.name):
num_mappings[mapping.name] += 1
else:
num_mappings[mapping.name] = 1
process_mappings[pid] = mappings
num_activities[pid] = 1
except Exception, e:
logging.warn('ActivitiesDonut: could not read /proc/%s/smaps: %r'
% (pid, e))
# Compute total memory used per process
process_size = {}
total_activity_size = 0
for activity in self._model:
pid = activity.get_pid()
if not process_mappings.has_key(pid):
continue
mappings = process_mappings[pid]
size = 0
for mapping in mappings:
size += mapping.private
if mapping.shared > 0:
num = num_mappings[mapping.name]
size += mapping.shared / num
process_size[pid] = size
total_activity_size += size / num_activities[pid]
# Now, see how much free memory is left.
free_memory = 0
try:
meminfo = open('/proc/meminfo')
for line in meminfo.readlines():
if line.startswith('MemFree:') or line.startswith('SwapFree:'):
free_memory += int(line[9:-3])
meminfo.close()
except IOError:
logging.warn('ActivitiesDonut: could not read /proc/meminfo')
except (IndexError, ValueError):
logging.warn('ActivitiesDonut: /proc/meminfo was not in ' +
'expected format')
total_memory = float(total_activity_size + free_memory)
# Each activity has an ideal size of:
# process_size[pid] / num_activities[pid] / total_memory
# (And the free memory wedge is ideally free_memory /
# total_memory) However, no activity wedge is allowed to be
# smaller than _MIN_WEDGE_SIZE. This means the small
# activities will use up extra space, which would make the
# ring overflow. We fix that by reducing the large activities
# and the free space proportionately. If there are activities
# of unknown size, they are simply carved out of the free
# space.
free_percent = free_memory / total_memory
activity_sizes = []
overflow = 0.0
reducible = free_percent
for icon in self._activities:
pid = icon.get_activity().get_pid()
if process_size.has_key(pid):
icon.size = (process_size[pid] / num_activities[pid] /
total_memory)
if icon.size < _MIN_WEDGE_SIZE:
overflow += _MIN_WEDGE_SIZE - icon.size
icon.size = _MIN_WEDGE_SIZE
else:
reducible += icon.size - _MIN_WEDGE_SIZE
else:
icon.size = _MIN_WEDGE_SIZE
if reducible > 0.0:
reduction = overflow / reducible
if unknown_size_activities > 0:
unknown_percent = _MIN_WEDGE_SIZE * unknown_size_activities
if (free_percent * (1 - reduction) < unknown_percent):
# The free wedge won't be large enough to fit the
# unknown-size activities. So adjust things
overflow += unknown_percent - free_percent
reducible -= free_percent
reduction = overflow / reducible
if reduction > 0.0:
for icon in self._activities:
if icon.size > _MIN_WEDGE_SIZE:
icon.size -= (icon.size - _MIN_WEDGE_SIZE) * reduction
def _compute_angles(self):
self._angles = []
if len(self._activities) == 0:
return
# Normally we don't _update_activity_sizes() when launching a
# new activity; but if the new wedge would overflow the ring
# then we have no choice.
total = reduce(lambda s1,s2: s1 + s2,
[icon.size for icon in self._activities])
if total > 1.0:
self._update_activity_sizes()
# The first wedge (Journal) should be centered at 6 o'clock
size = self._activities[0].size or _MIN_WEDGE_SIZE
angle = (math.pi - size * 2 * math.pi) / 2
self._angles.append(angle)
for icon in self._activities:
size = icon.size or _MIN_WEDGE_SIZE
self._angles.append(self._angles[-1] + size * 2 * math.pi)
def redraw(self):
self._update_activity_sizes()
self._compute_angles()
self.emit_request_changed()
def _get_angles(self, index):
return [self._angles[index],
self._angles[(index + 1) % len(self._angles)]]
def _get_radius(self):
[width, height] = self.get_allocation()
return min(width, height) / 2
def _get_inner_radius(self):
return self._get_radius() * 0.5
def do_paint_below_children(self, cr, damaged_box):
[width, height] = self.get_allocation()
cr.translate(width / 2, height / 2)
radius = self._get_radius()
# Outer Ring
cr.set_source_rgb(0xf1 / 255.0, 0xf1 / 255.0, 0xf1 / 255.0)
cr.arc(0, 0, radius, 0, 2 * math.pi)
cr.fill()
# Selected Wedge
current_activity = self._model.get_pending_activity()
if current_activity is not None:
selected_index = self._model.index(current_activity)
[angle_start, angle_end] = self._get_angles(selected_index)
cr.new_path()
cr.move_to(0, 0)
cr.line_to(radius * math.cos(angle_start),
radius * math.sin(angle_start))
cr.arc(0, 0, radius, angle_start, angle_end)
cr.line_to(0, 0)
cr.set_source_rgb(1, 1, 1)
cr.fill()
# Edges
if len(self._model):
n_edges = len(self._model) + 1
else:
n_edges = 0
for i in range(0, n_edges):
cr.new_path()
cr.move_to(0, 0)
[angle, unused_angle] = self._get_angles(i)
cr.line_to(radius * math.cos(angle),
radius * math.sin(angle))
cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0)
cr.set_line_width(4)
cr.stroke_preserve()
# Inner Ring
cr.new_path()
cr.arc(0, 0, self._get_inner_radius(), 0, 2 * math.pi)
cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0)
cr.fill()
def do_allocate(self, width, height, origin_changed):
hippo.CanvasBox.do_allocate(self, width, height, origin_changed)
radius = (self._get_inner_radius() + self._get_radius()) / 2
for i, icon in enumerate(self._activities):
[angle_start, angle_end] = self._get_angles(i)
angle = angle_start + (angle_end - angle_start) / 2
[icon_width, icon_height] = icon.get_allocation()
x = int(radius * math.cos(angle)) - icon_width / 2
y = int(radius * math.sin(angle)) - icon_height / 2
self.set_position(icon, x + width / 2, y + height / 2)
class _Layout(gobject.GObject,hippo.CanvasLayout):
__gtype_name__ = 'SugarDonutLayout'
def __init__(self):
gobject.GObject.__init__(self)
def do_set_box(self, box):
self._box = box
def do_get_height_request(self, for_width):
return _DONUT_SIZE, _DONUT_SIZE
def do_get_width_request(self):
return _DONUT_SIZE, _DONUT_SIZE
def do_allocate(self, x, y, width, height,
req_width, req_height, origin_changed):
for child in self._box.get_layout_children():
min_width, child_width = child.get_width_request()
min_height, child_height = child.get_height_request(child_width)
[angle_start, angle_end] = self._box._get_angles(i)
angle = angle_start + (angle_end - angle_start) / 2
x = int(radius * math.cos(angle)) - icon_width / 2
y = int(radius * math.sin(angle)) - icon_height / 2
child.allocate(x + (width - child_width) / 2,
y + (height - child_height) / 2,
icon_width, icon_height, origin_changed)
-107
View File
@@ -1,107 +0,0 @@
# Copyright (C) 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 os
# /proc/PID/maps consists of a number of lines like this:
# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
# 006b1000-006bb000 rw-p 000b1000 fd:00 5767206 /bin/bash
# 006bb000-006c0000 rw-p 006bb000 00:00 0
# ...
# The fields are: address, permissions, offset, device, inode, and
# (for non-anonymous mappings) pathname.
#
# /proc/PID/smaps gives additional information for each mapping:
# 00400000-004b1000 r-xp 00000000 fd:00 5767206 /bin/bash
# Size: 708 kB
# Rss: 476 kB
# Shared_Clean: 468 kB
# Shared_Dirty: 0 kB
# Private_Clean: 8 kB
# Private_Dirty: 0 kB
# Referenced: 0 kb
#
# The "Referenced" line only appears in kernel 2.6.22 and later.
def get_shared_mapping_names(pid):
"""Returns a set of the files for which PID has a shared mapping"""
mappings = set()
infile = open("/proc/%s/maps" % pid, "r")
for line in infile:
# sharable mappings are non-anonymous and either read-only
# (permissions "r-..") or writable but explicitly marked
# shared ("rw.s")
fields = line.split()
if len(fields) < 6 or not fields[5].startswith('/'):
continue
if fields[1][0] != 'r' or (fields[1][1] == 'w' and fields[1][3] != 's'):
continue
mappings.add(fields[5])
infile.close()
return mappings
_smaps_lines_per_entry = None
def get_mappings(pid, ignored_shared_mappings):
"""Returns a list of (name, private, shared) tuples describing the
memory mappings of PID. Shared mappings named in
ignored_shared_mappings are ignored
"""
global _smaps_lines_per_entry
if _smaps_lines_per_entry is None:
if os.path.isfile('/proc/%s/clear_refs' % os.getpid()):
_smaps_lines_per_entry = 8
else:
_smaps_lines_per_entry = 7
mappings = []
smapfile = "/proc/%s/smaps" % pid
infile = open(smapfile, "r")
input = infile.read()
infile.close()
lines = input.splitlines()
for line_idx in range(0, len(lines), _smaps_lines_per_entry):
name_idx = lines[line_idx].find('/')
if name_idx == -1:
name = None
else:
name = lines[line_idx][name_idx:]
private_clean = int(lines[line_idx + 5][14:-3])
private_dirty = int(lines[line_idx + 6][14:-3])
if name in ignored_shared_mappings:
shared_clean = 0
shared_dirty = 0
else:
shared_clean = int(lines[line_idx + 3][14:-3])
shared_dirty = int(lines[line_idx + 4][14:-3])
mapping = Mapping(name, private_clean + private_dirty,
shared_clean + shared_dirty)
mappings.append (mapping)
return mappings
class Mapping:
def __init__ (self, name, private, shared):
self.name = name
self.private = private
self.shared = shared
-108
View File
@@ -1,108 +0,0 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import math
import gobject
import hippo
from sugar.graphics import style
_BASE_DISTANCE = style.zoom(15)
_CHILDREN_FACTOR = style.zoom(3)
class SnowflakeLayout(gobject.GObject,hippo.CanvasLayout):
__gtype_name__ = 'SugarSnowflakeLayout'
def __init__(self):
gobject.GObject.__init__(self)
self._nflakes = 0
def add(self, child, center=False):
if not center:
self._nflakes += 1
self._box.append(child)
box_child = self._box.find_box_child(child)
box_child.is_center = center
def remove(self, child):
box_child = self._box.find_box_child(child)
if not box_child.is_center:
self._nflakes -= 1
self._box.remove(child)
def do_set_box(self, box):
self._box = box
def do_get_height_request(self, for_width):
size = self._calculate_size()
return (size, size)
def do_get_width_request(self):
size = self._calculate_size()
return (size, size)
def do_allocate(self, x, y, width, height,
req_width, req_height, origin_changed):
r = self._get_radius()
index = 0
for child in self._box.get_layout_children():
cx = x + width / 2
cy = x + height / 2
min_width, child_width = child.get_width_request()
min_height, child_height = child.get_height_request(child_width)
if child.is_center:
child.allocate(x + (width - child_width) / 2,
y + (height - child_height) / 2,
child_width, child_height, origin_changed)
else:
angle = 2 * math.pi * index / self._nflakes
dx = math.cos(angle) * r
dy = math.sin(angle) * r
child_x = int(x + (width - child_width) / 2 + dx)
child_y = int(y + (height - child_height) / 2 + dy)
child.allocate(child_x, child_y, child_width,
child_height, origin_changed)
index += 1
def _get_radius(self):
radius = int(_BASE_DISTANCE + _CHILDREN_FACTOR * self._nflakes)
for child in self._box.get_layout_children():
if child.is_center:
[min_w, child_w] = child.get_width_request()
[min_h, child_h] = child.get_height_request(child_w)
radius += max(child_w, child_h) / 2
return radius
def _calculate_size(self):
thickness = 0
for child in self._box.get_layout_children():
[min_width, child_width] = child.get_width_request()
[min_height, child_height] = child.get_height_request(child_width)
thickness = max(thickness, max(child_width, child_height))
return self._get_radius() * 2 + thickness
-246
View File
@@ -1,246 +0,0 @@
# Copyright (C) 2007 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
from numpy import array
from random import random
import hippo
import gobject
import gtk
from sugar.graphics import style
_PLACE_TRIALS = 20
_MAX_WEIGHT = 255
_CELL_SIZE = 4
class _Grid(gobject.GObject):
__gsignals__ = {
'child-changed' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self, width, height):
gobject.GObject.__init__(self)
self.width = width
self.height = height
self._children = []
self._collisions = []
self._collisions_sid = 0
self._array = array([0], dtype='b')
self._array.resize(width * height)
def add(self, child, width, height):
trials = _PLACE_TRIALS
weight = _MAX_WEIGHT
while trials > 0 and weight:
x = int(random() * (self.width - width))
y = int(random() * (self.height - height))
rect = gtk.gdk.Rectangle(x, y, width, height)
new_weight = self._compute_weight(rect)
if weight > new_weight:
weight = new_weight
trials -= 1
child.grid_rect = rect
child.locked = False
self._add_child(child)
if weight > 0:
self._detect_collisions(child)
def remove(self, child):
self._children.remove(child)
self._remove_weight(child.grid_rect)
child.grid_rect = None
def _add_child(self, child):
self._children.append(child)
self.add_weight(child.grid_rect)
def _move_child(self, child, new_rect):
self._remove_weight(child.grid_rect)
self.add_weight(new_rect)
child.grid_rect = new_rect
self.emit('child-changed', child)
def _shift_child(self, child):
rect = child.grid_rect
weight = self._compute_weight(rect)
new_rects = []
if (rect.x + rect.width < self.width - 1):
new_rects.append(gtk.gdk.Rectangle(rect.x + 1, rect.y,
rect.width, rect.height))
if (rect.x - 1 > 0):
new_rects.append(gtk.gdk.Rectangle(rect.x - 1, rect.y,
rect.width, rect.height))
if (rect.y + rect.height < self.height - 1):
new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y + 1,
rect.width, rect.height))
if (rect.y - 1 > 0):
new_rects.append(gtk.gdk.Rectangle(rect.x, rect.y - 1,
rect.width, rect.height))
best_rect = None
for new_rect in new_rects:
new_weight = self._compute_weight(new_rect)
if new_weight < weight:
best_rect = new_rect
weight = new_weight
if best_rect:
self._move_child(child, best_rect)
return weight
def _solve_collisions(self):
for collision in self._collisions[:]:
weight = self._shift_child(collision)
if not weight:
self._collisions.remove(collision)
return (len(self._collisions) > 0)
def _detect_collisions(self, child):
collision_found = False
for c in self._children:
intersection = child.grid_rect.intersect(c.grid_rect)
if c != child and intersection.width > 0:
if c not in self._collisions:
collision_found = True
self._collisions.append(c)
if collision_found:
if child not in self._collisions:
self._collisions.append(child)
# if len(self._collisions) and not self._collisions_sid:
# self._collisions_sid = gobject.idle_add(self._solve_collisions)
def add_weight(self, rect):
for i in range(rect.x, rect.x + rect.width):
for j in range(rect.y, rect.y + rect.height):
self[j, i] += 1
def _remove_weight(self, rect):
for i in range(rect.x, rect.x + rect.width):
for j in range(rect.y, rect.y + rect.height):
self[j, i] -= 1
def _compute_weight(self, rect):
weight = 0
for i in range(rect.x, rect.x + rect.width):
for j in range(rect.y, rect.y + rect.height):
weight += self[j, i]
return weight
def __getitem__(self, (row, col)):
return self._array[col + row * self.width]
def __setitem__(self, (row, col), value):
self._array[col + row * self.width] = value
class SpreadLayout(gobject.GObject, hippo.CanvasLayout):
__gtype_name__ = 'SugarSpreadLayout'
def __init__(self):
gobject.GObject.__init__(self)
min_width, width = self.do_get_width_request()
min_height, height = self.do_get_height_request(width)
self._grid = _Grid(width / _CELL_SIZE, height / _CELL_SIZE)
self._grid.connect('child-changed', self._grid_child_changed_cb)
def add_center(self, child, vertical_offset=0):
self._box.append(child)
width, height = self._get_child_grid_size(child)
rect = gtk.gdk.Rectangle(int((self._grid.width - width) / 2),
int((self._grid.height - height) / 2),
width + 1, height + 1)
self._grid.add_weight(rect)
box_child = self._box.find_box_child(child)
box_child.grid_rect = None
box_child.vertical_offset = vertical_offset
def add(self, child):
self._box.append(child)
width, height = self._get_child_grid_size(child)
box_child = self._box.find_box_child(child)
self._grid.add(box_child, width, height)
def remove(self, child):
box_child = self._box.find_box_child(child)
self._grid.remove(box_child)
self._box.remove(child)
def do_set_box(self, box):
self._box = box
def do_get_height_request(self, for_width):
return 0, gtk.gdk.screen_height() - style.GRID_CELL_SIZE
def do_get_width_request(self):
return 0, gtk.gdk.screen_width()
def do_allocate(self, x, y, width, height,
req_width, req_height, origin_changed):
for child in self._box.get_layout_children():
# We need to always get requests to not confuse hippo
min_w, child_width = child.get_width_request()
min_h, child_height = child.get_height_request(child_width)
rect = child.grid_rect
if child.grid_rect:
child.allocate(rect.x * _CELL_SIZE,
rect.y * _CELL_SIZE,
rect.width * _CELL_SIZE,
rect.height * _CELL_SIZE,
origin_changed)
else:
vertical_offset = child.vertical_offset
child_x = x + (width - child_width) / 2
child_y = y + (height - child_height + vertical_offset) / 2
child.allocate(child_x, child_y, child_width, child_height,
origin_changed)
def _get_child_grid_size(self, child):
min_width, width = child.get_width_request()
min_height, height = child.get_height_request(width)
return int(width / _CELL_SIZE), int(height / _CELL_SIZE)
def _grid_child_changed_cb(self, grid, box_child):
box_child.item.emit_request_changed()
-93
View File
@@ -1,93 +0,0 @@
# Copyright (C) 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 hippo
import gobject
from sugar.graphics import style
from sugar.graphics import animator
from view.home.MyIcon import MyIcon
from view.home.spreadlayout import SpreadLayout
class _Animation(animator.Animation):
def __init__(self, icon, start_size, end_size):
animator.Animation.__init__(self, 0.0, 1.0)
self._icon = icon
self.start_size = start_size
self.end_size = end_size
def next_frame(self, current):
d = (self.end_size - self.start_size) * current
self._icon.props.size = self.start_size + d
class _Layout(gobject.GObject,hippo.CanvasLayout):
__gtype_name__ = 'SugarTransitionBoxLayout'
def __init__(self):
gobject.GObject.__init__(self)
def do_set_box(self, box):
self._box = box
def do_get_height_request(self, for_width):
return 0, 0
def do_get_width_request(self):
return 0, 0
def do_allocate(self, x, y, width, height,
req_width, req_height, origin_changed):
for child in self._box.get_layout_children():
min_width, child_width = child.get_width_request()
min_height, child_height = child.get_height_request(child_width)
child.allocate(x + (width - child_width) / 2,
y + (height - child_height) / 2,
child_width, child_height, origin_changed)
class TransitionBox(hippo.CanvasBox):
__gtype_name__ = 'SugarTransitionBox'
__gsignals__ = {
'completed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([]))
}
def __init__(self):
hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff)
self._size = style.XLARGE_ICON_SIZE
self._layout = _Layout()
self.set_layout(self._layout)
self._my_icon = MyIcon(self._size)
self.append(self._my_icon)
self._animator = animator.Animator(0.3)
self._animator.connect('completed', self._animation_completed_cb)
def _animation_completed_cb(self, anim):
self.emit('completed')
def set_size(self, size):
self._animator.remove_all()
self._animator.add(_Animation(self._my_icon, self._size, size))
self._animator.start()
self._size = size
-237
View File
@@ -1,237 +0,0 @@
# 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 os
import signal
import logging
import subprocess
import dbus
import gtk
from hardware import hardwaremanager
from model.shellmodel import ShellModel
from sugar._sugarext import KeyGrabber
_BRIGHTNESS_STEP = 2
_VOLUME_STEP = 10
_BRIGHTNESS_MAX = 15
_VOLUME_MAX = 100
_actions_table = {
'F1' : 'zoom_mesh',
'F2' : 'zoom_friends',
'F3' : 'zoom_home',
'F4' : 'zoom_activity',
'F9' : 'brightness_down',
'F10' : 'brightness_up',
'<ctrl>F9' : 'brightness_min',
'<ctrl>F10' : 'brightness_max',
'F11' : 'volume_down',
'F12' : 'volume_up',
'<ctrl>F11' : 'volume_min',
'<ctrl>F12' : 'volume_max',
'<alt>1' : 'screenshot',
'<alt>f' : 'frame',
'0x93' : 'frame',
'<alt>o' : 'overlay',
'0xE0' : 'overlay',
'0xEB' : 'rotate',
'<alt>r' : 'rotate',
'<alt>q' : 'quit_emulator',
'<alt>Tab' : 'next_window',
'<alt>n' : 'next_window',
'<ctrl><alt>Tab' : 'previous_window',
'<alt>p' : 'previous_window',
'<ctrl>Escape' : 'close_window',
'<ctrl>q' : 'close_window',
'0xDC' : 'open_search',
'<ctrl>o' : 'open_search',
'<alt>s' : 'say_text'
}
J_DBUS_SERVICE = 'org.laptop.Journal'
J_DBUS_PATH = '/org/laptop/Journal'
J_DBUS_INTERFACE = 'org.laptop.Journal'
SPEECH_DBUS_SERVICE = 'org.laptop.Speech'
SPEECH_DBUS_PATH = '/org/laptop/Speech'
SPEECH_DBUS_INTERFACE = 'org.laptop.Speech'
class KeyHandler(object):
def __init__(self, shell):
self._shell = shell
self._screen_rotation = 0
self._key_pressed = None
self._keycode_pressed = 0
self._keystate_pressed = 0
self._speech_proxy = None
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed',
self._key_pressed_cb)
for key in _actions_table.keys():
self._key_grabber.grab(key)
def _change_volume(self, step=None, value=None):
hw_manager = hardwaremanager.get_manager()
if step is not None:
volume = hw_manager.get_volume() + step
elif value is not None:
volume = value
volume = min(max(0, volume), _VOLUME_MAX)
hw_manager.set_volume(volume)
hw_manager.set_mute(volume == 0)
def _change_brightness(self, step=None, value=None):
hw_manager = hardwaremanager.get_manager()
if step is not None:
level = hw_manager.get_display_brightness() + step
elif value is not None:
level = value
level = min(max(0, level), _BRIGHTNESS_MAX)
hw_manager.set_display_brightness(level)
if level == 0:
hw_manager.set_display_mode(hardwaremanager.B_AND_W_MODE)
else:
hw_manager.set_display_mode(hardwaremanager.COLOR_MODE)
def _get_speech_proxy(self):
if self._speech_proxy is None:
bus = dbus.SessionBus()
speech_obj = bus.get_object(SPEECH_DBUS_SERVICE, SPEECH_DBUS_PATH)
self._speech_proxy = dbus.Interface(speech_obj, SPEECH_DBUS_INTERFACE)
return self._speech_proxy
def _on_speech_err(self, ex):
logging.error("An error occurred with the ESpeak service: %r" % (ex, ))
def _primary_selection_cb(self, clipboard, text, user_data):
logging.debug('KeyHandler._primary_selection_cb: %r' % text)
if text:
self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \
error_handler=self._on_speech_err)
def handle_say_text(self):
clipboard = gtk.clipboard_get(selection="PRIMARY")
clipboard.request_text(self._primary_selection_cb)
def handle_previous_window(self):
self._shell.activate_previous_activity()
def handle_next_window(self):
self._shell.activate_next_activity()
def handle_close_window(self):
self._shell.close_current_activity()
def handle_zoom_mesh(self):
self._shell.set_zoom_level(ShellModel.ZOOM_MESH)
def handle_zoom_friends(self):
self._shell.set_zoom_level(ShellModel.ZOOM_FRIENDS)
def handle_zoom_home(self):
self._shell.set_zoom_level(ShellModel.ZOOM_HOME)
def handle_zoom_activity(self):
self._shell.set_zoom_level(ShellModel.ZOOM_ACTIVITY)
def handle_brightness_max(self):
self._change_brightness(value=_BRIGHTNESS_MAX)
def handle_brightness_min(self):
self._change_brightness(value=0)
def handle_volume_max(self):
self._change_volume(value=_VOLUME_MAX)
def handle_volume_min(self):
self._change_volume(value=0)
def handle_brightness_up(self):
self._change_brightness(step=_BRIGHTNESS_STEP)
def handle_brightness_down(self):
self._change_brightness(step=-_BRIGHTNESS_STEP)
def handle_volume_up(self):
self._change_volume(step=_VOLUME_STEP)
def handle_volume_down(self):
self._change_volume(step=-_VOLUME_STEP)
def handle_screenshot(self):
self._shell.take_screenshot()
def handle_frame(self):
self._shell.get_frame().notify_key_press()
def handle_overlay(self):
self._shell.toggle_chat_visibility()
def handle_rotate(self):
states = [ 'normal', 'left', 'inverted', 'right']
self._screen_rotation += 1
if self._screen_rotation == len(states):
self._screen_rotation = 0
subprocess.Popen(['xrandr', '-o', states[self._screen_rotation]])
def handle_quit_emulator(self):
if os.environ.has_key('SUGAR_EMULATOR_PID'):
pid = int(os.environ['SUGAR_EMULATOR_PID'])
os.kill(pid, signal.SIGTERM)
def focus_journal_search(self):
bus = dbus.SessionBus()
obj = bus.get_object(J_DBUS_SERVICE, J_DBUS_PATH)
journal = dbus.Interface(obj, J_DBUS_INTERFACE)
journal.FocusSearch({})
def handle_open_search(self):
self.focus_journal_search()
def _key_pressed_cb(self, grabber, keycode, state):
key = grabber.get_key(keycode, state)
logging.debug('_key_pressed_cb: %i %i %s' % (keycode, state, key))
if key:
self._key_pressed = key
self._keycode_pressed = keycode
self._keystate_pressed = state
"""
status = gtk.gdk.keyboard_grab(gtk.gdk.get_default_root_window(),
owner_events=False, time=0L)
if status != gtk.gdk.GRAB_SUCCESS:
logging.error("KeyHandler._key_pressed_cb(): keyboard grab failed: " + status)
"""
action = _actions_table[key]
method = getattr(self, 'handle_' + action)
method()
return True
return False
-90
View File
@@ -1,90 +0,0 @@
# 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 gobject
from sugar.graphics.icon import CanvasIcon
class PulsingIcon(CanvasIcon):
__gproperties__ = {
'paused' : (bool, None, None, False,
gobject.PARAM_READWRITE),
'colors' : (object, None, None,
gobject.PARAM_READWRITE),
'pulse-time' : (float, None, None,
0.0, 500.0, 0.0,
gobject.PARAM_READWRITE),
}
def __init__(self, **kwargs):
self._paused = False
self._pulse_time = 0.0
self._colors = None
self._pulse_sid = 0
self._pos = 0
CanvasIcon.__init__(self, **kwargs)
def do_set_property(self, pspec, value):
CanvasIcon.do_set_property(self, pspec, value)
if pspec.name == 'pulse-time':
self._pulse_time = value
self._stop()
if not self._paused and self._pulse_time > 0.0:
self._start()
elif pspec.name == 'colors':
self._colors = value
self._pos = 0
self._update_colors()
elif pspec.name == 'paused':
self._paused = value
if not self._paused and self._pulse_time > 0.0:
self._start()
else:
self._stop()
def do_get_property(self, pspec):
CanvasIcon.do_get_property(self, pspec)
if pspec.name == 'pulse-time':
return self._pulse_time
elif pspec.name == 'colors':
return self._colors
def _update_colors(self):
self.props.stroke_color = self._colors[self._pos][0]
self.props.fill_color = self._colors[self._pos][1]
def _pulse_timeout(self):
if self._colors:
self._update_colors()
self._pos += 1
if self._pos == len(self._colors):
self._pos = 0
return True
def _start(self):
if self._pulse_sid == 0:
self._pulse_sid = gobject.timeout_add(
int(self._pulse_time * 1000), self._pulse_timeout)
def _stop(self):
if self._pulse_sid:
gobject.source_remove(self._pulse_sid)
self._pulse_sid = 0