Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell/view
diff options
context:
space:
mode:
authorMarco Pesenti Gritti <marco@localhost.localdomain>2006-09-15 11:23:21 (GMT)
committer Marco Pesenti Gritti <marco@localhost.localdomain>2006-09-15 11:23:21 (GMT)
commitbcc1740f7f514f04629cecb7536c93d4feaf83cf (patch)
treea96559839563cf01d08c8132d8fd71e8a35a17ef /shell/view
parentca19f0f25100006c0f6c84e405e2e6a0cecdd15b (diff)
Move the view to his own module
Diffstat (limited to 'shell/view')
-rw-r--r--shell/view/ActivityHost.py72
-rw-r--r--shell/view/ConsoleWindow.py128
-rw-r--r--shell/view/FirstTimeDialog.py36
-rw-r--r--shell/view/FriendIcon.py104
-rw-r--r--shell/view/FriendPopup.py83
-rw-r--r--shell/view/Makefile.am11
-rw-r--r--shell/view/Shell.py74
-rw-r--r--shell/view/__init__.py0
-rw-r--r--shell/view/frame/BottomPanel.py83
-rw-r--r--shell/view/frame/Frame.py70
-rw-r--r--shell/view/frame/Makefile.am8
-rw-r--r--shell/view/frame/PanelWindow.py27
-rw-r--r--shell/view/frame/RightPanel.py81
-rw-r--r--shell/view/frame/TopPanel.py70
-rw-r--r--shell/view/frame/__init__.py0
-rw-r--r--shell/view/home/DonutItem.py114
-rw-r--r--shell/view/home/FriendsGroup.py33
-rw-r--r--shell/view/home/HomeGroup.py52
-rw-r--r--shell/view/home/HomeWindow.py50
-rw-r--r--shell/view/home/IconLayout.py34
-rw-r--r--shell/view/home/Makefile.am10
-rw-r--r--shell/view/home/MeshGroup.py82
-rw-r--r--shell/view/home/MyIcon.py10
-rw-r--r--shell/view/home/__init__.py0
24 files changed, 1232 insertions, 0 deletions
diff --git a/shell/view/ActivityHost.py b/shell/view/ActivityHost.py
new file mode 100644
index 0000000..456406b
--- /dev/null
+++ b/shell/view/ActivityHost.py
@@ -0,0 +1,72 @@
+import gtk
+import dbus
+
+import conf
+from sugar.activity import Activity
+from sugar.presence import PresenceService
+from sugar.canvas.IconColor import IconColor
+from sugar.p2p import Stream
+from sugar.p2p import network
+
+class ActivityHost:
+ def __init__(self, shell, window):
+ self._shell = shell
+ self._window = window
+ self._xid = window.get_xid()
+ self._pservice = PresenceService.get_instance()
+
+ bus = dbus.SessionBus()
+ proxy_obj = bus.get_object(Activity.get_service_name(self._xid),
+ Activity.get_object_path(self._xid))
+
+ self._activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE)
+ self._id = self._activity.get_id()
+ self._type = self._activity.get_type()
+ self._gdk_window = gtk.gdk.window_foreign_new(self._xid)
+
+ registry = conf.get_activity_registry()
+ info = registry.get_activity(self._type)
+ self._icon_name = info.get_icon()
+
+ def get_id(self):
+ return self._id
+
+ def get_xid(self):
+ return self._xid
+
+ def get_icon_name(self):
+ return self._icon_name
+
+ def get_icon_color(self):
+ activity = self._pservice.get_activity(self._id)
+ if activity != None:
+ return IconColor(activity.get_color())
+ else:
+ return conf.get_profile().get_color()
+
+ def share(self):
+ self._activity.share()
+
+ def invite(self, buddy):
+ if not self.get_shared():
+ self.share()
+
+ issuer = self._pservice.get_owner().get_name()
+ service = buddy.get_service_of_type("_presence_olpc._tcp")
+ stream = Stream.Stream.new_from_service(service, start_reader=False)
+ writer = stream.new_writer(service)
+ writer.custom_request("invite", None, None, issuer,
+ self._type, self._id)
+
+ def get_shared(self):
+ return self._activity.get_shared()
+
+ def get_type(self):
+ return self._type
+
+ def present(self):
+ self._window.activate(gtk.get_current_event_time())
+
+ def show_dialog(self, dialog):
+ dialog.show()
+ dialog.window.set_transient_for(self._gdk_window)
diff --git a/shell/view/ConsoleWindow.py b/shell/view/ConsoleWindow.py
new file mode 100644
index 0000000..4cdbbb1
--- /dev/null
+++ b/shell/view/ConsoleWindow.py
@@ -0,0 +1,128 @@
+import logging
+
+import gtk
+import dbus
+import dbus.service
+
+class Console(gtk.ScrolledWindow):
+ def __init__(self):
+ gtk.ScrolledWindow.__init__(self)
+
+ self._show_debug = False
+
+ self.set_policy(gtk.POLICY_AUTOMATIC,
+ gtk.POLICY_AUTOMATIC)
+
+ self._textview = gtk.TextView()
+ self._textview.set_wrap_mode(gtk.WRAP_WORD)
+ self.add(self._textview)
+ self._textview.show()
+
+ buf = self._textview.get_buffer()
+ self._debug_tag = buf.create_tag("debug")
+ self._debug_tag.set_property("invisible", True)
+
+ def get_show_debug(self):
+ return self._show_debug
+
+ def set_show_debug(self, show_debug):
+ self._show_debug = show_debug
+ self._debug_tag.set_property("invisible", not show_debug)
+
+ def log(self, level, message):
+ msg = message + '\n'
+ buf = self._textview.get_buffer()
+ it = buf.get_end_iter()
+
+ if level == logging.DEBUG:
+ buf.insert_with_tags(it, msg, self._debug_tag)
+ else:
+ buf.insert(it, msg)
+
+class ConsoleDbusService(dbus.service.Object):
+ def __init__(self, console, bus_name):
+ dbus.service.Object.__init__(self, bus_name, '/org/laptop/Sugar/Console')
+ self._console = console
+
+ @dbus.service.method('org.laptop.Sugar.Console',
+ in_signature="saias", out_signature="")
+ def log(self, module_id, levels, messages):
+ self._console.log(module_id, levels, messages)
+
+class ConsoleWindow(gtk.Window):
+ def __init__(self):
+ gtk.Window.__init__(self)
+
+ self.set_default_size(620, 440)
+ self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.set_title("Console")
+ self.connect("delete_event", lambda w, e: w.hide_on_delete())
+
+ vbox = gtk.VBox()
+
+ toolbar = gtk.Toolbar()
+
+ self._debug_toggle = gtk.ToggleToolButton()
+ self._debug_toggle.connect('toggled', self.__debug_toggled_cb)
+ self._debug_toggle.set_label('Debug')
+ toolbar.insert(self._debug_toggle, -1)
+ self._debug_toggle.show()
+
+ self._ignore_toggle = False
+
+ vbox.pack_start(toolbar, False)
+ toolbar.show()
+
+ self._nb = gtk.Notebook()
+ self._nb.connect('switch-page', self.__page_changed_cb)
+ vbox.pack_start(self._nb)
+ self._nb.show()
+
+ self.add(vbox)
+ vbox.show()
+
+ self._consoles = {}
+
+ session_bus = dbus.SessionBus()
+ bus_name = dbus.service.BusName('org.laptop.Sugar.Console', bus=session_bus)
+ ConsoleDbusService(self, bus_name)
+
+ def _add_console(self, page_id):
+ console = Console()
+ page = self._nb.append_page(console, gtk.Label(page_id))
+ console.show()
+
+ self._consoles[page_id] = console
+
+ return console
+
+ def _get_console(self, page_id):
+ if not self._consoles.has_key(page_id):
+ console = self._add_console(page_id)
+ else:
+ console = self._consoles[page_id]
+ return console
+
+ def __debug_toggled_cb(self, button):
+ if not self._ignore_toggle:
+ console = self._nb.get_nth_page(self._nb.get_current_page())
+ if console:
+ console.set_show_debug(button.get_active())
+
+ def __page_changed_cb(self, notebook, page, page_num):
+ console = self._nb.get_nth_page(page_num)
+
+ self._ignore_toggle = True
+ self._debug_toggle.set_active(console.get_show_debug())
+ self._ignore_toggle = False
+
+ def set_page(self, page_id):
+ page = self._nb.page_num(self._consoles[page_id])
+ self._nb.set_current_page(page)
+
+ def log(self, page_id, levels, messages):
+ console = self._get_console(page_id)
+ i = 0
+ while i < len(levels):
+ console.log(levels[i], messages[i])
+ i += 1
diff --git a/shell/view/FirstTimeDialog.py b/shell/view/FirstTimeDialog.py
new file mode 100644
index 0000000..abf6bd8
--- /dev/null
+++ b/shell/view/FirstTimeDialog.py
@@ -0,0 +1,36 @@
+import gtk
+
+from gettext import gettext as _
+
+import conf
+
+class FirstTimeDialog(gtk.Dialog):
+ def __init__(self):
+ gtk.Dialog.__init__(self)
+
+ label = gtk.Label(_('Nick Name:'))
+ label.set_alignment(0.0, 0.5)
+ self.vbox.pack_start(label)
+ label.show()
+
+ self._entry = gtk.Entry()
+ self.vbox.pack_start(self._entry)
+ self._entry.show()
+
+ button = gtk.Button(None, gtk.STOCK_OK)
+ self.vbox.pack_start(button)
+ button.connect('clicked', self.__ok_button_clicked_cb)
+ button.show()
+
+ def __ok_button_clicked_cb(self, button):
+ profile = conf.get_profile()
+ profile.set_nick_name(self._entry.get_text())
+ self.destroy()
+
+def get_profile():
+ profile = conf.get_profile()
+ if profile.get_nick_name() == None:
+ dialog = FirstTimeDialog()
+ dialog.connect('destroy', self.__first_time_dialog_destroy_cb)
+ dialog.show()
+ return profile
diff --git a/shell/view/FriendIcon.py b/shell/view/FriendIcon.py
new file mode 100644
index 0000000..e5158e2
--- /dev/null
+++ b/shell/view/FriendIcon.py
@@ -0,0 +1,104 @@
+from sugar.canvas.IconItem import IconItem
+from sugar.canvas.Grid import Grid
+from view.FriendPopup import FriendPopup
+
+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 FriendIcon(IconItem):
+ _popup_shell = _PopupShell()
+
+ def __init__(self, shell_model, friend):
+ IconItem.__init__(self, icon_name='stock-buddy',
+ color=friend.get_color(), size=96)
+
+ self._shell_model = shell_model
+ 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()
+
+ FriendIcon._popup_shell.set_active(None)
+
+ grid = Grid()
+ self._popup = FriendPopup(grid, 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()
+
+ FriendIcon._popup_shell.set_active(self)
+
+ def _popup_action_cb(self, popup, action):
+ self._popdown()
+
+ buddy = self._friend.get_buddy()
+ if buddy == None:
+ return
+
+ if action == FriendPopup.ACTION_INVITE:
+ activity = self._shell_model.get_current_activity()
+ activity.invite(buddy)
+ elif action == FriendPopup.ACTION_MAKE_FRIEND:
+ friends = self._shell_model.get_friends()
+ friends.add_buddy(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()
diff --git a/shell/view/FriendPopup.py b/shell/view/FriendPopup.py
new file mode 100644
index 0000000..06f10da
--- /dev/null
+++ b/shell/view/FriendPopup.py
@@ -0,0 +1,83 @@
+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):
+ ACTION_MAKE_FRIEND = 0
+ ACTION_INVITE = 1
+
+ __gsignals__ = {
+ 'action': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE, ([int])),
+ }
+
+ def __init__(self, grid, friend):
+ gtk.Window.__init__(self, gtk.WINDOW_POPUP)
+
+ self._friend = friend
+ self._hover = False
+ self._popdown_on_leave = False
+ self._width = 13
+ self._height = 10
+
+ canvas = CanvasView()
+ self.add(canvas)
+ canvas.show()
+
+ grid.set_constraints(canvas, 0, 0, self._width, self._height)
+
+ model = goocanvas.CanvasModelSimple()
+ root = model.get_root_item()
+
+ color = friend.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",
+ fill_color='black', anchor=gtk.ANCHOR_SW)
+ grid.set_constraints(text, 1, 3, self._width, self._height)
+ root.add_child(text)
+
+ separator = goocanvas.Path(data='M 15 0 L 185 0', line_width=3,
+ fill_color='black')
+ grid.set_constraints(separator, 0, 4)
+ root.add_child(separator)
+
+ box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
+ grid.set_constraints(box, 0, 5)
+
+ icon = IconItem(icon_name='stock-make-friend')
+ icon.connect('clicked', self._action_clicked_cb,
+ FriendPopup.ACTION_MAKE_FRIEND)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-chat')
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-invite')
+ icon.connect('clicked', self._action_clicked_cb,
+ FriendPopup.ACTION_INVITE)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ root.add_child(box)
+
+ canvas.set_model(model)
+
+ def _action_clicked_cb(self, icon, action):
+ self.emit('action', action)
+
+ def get_width(self):
+ return self._width
+
+ def get_height(self):
+ return self._height
diff --git a/shell/view/Makefile.am b/shell/view/Makefile.am
new file mode 100644
index 0000000..7481dbe
--- /dev/null
+++ b/shell/view/Makefile.am
@@ -0,0 +1,11 @@
+SUBDIRS = frame home
+
+sugardir = $(pkgdatadir)/shell/view
+sugar_PYTHON = \
+ __init__.py \
+ ActivityHost.py \
+ ConsoleWindow.py \
+ FirstTimeDialog.py \
+ FriendIcon.py \
+ FriendPopup.py \
+ Shell.py
diff --git a/shell/view/Shell.py b/shell/view/Shell.py
new file mode 100644
index 0000000..7e1050e
--- /dev/null
+++ b/shell/view/Shell.py
@@ -0,0 +1,74 @@
+import gtk
+import gobject
+import wnck
+
+from view.home.HomeWindow import HomeWindow
+from view.ActivityHost import ActivityHost
+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._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.get_model())
+ 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 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)
diff --git a/shell/view/__init__.py b/shell/view/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/shell/view/__init__.py
diff --git a/shell/view/frame/BottomPanel.py b/shell/view/frame/BottomPanel.py
new file mode 100644
index 0000000..d43160d
--- /dev/null
+++ b/shell/view/frame/BottomPanel.py
@@ -0,0 +1,83 @@
+import gtk
+import goocanvas
+import logging
+
+import conf
+from sugar.canvas.IconItem import IconItem
+from sugar.canvas.IconColor import IconColor
+from sugar.presence import PresenceService
+from sugar.canvas.CanvasBox import CanvasBox
+
+class ActivityItem(IconItem):
+ def __init__(self, activity):
+ icon_name = activity.get_icon()
+ IconItem.__init__(self, icon_name=icon_name, color=IconColor('white'))
+ self._activity = activity
+
+ def get_bundle_id(self):
+ return self._activity.get_id()
+
+class InviteItem(IconItem):
+ def __init__(self, invite):
+ IconItem.__init__(self, icon_name=invite.get_icon(),
+ color=invite.get_color())
+ 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 BottomPanel(CanvasBox):
+ def __init__(self, grid, shell_model):
+ CanvasBox.__init__(self, grid, CanvasBox.HORIZONTAL, 1)
+
+ self._shell_model = shell_model
+ self._invite_to_item = {}
+ self._invites = shell_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 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 __activity_clicked_cb(self, icon):
+ self._shell_model.start_activity(icon.get_bundle_id())
+
+ def __invite_clicked_cb(self, icon):
+ self._invites.remove_invite(icon.get_invite())
+ self._shell_model.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 add_activity(self, activity):
+ item = ActivityItem(activity)
+ item.connect('clicked', self.__activity_clicked_cb)
+ self.set_constraints(item, 3, 3)
+ self.add_child(item)
+
+ def add_invite(self, invite):
+ item = InviteItem(invite)
+ item.connect('clicked', self.__invite_clicked_cb)
+ self.set_constraints(item, 3, 3)
+ self.add_child(item, 0)
+
+ self._invite_to_item[invite] = item
+
+ def remove_invite(self, invite):
+ self.remove_child(self._invite_to_item[invite])
+ del self._invite_to_item[invite]
diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py
new file mode 100644
index 0000000..5ebeccc
--- /dev/null
+++ b/shell/view/frame/Frame.py
@@ -0,0 +1,70 @@
+import gtk
+import gobject
+import goocanvas
+
+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):
+ self._windows = []
+
+ shell_model = shell.get_model()
+
+ model = goocanvas.CanvasModelSimple()
+ root = model.get_root_item()
+
+ grid = 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_model)
+ 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)
+ root.add_child(panel)
+
+ panel_window = PanelWindow(grid, model, 0, 0, 80, 5)
+ self._windows.append(panel_window)
+
+ panel = RightPanel(grid, shell_model)
+ grid.set_constraints(panel, 75, 5)
+ root.add_child(panel)
+
+ panel_window = PanelWindow(grid, model, 75, 5, 5, 50)
+ self._windows.append(panel_window)
+
+ panel_window = PanelWindow(grid, model, 0, 5, 5, 50)
+ self._windows.append(panel_window)
+
+ def __hide_timeout_cb(self):
+ self.hide()
+ return False
+
+ def show_and_hide(self, seconds):
+ self.show()
+ gobject.timeout_add(seconds * 1000, self.__hide_timeout_cb)
+
+ def show(self):
+ for panel in self._windows:
+ panel.show()
+
+ def hide(self):
+ for panel in self._windows:
+ panel.hide()
+
+ def toggle_visibility(self):
+ for panel in self._windows:
+ if panel.props.visible:
+ panel.hide()
+ else:
+ panel.show()
diff --git a/shell/view/frame/Makefile.am b/shell/view/frame/Makefile.am
new file mode 100644
index 0000000..a737e01
--- /dev/null
+++ b/shell/view/frame/Makefile.am
@@ -0,0 +1,8 @@
+sugardir = $(pkgdatadir)/shell/view/frame
+sugar_PYTHON = \
+ __init__.py \
+ RightPanel.py \
+ PanelWindow.py \
+ Frame.py \
+ TopPanel.py \
+ BottomPanel.py
diff --git a/shell/view/frame/PanelWindow.py b/shell/view/frame/PanelWindow.py
new file mode 100644
index 0000000..549776f
--- /dev/null
+++ b/shell/view/frame/PanelWindow.py
@@ -0,0 +1,27 @@
+import gtk
+import goocanvas
+
+from sugar.canvas.CanvasView import CanvasView
+
+class PanelWindow(gtk.Window):
+ def __init__(self, grid, model, x, y, width, height):
+ gtk.Window.__init__(self)
+
+ self._grid = grid
+
+ self.set_decorated(False)
+
+ self.realize()
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.window.set_accept_focus(False)
+
+ screen = gtk.gdk.screen_get_default()
+ self.window.set_transient_for(screen.get_root_window())
+
+ view = CanvasView()
+ view.show()
+ self.add(view)
+ view.set_model(model)
+
+ self._grid.set_constraints(self, x, y, width, height)
+ self._grid.set_constraints(view, x, y, width, height)
diff --git a/shell/view/frame/RightPanel.py b/shell/view/frame/RightPanel.py
new file mode 100644
index 0000000..f12ccf5
--- /dev/null
+++ b/shell/view/frame/RightPanel.py
@@ -0,0 +1,81 @@
+import goocanvas
+
+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.FriendIcon import FriendIcon
+from model.Friends import Friend
+
+class RightPanel(CanvasBox):
+ def __init__(self, grid, shell_model):
+ CanvasBox.__init__(self, grid, CanvasBox.VERTICAL, 1)
+ self._shell_model = shell_model
+ self._friends = shell_model.get_friends()
+ 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)
+
+ shell_model.connect('activity-changed', self.__activity_changed_cb)
+
+ def add(self, buddy):
+ friend = Friend(buddy.get_name(), buddy.get_color())
+ icon = FriendIcon(self._shell_model, friend)
+ icon.set_popup_distance(1)
+ self.set_constraints(icon, 3, 3)
+ self.add_child(icon)
+
+ self._buddies[buddy.get_name()] = icon
+
+ def remove(self, buddy):
+ i = self.find_child(self._buddies[buddy.get_name()])
+ self.remove_child(i)
+
+ def clear(self):
+ while (self.get_n_children() > 0):
+ self.remove_child(0)
+ self._buddies = {}
+
+ def __activity_appeared_cb(self, pservice, activity_ps):
+ activity = self._shell_model.get_current_activity()
+ if activity and activity_ps.get_id() == activity.get_id():
+ self._set_activity_ps(activity_ps)
+
+ def _set_activity_ps(self, activity_ps):
+ 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 activity_ps != None:
+ for buddy in activity_ps.get_joined_buddies():
+ self.add(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)
+
+ def __activity_changed_cb(self, group, activity):
+ activity_ps = self._pservice.get_activity(activity.get_id())
+ self._set_activity_ps(activity_ps)
+
+ def __buddy_joined_cb(self, activity, buddy):
+ self.add(buddy)
+
+ def __buddy_left_cb(self, activity, buddy):
+ self.remove(buddy)
diff --git a/shell/view/frame/TopPanel.py b/shell/view/frame/TopPanel.py
new file mode 100644
index 0000000..1409d85
--- /dev/null
+++ b/shell/view/frame/TopPanel.py
@@ -0,0 +1,70 @@
+import goocanvas
+
+from sugar.canvas.CanvasBox import CanvasBox
+from sugar.canvas.IconItem import IconItem
+import sugar
+
+class TopPanel(goocanvas.Group):
+ def __init__(self, grid, shell):
+ goocanvas.Group.__init__(self)
+
+ self._grid = grid
+ self._shell = shell
+
+ box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
+ self._grid.set_constraints(box, 5, 0)
+ self.add_child(box)
+
+ icon = IconItem(icon_name='stock-zoom-activity')
+ icon.connect('clicked', self.__level_clicked_cb, sugar.ZOOM_ACTIVITY)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-zoom-home')
+ icon.connect('clicked', self.__level_clicked_cb, sugar.ZOOM_HOME)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-zoom-friends')
+ icon.connect('clicked', self.__level_clicked_cb, sugar.ZOOM_FRIENDS)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-zoom-mesh')
+ icon.connect('clicked', self.__level_clicked_cb, sugar.ZOOM_MESH)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
+ self._grid.set_constraints(box, 60, 0)
+ self.add_child(box)
+
+ icon = IconItem(icon_name='stock-share')
+ icon.connect('clicked', self.__share_clicked_cb)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-invite')
+ icon.connect('clicked', self.__invite_clicked_cb)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ icon = IconItem(icon_name='stock-chat')
+ icon.connect('clicked', self.__chat_clicked_cb)
+ box.set_constraints(icon, 3, 3)
+ box.add_child(icon)
+
+ def __level_clicked_cb(self, item, level):
+ self._shell.set_zoom_level(level)
+
+ def __share_clicked_cb(self, item):
+ shell_model = self._shell.get_model()
+ activity = shell_model.get_current_activity()
+ if activity != None:
+ activity.share()
+
+ def __invite_clicked_cb(self, item):
+ pass
+
+ def __chat_clicked_cb(self, item):
+ pass
diff --git a/shell/view/frame/__init__.py b/shell/view/frame/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/shell/view/frame/__init__.py
diff --git a/shell/view/home/DonutItem.py b/shell/view/home/DonutItem.py
new file mode 100644
index 0000000..9038523
--- /dev/null
+++ b/shell/view/home/DonutItem.py
@@ -0,0 +1,114 @@
+import math
+
+import goocanvas
+
+from sugar.canvas.IconItem import IconItem
+
+class PieceIcon(IconItem):
+ def __init__(self, piece_item, **kwargs):
+ IconItem.__init__(self, size=48, **kwargs)
+ self._piece_item = piece_item
+
+ def construct(self):
+ angle_start = self._piece_item.get_angle_start()
+ angle_end = self._piece_item.get_angle_end()
+ radius = self.get_parent().get_radius()
+ inner_radius = self.get_parent().get_inner_radius()
+
+ icon_radius = (radius + inner_radius) / 2
+ icon_angle = (angle_start + angle_end) / 2
+ x = icon_radius * math.cos(icon_angle)
+ y = - icon_radius * math.sin(icon_angle)
+
+ icon_width = self.get_property('size')
+ icon_height = self.get_property('size')
+ self.set_property('x', x - icon_width / 2)
+ self.set_property('y', y - icon_height / 2)
+
+class PieceItem(goocanvas.Path):
+ def __init__(self, angle_start, angle_end, **kwargs):
+ goocanvas.Path.__init__(self, **kwargs)
+ self._angle_start = angle_start
+ self._angle_end = angle_end
+
+ self.set_property('fill-color', '#ffffff')
+ self.set_property('stroke-color', '#e2e2e2')
+ self.set_property('line-width', 4)
+
+ def get_icon(self):
+ return self._icon
+
+ def set_icon(self, icon_name, color):
+ self._icon = PieceIcon(self, icon_name=icon_name, color=color)
+ self.get_parent().add_child(self._icon)
+ self._icon.construct()
+
+ def get_angle_start(self):
+ return self._angle_start
+
+ def get_angle_end(self):
+ return self._angle_end
+
+ def construct(self):
+ r = self.get_parent().get_radius()
+
+ data = 'M0,0 '
+
+ dx = r * math.cos(self._angle_start)
+ dy = - r * math.sin(self._angle_start)
+
+ data += 'l%f,%f ' % (dx, dy)
+
+ dx = r * math.cos(self._angle_end)
+ dy = - r * math.sin(self._angle_end)
+
+ data += 'A%f,%f 0 0,0 %f,%f ' % (r, r, dx, dy)
+
+ data += 'z'
+
+ self.set_property('data', data)
+
+class DonutItem(goocanvas.Group):
+ def __init__(self, radius, **kwargs):
+ goocanvas.Group.__init__(self, **kwargs)
+ self._radius = radius
+ self._angle_start = 0
+
+ bg = goocanvas.Ellipse(radius_x=radius, radius_y=radius,
+ fill_color='#f1f1f1', line_width=0)
+ self.add_child(bg)
+
+ self._inner_radius = radius / 2
+ fg = goocanvas.Ellipse(radius_x=self._inner_radius,
+ radius_y=self._inner_radius,
+ fill_color='#e2e2e2', line_width=0)
+ self.add_child(fg)
+
+ def add_piece(self, perc, icon_name, color):
+ # FIXME can't override set_parent on the
+ # PieceItem and there is no signal. So we
+ # call a construct method on the childs for now.
+
+ angle_end = self._angle_start + perc * 2 * math.pi / 100
+ piece_item = PieceItem(self._angle_start, angle_end)
+ self._angle_start = angle_end
+
+ self.add_child(piece_item, 1)
+ piece_item.construct()
+ piece_item.set_icon(icon_name, color)
+
+ return piece_item
+
+ def remove_piece(self, piece_item):
+ index = self.find_child(piece_item)
+ self.remove_child(index)
+
+ icon = piece_item.get_icon()
+ index = self.find_child(icon)
+ self.remove_child(index)
+
+ def get_radius(self):
+ return self._radius
+
+ def get_inner_radius(self):
+ return self._inner_radius
diff --git a/shell/view/home/FriendsGroup.py b/shell/view/home/FriendsGroup.py
new file mode 100644
index 0000000..1e2fb8f
--- /dev/null
+++ b/shell/view/home/FriendsGroup.py
@@ -0,0 +1,33 @@
+import random
+
+import goocanvas
+
+from view.home.IconLayout import IconLayout
+from view.home.MyIcon import MyIcon
+from view.FriendIcon import FriendIcon
+
+class FriendsGroup(goocanvas.Group):
+ def __init__(self, shell_model):
+ goocanvas.Group.__init__(self)
+
+ self._shell_model = shell_model
+ self._icon_layout = IconLayout(1200, 900)
+ self._friends = shell_model.get_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)
+
+ self._friends.connect('friend-added', self._friend_added_cb)
+
+ def add_friend(self, friend):
+ icon = FriendIcon(self._shell_model, friend)
+ self.add_child(icon)
+ self._icon_layout.add_icon(icon)
+
+ def _friend_added_cb(self, data_model, friend):
+ self.add_friend(friend)
diff --git a/shell/view/home/HomeGroup.py b/shell/view/home/HomeGroup.py
new file mode 100644
index 0000000..e6376cd
--- /dev/null
+++ b/shell/view/home/HomeGroup.py
@@ -0,0 +1,52 @@
+import goocanvas
+
+from view.home.DonutItem import DonutItem
+from view.home.MyIcon import MyIcon
+
+class TasksItem(DonutItem):
+ def __init__(self, shell_model):
+ DonutItem.__init__(self, 250)
+
+ self._items = {}
+
+ self._shell_model = shell_model
+ self._shell_model.connect('activity_opened', self.__activity_opened_cb)
+ self._shell_model.connect('activity_closed', self.__activity_closed_cb)
+
+ def __activity_opened_cb(self, model, activity):
+ self._add(activity)
+
+ def __activity_closed_cb(self, model, activity):
+ self._remove(activity)
+
+ def _remove(self, activity):
+ item = self._items[activity.get_id()]
+ self.remove_piece(item)
+ del self._items[activity.get_id()]
+
+ def _add(self, activity):
+ icon_name = activity.get_icon_name()
+ icon_color = activity.get_icon_color()
+
+ item = self.add_piece(100 / 8, icon_name, icon_color)
+ item.get_icon().connect('clicked',
+ self.__activity_icon_clicked_cb,
+ activity)
+
+ self._items[activity.get_id()] = item
+
+ def __activity_icon_clicked_cb(self, item, activity):
+ activity.present()
+
+class HomeGroup(goocanvas.Group):
+ def __init__(self, shell_model):
+ goocanvas.Group.__init__(self)
+
+ tasks = TasksItem(shell_model)
+ tasks.translate(600, 450)
+ self.add_child(tasks)
+
+ me = MyIcon(150)
+ me.translate(600 - (me.get_property('size') / 2),
+ 450 - (me.get_property('size') / 2))
+ self.add_child(me)
diff --git a/shell/view/home/HomeWindow.py b/shell/view/home/HomeWindow.py
new file mode 100644
index 0000000..b89420f
--- /dev/null
+++ b/shell/view/home/HomeWindow.py
@@ -0,0 +1,50 @@
+import gtk
+import goocanvas
+import cairo
+
+from sugar.canvas.CanvasView import CanvasView
+from view.home.MeshGroup import MeshGroup
+from view.home.HomeGroup import HomeGroup
+from view.home.FriendsGroup import FriendsGroup
+import sugar
+
+class HomeWindow(gtk.Window):
+ def __init__(self, shell_model):
+ gtk.Window.__init__(self)
+ self._shell_model = shell_model
+
+ self.realize()
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP)
+
+ self._nb = gtk.Notebook()
+ self._nb.set_show_border(False)
+ self._nb.set_show_tabs(False)
+
+ self.add(self._nb)
+ self._nb.show()
+
+ self._add_page(HomeGroup(shell_model))
+ self._add_page(FriendsGroup(shell_model))
+ self._add_page(MeshGroup())
+
+ def _add_page(self, group):
+ view = CanvasView()
+ self._nb.append_page(view)
+ view.show()
+
+ model = goocanvas.CanvasModelSimple()
+ root = model.get_root_item()
+ view.set_model(model)
+
+ bg = goocanvas.Rect(width=1900, height=1200,
+ line_width=0, fill_color='#e2e2e2')
+ root.add_child(bg)
+ root.add_child(group)
+
+ def set_zoom_level(self, level):
+ if level == sugar.ZOOM_HOME:
+ self._nb.set_current_page(0)
+ elif level == sugar.ZOOM_FRIENDS:
+ self._nb.set_current_page(1)
+ elif level == sugar.ZOOM_MESH:
+ self._nb.set_current_page(2)
diff --git a/shell/view/home/IconLayout.py b/shell/view/home/IconLayout.py
new file mode 100644
index 0000000..eedced8
--- /dev/null
+++ b/shell/view/home/IconLayout.py
@@ -0,0 +1,34 @@
+import random
+
+class IconLayout:
+ def __init__(self, width, height):
+ self._icons = []
+ self._width = width
+ self._height = height
+
+ def add_icon(self, icon):
+ self._icons.append(icon)
+ self._layout_icon(icon)
+
+ def remove_icon(self, icon):
+ self._icons.remove(icon)
+
+ def _is_valid_position(self, icon, x, y):
+ icon_size = icon.props.size
+ border = 20
+
+ if not (border < x < self._width - icon_size - border and \
+ border < y < self._height - icon_size - border):
+ return False
+
+ return True
+
+ def _layout_icon(self, icon):
+ while True:
+ x = random.random() * self._width
+ y = random.random() * self._height
+ if self._is_valid_position(icon, x, y):
+ break
+
+ icon.props.x = x
+ icon.props.y = y
diff --git a/shell/view/home/Makefile.am b/shell/view/home/Makefile.am
new file mode 100644
index 0000000..ddf9653
--- /dev/null
+++ b/shell/view/home/Makefile.am
@@ -0,0 +1,10 @@
+sugardir = $(pkgdatadir)/shell/view/home
+sugar_PYTHON = \
+ __init__.py \
+ DonutItem.py \
+ FriendsGroup.py \
+ IconLayout.py \
+ HomeGroup.py \
+ HomeWindow.py \
+ MeshGroup.py \
+ MyIcon.py
diff --git a/shell/view/home/MeshGroup.py b/shell/view/home/MeshGroup.py
new file mode 100644
index 0000000..90dce0e
--- /dev/null
+++ b/shell/view/home/MeshGroup.py
@@ -0,0 +1,82 @@
+import random
+
+import goocanvas
+
+import conf
+from sugar.canvas.IconItem import IconItem
+from sugar.canvas.IconItem import IconColor
+from sugar.presence import PresenceService
+from view.home.IconLayout import IconLayout
+
+class ActivityItem(IconItem):
+ def __init__(self, activity, service):
+ self._service = service
+ self._activity = activity
+
+ IconItem.__init__(self, icon_name=self.get_icon_name(),
+ color=self.get_color(), size=96)
+
+ def get_id(self):
+ return self._activity.get_id()
+
+ def get_icon_name(self):
+ registry = conf.get_activity_registry()
+ info = registry.get_activity_from_type(self._service.get_type())
+
+ return info.get_icon()
+
+ def get_color(self):
+ return IconColor(self._activity.get_color())
+
+ def get_service(self):
+ return self._service
+
+class MeshGroup(goocanvas.Group):
+ def __init__(self):
+ goocanvas.Group.__init__(self)
+ self._icon_layout = IconLayout(1200, 900)
+ self._activities = {}
+
+ self._pservice = PresenceService.get_instance()
+ self._pservice.connect("service-appeared", self._service_appeared_cb)
+ self._pservice.connect('activity-disappeared', self._activity_disappeared_cb)
+
+ for service in self._pservice.get_services():
+ self._check_service(service)
+
+ def _service_appeared_cb(self, pservice, service):
+ self._check_service(service)
+
+ def _check_service(self, service):
+ registry = conf.get_activity_registry()
+ if registry.get_activity_from_type(service.get_type()) != None:
+ activity_id = service.get_activity_id()
+ if not self.has_activity(activity_id):
+ activity = self._pservice.get_activity(activity_id)
+ if activity != None:
+ self.add_activity(activity, service)
+
+ def has_activity(self, activity_id):
+ return self._activities.has_key(activity_id)
+
+ def add_activity(self, activity, service):
+ item = ActivityItem(activity, service)
+ item.connect('clicked', self._activity_clicked_cb)
+ self._icon_layout.add_icon(item)
+ self.add_child(item)
+
+ self._activities[item.get_id()] = item
+
+ def _activity_disappeared_cb(self, pservice, activity):
+ if self._activities.has_key(activity.get_id()):
+ self.remove_child(self._activities[activity.get_id()])
+ del self._activities[activity.get_id()]
+
+ def _activity_clicked_cb(self, item):
+ default_type = item.get_service().get_type()
+ registry = conf.get_activity_registry()
+
+ bundle_id = registry.get_activity_from_type(default_type).get_id()
+ activity_id = item.get_id()
+
+ self._shell.join_activity(bundle_id, activity_id)
diff --git a/shell/view/home/MyIcon.py b/shell/view/home/MyIcon.py
new file mode 100644
index 0000000..226520d
--- /dev/null
+++ b/shell/view/home/MyIcon.py
@@ -0,0 +1,10 @@
+import conf
+from sugar.canvas.IconItem import IconItem
+from sugar.canvas.IconColor import IconColor
+
+class MyIcon(IconItem):
+ def __init__(self, size):
+ profile = conf.get_profile()
+
+ IconItem.__init__(self, icon_name='stock-buddy',
+ color=profile.get_color(), size=size)
diff --git a/shell/view/home/__init__.py b/shell/view/home/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/shell/view/home/__init__.py