This commit is contained in:
Dan Williams 2006-09-15 16:50:06 -04:00
commit 922b7238b9
54 changed files with 519 additions and 483 deletions

2
.gitignore vendored
View File

@ -38,7 +38,7 @@ po/*.gmo
sugar/__installed__.py
sugar/__uninstalled__.py
tools/sugar-setup-activity
shell/PresenceService/org.laptop.Presence.service
services/presence/org.laptop.Presence.service
threadframe
config.guess
config.sub

View File

@ -1,4 +1,4 @@
SUBDIRS = activities bindings po shell sugar tools
SUBDIRS = activities bindings po shell sugar services tools
ACLOCAL_AMFLAGS = -I m4

View File

@ -45,12 +45,15 @@ activities/terminal/Makefile
bindings/Makefile
bindings/globalkeys/Makefile
bindings/threadframe/Makefile
services/Makefile
services/presence/Makefile
shell/Makefile
shell/conf/Makefile
shell/data/Makefile
shell/home/Makefile
shell/frame/Makefile
shell/PresenceService/Makefile
shell/view/Makefile
shell/view/home/Makefile
shell/view/frame/Makefile
shell/model/Makefile
sugar/Makefile
sugar/__installed__.py
sugar/__uninstalled__.py

1
services/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = presence

View File

@ -5,7 +5,7 @@ service_DATA = $(service_in_files:.service.in=.service)
$(service_DATA): $(service_in_files) Makefile
@sed -e "s|\@bindir\@|$(bindir)|" $< > $@
sugardir = $(pkgdatadir)/shell/PresenceService
sugardir = $(pkgdatadir)/services/presence
sugar_PYTHON = \
__init__.py \
Activity.py \
@ -13,6 +13,8 @@ sugar_PYTHON = \
PresenceService.py \
Service.py
bin_SCRIPTS = sugar-presence-service
DISTCLEANFILES = $(service_DATA)
EXTRA_DIST = $(service_in_files)
EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS)

View File

@ -1,7 +1,7 @@
#!/usr/bin/python
import logging
from PresenceService import PresenceService
from presence import PresenceService
import sugar.logger
sugar.logger.start('PresenceService')

View File

@ -1,54 +0,0 @@
import conf
from sugar.chat.BuddyChat import BuddyChat
from sugar.activity import ActivityFactory
from sugar.presence import PresenceService
from sugar.p2p.Stream import Stream
from sugar.chat.Chat import Chat
class ChatController:
def __init__(self, shell):
self._shell = shell
self._id_to_name = {}
self._name_to_chat = {}
self._shell.connect('activity-closed', self.__activity_closed_cb)
def __activity_closed_cb(self, shell, activity):
activity_id = activity.get_id()
if self._id_to_name.has_key(activity_id):
name = self._id_to_name[activity_id]
del self._name_to_chat[name]
del self._id_to_name[activity_id]
def listen(self):
self._pservice = PresenceService.get_instance()
self._pservice.register_service_type(BuddyChat.SERVICE_TYPE)
profile = conf.get_profile()
self._service = self._pservice.register_service(profile.get_nick_name(),
BuddyChat.SERVICE_TYPE)
self._buddy_stream = Stream.new_from_service(self._service)
self._buddy_stream.set_data_listener(self._recv_message)
def open_chat_activity(self, buddy):
service = buddy.get_service_of_type(BuddyChat.SERVICE_TYPE)
if service:
activity = self._shell.start_activity('com.redhat.Sugar.ChatActivity')
activity.execute('connect', [service.object_path()])
self._name_to_chat[buddy.get_name()] = activity
self._id_to_name[activity.get_id()] = buddy.get_name()
def _get_chat_activity(self, buddy):
nick = buddy.get_name()
if not self._name_to_chat.has_key(nick):
self.open_chat_activity(buddy)
return self._name_to_chat[nick]
def _recv_message(self, address, message):
[nick, msg] = Chat.deserialize_message(message)
buddy = self._pservice.get_buddy_by_name(nick)
if buddy:
activity = self._get_chat_activity(buddy)
if activity:
activity.execute('message', [message])

View File

@ -1,24 +1,14 @@
SUBDIRS = conf data frame home PresenceService
SUBDIRS = conf data model view
bin_SCRIPTS = \
sugar \
sugar-activity \
sugar-activity-factory \
sugar-console \
sugar-presence-service
sugar-console
sugardir = $(pkgdatadir)/shell
sugar_PYTHON = \
__init__.py \
ActivityHost.py \
ChatController.py \
ConsoleWindow.py \
FirstTimeDialog.py \
FriendPopup.py \
Friends.py \
Invites.py \
Owner.py \
Shell.py \
Session.py
EXTRA_DIST = $(bin_SCRIPTS)

View File

@ -1,8 +1,8 @@
import os
import gtk
from Shell import Shell
from ConsoleWindow import ConsoleWindow
from view.Shell import Shell
from model.ShellModel import ShellModel
from sugar import env
from sugar import logger
@ -10,7 +10,7 @@ from sugar.session.Process import Process
from sugar.session.DbusProcess import DbusProcess
from sugar.session.MatchboxProcess import MatchboxProcess
from FirstTimeDialog import FirstTimeDialog
from view.FirstTimeDialog import FirstTimeDialog
import conf
class DBusMonitorProcess(Process):
@ -47,11 +47,8 @@ class Session:
dbm = DBusMonitorProcess()
dbm.start()
console = ConsoleWindow()
logger.start('Shell', console)
shell = Shell()
shell.set_console(console)
model = ShellModel()
shell = Shell(model)
from sugar import TracebackUtils
tbh = TracebackUtils.TracebackHelper()

View File

@ -1,174 +0,0 @@
import os
import logging
import dbus
import dbus.glib
import gtk
import gobject
import wnck
from home.HomeWindow import HomeWindow
from Owner import ShellOwner
from sugar.presence import PresenceService
from ActivityHost import ActivityHost
from ChatController import ChatController
from sugar.activity import ActivityFactory
from sugar.activity import Activity
from frame.Frame import Frame
from globalkeys import KeyGrabber
import conf
import sugar
class ShellDbusService(dbus.service.Object):
def __init__(self, shell, bus_name):
dbus.service.Object.__init__(self, bus_name, '/com/redhat/Sugar/Shell')
self._shell = shell
def __show_console_idle(self):
self._shell.show_console()
@dbus.service.method('com.redhat.Sugar.Shell')
def show_console(self):
gobject.idle_add(self.__show_console_idle)
class Shell(gobject.GObject):
__gsignals__ = {
'activity-opened': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'activity-closed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._screen = wnck.screen_get_default()
self._hosts = {}
self._current_window = None
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed', self.__global_key_pressed_cb)
self._key_grabber.grab('F1')
self._key_grabber.grab('F2')
self._key_grabber.grab('F3')
self._key_grabber.grab('F4')
self._key_grabber.grab('F5')
self._key_grabber.grab('F6')
self._home_window = HomeWindow(self)
self._home_window.show()
self.set_zoom_level(sugar.ZOOM_HOME)
self._screen.connect('window-opened', self.__window_opened_cb)
self._screen.connect('window-closed', self.__window_closed_cb)
self._screen.connect('active-window-changed',
self.__active_window_changed_cb)
session_bus = dbus.SessionBus()
bus_name = dbus.service.BusName('com.redhat.Sugar.Shell', bus=session_bus)
ShellDbusService(self, bus_name)
PresenceService.start()
self._pservice = PresenceService.get_instance()
self._owner = ShellOwner(self)
self._owner.announce()
self._home_window.set_owner(self._owner)
self._chat_controller = ChatController(self)
self._chat_controller.listen()
self._frame = Frame(self, self._owner)
self._frame.show_and_hide(10)
def __global_key_pressed_cb(self, grabber, key):
if key == 'F1':
self.set_zoom_level(sugar.ZOOM_ACTIVITY)
elif key == 'F2':
self.set_zoom_level(sugar.ZOOM_HOME)
elif key == 'F3':
self.set_zoom_level(sugar.ZOOM_FRIENDS)
elif key == 'F4':
self.set_zoom_level(sugar.ZOOM_MESH)
elif key == 'F5':
self._frame.toggle_visibility()
elif key == 'F6':
ActivityFactory.create('org.sugar.Terminal')
def set_console(self, console):
self._console = console
def __window_opened_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
host = ActivityHost(self, window)
self._hosts[window.get_xid()] = host
self.emit('activity-opened', host)
def __active_window_changed_cb(self, screen):
window = screen.get_active_window()
if window and window.get_window_type() == wnck.WINDOW_NORMAL:
if self._current_window != window:
self._current_window = window
self.emit('activity-changed', self.get_current_activity())
def __window_closed_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
xid = window.get_xid()
if self._hosts.has_key(xid):
host = self._hosts[xid]
self.emit('activity-closed', host)
del self._hosts[xid]
def get_activity(self, activity_id):
for host in self._hosts.values():
if host.get_id() == activity_id:
return host
return None
def get_current_activity(self):
if self._current_window != None:
xid = self._current_window.get_xid()
return self._hosts[xid]
else:
return None
def show_console(self):
self._console.show()
activity = self.get_current_activity()
if activity:
registry = conf.get_activity_registry()
module = registry.get_activity(activity.get_type())
self._console.set_page(module.get_id())
def join_activity(self, bundle_id, activity_id):
activity = self.get_activity(activity_id)
if activity:
activity.present()
else:
activity_ps = self._pservice.get_activity(activity_id)
if activity_ps:
activity = ActivityFactory.create(bundle_id)
activity.join(activity_ps.object_path())
else:
logging.error('Cannot start activity.')
def start_activity(self, activity_type):
activity = ActivityFactory.create(activity_type)
activity.execute('test', [])
return activity
def get_chat_controller(self):
return self._chat_controller
def set_zoom_level(self, level):
if level == sugar.ZOOM_ACTIVITY:
self._screen.toggle_showing_desktop(False)
else:
self._screen.toggle_showing_desktop(True)
self._home_window.set_zoom_level(level)

View File

@ -1,84 +0,0 @@
import random
import goocanvas
from sugar.canvas.IconItem import IconItem
from home.IconLayout import IconLayout
from home.MyIcon import MyIcon
from FriendPopup import FriendPopup
from sugar.canvas.Grid import Grid
class FriendIcon(IconItem):
def __init__(self, shell, friend):
IconItem.__init__(self, icon_name='stock-buddy',
color=friend.get_color(), size=96)
self._shell = shell
self._friend = friend
self._popup = None
self.connect('popup', self._popup_cb)
self.connect('popdown', self._popdown_cb)
def get_friend(self):
return self._friend
def _popup_cb(self, icon, x1, y1, x2, y2):
grid = Grid()
if not self._popup:
self._popup = FriendPopup(self._shell, grid, icon.get_friend())
[grid_x1, grid_y1] = grid.convert_from_screen(x1, y1)
[grid_x2, grid_y2] = grid.convert_from_screen(x2, y2)
if grid_x2 + self._popup.get_width() + 1 > Grid.ROWS:
grid_x = grid_x1 - self._popup.get_width() + 1
else:
grid_x = grid_x2 - 1
grid_y = grid_y1
if grid_y < 0:
grid_y = 0
if grid_y + self._popup.get_width() > Grid.ROWS:
grid_y = Grid.ROWS - self._popup.get_width()
grid.set_constraints(self._popup, grid_x, grid_y,
self._popup.get_width(), self._popup.get_height())
self._popup.show()
def _popup_destroy_cb(self, popup):
self._popup = None
def _popdown_cb(self, friend):
if self._popup:
self._popup.connect('destroy', self._popup_destroy_cb)
self._popup.popdown()
class FriendsGroup(goocanvas.Group):
def __init__(self, shell, friends):
goocanvas.Group.__init__(self)
self._shell = shell
self._icon_layout = IconLayout(1200, 900)
self._friends = friends
me = MyIcon(100)
me.translate(600 - (me.get_property('size') / 2),
450 - (me.get_property('size') / 2))
self.add_child(me)
for friend in self._friends:
self.add_friend(friend)
friends.connect('friend-added', self._friend_added_cb)
def add_friend(self, friend):
icon = FriendIcon(self._shell, friend)
self.add_child(icon)
self._icon_layout.add_icon(icon)
def _friend_added_cb(self, data_model, friend):
self.add_friend(friend)

24
shell/model/BuddyInfo.py Normal file
View File

@ -0,0 +1,24 @@
from sugar.presence import PresenceService
from sugar.canvas.IconColor import IconColor
class BuddyInfo:
def __init__(self, buddy=None):
if buddy:
self.set_name(buddy.get_name())
self.set_color(buddy.get_color())
def set_name(self, name):
self._name = name
def set_color(self, color_string):
self._color = IconColor(color_string)
def get_name(self):
return self._name
def get_color(self):
return self._color
def get_buddy(self):
pservice = PresenceService.get_instance()
return pservice.get_buddy_by_name(self._name)

View File

@ -3,67 +3,54 @@ from ConfigParser import ConfigParser
import gobject
from sugar.canvas.IconColor import IconColor
from sugar.presence import PresenceService
from model.BuddyInfo import BuddyInfo
from sugar import env
class Friend:
def __init__(self, name, color):
self._name = name
self._color = color
def get_name(self):
return self._name
def get_color(self):
return IconColor(self._color)
def get_buddy(self):
pservice = PresenceService.get_instance()
return pservice.get_buddy_by_name(self._name)
class Friends(gobject.GObject):
__gsignals__ = {
'friend-added': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([object])),
'friend-removed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([object])),
gobject.TYPE_NONE, ([str])),
}
def __init__(self):
gobject.GObject.__init__(self)
self._list = []
self._friends = {}
self._path = os.path.join(env.get_profile_path(), 'friends')
self.load()
def has_buddy(self, buddy):
for friend in self:
if friend.get_name() == buddy.get_name():
return True
return False
return self._friends.has_key(buddy.get_name())
def add_friend(self, name, color):
friend = Friend(name, color)
self._list.append(friend)
def add_friend(self, buddy_info):
self._friends[buddy_info.get_name()] = buddy_info
self.emit('friend-added', buddy_info)
self.emit('friend-added', friend)
def add_buddy(self, buddy):
def make_friend(self, buddy):
if not self.has_buddy(buddy):
self.add_friend(buddy.get_name(), buddy.get_color())
self.add_friend(BuddyInfo(buddy))
self.save()
def remove(self, buddy_info):
del self._friends[buddy_info.get_name()]
self.save()
self.emit('friend-removed', buddy_info.get_name())
def __iter__(self):
return self._list.__iter__()
return self._friends.values().__iter__()
def load(self):
cp = ConfigParser()
if cp.read([self._path]):
for name in cp.sections():
self.add_friend(name, cp.get(name, 'color'))
buddy = BuddyInfo()
buddy.set_name(name)
buddy.set_color(cp.get(name, 'color'))
self.add_friend(buddy)
def save(self):
cp = ConfigParser()

8
shell/model/Makefile.am Normal file
View File

@ -0,0 +1,8 @@
sugardir = $(pkgdatadir)/shell/model
sugar_PYTHON = \
__init__.py \
BuddyInfo.py \
Friends.py \
Invites.py \
Owner.py \
ShellModel.py

View File

@ -8,8 +8,7 @@ from sugar import env
import logging
from sugar.p2p import Stream
from sugar.presence import PresenceService
from Friends import Friends
from Invites import Invites
from model.Invites import Invites
PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp"
@ -33,7 +32,7 @@ class ShellOwner(object):
break
self._pservice = PresenceService.get_instance()
self._friends = Friends()
self._invites = Invites()
self._shell = shell
@ -42,9 +41,6 @@ class ShellOwner(object):
self._pending_activity_update_timer = None
self._pending_activity_update = None
def get_friends(self):
return self._friends
def get_invites(self):
return self._invites

64
shell/model/ShellModel.py Normal file
View File

@ -0,0 +1,64 @@
import gobject
from sugar.presence import PresenceService
from model.Friends import Friends
from model.Owner import ShellOwner
class ShellModel(gobject.GObject):
__gsignals__ = {
'activity-opened': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'activity-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])),
'activity-closed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
}
def __init__(self):
gobject.GObject.__init__(self)
self._hosts = {}
self._current_activity = None
PresenceService.start()
self._pservice = PresenceService.get_instance()
self._owner = ShellOwner(self)
self._owner.announce()
self._friends = Friends()
def get_friends(self):
return self._friends
def get_invites(self):
return self._owner.get_invites()
def get_owner(self):
return self._owner
def add_activity(self, activity_host):
self._hosts[activity_host.get_xid()] = activity_host
self.emit('activity-opened', activity_host)
def set_current_activity(self, activity_xid):
activity_host = self._hosts[activity_xid]
if self._current_activity == activity_host:
return
self._current_activity = activity_host
self.emit('activity-changed', activity_host)
def remove_activity(self, activity_xid):
if self._hosts.has_key(activity_xid):
host = self._hosts[activity_xid]
self.emit('activity-closed', host)
del self._hosts[activity_xid]
def get_activity(self, activity_id):
for host in self._hosts.values():
if host.get_id() == activity_id:
return host
return None
def get_current_activity(self):
return self._current_activity

View File

@ -31,6 +31,9 @@ class ActivityHost:
def get_id(self):
return self._id
def get_xid(self):
return self._xid
def get_icon_name(self):
return self._icon_name

108
shell/view/BuddyIcon.py Normal file
View File

@ -0,0 +1,108 @@
from sugar.canvas.IconItem import IconItem
from sugar.canvas.Grid import Grid
from view.BuddyPopup import BuddyPopup
class _PopupShell:
def __init__(self):
self._popup_controller = None
def set_active(self, controller):
if self._popup_controller:
self._popup_controller._popdown()
self._popup_controller = controller
class BuddyIcon(IconItem):
_popup_shell = _PopupShell()
def __init__(self, shell, friend):
IconItem.__init__(self, icon_name='stock-buddy',
color=friend.get_color(), size=96)
self._shell = shell
self._friend = friend
self._popup = None
self._popup_distance = 0
self._hover_popup = False
self._popdown_on_leave = False
self.connect('popup', self._popup_cb)
self.connect('popdown', self._popdown_cb)
def set_popup_distance(self, distance):
self._popup_distance = distance
def get_friend(self):
return self._friend
def _popdown(self):
if self._popup:
self._popup.destroy()
self._popup = None
def _popup_cb(self, icon, x1, y1, x2, y2):
self._popdown()
BuddyIcon._popup_shell.set_active(None)
grid = self._shell.get_grid()
self._popup = BuddyPopup(self._shell, icon.get_friend())
self._popup.connect('action', self._popup_action_cb)
self._popup.connect('enter-notify-event',
self._popup_enter_notify_event_cb)
self._popup.connect('leave-notify-event',
self._popup_leave_notify_event_cb)
distance = self._popup_distance
[grid_x1, grid_y1] = grid.convert_from_screen(x1, y1)
[grid_x2, grid_y2] = grid.convert_from_screen(x2, y2)
grid_x = grid_x2 + distance
if grid_x + self._popup.get_width() > Grid.ROWS:
grid_x = grid_x1 - self._popup.get_width() + 1 - distance
grid_y = grid_y1
if grid_y < 0:
grid_y = 0
if grid_y + self._popup.get_width() > Grid.ROWS:
grid_y = Grid.ROWS - self._popup.get_width()
grid.set_constraints(self._popup, grid_x, grid_y,
self._popup.get_width(), self._popup.get_height())
self._popup.show()
BuddyIcon._popup_shell.set_active(self)
def _popup_action_cb(self, popup, action):
self._popdown()
buddy = self._friend.get_buddy()
if buddy == None:
return
model = self._shell.get_model()
if action == BuddyPopup.ACTION_INVITE:
activity = model.get_current_activity()
activity.invite(buddy)
elif action == BuddyPopup.ACTION_MAKE_FRIEND:
friends = model.get_friends()
friends.make_friend(buddy)
elif action == BuddyPopup.ACTION_REMOVE_FRIEND:
friends = model.get_friends()
friends.remove(buddy)
def _popdown_cb(self, friend):
if not self._hover_popup:
self._popdown()
else:
self._popdown_on_leave = True
def _popup_enter_notify_event_cb(self, widget, event):
self._hover_popup = True
def _popup_leave_notify_event_cb(self, widget, event):
self._hover_popup = False
if self._popdown_on_leave:
self._popdown()

View File

@ -1,21 +1,32 @@
import gtk
import goocanvas
import gobject
from sugar.canvas.CanvasView import CanvasView
from sugar.canvas.CanvasBox import CanvasBox
from sugar.canvas.IconItem import IconItem
class FriendPopup(gtk.Window):
def __init__(self, shell, grid, friend):
class BuddyPopup(gtk.Window):
ACTION_MAKE_FRIEND = 0
ACTION_INVITE = 1
ACTION_REMOVE_FRIEND = 2
__gsignals__ = {
'action': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([int])),
}
def __init__(self, shell, buddy):
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
self._shell = shell
self._friend = friend
self._buddy = buddy
self._hover = False
self._popdown_on_leave = False
self._width = 13
self._height = 10
grid = shell.get_grid()
canvas = CanvasView()
self.add(canvas)
canvas.show()
@ -25,14 +36,14 @@ class FriendPopup(gtk.Window):
model = goocanvas.CanvasModelSimple()
root = model.get_root_item()
color = friend.get_color()
color = buddy.get_color()
rect = goocanvas.Rect(fill_color=color.get_fill_color(),
stroke_color=color.get_stroke_color(),
line_width=3)
grid.set_constraints(rect, 0, 0, self._width, self._height)
root.add_child(rect)
text = goocanvas.Text(text=friend.get_name(), font="Sans bold 18",
text = goocanvas.Text(text=buddy.get_name(), font="Sans bold 18",
fill_color='black', anchor=gtk.ANCHOR_SW)
grid.set_constraints(text, 1, 3, self._width, self._height)
root.add_child(text)
@ -45,8 +56,16 @@ class FriendPopup(gtk.Window):
box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
grid.set_constraints(box, 0, 5)
icon = IconItem(icon_name='stock-make-friend')
icon.connect('clicked', self._make_friend_clicked_cb)
friends = shell.get_model().get_friends()
if friends.has_buddy(buddy):
icon = IconItem(icon_name='stock-remove-friend')
icon.connect('clicked', self._action_clicked_cb,
BuddyPopup.ACTION_REMOVE_FRIEND)
else:
icon = IconItem(icon_name='stock-make-friend')
icon.connect('clicked', self._action_clicked_cb,
BuddyPopup.ACTION_MAKE_FRIEND)
box.set_constraints(icon, 3, 3)
box.add_child(icon)
@ -55,7 +74,8 @@ class FriendPopup(gtk.Window):
box.add_child(icon)
icon = IconItem(icon_name='stock-invite')
icon.connect('clicked', self._invite_clicked_cb)
icon.connect('clicked', self._action_clicked_cb,
BuddyPopup.ACTION_INVITE)
box.set_constraints(icon, 3, 3)
box.add_child(icon)
@ -63,33 +83,8 @@ class FriendPopup(gtk.Window):
canvas.set_model(model)
self.connect('enter-notify-event', self._enter_notify_event_cb)
self.connect('leave-notify-event', self._leave_notify_event_cb)
def _invite_clicked_cb(self, icon):
activity = self._shell.get_current_activity()
buddy = self._friend.get_buddy()
if buddy != None:
activity.invite(buddy)
else:
print 'Friend not online'
def _make_friend_clicked_cb(self, icon):
pass
def _enter_notify_event_cb(self, widget, event):
self._hover = True
def _leave_notify_event_cb(self, widget, event):
self._hover = False
if self._popdown_on_leave:
self.popdown()
def popdown(self):
if not self._hover:
self.destroy()
else:
self._popdown_on_leave = True
def _action_clicked_cb(self, icon, action):
self.emit('action', action)
def get_width(self):
return self._width

11
shell/view/Makefile.am Normal file
View File

@ -0,0 +1,11 @@
SUBDIRS = frame home
sugardir = $(pkgdatadir)/shell/view
sugar_PYTHON = \
__init__.py \
ActivityHost.py \
ConsoleWindow.py \
FirstTimeDialog.py \
BuddyIcon.py \
BuddyPopup.py \
Shell.py

102
shell/view/Shell.py Normal file
View File

@ -0,0 +1,102 @@
import gtk
import gobject
import wnck
from sugar.canvas.Grid import Grid
from view.home.HomeWindow import HomeWindow
from sugar.presence import PresenceService
from view.ActivityHost import ActivityHost
from sugar.activity import ActivityFactory
from sugar.activity import Activity
from view.frame.Frame import Frame
from globalkeys import KeyGrabber
import sugar
class Shell(gobject.GObject):
def __init__(self, model):
gobject.GObject.__init__(self)
self._model = model
self._screen = wnck.screen_get_default()
self._grid = Grid()
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed', self.__global_key_pressed_cb)
self._key_grabber.grab('F1')
self._key_grabber.grab('F2')
self._key_grabber.grab('F3')
self._key_grabber.grab('F4')
self._key_grabber.grab('F5')
self._key_grabber.grab('F6')
self._home_window = HomeWindow(self)
self._home_window.show()
self.set_zoom_level(sugar.ZOOM_HOME)
self._screen.connect('window-opened', self.__window_opened_cb)
self._screen.connect('window-closed', self.__window_closed_cb)
self._screen.connect('active-window-changed',
self.__active_window_changed_cb)
self._frame = Frame(self)
self._frame.show_and_hide(10)
def __global_key_pressed_cb(self, grabber, key):
if key == 'F1':
self.set_zoom_level(sugar.ZOOM_ACTIVITY)
elif key == 'F2':
self.set_zoom_level(sugar.ZOOM_HOME)
elif key == 'F3':
self.set_zoom_level(sugar.ZOOM_FRIENDS)
elif key == 'F4':
self.set_zoom_level(sugar.ZOOM_MESH)
elif key == 'F5':
self._frame.toggle_visibility()
elif key == 'F6':
self._model.start_activity('org.sugar.Terminal')
def __window_opened_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
self._model.add_activity(ActivityHost(self, window))
def __active_window_changed_cb(self, screen):
window = screen.get_active_window()
if window and window.get_window_type() == wnck.WINDOW_NORMAL:
self._model.set_current_activity(window.get_xid())
def __window_closed_cb(self, screen, window):
if window.get_window_type() == wnck.WINDOW_NORMAL:
self._model.remove_activity(window.get_xid())
def get_model(self):
return self._model
def get_grid(self):
return self._grid
def join_activity(self, bundle_id, activity_id):
pservice = PresenceService.get_instance()
activity = self._model.get_activity(activity_id)
if activity:
activity.present()
else:
activity_ps = pservice.get_activity(activity_id)
if activity_ps:
activity = ActivityFactory.create(bundle_id)
activity.join(activity_ps.object_path())
else:
logging.error('Cannot start activity.')
def start_activity(self, activity_type):
activity = ActivityFactory.create(activity_type)
activity.execute('test', [])
return activity
def set_zoom_level(self, level):
if level == sugar.ZOOM_ACTIVITY:
self._screen.toggle_showing_desktop(False)
else:
self._screen.toggle_showing_desktop(True)
self._home_window.set_zoom_level(level)

View File

@ -33,22 +33,22 @@ class InviteItem(IconItem):
return self._invite
class BottomPanel(CanvasBox):
def __init__(self, grid, shell, invites):
CanvasBox.__init__(self, grid, CanvasBox.HORIZONTAL, 1)
def __init__(self, shell):
CanvasBox.__init__(self, shell.get_grid(), CanvasBox.HORIZONTAL, 1)
self._shell = shell
self._invite_to_item = {}
self._invites = invites
self._invites = self._shell.get_model().get_invites()
registry = conf.get_activity_registry()
for activity in registry.list_activities():
if activity.get_show_launcher():
self.add_activity(activity)
for invite in invites:
for invite in self._invites:
self.add_invite(invite)
invites.connect('invite-added', self.__invite_added_cb)
invites.connect('invite-removed', self.__invite_removed_cb)
self._invites.connect('invite-added', self.__invite_added_cb)
self._invites.connect('invite-removed', self.__invite_removed_cb)
def __activity_clicked_cb(self, icon):
self._shell.start_activity(icon.get_bundle_id())

View File

@ -2,39 +2,39 @@ import gtk
import gobject
import goocanvas
from frame.BottomPanel import BottomPanel
from frame.RightPanel import RightPanel
from frame.TopPanel import TopPanel
from frame.PanelWindow import PanelWindow
from view.frame.BottomPanel import BottomPanel
from view.frame.RightPanel import RightPanel
from view.frame.TopPanel import TopPanel
from view.frame.PanelWindow import PanelWindow
from sugar.canvas.Grid import Grid
class Frame:
def __init__(self, shell, owner):
def __init__(self, shell):
self._windows = []
model = goocanvas.CanvasModelSimple()
root = model.get_root_item()
grid = Grid()
grid = shell.get_grid()
bg = goocanvas.Rect(fill_color="#4f4f4f", line_width=0)
grid.set_constraints(bg, 0, 0, 80, 60)
root.add_child(bg)
panel = BottomPanel(grid, shell, owner.get_invites())
panel = BottomPanel(shell)
grid.set_constraints(panel, 5, 55)
root.add_child(panel)
panel_window = PanelWindow(grid, model, 0, 55, 80, 5)
self._windows.append(panel_window)
panel = TopPanel(grid, shell)
panel = TopPanel(shell)
root.add_child(panel)
panel_window = PanelWindow(grid, model, 0, 0, 80, 5)
self._windows.append(panel_window)
panel = RightPanel(grid, shell, owner.get_friends())
panel = RightPanel(shell)
grid.set_constraints(panel, 75, 5)
root.add_child(panel)

View File

@ -1,4 +1,4 @@
sugardir = $(pkgdatadir)/shell/frame
sugardir = $(pkgdatadir)/shell/view/frame
sugar_PYTHON = \
__init__.py \
RightPanel.py \

View File

@ -4,12 +4,13 @@ from sugar.canvas.IconItem import IconItem
from sugar.canvas.IconColor import IconColor
from sugar.canvas.CanvasBox import CanvasBox
from sugar.presence import PresenceService
from view.BuddyIcon import BuddyIcon
from model.BuddyInfo import BuddyInfo
class RightPanel(CanvasBox):
def __init__(self, grid, shell, friends):
CanvasBox.__init__(self, grid, CanvasBox.VERTICAL, 1)
def __init__(self, shell):
CanvasBox.__init__(self, shell.get_grid(), CanvasBox.VERTICAL, 1)
self._shell = shell
self._friends = friends
self._activity_ps = None
self._joined_hid = -1
self._left_hid = -1
@ -19,14 +20,13 @@ class RightPanel(CanvasBox):
self._pservice.connect('activity-appeared',
self.__activity_appeared_cb)
shell.connect('activity-changed', self.__activity_changed_cb)
shell.get_model().connect('activity-changed',
self.__activity_changed_cb)
def add(self, buddy):
icon = IconItem(icon_name='stock-buddy',
color=IconColor(buddy.get_color()))
icon = BuddyIcon(self._shell, BuddyInfo(buddy))
icon.set_popup_distance(1)
self.set_constraints(icon, 3, 3)
icon.connect('clicked', self.__buddy_clicked_cb, buddy)
self.add_child(icon)
self._buddies[buddy.get_name()] = icon
@ -41,7 +41,7 @@ class RightPanel(CanvasBox):
self._buddies = {}
def __activity_appeared_cb(self, pservice, activity_ps):
activity = self._shell.get_current_activity()
activity = self._shell.get_model().get_current_activity()
if activity and activity_ps.get_id() == activity.get_id():
self._set_activity_ps(activity_ps)
@ -78,6 +78,3 @@ class RightPanel(CanvasBox):
def __buddy_left_cb(self, activity, buddy):
self.remove(buddy)
def __buddy_clicked_cb(self, icon, buddy):
self._friends.add_buddy(buddy)

View File

@ -5,14 +5,15 @@ from sugar.canvas.IconItem import IconItem
import sugar
class TopPanel(goocanvas.Group):
def __init__(self, grid, shell):
def __init__(self, shell):
goocanvas.Group.__init__(self)
self._grid = grid
self._shell = shell
grid = shell.get_grid()
box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
self._grid.set_constraints(box, 5, 0)
grid.set_constraints(box, 5, 0)
self.add_child(box)
icon = IconItem(icon_name='stock-zoom-activity')
@ -36,7 +37,7 @@ class TopPanel(goocanvas.Group):
box.add_child(icon)
box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
self._grid.set_constraints(box, 60, 0)
grid.set_constraints(box, 60, 0)
self.add_child(box)
icon = IconItem(icon_name='stock-share')
@ -58,7 +59,8 @@ class TopPanel(goocanvas.Group):
self._shell.set_zoom_level(level)
def __share_clicked_cb(self, item):
activity = self._shell.get_current_activity()
shell_model = self._shell.get_model()
activity = shell_model.get_current_activity()
if activity != None:
activity.share()

View File

View File

@ -0,0 +1,42 @@
import random
import goocanvas
from view.home.IconLayout import IconLayout
from view.home.MyIcon import MyIcon
from view.BuddyIcon import BuddyIcon
class FriendsGroup(goocanvas.Group):
def __init__(self, shell):
goocanvas.Group.__init__(self)
self._shell = shell
self._icon_layout = IconLayout(1200, 900)
self._friends = {}
me = MyIcon(100)
me.translate(600 - (me.get_property('size') / 2),
450 - (me.get_property('size') / 2))
self.add_child(me)
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 = BuddyIcon(self._shell, buddy_info)
self.add_child(icon)
self._icon_layout.add_icon(icon)
self._friends[buddy_info.get_name()] = icon
def _friend_added_cb(self, data_model, buddy_info):
self.add_friend(buddy_info)
def _friend_removed_cb(self, data_model, name):
self.remove_child(self._friends[name])
del self._friends[name]

View File

@ -1,7 +1,7 @@
import goocanvas
from home.DonutItem import DonutItem
from home.MyIcon import MyIcon
from view.home.DonutItem import DonutItem
from view.home.MyIcon import MyIcon
class TasksItem(DonutItem):
def __init__(self, shell):
@ -9,14 +9,14 @@ class TasksItem(DonutItem):
self._items = {}
self._shell = shell
self._shell.connect('activity_opened', self.__activity_opened_cb)
self._shell.connect('activity_closed', self.__activity_closed_cb)
shell_model = shell.get_model()
shell_model.connect('activity_opened', self.__activity_opened_cb)
shell_model.connect('activity_closed', self.__activity_closed_cb)
def __activity_opened_cb(self, shell, activity):
def __activity_opened_cb(self, model, activity):
self._add(activity)
def __activity_closed_cb(self, shell, activity):
def __activity_closed_cb(self, model, activity):
self._remove(activity)
def _remove(self, activity):

View File

@ -3,9 +3,9 @@ import goocanvas
import cairo
from sugar.canvas.CanvasView import CanvasView
from home.MeshGroup import MeshGroup
from home.HomeGroup import HomeGroup
from home.FriendsGroup import FriendsGroup
from view.home.MeshGroup import MeshGroup
from view.home.HomeGroup import HomeGroup
from view.home.FriendsGroup import FriendsGroup
import sugar
class HomeWindow(gtk.Window):
@ -23,6 +23,10 @@ class HomeWindow(gtk.Window):
self.add(self._nb)
self._nb.show()
self._add_page(HomeGroup(shell))
self._add_page(FriendsGroup(shell))
self._add_page(MeshGroup(shell))
def _add_page(self, group):
view = CanvasView()
self._nb.append_page(view)
@ -37,11 +41,6 @@ class HomeWindow(gtk.Window):
root.add_child(bg)
root.add_child(group)
def set_owner(self, owner):
self._add_page(HomeGroup(self._shell))
self._add_page(FriendsGroup(self._shell, owner.get_friends()))
self._add_page(MeshGroup(self._shell))
def set_zoom_level(self, level):
if level == sugar.ZOOM_HOME:
self._nb.set_current_page(0)

View File

@ -1,4 +1,4 @@
sugardir = $(pkgdatadir)/shell/home
sugardir = $(pkgdatadir)/shell/view/home
sugar_PYTHON = \
__init__.py \
DonutItem.py \

View File

@ -6,7 +6,7 @@ import conf
from sugar.canvas.IconItem import IconItem
from sugar.canvas.IconItem import IconColor
from sugar.presence import PresenceService
from home.IconLayout import IconLayout
from view.home.IconLayout import IconLayout
class ActivityItem(IconItem):
def __init__(self, activity, service):
@ -34,7 +34,9 @@ class ActivityItem(IconItem):
class MeshGroup(goocanvas.Group):
def __init__(self, shell):
goocanvas.Group.__init__(self)
self._shell = shell
self._icon_layout = IconLayout(1200, 900)
self._activities = {}

View File

View File

@ -6,5 +6,6 @@ sugar_dbus_config = '@prefix@/share/sugar/dbus-installed.conf'
sugar_python_path = ['@prefix@/share/sugar/shell',
'@prefix@/share/sugar/bindings',
'@prefix@/share/sugar/activities']
'@prefix@/share/sugar/activities',
'@prefix@/share/sugar/services']
sugar_bin_path = []

View File

@ -14,6 +14,8 @@ sugar_python_path = ['@prefix@/share/sugar/bindings']
sugar_python_path.append(sugar_source_dir)
sugar_python_path.append(os.path.join(sugar_source_dir, 'shell'))
sugar_python_path.append(os.path.join(sugar_source_dir, 'activities'))
sugar_python_path.append(os.path.join(sugar_source_dir, 'services'))
sugar_bin_path = []
sugar_bin_path.append(os.path.join(sugar_source_dir, 'shell'))
sugar_bin_path.append(os.path.join(sugar_source_dir, 'services/presence'))

View File

@ -10,7 +10,11 @@ class Grid:
def convert_from_screen(self, x, y):
factor = Grid.COLS / gtk.gdk.screen_width()
return [int(x * factor), int(y * factor)]
grid_x = round(x * factor) - 1
grid_y = round(y * factor) - 1
return [grid_x, grid_y]
def set_constraints(self, component, x, y, width=-1, height=-1):
if isinstance(component, gtk.Window):

View File

@ -4,7 +4,7 @@ from sugar.canvas import Colors
def _parse_string(color_string):
if color_string == 'white':
return ['#4f4f4f', 'white']
return ['white', '#4f4f4f']
splitted = color_string.split(',')
if len(splitted) == 2:
@ -19,9 +19,9 @@ class IconColor:
def __init__(self, color_string=None):
if color_string == None or not is_valid(color_string):
n = int(random.random() * (len(Colors.colors) - 1))
[self._fill, self._stroke] = Colors.colors[n]
[self._stroke, self._fill] = Colors.colors[n]
else:
[self._fill, self._stroke] = _parse_string(color_string)
[self._stroke, self._fill] = _parse_string(color_string)
def get_stroke_color(self):
return self._stroke
@ -30,5 +30,5 @@ class IconColor:
return self._fill
def to_string(self):
return '%s,%s' % (self._fill, self._stroke)
return '%s,%s' % (self._stroke, self._fill)

View File

@ -195,7 +195,7 @@ class IconItem(goocanvas.ItemSimple, goocanvas.Item):
self.size = 24
self.color = None
self.icon_name = None
self._popdown_timeout = 0
self._popdown_sid = 0
goocanvas.ItemSimple.__init__(self, **kwargs)
@ -240,29 +240,36 @@ class IconItem(goocanvas.ItemSimple, goocanvas.Item):
def _button_press_cb(self, view, target, event):
self.emit('clicked')
def _start_popup_timeout(self):
self._stop_popup_timeout()
self._popdown_timeout = gobject.timeout_add(1000, self._popdown)
def _start_popdown_timeout(self):
self._stop_popdown_timeout()
self._popdown_sid = gobject.timeout_add(1000, self._popdown_timeout_cb)
def _stop_popup_timeout(self):
if self._popdown_timeout > 0:
gobject.source_remove(self._popdown_timeout)
self._popdown_timeout = 0
def _stop_popdown_timeout(self):
if self._popdown_sid > 0:
gobject.source_remove(self._popdown_sid)
self._popdown_sid = 0
def _enter_notify_event_cb(self, view, target, event, canvas):
self._stop_popup_timeout()
self._stop_popdown_timeout()
[x1, y1] = canvas.convert_to_pixels(view.get_bounds().x1,
view.get_bounds().y1)
[x2, y2] = canvas.convert_to_pixels(view.get_bounds().x2,
view.get_bounds().y2)
[window_x, window_y] = canvas.window.get_origin()
x1 += window_x
y1 += window_y
x2 += window_x
y2 += window_y
self.emit('popup', int(x1), int(y1), int(x2), int(y2))
def _popdown(self):
self._popdown_timeout = 0
def _popdown_timeout_cb(self):
self._popdown_sid = 0
self.emit('popdown')
return False
def _leave_notify_event_cb(self, view, target, event):
self._start_popup_timeout()
self._start_popdown_timeout()

View File

@ -43,7 +43,8 @@ def setup_system():
runner = os.path.join(sugar_source_dir, 'shell/sugar-activity-factory')
sugar.setup.setup_activities(source, sugar_activities_dir, runner)
bin = os.path.join(sugar_source_dir, 'shell/sugar-presence-service')
bin = os.path.join(sugar_source_dir,
'services/presence/sugar-presence-service')
sugar.setup.write_service('org.laptop.Presence', bin,
sugar_activities_dir)