diff options
author | Marco 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) |
commit | bcc1740f7f514f04629cecb7536c93d4feaf83cf (patch) | |
tree | a96559839563cf01d08c8132d8fd71e8a35a17ef /shell/view | |
parent | ca19f0f25100006c0f6c84e405e2e6a0cecdd15b (diff) |
Move the view to his own module
Diffstat (limited to 'shell/view')
-rw-r--r-- | shell/view/ActivityHost.py | 72 | ||||
-rw-r--r-- | shell/view/ConsoleWindow.py | 128 | ||||
-rw-r--r-- | shell/view/FirstTimeDialog.py | 36 | ||||
-rw-r--r-- | shell/view/FriendIcon.py | 104 | ||||
-rw-r--r-- | shell/view/FriendPopup.py | 83 | ||||
-rw-r--r-- | shell/view/Makefile.am | 11 | ||||
-rw-r--r-- | shell/view/Shell.py | 74 | ||||
-rw-r--r-- | shell/view/__init__.py | 0 | ||||
-rw-r--r-- | shell/view/frame/BottomPanel.py | 83 | ||||
-rw-r--r-- | shell/view/frame/Frame.py | 70 | ||||
-rw-r--r-- | shell/view/frame/Makefile.am | 8 | ||||
-rw-r--r-- | shell/view/frame/PanelWindow.py | 27 | ||||
-rw-r--r-- | shell/view/frame/RightPanel.py | 81 | ||||
-rw-r--r-- | shell/view/frame/TopPanel.py | 70 | ||||
-rw-r--r-- | shell/view/frame/__init__.py | 0 | ||||
-rw-r--r-- | shell/view/home/DonutItem.py | 114 | ||||
-rw-r--r-- | shell/view/home/FriendsGroup.py | 33 | ||||
-rw-r--r-- | shell/view/home/HomeGroup.py | 52 | ||||
-rw-r--r-- | shell/view/home/HomeWindow.py | 50 | ||||
-rw-r--r-- | shell/view/home/IconLayout.py | 34 | ||||
-rw-r--r-- | shell/view/home/Makefile.am | 10 | ||||
-rw-r--r-- | shell/view/home/MeshGroup.py | 82 | ||||
-rw-r--r-- | shell/view/home/MyIcon.py | 10 | ||||
-rw-r--r-- | shell/view/home/__init__.py | 0 |
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 |