diff --git a/browser/README b/browser/README index fce122b2..55aec22f 100644 --- a/browser/README +++ b/browser/README @@ -3,5 +3,5 @@ DEPENDENCIES gecko-embed -http://gnome.org/~marco/gecko-embed-0.1-4.i386.rpm -http://gnome.org/~marco/gecko-embed-0.1-4.src.rpm +http://gnome.org/~marco/gecko-embed-0.1-5.i386.rpm +http://gnome.org/~marco/gecko-embed-0.1-5.src.rpm diff --git a/browser/browser.py b/browser/browser.py index 41a7f800..c2ad8e14 100755 --- a/browser/browser.py +++ b/browser/browser.py @@ -23,8 +23,7 @@ class AddressToolbar(gtk.Toolbar): address_item.show() def __open_address_cb(self, address): - browser = BrowserActivity(address) - browser.activity_connect_to_shell() + browser_shell.open_browser(address) class AddressItem(gtk.ToolItem): def __init__(self, callback): @@ -140,7 +139,7 @@ class NavigationToolbar(gtk.Toolbar): self._update_sensitivity() def __open_address_cb(self, address): - self.embed.load_url(address) + self.embed.load_address(address) class BrowserActivity(activity.Activity): def __init__(self, uri): @@ -159,7 +158,7 @@ class BrowserActivity(activity.Activity): vbox.pack_start(self.embed) self.embed.show() - self.embed.load_url(self.uri) + self.embed.load_address(self.uri) nav_toolbar = NavigationToolbar(self.embed) vbox.pack_start(nav_toolbar, False) @@ -170,7 +169,10 @@ class BrowserActivity(activity.Activity): plug.show() vbox.show() - + + def get_embed(self): + return self.embed + def __title_cb(self, embed): self.activity_set_tab_text(embed.get_title()) @@ -201,18 +203,44 @@ class WebActivity(activity.Activity): vbox.show() - self.embed.load_url("http://www.google.com") + self.embed.load_address("http://www.google.com") def __open_address(self, embed, uri, data=None): if uri.startswith("http://www.google.com"): return False else: - browser = BrowserActivity(uri) - browser.activity_connect_to_shell() + browser_shell.open_browser(uri) return True +class BrowserShell(dbus.service.Object): + def __init__(self, bus_name, object_path='/com/redhat/Sugar/Browser'): + dbus.service.Object.__init__(self, bus_name, object_path) + self.__browsers = [] + + @dbus.service.method('com.redhat.Sugar.BrowserShell') + def get_links(self): + links = [] + for browser in self.__browsers: + embed = browser.get_embed() + link = {} + link['title'] = embed.get_title() + link['address'] = embed.get_address() + links.append(link) + return links + + @dbus.service.method('com.redhat.Sugar.BrowserShell') + def open_browser(self, uri): + browser = BrowserActivity(uri) + self.__browsers.append(browser) + browser.activity_connect_to_shell() + web_activity = WebActivity() web_activity.activity_connect_to_shell() + +session_bus = dbus.SessionBus() +bus_name = dbus.service.BusName('com.redhat.Sugar.Browser', bus=session_bus) +browser_shell = BrowserShell(bus_name) + try: gtk.main() except KeyboardInterrupt: diff --git a/chat/chat.py b/chat/chat.py index 0bacf8fc..f5e7315e 100755 --- a/chat/chat.py +++ b/chat/chat.py @@ -142,6 +142,13 @@ class ChatActivity(activity.Activity): self._buddy_list = BuddyList.BuddyList(self._realname) self._buddy_list.add_buddy_listener(self._on_buddy_presence_event) + bus = dbus.SessionBus() + proxy_obj = bus.get_object('com.redhat.Sugar.Browser', '/com/redhat/Sugar/Browser') + self.browser_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.BrowserShell') + + def __link_clicked_cb(self, view, address): + self.browser_shell.open_browser(address) + def _create_chat(self): chat_vbox = gtk.VBox() chat_vbox.set_spacing(6) @@ -154,7 +161,8 @@ class ChatActivity(activity.Activity): sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_IN) sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) - self._chat_view = gtk.TextView() + self._chat_view = richtext.RichTextView() + self._chat_view.connect("link-clicked", self.__link_clicked_cb) sw.add(self._chat_view) self._chat_view.show() chat_vbox.pack_start(sw) @@ -164,7 +172,7 @@ class ChatActivity(activity.Activity): chat_view_sw = gtk.ScrolledWindow() chat_view_sw.set_shadow_type(gtk.SHADOW_IN) chat_view_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self._editor = gtk.TextView(rich_buf) + self._editor = richtext.RichTextView(rich_buf) self._editor.connect("key-press-event", self.__key_press_event_cb) self._editor.set_size_request(-1, 50) chat_view_sw.add(self._editor) @@ -229,6 +237,34 @@ class ChatActivity(activity.Activity): return vbox + def _create_toolbar(self, rich_buf): + toolbar = richtext.RichTextToolbar(rich_buf) + + item = gtk.MenuToolButton(None, "Links") + item.set_menu(gtk.Menu()) + item.connect("show-menu", self.__show_link_menu_cb) + toolbar.insert(item, -1) + item.show() + + return toolbar + + def __link_activate_cb(self, item, link): + buf = self._editor.get_buffer() + buf.append_link(link['title'], link['address']) + + def __show_link_menu_cb(self, button): + menu = gtk.Menu() + + links = self.browser_shell.get_links() + + for link in links: + item = gtk.MenuItem(link['title'], False) + item.connect("activate", self.__link_activate_cb, link) + menu.append(item) + item.show() + + button.set_menu(menu) + def _ui_setup(self, plug): vbox = gtk.VBox(False, 6) @@ -246,7 +282,7 @@ class ChatActivity(activity.Activity): vbox.pack_start(hbox) hbox.show() - toolbar = richtext.RichTextToolbar(rich_buf) + toolbar = self._create_toolbar(rich_buf) vbox.pack_start(toolbar, False); toolbar.show() diff --git a/chat/richtext.py b/chat/richtext.py index 15e79122..da7293c9 100644 --- a/chat/richtext.py +++ b/chat/richtext.py @@ -1,11 +1,43 @@ #!/usr/bin/env python import pygtk +import gobject pygtk.require('2.0') import gtk import pango import xml.sax +class RichTextView(gtk.TextView): + + __gsignals__ = { + 'link-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_STRING])) + } + + def __init__(self, rich_buf = None): + gtk.TextView.__init__(self, rich_buf) +# self.connect("motion-notify-event", self.__motion_notify_cb) + self.connect("button-press-event", self.__button_press_cb) + +# def __motion_notify_cb(self, widget, event): +# if event.is_hint: +# [x, y, state] = event.window.get_pointer(); + + def __button_press_cb(self, widget, event): + buf = self.get_buffer() + it = self.get_iter_at_location(int(event.x), int(event.y)) + if it.has_tag(buf.get_tag_table().lookup("link")): + address_tag = buf.get_tag_table().lookup("link-address") + + address_end = it.copy() + address_end.backward_to_tag_toggle(address_tag) + + address_start = address_end.copy() + address_start.backward_to_tag_toggle(address_tag) + + address = buf.get_text(address_start, address_end) + self.emit("link-clicked", address) + class RichTextBuffer(gtk.TextBuffer): def __init__(self): gtk.TextBuffer.__init__(self) @@ -14,6 +46,11 @@ class RichTextBuffer(gtk.TextBuffer): self.__create_tags() self.active_tags = [] + + def append_link(self, title, address): + it = self.get_iter_at_mark(self.get_insert()) + self.insert_with_tags_by_name(it, address, "link", "link-address") + self.insert_with_tags_by_name(it, title, "link") def apply_tag(self, tag_name): self.active_tags.append(tag_name) @@ -32,6 +69,13 @@ class RichTextBuffer(gtk.TextBuffer): self.remove_tag_by_name(tag_name, start, end) def __create_tags(self): + tag = self.create_tag("link") + tag.set_property("underline", pango.UNDERLINE_SINGLE) + tag.set_property("foreground", "#0000FF") + + tag = self.create_tag("link-address") + tag.set_property("invisible", True) + tag = self.create_tag("bold") tag.set_property("weight", pango.WEIGHT_BOLD) @@ -137,6 +181,8 @@ class RichTextHandler(xml.sax.handler.ContentHandler): if name != "richtext": tag = self.serializer.deserialize_element(name, attrs) self.tags.append(tag) + if name == "link": + self.href = attrs['href'] def characters(self, data): start_it = it = self.buf.get_end_iter() @@ -146,6 +192,9 @@ class RichTextHandler(xml.sax.handler.ContentHandler): for tag in self.tags: self.buf.apply_tag_by_name(tag, start_it, it) + if tag == "link": + self.buf.insert_with_tags_by_name(start_it, self.href, + "link", "link-address") def endElement(self, name): if name != "richtext": @@ -162,15 +211,25 @@ class RichTextSerializer: return "italic" elif el_name == "font": return "font-size-" + attributes["size"] + elif el_name == "link": + return "link" else: return None - def serialize_tag_start(self, tag): + def serialize_tag_start(self, tag, it): name = tag.get_property("name") if name == "bold": return "" elif name == "italic": return "" + elif name == "link": + address_tag = self.buf.get_tag_table().lookup("link-address") + end = it.copy() + end.forward_to_tag_toggle(address_tag) + address = self.buf.get_text(it, end) + return "" + elif name == "link-address": + return "" elif name.startswith("font-size-"): tag_name = name.replace("font-size-", "", 1) return "" @@ -179,16 +238,22 @@ class RichTextSerializer: def serialize_tag_end(self, tag): name = tag.get_property("name") - if tag.get_property("name") == "bold": + if name == "bold": return "" - elif tag.get_property("name") == "italic": + elif name == "italic": return "" + elif name == "link": + return "" + elif name == "link-address": + return "" elif name.startswith("font-size-"): return "" else: return "" - + def serialize(self, buf): + self.buf = buf + xml = "" next_it = buf.get_start_iter() @@ -207,11 +272,15 @@ class RichTextSerializer: break tags_to_reopen.append(open_tag) - for tag in tags_to_reopen + it.get_toggled_tags(True): + for tag in tags_to_reopen: self._open_tags.append(tag) - xml += self.serialize_tag_start(tag) + xml += self.serialize_tag_start(tag, it) - xml += buf.get_text(it, next_it) + for tag in it.get_toggled_tags(True): + self._open_tags.append(tag) + xml += self.serialize_tag_start(tag, it) + + xml += buf.get_text(it, next_it, False) if next_it.is_end(): self._open_tags.reverse() @@ -232,6 +301,9 @@ class RichTextSerializer: def test_quit(window, rich_buf): print RichTextSerializer().serialize(rich_buf) gtk.main_quit() + +def link_clicked(view, address): + print "Link clicked " + address if __name__ == "__main__": window = gtk.Window() @@ -246,12 +318,13 @@ if __name__ == "__main__": xml_string += "Testone\n" xml_string += "Test two" xml_string += "Test three" - + xml_string += "Test link" xml_string += "" RichTextSerializer().deserialize(xml_string, rich_buf) - view = gtk.TextView(rich_buf) + view = RichTextView(rich_buf) + view.connect("link-clicked", link_clicked) vbox.pack_start(view) view.show()