diff --git a/activities/Makefile.am b/activities/Makefile.am index 84bfa853..c900fb64 100644 --- a/activities/Makefile.am +++ b/activities/Makefile.am @@ -1 +1 @@ -SUBDIRS = browser chat terminal +SUBDIRS = browser chat groupchat terminal diff --git a/activities/browser/browser.activity b/activities/browser/browser.activity index dcf4e438..47eea7f3 100644 --- a/activities/browser/browser.activity +++ b/activities/browser/browser.activity @@ -1,6 +1,7 @@ [Activity] name = Web id = com.redhat.Sugar.BrowserActivity +icon = activity-web python_module = browser.BrowserActivity.BrowserActivity default_type = _web_olpc._udp show_launcher = yes diff --git a/activities/groupchat/GroupChatActivity.py b/activities/groupchat/GroupChatActivity.py new file mode 100644 index 00000000..32cfac55 --- /dev/null +++ b/activities/groupchat/GroupChatActivity.py @@ -0,0 +1,8 @@ +from gettext import gettext as _ + +from sugar.activity.Activity import Activity + +class GroupChatActivity(Activity): + def __init__(self): + Activity.__init__(self) + self.set_title(_('Group chat')) diff --git a/activities/groupchat/Makefile.am b/activities/groupchat/Makefile.am new file mode 100644 index 00000000..66e99151 --- /dev/null +++ b/activities/groupchat/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pkgdatadir)/activities/groupchat +sugar_PYTHON = \ + __init__.py \ + GroupChatActivity.py + +EXTRA_DIST = groupchat.activity diff --git a/activities/groupchat/__init__.py b/activities/groupchat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/activities/groupchat/groupchat.activity b/activities/groupchat/groupchat.activity new file mode 100644 index 00000000..2c8734a1 --- /dev/null +++ b/activities/groupchat/groupchat.activity @@ -0,0 +1,6 @@ +[Activity] +name = GroupChat +icon = activity-groupchat +id = com.redhat.Sugar.GroupChatActivity +python_module = groupchat.GroupChatActivity.GroupChatActivity +show_launcher = yes diff --git a/configure.ac b/configure.ac index 95c4c46f..ab66e07c 100644 --- a/configure.ac +++ b/configure.ac @@ -27,14 +27,17 @@ dbus-installed.conf activities/Makefile activities/browser/Makefile activities/chat/Makefile +activities/groupchat/Makefile activities/terminal/Makefile shell/Makefile shell/data/Makefile +shell/home/Makefile shell/session/Makefile shell/PresenceService/Makefile sugar/Makefile sugar/__installed__.py sugar/activity/Makefile +sugar/canvas/Makefile sugar/chat/Makefile sugar/chat/sketchpad/Makefile sugar/p2p/Makefile diff --git a/shell/ActivityHost.py b/shell/ActivityHost.py index 639c589d..84577c1f 100644 --- a/shell/ActivityHost.py +++ b/shell/ActivityHost.py @@ -20,9 +20,15 @@ class ActivityHost: self._gdk_window = gtk.gdk.window_foreign_new(self._xid) self._people_window = PeopleWindow(shell, self) + info = self._shell.get_registry().get_activity(self._default_type) + self._icon_name = info.get_icon() + def get_id(self): return self._id + def get_icon_name(self): + return self._icon_name + def share(self): self._people_window.share() self._activity.share() diff --git a/shell/ActivityRegistry.py b/shell/ActivityRegistry.py index acf379c9..5ff0059f 100644 --- a/shell/ActivityRegistry.py +++ b/shell/ActivityRegistry.py @@ -8,6 +8,7 @@ class ActivityModule: def __init__(self, name, activity_id, directory): self._name = name + self._icon = None self._id = activity_id self._directory = directory self._show_launcher = False @@ -20,6 +21,14 @@ class ActivityModule: """Get the activity identifier""" return self._id + def get_icon(self): + """Get the activity icon name""" + return self._icon + + def set_icon(self, icon): + """Set the activity icon name""" + self._icon = icon + def get_directory(self): """Get the path to activity directory.""" return self._directory @@ -97,6 +106,9 @@ class ActivityRegistry: if cp.has_option('Activity', 'show_launcher'): module.set_show_launcher(True) + if cp.has_option('Activity', 'icon'): + module.set_icon(cp.get('Activity', 'icon')) + module.set_default_type(default_type) return True diff --git a/shell/ChatController.py b/shell/ChatController.py index 96d2fd00..00bab802 100644 --- a/shell/ChatController.py +++ b/shell/ChatController.py @@ -13,7 +13,8 @@ class ChatController: self._shell.connect('activity-closed', self.__activity_closed_cb) - def __activity_closed_cb(self, shell, activity_id): + 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] diff --git a/shell/HomeWindow.py b/shell/HomeWindow.py deleted file mode 100644 index 25a777b0..00000000 --- a/shell/HomeWindow.py +++ /dev/null @@ -1,165 +0,0 @@ -from gettext import gettext as _ - -import gtk -import wnck - -from sugar.activity import ActivityFactory -from ActivitiesModel import ActivitiesModel -from sugar.presence.PresenceService import PresenceService - -class NewActivityButton(gtk.MenuToolButton): - def __init__(self, home): - gtk.MenuToolButton.__init__(self, None, _('New Activity')) - - self._home = home - - self.set_menu(gtk.Menu()) - self.connect("show-menu", self.__show_menu_cb) - - def __show_menu_cb(self, button): - menu = gtk.Menu() - - for module in self._home.list_activities(): - if module.get_show_launcher(): - item = gtk.MenuItem(module.get_name(), False) - activity_id = module.get_id() - item.connect('activate', - self.__menu_item_activate_cb, activity_id) - menu.append(item) - item.show() - - self.set_menu(menu) - - def __menu_item_activate_cb(self, item, activity_id): - self._home.create(activity_id) - -class Toolbar(gtk.Toolbar): - def __init__(self, shell): - gtk.Toolbar.__init__(self) - - new_activity_button = NewActivityButton(shell) - self.insert(new_activity_button, -1) - new_activity_button.show() - -class ActivitiesGrid(gtk.VBox): - def __init__(self, shell, model): - gtk.VBox.__init__(self, shell) - - self._shell = shell - self._buttons = {} - - for activity in model: - self._add(activity) - model.connect('activity-added', self.__activity_added_cb) - model.connect('activity-removed', self.__activity_removed_cb) - - def __activity_added_cb(self, model, activity): - self._add(activity) - - def __activity_removed_cb(self, model, activity): - self._remove(window) - - def _remove(self, activity): - button = self._buttons[activity.get_id()] - self.remove(button) - - def _add(self, activity): - button = gtk.Button(activity.get_title()) - button.connect('clicked', self.__button_clicked_cb, activity) - self.pack_start(button, False) - button.show() - - self._buttons[activity.get_id()] = button - - def __button_clicked_cb(self, button, info): - self._shell.join_activity(info.get_service()) - -class TasksGrid(gtk.VBox): - def __init__(self, home): - gtk.VBox.__init__(self) - - self._home = home - self._buttons = {} - - screen = wnck.screen_get_default() - for window in screen.get_windows(): - if not window.is_skip_tasklist(): - self._add(window) - screen.connect('window_opened', self.__window_opened_cb) - screen.connect('window_closed', self.__window_closed_cb) - - def __window_opened_cb(self, screen, window): - if not window.is_skip_tasklist(): - self._add(window) - - def __window_closed_cb(self, screen, window): - if not window.is_skip_tasklist(): - self._remove(window) - - def _remove(self, window): - button = self._buttons[window.get_xid()] - self.remove(button) - - def __window_name_changed_cb(self, window, button): - button.set_label(window.get_name()) - - def _add(self, window): - button = gtk.Button(window.get_name()) - window.connect('name-changed', self.__window_name_changed_cb, button) - button.connect('clicked', self.__button_clicked_cb, window) - self.pack_start(button, False) - button.show() - - self._buttons[window.get_xid()] = button - - def __button_clicked_cb(self, button, window): - self._home.activate(window) - -class HomeWindow(gtk.Window): - def __init__(self, shell): - gtk.Window.__init__(self) - - self._shell = shell - - self.connect('realize', self.__realize_cb) - - vbox = gtk.VBox(False, 6) - vbox.set_border_width(24) - - toolbar = Toolbar(self) - vbox.pack_start(toolbar, False) - toolbar.show() - - label = gtk.Label('Open activities:') - label.set_alignment(0.0, 0.5) - vbox.pack_start(label, False) - label.show() - - grid = TasksGrid(self) - vbox.pack_start(grid) - grid.show() - - label = gtk.Label('Shared activities:') - label.set_alignment(0.0, 0.5) - vbox.pack_start(label, False) - label.show() - - model = ActivitiesModel(shell.get_registry()) - grid = ActivitiesGrid(shell, model) - vbox.pack_start(grid) - grid.show() - - self.add(vbox) - vbox.show() - - def __realize_cb(self, window): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) - - def list_activities(self): - return self._shell.get_registry().list_activities() - - def create(self, activity_name): - self._shell.start_activity(activity_name) - - def activate(self, activity_window): - activity_window.activate(gtk.get_current_event_time()) diff --git a/shell/Makefile.am b/shell/Makefile.am index 5642a4ab..61483ae8 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = data session PresenceService +SUBDIRS = data session home PresenceService bin_SCRIPTS = \ sugar \ @@ -6,18 +6,17 @@ bin_SCRIPTS = \ sugar-activity-factory \ sugar-console \ sugar-people \ + sugar-zoom \ sugar-presence-service sugardir = $(pkgdatadir)/shell sugar_PYTHON = \ __init__.py \ - ActivitiesModel.py \ ActivityHost.py \ ActivityRegistry.py \ ChatController.py \ ConsoleWindow.py \ Owner.py \ - HomeWindow.py \ PeopleWindow.py \ PresenceView.py \ Shell.py diff --git a/shell/Shell.py b/shell/Shell.py index 614e1d15..5876f703 100755 --- a/shell/Shell.py +++ b/shell/Shell.py @@ -8,7 +8,8 @@ import gobject import wnck from ActivityRegistry import ActivityRegistry -from HomeWindow import HomeWindow +from home.HomeWindow import HomeWindow +from home.HomeModel import HomeModel from sugar import env from Owner import ShellOwner from sugar.presence.PresenceService import PresenceService @@ -37,9 +38,25 @@ class ShellDbusService(dbus.service.Object): def show_console(self): gobject.idle_add(self.__show_console_idle) + @dbus.service.method('com.redhat.Sugar.Shell') + def zoom_in(self): + self._shell.zoom_in() + + @dbus.service.method('com.redhat.Sugar.Shell') + def zoom_out(self): + self._shell.zoom_out() + class Shell(gobject.GObject): + ZOOM_MESH = 0 + ZOOM_FRIENDS = 1 + ZOOM_HOME = 2 + ZOOM_ACTIVITY = 3 + __gsignals__ = { - 'activity-closed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])) + 'activity-opened': (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, registry): @@ -48,6 +65,7 @@ class Shell(gobject.GObject): self._screen = wnck.screen_get_default() self._registry = registry self._hosts = {} + self._zoom_level = Shell.ZOOM_HOME def start(self): session_bus = dbus.SessionBus() @@ -60,7 +78,8 @@ class Shell(gobject.GObject): self._chat_controller = ChatController(self) self._chat_controller.listen() - self._home_window = HomeWindow(self) + home_model = HomeModel(self._registry) + self._home_window = HomeWindow(self, home_model) self._home_window.show() self._screen.connect('window-opened', self.__window_opened_cb) @@ -71,14 +90,16 @@ class Shell(gobject.GObject): def __window_opened_cb(self, screen, window): if window.get_window_type() == wnck.WINDOW_NORMAL: - self._hosts[window.get_xid()] = ActivityHost(self, window) + host = ActivityHost(self, window) + self._hosts[window.get_xid()] = host + self.emit('activity-opened', host) def __window_closed_cb(self, screen, window): if window.get_window_type() == wnck.WINDOW_NORMAL: xid = window.get_xid() - activity = self._hosts[xid] - self.emit('activity-closed', activity.get_id()) + host = self._hosts[xid] + self.emit('activity-closed', host) del self._hosts[xid] @@ -154,3 +175,28 @@ class Shell(gobject.GObject): def get_chat_controller(self): return self._chat_controller + + def _set_zoom_level(self, level): + self._zoom_level = level + + if level == Shell.ZOOM_ACTIVITY: + self._screen.toggle_showing_desktop(False) + else: + self._screen.toggle_showing_desktop(True) + + if level == Shell.ZOOM_HOME: + self._home_window.set_view(HomeWindow.HOME_VIEW) + elif level == Shell.ZOOM_FRIENDS: + self._home_window.set_view(HomeWindow.FRIENDS_VIEW) + elif level == Shell.ZOOM_MESH: + self._home_window.set_view(HomeWindow.MESH_VIEW) + + def zoom_in(self): + level = self._zoom_level + 1 + if level <= Shell.ZOOM_ACTIVITY: + self._set_zoom_level(level) + + def zoom_out(self): + level = self._zoom_level - 1 + if level >= Shell.ZOOM_MESH: + self._set_zoom_level(level) diff --git a/shell/data/Makefile.am b/shell/data/Makefile.am index fa14d6bb..1f0f9c05 100644 --- a/shell/data/Makefile.am +++ b/shell/data/Makefile.am @@ -1,8 +1,4 @@ confdir = $(pkgdatadir) conf_DATA = kbdconfig -imagesdir = $(pkgdatadir) -images_DATA = \ - home-background.png - -EXTRA_DIST = $(conf_DATA) $(images_DATA) +EXTRA_DIST = $(conf_DATA) diff --git a/shell/data/activity-placeholder.png b/shell/data/activity-placeholder.png deleted file mode 100644 index 9fea97b8..00000000 Binary files a/shell/data/activity-placeholder.png and /dev/null differ diff --git a/shell/data/home-background.png b/shell/data/home-background.png deleted file mode 100644 index 346b5dce..00000000 Binary files a/shell/data/home-background.png and /dev/null differ diff --git a/shell/data/kbdconfig b/shell/data/kbdconfig index ef9d96a4..71d8032a 100644 --- a/shell/data/kbdconfig +++ b/shell/data/kbdconfig @@ -5,7 +5,7 @@ n=next p=prev c=close -f1=desktop -f2=!sugar-people -f3=!sugar-console +f1=!sugar-zoom out +f2=!sugar-zoom in +f3=!sugar-people f4=!sugar-activity org.sugar.Terminal diff --git a/shell/home/FriendsModel.py b/shell/home/FriendsModel.py new file mode 100644 index 00000000..6f18083a --- /dev/null +++ b/shell/home/FriendsModel.py @@ -0,0 +1,40 @@ +import gobject + +from sugar.presence.PresenceService import PresenceService + +class Friend: + def __init__(self, buddy): + self._buddy = buddy + + def get_name(self): + return self._buddy.get_name() + +class FriendsModel(gobject.GObject): + __gsignals__ = { + 'friend-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'friend-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self): + gobject.GObject.__init__(self) + + self._friends = [] + + self._pservice = PresenceService() + self._pservice.connect("buddy-appeared", self.__buddy_appeared_cb) + + for buddy in self._pservice.get_buddies(): + self.add_friend(buddy) + + def add_friend(self, buddy): + friend = Friend(buddy) + self._friends.append(friend) + self.emit('friend-added', friend) + + def __iter__(self): + return self._friends.__iter__() + + def __buddy_appeared_cb(self, pservice, buddy): + self.add_friend(buddy) diff --git a/shell/home/FriendsView.py b/shell/home/FriendsView.py new file mode 100644 index 00000000..b12616a4 --- /dev/null +++ b/shell/home/FriendsView.py @@ -0,0 +1,45 @@ +import random + +import goocanvas + +from sugar.canvas.IconItem import IconItem + +class Model(goocanvas.CanvasModelSimple): + def __init__(self, data_model): + goocanvas.CanvasModelSimple.__init__(self) + + root = self.get_root_item() + + item = goocanvas.Rect(width=1200, height=900, + fill_color="#d8d8d8") + root.add_child(item) + + for friend in data_model: + self.add_friend(friend) + + data_model.connect('friend-added', self.__friend_added_cb) + + def add_friend(self, friend): + root = self.get_root_item() + + icon = IconItem('stock-buddy', 'green', 48) + icon.set_property('x', random.random() * 1100) + icon.set_property('y', random.random() * 800) + + root.add_child(icon) + + def __friend_added_cb(self, data_model, friend): + self.add_friend(friend) + +class FriendsView(goocanvas.CanvasView): + def __init__(self, shell, data_model): + goocanvas.CanvasView.__init__(self) + self._shell = shell + + self.connect("item_view_created", self.__item_view_created_cb) + + canvas_model = Model(data_model) + self.set_model(canvas_model) + + def __item_view_created_cb(self, view, item_view, item): + pass diff --git a/shell/home/HomeModel.py b/shell/home/HomeModel.py new file mode 100644 index 00000000..5f4965d3 --- /dev/null +++ b/shell/home/HomeModel.py @@ -0,0 +1,13 @@ +from home.FriendsModel import FriendsModel +from home.MeshModel import MeshModel + +class HomeModel: + def __init__(self, registry): + self._friends = FriendsModel() + self._mesh = MeshModel(registry) + + def get_friends(self): + return self._friends + + def get_mesh(self): + return self._mesh diff --git a/shell/home/HomeView.py b/shell/home/HomeView.py new file mode 100644 index 00000000..09627b66 --- /dev/null +++ b/shell/home/HomeView.py @@ -0,0 +1,128 @@ +import gtk +import goocanvas +import wnck + +from sugar.canvas.IconItem import IconItem +from sugar.canvas.DonutItem import DonutItem +from sugar.canvas.DonutItem import PieceItem +from sugar.canvas.DonutItem import PieceIcon + +class TasksItem(DonutItem): + def __init__(self, shell): + DonutItem.__init__(self, 250) + + self._items = {} + + shell.connect('activity_opened', self.__activity_opened_cb) + shell.connect('activity_closed', self.__activity_closed_cb) + + def __activity_opened_cb(self, shell, activity): + self._add(activity) + + def __activity_closed_cb(self, shell, 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() + item = self.add_piece(100 / 8, icon_name, 'blue') + + # FIXME This really sucks. Fix goocanvas event handling. + item.set_data('activity', activity) + item.get_icon().set_data('activity', activity) + + self._items[activity.get_id()] = item + +class ActivityItem(IconItem): + ICON_SIZE = 30 + + def __init__(self, activity): + IconItem.__init__(self, activity.get_icon(), 'white', + ActivityItem.ICON_SIZE) + self._activity = activity + + def get_activity_id(self): + return self._activity.get_id() + +class ActivityBar(goocanvas.Group): + def __init__(self, shell): + goocanvas.Group.__init__(self) + + self._shell = shell + + registry = shell.get_registry() + for activity in registry.list_activities(): + if activity.get_show_launcher(): + self.add_activity(activity) + + def add_activity(self, activity): + item = ActivityItem(activity) + x = (ActivityItem.ICON_SIZE + 6) * self.get_n_children() + item.set_property('x', x) + self.add_child(item) + +class Background(goocanvas.Group): + def __init__(self): + goocanvas.Group.__init__(self) + + item = goocanvas.Rect(width=1200, height=900, + fill_color="#4f4f4f") + self.add_child(item) + + item = goocanvas.Rect(x=50, y=50, width=1100, height=800, + line_width=0, fill_color="#d8d8d8", + radius_x=30, radius_y=30) + self.add_child(item) + + item = goocanvas.Text(text="My Activities", + x=60, y=10, fill_color="white", + font="Sans 21") + self.add_child(item) + +class Model(goocanvas.CanvasModelSimple): + def __init__(self, shell): + goocanvas.CanvasModelSimple.__init__(self) + + root = self.get_root_item() + + background = Background() + root.add_child(background) + + activity_bar = ActivityBar(shell) + activity_bar.translate(50, 860) + root.add_child(activity_bar) + + tasks = TasksItem(shell) + tasks.translate(600, 450) + root.add_child(tasks) + +class HomeView(goocanvas.CanvasView): + def __init__(self, shell): + goocanvas.CanvasView.__init__(self) + self._shell = shell + + self.connect("item_view_created", self.__item_view_created_cb) + + canvas_model = Model(shell) + self.set_model(canvas_model) + + def __item_view_created_cb(self, view, item_view, item): + if isinstance(item, ActivityItem): + item_view.connect("button_press_event", + self.__activity_button_press_cb, + item.get_activity_id()) + elif isinstance(item, PieceItem) or \ + isinstance(item, PieceIcon): + item_view.connect("button_press_event", + self.__task_button_press_cb) + + def __activity_button_press_cb(self, view, target, event, activity_id): + self._shell.start_activity(activity_id) + + def __task_button_press_cb(self, view, target, event): + activity = view.get_item().get_data('activity') + activity.present() diff --git a/shell/home/HomeWindow.py b/shell/home/HomeWindow.py new file mode 100644 index 00000000..86c83ab1 --- /dev/null +++ b/shell/home/HomeWindow.py @@ -0,0 +1,48 @@ +import gtk + +from home.MeshView import MeshView +from home.HomeView import HomeView +from home.FriendsView import FriendsView + +class HomeWindow(gtk.Window): + HOME_VIEW = 0 + FRIENDS_VIEW = 1 + MESH_VIEW = 2 + + def __init__(self, shell, model): + gtk.Window.__init__(self) + + self.connect('realize', self.__realize_cb) + + self._nb = gtk.Notebook() + self._nb.set_show_tabs(False) + self._nb.set_show_border(False) + + home_view = HomeView(shell) + self._nb.append_page(home_view) + self._setup_canvas(home_view) + home_view.show() + + friends_view = FriendsView(shell, model.get_friends()) + self._nb.append_page(friends_view) + self._setup_canvas(friends_view) + friends_view.show() + + mesh_view = MeshView(shell, model.get_mesh()) + self._setup_canvas(mesh_view) + self._nb.append_page(mesh_view) + mesh_view.show() + + self.add(self._nb) + self._nb.show() + + def set_view(self, view): + self._nb.set_current_page(view) + + def _setup_canvas(self, canvas): + canvas.set_bounds(0, 0, 1200, 900) + canvas.set_scale(float(800) / float(1200)) + canvas.set_size_request(800, 600) + + def __realize_cb(self, window): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) diff --git a/shell/home/Makefile.am b/shell/home/Makefile.am new file mode 100644 index 00000000..2a86a261 --- /dev/null +++ b/shell/home/Makefile.am @@ -0,0 +1,10 @@ +sugardir = $(pkgdatadir)/shell/home +sugar_PYTHON = \ + __init__.py \ + FriendsModel.py \ + FriendsView.py \ + MeshModel.py \ + MeshView.py \ + HomeView.py \ + HomeWindow.py \ + HomeModel.py diff --git a/shell/ActivitiesModel.py b/shell/home/MeshModel.py similarity index 97% rename from shell/ActivitiesModel.py rename to shell/home/MeshModel.py index c46256d2..b2163ac6 100644 --- a/shell/ActivitiesModel.py +++ b/shell/home/MeshModel.py @@ -19,7 +19,7 @@ class ActivityInfo: def get_service(self): return self._service -class ActivitiesModel(gobject.GObject): +class MeshModel(gobject.GObject): __gsignals__ = { 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), diff --git a/shell/home/MeshView.py b/shell/home/MeshView.py new file mode 100644 index 00000000..bd8f230c --- /dev/null +++ b/shell/home/MeshView.py @@ -0,0 +1,63 @@ +import random + +import goocanvas + +from sugar.canvas.IconItem import IconItem + +class ActivityItem(IconItem): + def __init__(self, activity, registry): + info = registry.get_activity(activity.get_type()) + icon_name = info.get_icon() + + IconItem.__init__(self, icon_name, 'green', 48) + + self._activity = activity + + def get_service(self): + return self._activity.get_service() + +class Model(goocanvas.CanvasModelSimple): + def __init__(self, data_model, registry): + goocanvas.CanvasModelSimple.__init__(self) + self._registry = registry + + root = self.get_root_item() + + item = goocanvas.Rect(width=1200, height=900, + fill_color="#d8d8d8") + root.add_child(item) + + for activity in data_model: + self.add_activity(activity) + + data_model.connect('activity-added', self.__activity_added_cb) + + def add_activity(self, activity): + root = self.get_root_item() + + item = ActivityItem(activity, self._registry) + item.set_property('x', random.random() * 1100) + item.set_property('y', random.random() * 800) + root.add_child(item) + + def __activity_added_cb(self, data_model, activity): + self.add_activity(activity) + +class MeshView(goocanvas.CanvasView): + def __init__(self, shell, data_model): + goocanvas.CanvasView.__init__(self) + self._shell = shell + + self.connect("item_view_created", self.__item_view_created_cb) + + canvas_model = Model(data_model, shell.get_registry()) + self.set_model(canvas_model) + + def __activity_button_press_cb(self, view, target, event, service): + self._shell.join_activity(service) + + def __item_view_created_cb(self, view, item_view, item): + if isinstance(item, ActivityItem): + item_view.connect("button_press_event", + self.__activity_button_press_cb, + item.get_service()) diff --git a/shell/home/__init__.py b/shell/home/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/shell/session/Emulator.py b/shell/session/Emulator.py index 2bb2b3ac..c18d92e4 100644 --- a/shell/session/Emulator.py +++ b/shell/session/Emulator.py @@ -30,7 +30,7 @@ def get_display_number(): class XephyrProcess(Process): def __init__(self): self._display = get_display_number() - cmd = 'Xephyr :%d -ac -screen 640x480' % (self._display) + cmd = 'Xephyr :%d -ac -screen 800x600' % (self._display) Process.__init__(self, cmd) def get_name(self): @@ -43,7 +43,7 @@ class XephyrProcess(Process): class XnestProcess(Process): def __init__(self): self._display = get_display_number() - cmd = 'Xnest :%d -ac -geometry 640x480' % (self._display) + cmd = 'Xnest :%d -ac -geometry 800x600' % (self._display) Process.__init__(self, cmd) def get_name(self): diff --git a/shell/sugar-zoom b/shell/sugar-zoom new file mode 100644 index 00000000..ee435170 --- /dev/null +++ b/shell/sugar-zoom @@ -0,0 +1,14 @@ +#!/usr/bin/python +import sys + +import dbus +import dbus.glib + +bus = dbus.SessionBus() +proxy_obj = bus.get_object('com.redhat.Sugar.Shell', '/com/redhat/Sugar/Shell') +shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.Shell') + +if sys.argv[1] == 'in': + shell.zoom_in() +elif sys.argv[1] == 'out': + shell.zoom_out() diff --git a/sugar/Makefile.am b/sugar/Makefile.am index 6fad678a..d339fbaf 100644 --- a/sugar/Makefile.am +++ b/sugar/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = activity chat p2p presence +SUBDIRS = activity canvas chat p2p presence sugardir = $(pythondir)/sugar sugar_PYTHON = \ diff --git a/sugar/canvas/DonutItem.py b/sugar/canvas/DonutItem.py new file mode 100644 index 00000000..a6b49ea9 --- /dev/null +++ b/sugar/canvas/DonutItem.py @@ -0,0 +1,114 @@ +import math + +import goocanvas + +from sugar.canvas.IconItem import IconItem + +class PieceIcon(IconItem): + def __init__(self, piece_item, icon_name, color, **kwargs): + IconItem.__init__(self, icon_name, color, 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('width') + icon_height = self.get_property('height') + 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', '#e8e8e8') + self.set_property('stroke-color', '#d8d8d8') + 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, 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='#c2c3c5', 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='#d8d8d8', 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/sugar/canvas/IconItem.py b/sugar/canvas/IconItem.py new file mode 100644 index 00000000..73098b72 --- /dev/null +++ b/sugar/canvas/IconItem.py @@ -0,0 +1,52 @@ +import re + +import gobject +import gtk +import goocanvas + +from sugar.util import GObjectSingletonMeta + +class IconCache(gobject.GObject): + __metaclass__ = GObjectSingletonMeta + + def __init__(self): + gobject.GObject.__init__(self) + self._icons = {} + + def _create_icon(self, name, color, size): + theme = gtk.icon_theme_get_default() + info = theme.lookup_icon(name, size, 0) + icon_file = open(info.get_filename(), 'r') + data = icon_file.read() + icon_file.close() + + if color != None: + style = '.fill-color {fill: %s;}' % (color) + data = re.sub('\.fill-color \{.*\}', style, data) + + style = '.fill-and-stroke-color {fill: %s; stroke: %s;}' % (color, color) + data = re.sub('\.fill-and-stroke-color \{.*\}', style, data) + + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/svg-xml') + loader.set_size(size, size) + loader.write(data) + loader.close() + + return loader.get_pixbuf() + + def get_icon(self, name, color, size): + key = (name, color, size) + if self._icons.has_key(key): + return self._icons[key] + else: + icon = self._create_icon(name, color, size) + self._icons[key] = icon + return icon + +class IconItem(goocanvas.Image): + def __init__(self, icon_name, color, size, **kwargs): + goocanvas.Image.__init__(self, **kwargs) + + icon_cache = IconCache() + pixbuf = icon_cache.get_icon(icon_name, color, size) + self.set_property('pixbuf', pixbuf) diff --git a/sugar/canvas/Makefile.am b/sugar/canvas/Makefile.am new file mode 100644 index 00000000..35146695 --- /dev/null +++ b/sugar/canvas/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/sugar/canvas +sugar_PYTHON = \ + __init__.py \ + DonutItem.py \ + IconItem.py diff --git a/sugar/canvas/__init__.py b/sugar/canvas/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sugar/util.py b/sugar/util.py index bfddf32b..9cb7d55a 100644 --- a/sugar/util.py +++ b/sugar/util.py @@ -4,6 +4,20 @@ import random import binascii import string +import gobject + +class GObjectSingletonMeta(gobject.GObjectMeta): + """GObject Singleton Metaclass""" + + def __init__(klass, name, bases, dict): + gobject.GObjectMeta.__init__(klass, name, bases, dict) + klass.__instance = None + + def __call__(klass, *args, **kwargs): + if klass.__instance is None: + klass.__instance = gobject.GObjectMeta.__call__(klass, *args, **kwargs) + return klass.__instance + def _stringify_sha(sha_hash): """Convert binary sha1 hash data into printable characters.""" print_sha = ""