From 00b269ddc1cd9eec0fd051968a98bb5786ada980 Mon Sep 17 00:00:00 2001 From: Justin Gallardo Date: Wed, 29 Nov 2006 22:19:56 +0000 Subject: Initial commit of the new Web activity bundle --- diff --git a/.test.py.swp b/.test.py.swp new file mode 100644 index 0000000..668b8c9 --- /dev/null +++ b/.test.py.swp Binary files differ diff --git a/.webbrowser.py.swp b/.webbrowser.py.swp new file mode 100644 index 0000000..b97bd6e --- /dev/null +++ b/.webbrowser.py.swp Binary files differ diff --git a/activity/activity-web.svg b/activity/activity-web.svg new file mode 100755 index 0000000..9ddea1e --- /dev/null +++ b/activity/activity-web.svg @@ -0,0 +1,21 @@ + + + + + + +]> + + + + + + + + + + diff --git a/activity/activity.info b/activity/activity.info new file mode 100755 index 0000000..fbbb591 --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,8 @@ +[Activity] +name = Web +activity_version = 1 +host_version = 1 +service_name = org.laptop.WebActivity +icon = activity-web +show_launcher = 1 +exec = sugar-activity-factory org.laptop.WebActivity webactivity.WebActivity diff --git a/linkscontroller.py b/linkscontroller.py new file mode 100755 index 0000000..bdc1296 --- /dev/null +++ b/linkscontroller.py @@ -0,0 +1,60 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.p2p.Stream import Stream +from sugar.presence import PresenceService + +class _Marshaller(object): + def __init__(self, title, address): + pservice = PresenceService.get_instance() + name = pservice.get_owner().get_name() + self._message = name + '\n' + title + '\n' + address + + def get_message(self): + return self._message + +class _Demarshaller(object): + def __init__(self, message): + self._pservice = PresenceService.get_instance() + self._split_msg = message.split('\n') + + def get_buddy(self): + return self._pservice.get_buddy_by_name(self._split_msg[0]) + + def get_title(self): + return self._split_msg[1] + + def get_address(self): + return self._split_msg[2] + +class LinksController(object): + def __init__(self, service, model): + self._model = model + + self._stream = Stream.new_from_service(service) + self._stream.set_data_listener(self._recv_message) + self._stream_writer = self._stream.new_writer() + + def post_link(self, title, address): + marshaller = _Marshaller(title, address) + self._stream_writer.write(marshaller.get_message()) + + def _recv_message(self, address, msg): + demarshaller = _Demarshaller(msg) + buddy = demarshaller.get_buddy() + if buddy: + self._model.add_link(buddy, demarshaller.get_title(), + demarshaller.get_address()) diff --git a/linkscontroller.pyc b/linkscontroller.pyc new file mode 100755 index 0000000..ab50724 --- /dev/null +++ b/linkscontroller.pyc Binary files differ diff --git a/linksmodel.py b/linksmodel.py new file mode 100755 index 0000000..4318bfc --- /dev/null +++ b/linksmodel.py @@ -0,0 +1,51 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gobject + +class Link(object): + def __init__(self, buddy, title, url): + self.buddy = buddy + self.title = title + self.url = url + +class LinksModel(gobject.GObject): + __gsignals__ = { + 'link-added': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'link-removed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + } + + def __init__(self): + gobject.GObject.__init__(self) + self._links = {} + + def add_link(self, buddy, title, url): + link = Link(buddy, title, url) + self._links[(buddy.get_name(), url)] = link + + self.emit('link-added', link) + + def remove_link(self, buddy, url): + key = (buddy.get_name(), url) + if self._links.haskey(key): + link = self._links[key] + del self._links[key] + self.emit('link-removed', link) + + def __iter__(self): + return self._links.values().__iter__() diff --git a/linksmodel.pyc b/linksmodel.pyc new file mode 100755 index 0000000..fd84e46 --- /dev/null +++ b/linksmodel.pyc Binary files differ diff --git a/linksview.py b/linksview.py new file mode 100755 index 0000000..70af786 --- /dev/null +++ b/linksview.py @@ -0,0 +1,83 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk +import hippo + +from sugar.graphics.menu import Menu +from sugar.graphics.menushell import MenuShell +from sugar.graphics.menuicon import MenuIcon +from sugar.graphics.iconcolor import IconColor +from sugar.graphics import style + +class LinkIcon(MenuIcon): + def __init__(self, menu_shell, link): + color = IconColor(link.buddy.get_color()) + MenuIcon.__init__(self, menu_shell, color=color, + icon_name='activity-web') + + self._link = link + + def create_menu(self): + menu = Menu(self._link.title) + return menu + +class LinksView(hippo.Canvas): + def __init__(self, model, browser): + hippo.Canvas.__init__(self) + + self._icons = {} + self._browser = browser + self._menu_shell = MenuShell(self) + + self._box = hippo.CanvasBox() + style.apply_stylesheet(self._box, 'links.Box') + self.set_root(self._box) + + for link in model: + self._add_link(link) + + model.connect('link_added', self._link_added_cb) + model.connect('link_removed', self._link_removed_cb) + + def _add_link(self, link): + if len(self._icons) == 0: + self.show() + + icon = LinkIcon(self._menu_shell, link) + icon.connect('activated', self._link_activated_cb, link) + style.apply_stylesheet(icon, 'links.Icon') + self._box.append(icon) + + self._icons[link] = icon + + def _remove_link(self, link): + icon = self._icons[link] + self._box.remove(icon) + + del self._icons[link] + + if len(self._icons) == 0: + self.hide() + + def _link_added_cb(self, model, link): + self._add_link(link) + + def _link_removed_cb(self, model, link): + self._remove_link(link) + + def _link_activated_cb(self, link_item, link): + self._browser.load_url(link.url) diff --git a/linksview.pyc b/linksview.pyc new file mode 100755 index 0000000..683179d --- /dev/null +++ b/linksview.pyc Binary files differ diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..876cd3f --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.activity import bundlebuilder + +bundlebuilder.start() + diff --git a/stylesheet.py b/stylesheet.py new file mode 100755 index 0000000..3628818 --- /dev/null +++ b/stylesheet.py @@ -0,0 +1,27 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk + +from sugar.graphics import style + +links_Icon = { + 'size' : style.standard_icon_size +} + +links_Box = { + 'background_color' : 0x414141ff, +} diff --git a/stylesheet.pyc b/stylesheet.pyc new file mode 100755 index 0000000..e8e4bd9 --- /dev/null +++ b/stylesheet.pyc Binary files differ diff --git a/toolbar.py b/toolbar.py new file mode 100755 index 0000000..a516d9f --- /dev/null +++ b/toolbar.py @@ -0,0 +1,148 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gtk + +from _sugar import AddressEntry + +class Toolbar(gtk.Toolbar): + def __init__(self, embed): + gtk.Toolbar.__init__(self) + + self.set_style(gtk.TOOLBAR_BOTH_HORIZ) + + self._insert_spring() + + self._back = gtk.ToolButton() + self._back.props.sensitive = False + self._back.set_icon_name('stock-back') + self._back.connect("clicked", self._go_back_cb) + self.insert(self._back, -1) + self._back.show() + + self._forward = gtk.ToolButton() + self._forward.props.sensitive = False + self._forward.set_icon_name('stock-forward') + self._forward.connect("clicked", self._go_forward_cb) + self.insert(self._forward, -1) + self._forward.show() + + self._stop_and_reload = gtk.ToolButton() + self._forward.props.sensitive = False + self._stop_and_reload.connect("clicked", self._stop_and_reload_cb) + self.insert(self._stop_and_reload, -1) + self._stop_and_reload.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + self.insert(separator, -1) + separator.show() + + address_item = gtk.ToolItem() + + self._entry = AddressEntry() + self._entry.connect("activate", self._entry_activate_cb) + + width = int(gtk.gdk.screen_width() / 1.8) + self._entry.set_size_request(width, -1) + + address_item.add(self._entry) + self._entry.show() + + self.insert(address_item, -1) + address_item.show() + + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + self.insert(separator, -1) + separator.show() + + self._post = gtk.ToolButton() + self._post.props.sensitive = False + self._post.set_icon_name('stock-add') + self._post.connect("clicked", self._post_cb) + self.insert(self._post, -1) + self._post.show() + + self._insert_spring() + + self._embed = embed + self._embed.connect("notify::progress", self._progress_changed_cb) + self._embed.connect("notify::loading", self._loading_changed_cb) + self._embed.connect("notify::address", self._address_changed_cb) + self._embed.connect("notify::title", self._title_changed_cb) + self._embed.connect("notify::can-go-back", + self._can_go_back_changed_cb) + self._embed.connect("notify::can-go-forward", + self._can_go_forward_changed_cb) + + self._update_stop_and_reload_icon() + + def set_links_controller(self, links_controller): + self._links_controller = links_controller + self._post.props.sensitive = True + + def _update_stop_and_reload_icon(self): + if self._embed.props.loading: + self._stop_and_reload.set_icon_name('stock-close') + else: + self._stop_and_reload.set_icon_name('stock-continue') + + def _progress_changed_cb(self, embed, spec): + self._entry.props.progress = embed.props.progress + + def _loading_changed_cb(self, embed, spec): + self._update_stop_and_reload_icon() + + def _address_changed_cb(self, embed, spec): + self._entry.props.address = embed.props.address + + def _title_changed_cb(self, embed, spec): + self._entry.props.title = embed.props.title + + def _can_go_back_changed_cb(self, embed, spec): + self._back.props.sensitive = embed.props.can_go_back + + def _can_go_forward_changed_cb(self, embed, spec): + self._forward.props.sensitive = embed.props.can_go_forward + + def _entry_activate_cb(self, entry): + self._embed.load_url(entry.get_text()) + self._embed.grab_focus() + + def _go_back_cb(self, button): + self._embed.go_back() + + def _go_forward_cb(self, button): + self._embed.go_forward() + + def _stop_and_reload_cb(self, button): + if self._embed.props.loading: + self._embed.stop_load() + else: + self._embed.reload(0) + + def _post_cb(self, button): + title = self._embed.get_title() + address = self._embed.get_location() + self._links_controller.post_link(title, address) + + def _insert_spring(self): + separator = gtk.SeparatorToolItem() + separator.set_draw(False) + separator.set_expand(True) + self.insert(separator, -1) + separator.show() diff --git a/toolbar.pyc b/toolbar.pyc new file mode 100755 index 0000000..4e2f290 --- /dev/null +++ b/toolbar.pyc Binary files differ diff --git a/webactivity.py b/webactivity.py new file mode 100755 index 0000000..64c20f9 --- /dev/null +++ b/webactivity.py @@ -0,0 +1,139 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gettext import gettext as _ +import gtk +import gtkmozembed +import logging +import dbus + +import _sugar +from sugar.activity import ActivityFactory +from sugar.activity.Activity import Activity +from sugar.clipboard import ClipboardService +from sugar import env +from sugar.graphics import style + +import stylesheet +from webbrowser import WebBrowser +from toolbar import Toolbar +from linksmodel import LinksModel +from linksview import LinksView +from linkscontroller import LinksController + +_HOMEPAGE = 'http://www.google.com' + +class WebActivity(Activity): + def __init__(self, browser=None): + Activity.__init__(self) + + logging.debug('Starting the web activity') + + self.set_title(_('Web Activity')) + + vbox = gtk.VBox() + + if browser: + self._browser = browser + else: + self._browser = WebBrowser() + self._browser.connect('notify::title', self._title_changed_cb) + + self._links_model = LinksModel() + links_view = LinksView(self._links_model, self._browser) + + self._toolbar = Toolbar(self._browser) + vbox.pack_start(self._toolbar, False) + self._toolbar.show() + + hbox = gtk.HBox() + + hbox.pack_start(links_view, False) + hbox.pack_start(self._browser) + self._browser.show() + + vbox.pack_start(hbox) + hbox.show() + + self.add(vbox) + vbox.show() + + self._browser.load_url(_HOMEPAGE) + + def _setup_links_controller(self): + links_controller = LinksController(self._service, self._links_model) + self._toolbar.set_links_controller(links_controller) + + def join(self, activity_ps): + Activity.join(self, activity_ps) + + self._setup_links_controller() + + url = self._service.get_published_value('URL') + if url: + self._browser.load_url(url) + + def share(self): + Activity.share(self) + + self._setup_links_controller() + + url = self._browser.get_location() + if url: + self._service.set_published_value('URL', url) + + def _title_changed_cb(self, embed, pspec): + self.set_title(embed.props.title) + +def start(): + gtkmozembed.set_profile_path(env.get_profile_path(), 'gecko') + + gtkmozembed.push_startup() + if not _sugar.startup_browser(): + raise "Error when initializising the web activity." + + style.load_stylesheet(stylesheet) + + download_manager = _sugar.get_download_manager() + download_manager.connect('download-started', download_started_cb) + download_manager.connect('download-completed', download_completed_cb) + download_manager.connect('download-cancelled', download_started_cb) + download_manager.connect('download-progress', download_progress_cb) + +def stop(): + gtkmozembed.pop_startup() + +def download_started_cb(download_manager, download): + name = download.get_url().rsplit('/', 1)[1] + + cbService = ClipboardService.get_instance() + cbService.add_object(name, + download.get_mime_type(), + download.get_file_name()) + +def download_completed_cb(download_manager, download): + cbService = ClipboardService.get_instance() + cbService.set_object_state(download.get_file_name(), 100) + +def download_cancelled_cb(download_manager, download): + #FIXME: Needs to update the state of the object to 'download stopped'. + #FIXME: Will do it when we complete progress on the definition of the + #FIXME: clipboard API. + raise "Cancelling downloads still not implemented." + +def download_progress_cb(download_manager, download): + cbService = ClipboardService.get_instance() + cbService.set_object_state(download.get_file_name(), download.get_percent()) diff --git a/webactivity.pyc b/webactivity.pyc new file mode 100755 index 0000000..60a55ee --- /dev/null +++ b/webactivity.pyc Binary files differ diff --git a/webbrowser.py b/webbrowser.py new file mode 100755 index 0000000..07e807b --- /dev/null +++ b/webbrowser.py @@ -0,0 +1,97 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gobject +import gtk +import logging + +from _sugar import Browser +from _sugar import PushScroller + +class _PopupCreator(gobject.GObject): + __gsignals__ = { + 'popup-created': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + } + + def __init__(self, parent_window): + gobject.GObject.__init__(self) + + logging.debug('Creating the popup widget') + + self._sized_popup = False + self._parent_window = parent_window + + self._dialog = gtk.Window() + self._dialog.set_resizable(True) + + self._dialog.realize() + self._dialog.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + + self._embed = Browser() + self._size_to_sid = self._embed.connect('size_to', self._size_to_cb) + self._vis_sid = self._embed.connect('visibility', self._visibility_cb) + + self._dialog.add(self._embed) + + def _size_to_cb(self, embed, width, height): + logging.debug('Resize the popup to %d %d' % (width, height)) + self._sized_popup = True + self._dialog.resize(width, height) + + def _visibility_cb(self, embed, visible): + if visible: + if self._sized_popup: + logging.debug('Show the popup') + self._embed.show() + self._dialog.set_transient_for(self._parent_window) + self._dialog.show() + else: + logging.debug('Open a new activity for the popup') + self._dialog.remove(self._embed) + + # FIXME We need a better way to handle this. + # It seem like a pretty special case though, I doubt + # other activities will need something similar. + from webactivity import WebActivity + activity = WebActivity(self._embed) + activity.set_type('org.laptop.WebActivity') + + self._embed.disconnect(self._size_to_sid) + self._embed.disconnect(self._vis_sid) + + self.emit('popup-created') + + def get_embed(self): + return self._embed + +class WebBrowser(Browser): + __gtype_name__ = "SugarWebBrowser" + + def __init__(self): + Browser.__init__(self) + self._popup_creators = [] + + def do_create_window(self): + popup_creator = _PopupCreator(self.get_toplevel()) + popup_creator.connect('popup-created', self._popup_created_cb) + + self._popup_creators.append(popup_creator) + + return popup_creator.get_embed() + + def _popup_created_cb(self, creator): + self._popup_creators.remove(creator) -- cgit v0.9.1