Kill a lot of old old unused code
This commit is contained in:
parent
56b97575a9
commit
b6a1445573
@ -154,8 +154,6 @@ services/console/interface/logviewer/Makefile
|
|||||||
services/console/interface/terminal/Makefile
|
services/console/interface/terminal/Makefile
|
||||||
sugar/Makefile
|
sugar/Makefile
|
||||||
sugar/activity/Makefile
|
sugar/activity/Makefile
|
||||||
sugar/chat/Makefile
|
|
||||||
sugar/chat/sketchpad/Makefile
|
|
||||||
sugar/clipboard/Makefile
|
sugar/clipboard/Makefile
|
||||||
sugar/graphics/Makefile
|
sugar/graphics/Makefile
|
||||||
sugar/p2p/Makefile
|
sugar/p2p/Makefile
|
||||||
|
109
po/sugar.pot
109
po/sugar.pot
@ -1,109 +0,0 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
|
||||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
|
||||||
#
|
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2007-03-23 14:50+0100\n"
|
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=CHARSET\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:29
|
|
||||||
msgid "Text snippet"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:57
|
|
||||||
msgid "Image"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:77
|
|
||||||
msgid "Web Page"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:103
|
|
||||||
msgid "PDF file"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:123
|
|
||||||
msgid "MS Word file"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:143
|
|
||||||
msgid "RTF file"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:154
|
|
||||||
msgid "Abiword file"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:165
|
|
||||||
msgid "Squeak project"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:185
|
|
||||||
msgid "OpenOffice text file"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../services/clipboard/typeregistry.py:202
|
|
||||||
msgid "Object"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/intro/intro.py:78
|
|
||||||
msgid "Pick a buddy picture"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/intro/intro.py:101
|
|
||||||
msgid "My Picture:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/intro/intro.py:181
|
|
||||||
msgid "My Name:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/intro/intro.py:203
|
|
||||||
msgid "My Color:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/BuddyMenu.py:89
|
|
||||||
msgid "Remove friend"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/BuddyMenu.py:93
|
|
||||||
msgid "Make friend"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/BuddyMenu.py:103
|
|
||||||
msgid "Invite"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/clipboardmenu.py:63 ../shell/view/clipboardmenu.py:79
|
|
||||||
msgid "Remove"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/clipboardmenu.py:69
|
|
||||||
msgid "Open"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/clipboardmenu.py:93
|
|
||||||
msgid "Stop download"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../shell/view/frame/ZoomBox.py:41
|
|
||||||
msgid "Close"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../sugar/graphics/optionmenu.py:72
|
|
||||||
msgid "No options"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: ../sugar/chat/ChatEditor.py:56
|
|
||||||
msgid "Send"
|
|
||||||
msgstr ""
|
|
@ -19,7 +19,6 @@ import dbus
|
|||||||
|
|
||||||
from sugar.p2p import Stream
|
from sugar.p2p import Stream
|
||||||
from sugar.p2p import network
|
from sugar.p2p import network
|
||||||
from sugar.chat import ActivityChat
|
|
||||||
import OverlayWindow
|
import OverlayWindow
|
||||||
|
|
||||||
class ActivityChatWindow(gtk.Window):
|
class ActivityChatWindow(gtk.Window):
|
||||||
@ -51,8 +50,8 @@ class ActivityHost:
|
|||||||
self._overlay_window = None
|
self._overlay_window = None
|
||||||
win = self._gdk_window
|
win = self._gdk_window
|
||||||
|
|
||||||
self._chat_widget = ActivityChat.ActivityChat(self)
|
#self._chat_widget = ActivityChat.ActivityChat(self)
|
||||||
self._chat_window = ActivityChatWindow(win, self._chat_widget)
|
#self._chat_window = ActivityChatWindow(win, self._chat_widget)
|
||||||
|
|
||||||
self._frame_was_visible = False
|
self._frame_was_visible = False
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = activity chat clipboard graphics p2p presence datastore
|
SUBDIRS = activity clipboard graphics p2p presence datastore
|
||||||
|
|
||||||
sugardir = $(pythondir)/sugar
|
sugardir = $(pythondir)/sugar
|
||||||
sugar_PYTHON = \
|
sugar_PYTHON = \
|
||||||
@ -7,6 +7,5 @@ sugar_PYTHON = \
|
|||||||
env.py \
|
env.py \
|
||||||
logger.py \
|
logger.py \
|
||||||
profile.py \
|
profile.py \
|
||||||
simulator.py \
|
|
||||||
TracebackUtils.py \
|
TracebackUtils.py \
|
||||||
util.py
|
util.py
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sugar.chat.GroupChat import GroupChat
|
|
||||||
|
|
||||||
class ActivityChat(GroupChat):
|
|
||||||
SERVICE_TYPE = "_olpc_activity_chat._udp"
|
|
||||||
|
|
||||||
def __init__(self, activity):
|
|
||||||
GroupChat.__init__(self)
|
|
||||||
self._chat_service = None
|
|
||||||
|
|
||||||
self.connect('destroy', self._destroy_cb)
|
|
||||||
|
|
||||||
self._activity = activity
|
|
||||||
self._pservice.register_service_type(ActivityChat.SERVICE_TYPE)
|
|
||||||
self._pservice.connect('service-appeared', self._service_appeared_cb)
|
|
||||||
|
|
||||||
# Find an existing activity chat to latch onto
|
|
||||||
ps_activity = self._pservice.get_activity(activity.get_id())
|
|
||||||
if ps_activity is not None:
|
|
||||||
services = ps_activity.get_services_of_type(ActivityChat.SERVICE_TYPE)
|
|
||||||
if len(services) > 0:
|
|
||||||
self._service_appeared_cb(self._pservice, services[0])
|
|
||||||
|
|
||||||
def _service_appeared_cb(self, pservice, service):
|
|
||||||
if service.get_activity_id() != self._activity.get_id():
|
|
||||||
return
|
|
||||||
if service.get_type() != ActivityChat.SERVICE_TYPE:
|
|
||||||
return
|
|
||||||
if self._chat_service:
|
|
||||||
return
|
|
||||||
|
|
||||||
logging.debug('Activity chat service appeared, setup the stream.')
|
|
||||||
# Ok, there's an existing chat service that we copy
|
|
||||||
# parameters and such from
|
|
||||||
addr = service.get_address()
|
|
||||||
port = service.get_port()
|
|
||||||
self._chat_service = self._pservice.share_activity(self._activity,
|
|
||||||
stype=ActivityChat.SERVICE_TYPE, address=addr, port=port)
|
|
||||||
self._setup_stream(self._chat_service)
|
|
||||||
|
|
||||||
def share(self):
|
|
||||||
"""Only called when we share the activity this chat is tied to."""
|
|
||||||
self._chat_service = self._pservice.share_activity(self._activity,
|
|
||||||
stype=ActivityChat.SERVICE_TYPE)
|
|
||||||
self._setup_stream(self._chat_service)
|
|
||||||
|
|
||||||
def _destroy_cb(self, widget):
|
|
||||||
if self._chat_service:
|
|
||||||
self._pservice.unregister_service(self._chat_service)
|
|
@ -1,280 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import sha
|
|
||||||
|
|
||||||
import dbus
|
|
||||||
import dbus.service
|
|
||||||
import dbus.glib
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
import pango
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sugar.chat.Emoticons import Emoticons
|
|
||||||
from sugar.chat.ChatToolbar import ChatToolbar
|
|
||||||
from sugar.chat.ChatEditor import ChatEditor
|
|
||||||
from sugar.presence import PresenceService
|
|
||||||
import richtext
|
|
||||||
|
|
||||||
PANGO_SCALE = 1024 # Where is this defined?
|
|
||||||
|
|
||||||
class Chat(gtk.VBox):
|
|
||||||
SERVICE_TYPE = "_olpc_chat._tcp"
|
|
||||||
SERVICE_PORT = 6100
|
|
||||||
|
|
||||||
TEXT_MODE = 0
|
|
||||||
SKETCH_MODE = 1
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.VBox.__init__(self, False, 6)
|
|
||||||
|
|
||||||
self._pservice = PresenceService.get_instance()
|
|
||||||
|
|
||||||
self._stream_writer = None
|
|
||||||
self.set_border_width(12)
|
|
||||||
|
|
||||||
chat_vbox = gtk.VBox()
|
|
||||||
chat_vbox.set_spacing(6)
|
|
||||||
|
|
||||||
self._chat_sw = gtk.ScrolledWindow()
|
|
||||||
self._chat_sw.set_shadow_type(gtk.SHADOW_IN)
|
|
||||||
self._chat_sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
||||||
self._chat_view = richtext.RichTextView()
|
|
||||||
self._chat_view.connect("link-clicked", self.__link_clicked_cb)
|
|
||||||
self._chat_view.set_editable(False)
|
|
||||||
self._chat_view.set_cursor_visible(False)
|
|
||||||
self._chat_view.set_pixels_above_lines(7)
|
|
||||||
self._chat_view.set_left_margin(5)
|
|
||||||
self._chat_sw.add(self._chat_view)
|
|
||||||
self._chat_view.show()
|
|
||||||
chat_vbox.pack_start(self._chat_sw)
|
|
||||||
self._chat_sw.show()
|
|
||||||
|
|
||||||
self.pack_start(chat_vbox)
|
|
||||||
chat_vbox.show()
|
|
||||||
|
|
||||||
self._mode = Chat.TEXT_MODE
|
|
||||||
self._editor = ChatEditor(self, ChatEditor.TEXT_MODE)
|
|
||||||
|
|
||||||
toolbar = ChatToolbar(self._editor)
|
|
||||||
self.pack_start(toolbar, False)
|
|
||||||
toolbar.show()
|
|
||||||
|
|
||||||
self.pack_start(self._editor, False)
|
|
||||||
self._editor.show()
|
|
||||||
|
|
||||||
self.connect("key-press-event", self.__key_press_event_cb)
|
|
||||||
|
|
||||||
def __key_press_event_cb(self, window, event):
|
|
||||||
if event.keyval == gtk.keysyms.s and \
|
|
||||||
event.state & gtk.gdk.CONTROL_MASK:
|
|
||||||
if self.get_mode() == Chat.SKETCH_MODE:
|
|
||||||
self.set_mode(Chat.TEXT_MODE)
|
|
||||||
elif self.get_mode() == Chat.TEXT_MODE:
|
|
||||||
self.set_mode(Chat.SKETCH_MODE)
|
|
||||||
|
|
||||||
def get_mode(self):
|
|
||||||
return self._mode
|
|
||||||
|
|
||||||
def set_mode(self, mode):
|
|
||||||
self._mode = mode
|
|
||||||
if self._mode == Chat.TEXT_MODE:
|
|
||||||
self._editor.set_mode(ChatEditor.TEXT_MODE)
|
|
||||||
elif self._mode == Chat.SKETCH_MODE:
|
|
||||||
self._editor.set_mode(ChatEditor.SKETCH_MODE)
|
|
||||||
|
|
||||||
def __get_browser_shell(self):
|
|
||||||
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.__get_browser_shell().open_browser(address)
|
|
||||||
|
|
||||||
def _scroll_chat_view_to_bottom(self):
|
|
||||||
# Only scroll to bottom if the view is already close to the bottom
|
|
||||||
vadj = self._chat_sw.get_vadjustment()
|
|
||||||
if vadj.value + vadj.page_size > vadj.upper * 0.8:
|
|
||||||
vadj.value = vadj.upper - vadj.page_size
|
|
||||||
self._chat_sw.set_vadjustment(vadj)
|
|
||||||
|
|
||||||
def _message_inserted(self):
|
|
||||||
gobject.idle_add(self._scroll_chat_view_to_bottom)
|
|
||||||
|
|
||||||
def _insert_buddy(self, buf, buddy):
|
|
||||||
# Stuff in the buddy icon, if we have one for this buddy
|
|
||||||
icon = buddy.get_icon_pixbuf()
|
|
||||||
if icon:
|
|
||||||
rise = int(icon.get_height() / 4) * -1
|
|
||||||
|
|
||||||
hash_string = "%s-%s" % (buddy.get_name(), buddy.get_ip4_address())
|
|
||||||
sha_hash = sha.new()
|
|
||||||
sha_hash.update(hash_string)
|
|
||||||
tagname = "buddyicon-%s" % sha_hash.hexdigest()
|
|
||||||
|
|
||||||
if not buf.get_tag_table().lookup(tagname):
|
|
||||||
buf.create_tag(tagname, rise=(rise * PANGO_SCALE))
|
|
||||||
|
|
||||||
aniter = buf.get_end_iter()
|
|
||||||
buf.insert_pixbuf(aniter, icon)
|
|
||||||
aniter.backward_char()
|
|
||||||
enditer = buf.get_end_iter()
|
|
||||||
buf.apply_tag_by_name(tagname, aniter, enditer)
|
|
||||||
|
|
||||||
# Stick in the buddy's nickname
|
|
||||||
if not buf.get_tag_table().lookup("nickname"):
|
|
||||||
buf.create_tag("nickname", weight=pango.WEIGHT_BOLD)
|
|
||||||
aniter = buf.get_end_iter()
|
|
||||||
offset = aniter.get_offset()
|
|
||||||
buf.insert(aniter, " " + buddy.get_name() + ": ")
|
|
||||||
enditer = buf.get_iter_at_offset(offset)
|
|
||||||
buf.apply_tag_by_name("nickname", aniter, enditer)
|
|
||||||
|
|
||||||
def _insert_rich_message(self, buddy, msg):
|
|
||||||
msg = Emoticons.get_instance().replace(msg)
|
|
||||||
|
|
||||||
buf = self._chat_view.get_buffer()
|
|
||||||
self._insert_buddy(buf, buddy)
|
|
||||||
|
|
||||||
serializer = richtext.RichTextSerializer()
|
|
||||||
serializer.deserialize(msg, buf)
|
|
||||||
aniter = buf.get_end_iter()
|
|
||||||
buf.insert(aniter, "\n")
|
|
||||||
|
|
||||||
self._message_inserted()
|
|
||||||
|
|
||||||
def _insert_sketch(self, buddy, svgdata):
|
|
||||||
"""Insert a sketch object into the chat buffer."""
|
|
||||||
pbl = gtk.gdk.PixbufLoader("svg")
|
|
||||||
pbl.write(svgdata)
|
|
||||||
pbl.close()
|
|
||||||
pbuf = pbl.get_pixbuf()
|
|
||||||
|
|
||||||
buf = self._chat_view.get_buffer()
|
|
||||||
|
|
||||||
self._insert_buddy(buf, buddy)
|
|
||||||
|
|
||||||
rise = int(pbuf.get_height() / 3) * -1
|
|
||||||
sha_hash = sha.new()
|
|
||||||
sha_hash.update(svgdata)
|
|
||||||
tagname = "sketch-%s" % sha_hash.hexdigest()
|
|
||||||
if not buf.get_tag_table().lookup(tagname):
|
|
||||||
buf.create_tag(tagname, rise=(rise * PANGO_SCALE))
|
|
||||||
|
|
||||||
aniter = buf.get_end_iter()
|
|
||||||
buf.insert_pixbuf(aniter, pbuf)
|
|
||||||
aniter.backward_char()
|
|
||||||
enditer = buf.get_end_iter()
|
|
||||||
buf.apply_tag_by_name(tagname, aniter, enditer)
|
|
||||||
aniter = buf.get_end_iter()
|
|
||||||
buf.insert(aniter, "\n")
|
|
||||||
|
|
||||||
self._message_inserted()
|
|
||||||
|
|
||||||
def _get_first_richtext_chunk(self, msg):
|
|
||||||
"""Scan the message for the first richtext-tagged chunk and return it."""
|
|
||||||
rt_last = -1
|
|
||||||
tag_rt_start = "<richtext>"
|
|
||||||
tag_rt_end = "</richtext>"
|
|
||||||
rt_first = msg.find(tag_rt_start)
|
|
||||||
length = -1
|
|
||||||
if rt_first >= 0:
|
|
||||||
length = len(msg)
|
|
||||||
rt_last = msg.find(tag_rt_end, rt_first)
|
|
||||||
if rt_first >= 0 and rt_last >= (rt_first + len(tag_rt_start)) and length > 0:
|
|
||||||
return msg[rt_first:rt_last + len(tag_rt_end)]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _get_first_sketch_chunk(self, msg):
|
|
||||||
"""Scan the message for the first SVG-tagged chunk and return it."""
|
|
||||||
svg_last = -1
|
|
||||||
tag_svg_start = "<svg"
|
|
||||||
tag_svg_end = "</svg>"
|
|
||||||
desc_start = msg.find("<?xml version='1.0' encoding='UTF-8'?>")
|
|
||||||
if desc_start < 0:
|
|
||||||
return None
|
|
||||||
ignore = msg.find("<!DOCTYPE svg")
|
|
||||||
if ignore < 0:
|
|
||||||
return None
|
|
||||||
svg_first = msg.find(tag_svg_start)
|
|
||||||
length = -1
|
|
||||||
if svg_first >= 0:
|
|
||||||
length = len(msg)
|
|
||||||
svg_last = msg.find(tag_svg_end, svg_first)
|
|
||||||
if svg_first >= 0 and svg_last >= (svg_first + len(tag_svg_start)) and length > 0:
|
|
||||||
return msg[desc_start:svg_last + len(tag_svg_end)]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def recv_message(self, message):
|
|
||||||
"""Insert a remote chat message into the chat buffer."""
|
|
||||||
[nick, msg] = Chat.deserialize_message(message)
|
|
||||||
buddy = self._pservice.get_buddy_by_name(nick)
|
|
||||||
if not buddy:
|
|
||||||
logging.error('The buddy %s is not present.' % (nick))
|
|
||||||
return
|
|
||||||
|
|
||||||
# FIXME a better way to compare buddies?
|
|
||||||
owner = self._pservice.get_owner()
|
|
||||||
if buddy.get_name() == owner.get_name():
|
|
||||||
return
|
|
||||||
|
|
||||||
chunk = self._get_first_richtext_chunk(msg)
|
|
||||||
if chunk:
|
|
||||||
self._insert_rich_message(buddy, chunk)
|
|
||||||
return
|
|
||||||
|
|
||||||
chunk = self._get_first_sketch_chunk(msg)
|
|
||||||
if chunk:
|
|
||||||
self._insert_sketch(buddy, chunk)
|
|
||||||
return
|
|
||||||
|
|
||||||
def set_stream_writer(self, stream_writer):
|
|
||||||
self._stream_writer = stream_writer
|
|
||||||
|
|
||||||
def send_sketch(self, svgdata):
|
|
||||||
if not svgdata or not len(svgdata):
|
|
||||||
return
|
|
||||||
if self._stream_writer:
|
|
||||||
self._stream_writer.write(self.serialize_message(svgdata))
|
|
||||||
owner = self._pservice.get_owner()
|
|
||||||
if owner:
|
|
||||||
self._insert_sketch(owner, svgdata)
|
|
||||||
|
|
||||||
def send_text_message(self, text):
|
|
||||||
"""Send a chat message and insert it into the local buffer."""
|
|
||||||
if len(text) <= 0:
|
|
||||||
return
|
|
||||||
if self._stream_writer:
|
|
||||||
self._stream_writer.write(self.serialize_message(text))
|
|
||||||
else:
|
|
||||||
logging.warning("Cannot send message, there is no stream writer")
|
|
||||||
owner = self._pservice.get_owner()
|
|
||||||
if owner:
|
|
||||||
self._insert_rich_message(owner, text)
|
|
||||||
|
|
||||||
def serialize_message(self, message):
|
|
||||||
owner = self._pservice.get_owner()
|
|
||||||
return owner.get_name() + '||' + message
|
|
||||||
|
|
||||||
def deserialize_message(message):
|
|
||||||
return message.split('||', 1)
|
|
||||||
|
|
||||||
deserialize_message = staticmethod(deserialize_message)
|
|
@ -1,104 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
from gettext import gettext as _
|
|
||||||
|
|
||||||
from sugar.chat.sketchpad.SketchPad import SketchPad
|
|
||||||
import richtext
|
|
||||||
|
|
||||||
class ChatEditor(gtk.HBox):
|
|
||||||
TEXT_MODE = 0
|
|
||||||
SKETCH_MODE = 1
|
|
||||||
|
|
||||||
def __init__(self, chat, mode):
|
|
||||||
gtk.HBox.__init__(self, False, 6)
|
|
||||||
|
|
||||||
self._chat = chat
|
|
||||||
|
|
||||||
self._notebook = gtk.Notebook()
|
|
||||||
self._notebook.set_show_tabs(False)
|
|
||||||
self._notebook.set_show_border(False)
|
|
||||||
self._notebook.set_size_request(-1, 70)
|
|
||||||
|
|
||||||
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._text_view = richtext.RichTextView()
|
|
||||||
self._text_view.connect("key-press-event", self.__key_press_event_cb)
|
|
||||||
chat_view_sw.add(self._text_view)
|
|
||||||
self._text_view.show()
|
|
||||||
|
|
||||||
self._notebook.append_page(chat_view_sw)
|
|
||||||
chat_view_sw.show()
|
|
||||||
|
|
||||||
self._sketchpad = SketchPad()
|
|
||||||
self._notebook.append_page(self._sketchpad)
|
|
||||||
self._sketchpad.show()
|
|
||||||
|
|
||||||
self.pack_start(self._notebook)
|
|
||||||
self._notebook.show()
|
|
||||||
|
|
||||||
send_button = gtk.Button(_("Send"))
|
|
||||||
send_button.set_size_request(60, -1)
|
|
||||||
send_button.connect('clicked', self.__send_button_clicked_cb)
|
|
||||||
self.pack_start(send_button, False, True)
|
|
||||||
send_button.show()
|
|
||||||
|
|
||||||
self.set_mode(mode)
|
|
||||||
|
|
||||||
def set_color(self, color):
|
|
||||||
self._sketchpad.set_color(color)
|
|
||||||
|
|
||||||
def get_buffer(self):
|
|
||||||
return self._text_view.get_buffer()
|
|
||||||
|
|
||||||
def set_mode(self, mode):
|
|
||||||
self._mode = mode
|
|
||||||
if self._mode == ChatEditor.SKETCH_MODE:
|
|
||||||
self._notebook.set_current_page(1)
|
|
||||||
elif self._mode == ChatEditor.TEXT_MODE:
|
|
||||||
self._notebook.set_current_page(0)
|
|
||||||
|
|
||||||
def __send_button_clicked_cb(self, button):
|
|
||||||
self._send()
|
|
||||||
|
|
||||||
def _send(self):
|
|
||||||
if self._mode == ChatEditor.SKETCH_MODE:
|
|
||||||
self._send_sketch()
|
|
||||||
elif self._mode == ChatEditor.TEXT_MODE:
|
|
||||||
self._send_text()
|
|
||||||
|
|
||||||
def _send_sketch(self):
|
|
||||||
self._chat.send_sketch(self._sketchpad.to_svg())
|
|
||||||
self._sketchpad.clear()
|
|
||||||
|
|
||||||
def _send_text(self):
|
|
||||||
buf = self._text_view.get_buffer()
|
|
||||||
text = buf.get_text(buf.get_start_iter(), buf.get_end_iter())
|
|
||||||
if len(text.strip()) > 0:
|
|
||||||
serializer = richtext.RichTextSerializer()
|
|
||||||
text = serializer.serialize(buf)
|
|
||||||
self._chat.send_text_message(text)
|
|
||||||
|
|
||||||
buf.set_text("")
|
|
||||||
buf.place_cursor(buf.get_start_iter())
|
|
||||||
|
|
||||||
def __key_press_event_cb(self, text_view, event):
|
|
||||||
if event.keyval == gtk.keysyms.Return:
|
|
||||||
self._send()
|
|
||||||
return True
|
|
@ -1,150 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import gtk, gobject
|
|
||||||
|
|
||||||
from sugar.chat.Emoticons import Emoticons
|
|
||||||
from sugar.chat.sketchpad.Toolbox import Toolbox
|
|
||||||
import richtext
|
|
||||||
|
|
||||||
class ChatToolbar(gtk.HBox):
|
|
||||||
def __init__(self, editor):
|
|
||||||
gtk.HBox.__init__(self, False, 24)
|
|
||||||
|
|
||||||
self._editor = editor
|
|
||||||
self._emt_popup = None
|
|
||||||
|
|
||||||
spring = gtk.Label('')
|
|
||||||
self.pack_start(spring, True)
|
|
||||||
spring.show()
|
|
||||||
|
|
||||||
toolbox = richtext.RichTextToolbox(editor.get_buffer())
|
|
||||||
self.pack_start(toolbox, False)
|
|
||||||
toolbox.show()
|
|
||||||
|
|
||||||
item = gtk.Button()
|
|
||||||
item.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
|
|
||||||
e_hbox = gtk.HBox(False, 6)
|
|
||||||
|
|
||||||
e_image = gtk.Image()
|
|
||||||
e_image.set_from_icon_name('stock_smiley-1', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
|
||||||
e_hbox.pack_start(e_image)
|
|
||||||
e_image.show()
|
|
||||||
|
|
||||||
arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
|
|
||||||
e_hbox.pack_start(arrow)
|
|
||||||
arrow.show()
|
|
||||||
|
|
||||||
item.set_image(e_hbox)
|
|
||||||
item.connect("clicked", self.__emoticons_button_clicked_cb)
|
|
||||||
toolbox.pack_start(item, False)
|
|
||||||
item.show()
|
|
||||||
|
|
||||||
# separator = gtk.SeparatorToolItem()
|
|
||||||
# toolbar.insert(separator, -1)
|
|
||||||
# separator.show()
|
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
toolbox = Toolbox()
|
|
||||||
toolbox.connect('color-selected', self._color_selected)
|
|
||||||
self.pack_start(toolbox, False)
|
|
||||||
toolbox.show()
|
|
||||||
|
|
||||||
spring = gtk.Label('')
|
|
||||||
self.pack_start(spring, True)
|
|
||||||
spring.show()
|
|
||||||
|
|
||||||
def _color_selected(self, toolbox, color):
|
|
||||||
self._editor.set_color(color)
|
|
||||||
|
|
||||||
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.__get_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 _create_emoticons_popup(self):
|
|
||||||
model = gtk.ListStore(gtk.gdk.Pixbuf, str)
|
|
||||||
|
|
||||||
for name in Emoticons.get_instance().get_all():
|
|
||||||
icon_theme = gtk.icon_theme_get_default()
|
|
||||||
try:
|
|
||||||
pixbuf = icon_theme.load_icon(name, 16, 0)
|
|
||||||
model.append([pixbuf, name])
|
|
||||||
except gobject.GError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
icon_view = gtk.IconView(model)
|
|
||||||
icon_view.connect('selection-changed', self.__emoticon_selection_changed_cb)
|
|
||||||
icon_view.set_pixbuf_column(0)
|
|
||||||
icon_view.set_selection_mode(gtk.SELECTION_SINGLE)
|
|
||||||
|
|
||||||
frame = gtk.Frame()
|
|
||||||
frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
|
|
||||||
frame.add(icon_view)
|
|
||||||
icon_view.show()
|
|
||||||
|
|
||||||
window = gtk.Window(gtk.WINDOW_POPUP)
|
|
||||||
window.add(frame)
|
|
||||||
frame.show()
|
|
||||||
|
|
||||||
return window
|
|
||||||
|
|
||||||
def __emoticon_selection_changed_cb(self, icon_view):
|
|
||||||
items = icon_view.get_selected_items()
|
|
||||||
if items:
|
|
||||||
model = icon_view.get_model()
|
|
||||||
icon_name = model[items[0]][1]
|
|
||||||
self._editor.get_buffer().append_icon(icon_name)
|
|
||||||
self._emt_popup.hide()
|
|
||||||
|
|
||||||
def __emoticons_button_clicked_cb(self, button):
|
|
||||||
# FIXME grabs...
|
|
||||||
if not self._emt_popup:
|
|
||||||
self._emt_popup = self._create_emoticons_popup()
|
|
||||||
|
|
||||||
if self._emt_popup.get_property('visible'):
|
|
||||||
self._emt_popup.hide()
|
|
||||||
else:
|
|
||||||
width = 180
|
|
||||||
height = 130
|
|
||||||
|
|
||||||
self._emt_popup.set_default_size(width, height)
|
|
||||||
|
|
||||||
[x, y] = button.window.get_origin()
|
|
||||||
x += button.allocation.x
|
|
||||||
y += button.allocation.y - height
|
|
||||||
self._emt_popup.move(x, y)
|
|
||||||
|
|
||||||
self._emt_popup.show()
|
|
@ -1,84 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
emoticons_table = [ \
|
|
||||||
[ 'stock_smiley-10', [ ':P', ':p' ] ], \
|
|
||||||
[ 'stock_smiley-19', None ], \
|
|
||||||
[ 'stock_smiley-2', None ], \
|
|
||||||
[ 'stock_smiley-11', None ], \
|
|
||||||
[ 'stock_smiley-1', [ ':)' ] ], \
|
|
||||||
[ 'stock_smiley-3', None ], \
|
|
||||||
[ 'stock_smiley-12', None ], \
|
|
||||||
[ 'stock_smiley-20', None ], \
|
|
||||||
[ 'stock_smiley-4', [ ':(' ] ], \
|
|
||||||
[ 'stock_smiley-13', None ], \
|
|
||||||
[ 'stock_smiley-21', None ], \
|
|
||||||
[ 'stock_smiley-5', None ], \
|
|
||||||
[ 'stock_smiley-14', None ], \
|
|
||||||
[ 'stock_smiley-22', None ], \
|
|
||||||
[ 'stock_smiley-6', None ], \
|
|
||||||
[ 'stock_smiley-15', None ], \
|
|
||||||
[ 'stock_smiley-23', None ], \
|
|
||||||
[ 'stock_smiley-7', None ], \
|
|
||||||
[ 'stock_smiley-16', None ], \
|
|
||||||
[ 'stock_smiley-24', None ], \
|
|
||||||
[ 'stock_smiley-8', None ], \
|
|
||||||
[ 'stock_smiley-17', None ], \
|
|
||||||
[ 'stock_smiley-25', None ], \
|
|
||||||
[ 'stock_smiley-9', None ], \
|
|
||||||
[ 'stock_smiley-18', None ], \
|
|
||||||
[ 'stock_smiley-26', None ], \
|
|
||||||
]
|
|
||||||
|
|
||||||
class Emoticons:
|
|
||||||
instance = None
|
|
||||||
|
|
||||||
def get_instance():
|
|
||||||
if not Emoticons.instance:
|
|
||||||
Emoticons.instance = Emoticons()
|
|
||||||
return Emoticons.instance
|
|
||||||
|
|
||||||
get_instance = staticmethod(get_instance)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._table = {}
|
|
||||||
|
|
||||||
for emoticon in emoticons_table:
|
|
||||||
[ name, text_emts ] = emoticon
|
|
||||||
self.add(name, text_emts)
|
|
||||||
|
|
||||||
def add(self, icon_name, text=None):
|
|
||||||
self._table[icon_name] = text
|
|
||||||
|
|
||||||
def get_all(self):
|
|
||||||
return self._table.keys()
|
|
||||||
|
|
||||||
"""Replace emoticons text with the icon name.
|
|
||||||
|
|
||||||
Parse the provided text to find emoticons (in
|
|
||||||
textual form) and replace them with their xml
|
|
||||||
representation in the form:
|
|
||||||
<icon name="$EMOTICON_ICON_NAME"/>
|
|
||||||
"""
|
|
||||||
def replace(self, text):
|
|
||||||
for icon_name in self._table.keys():
|
|
||||||
text_emts = self._table[icon_name]
|
|
||||||
if text_emts:
|
|
||||||
for emoticon_text in text_emts:
|
|
||||||
xml = '<icon name="' + icon_name + '"/>'
|
|
||||||
text = text.replace(emoticon_text, xml)
|
|
||||||
return text
|
|
@ -1,37 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sugar.chat.Chat import Chat
|
|
||||||
from sugar.p2p.Stream import Stream
|
|
||||||
from sugar.presence.PresenceService import PresenceService
|
|
||||||
import sugar.env
|
|
||||||
|
|
||||||
class GroupChat(Chat):
|
|
||||||
def __init__(self):
|
|
||||||
Chat.__init__(self)
|
|
||||||
self._group_stream = None
|
|
||||||
|
|
||||||
def _setup_stream(self, service):
|
|
||||||
self._group_stream = Stream.new_from_service(service)
|
|
||||||
self._group_stream.set_data_listener(self._group_recv_message)
|
|
||||||
self._stream_writer = self._group_stream.new_writer()
|
|
||||||
|
|
||||||
def _group_recv_message(self, address, msg):
|
|
||||||
logging.debug('Group chat received from %s message %s' % (address, msg))
|
|
||||||
self.recv_message(msg)
|
|
@ -1,15 +0,0 @@
|
|||||||
SUBDIRS = sketchpad
|
|
||||||
|
|
||||||
sugardir = $(pythondir)/sugar/chat
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
ActivityChat.py \
|
|
||||||
Chat.py \
|
|
||||||
ChatEditor.py \
|
|
||||||
ChatToolbar.py \
|
|
||||||
Emoticons.py \
|
|
||||||
GroupChat.py \
|
|
||||||
richtext.py
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
|
||||||
$(icon_DATA)
|
|
@ -1,451 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
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):
|
|
||||||
gtk.TextView.__init__(self, RichTextBuffer())
|
|
||||||
self.connect("motion-notify-event", self.__motion_notify_cb)
|
|
||||||
self.connect("button-press-event", self.__button_press_cb)
|
|
||||||
self.__hover_link = False
|
|
||||||
|
|
||||||
def _set_hover_link(self, hover_link):
|
|
||||||
if hover_link != self.__hover_link:
|
|
||||||
self.__hover_link = hover_link
|
|
||||||
display = self.get_toplevel().get_display()
|
|
||||||
child_window = self.get_window(gtk.TEXT_WINDOW_TEXT)
|
|
||||||
|
|
||||||
if hover_link:
|
|
||||||
cursor = gtk.gdk.Cursor(display, gtk.gdk.HAND2)
|
|
||||||
else:
|
|
||||||
cursor = gtk.gdk.Cursor(display, gtk.gdk.XTERM)
|
|
||||||
|
|
||||||
child_window.set_cursor(cursor)
|
|
||||||
gtk.gdk.flush()
|
|
||||||
|
|
||||||
def __iter_is_link(self, it):
|
|
||||||
item = self.get_buffer().get_tag_table().lookup("link")
|
|
||||||
if item:
|
|
||||||
return it.has_tag(item)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __get_event_iter(self, event):
|
|
||||||
return self.get_iter_at_location(int(event.x), int(event.y))
|
|
||||||
|
|
||||||
def __motion_notify_cb(self, widget, event):
|
|
||||||
if event.is_hint:
|
|
||||||
event.window.get_pointer();
|
|
||||||
|
|
||||||
it = self.__get_event_iter(event)
|
|
||||||
if it:
|
|
||||||
hover_link = self.__iter_is_link(it)
|
|
||||||
else:
|
|
||||||
hover_link = False
|
|
||||||
|
|
||||||
self._set_hover_link(hover_link)
|
|
||||||
|
|
||||||
def __button_press_cb(self, widget, event):
|
|
||||||
it = self.__get_event_iter(event)
|
|
||||||
if it and self.__iter_is_link(it):
|
|
||||||
buf = self.get_buffer()
|
|
||||||
address_tag = buf.get_tag_table().lookup("object-id")
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
self.connect_after("insert-text", self.__insert_text_cb)
|
|
||||||
|
|
||||||
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", "object-id")
|
|
||||||
self.insert_with_tags_by_name(it, title, "link")
|
|
||||||
|
|
||||||
def append_icon(self, name, it = None):
|
|
||||||
if not it:
|
|
||||||
it = self.get_iter_at_mark(self.get_insert())
|
|
||||||
|
|
||||||
self.insert_with_tags_by_name(it, name, "icon", "object-id")
|
|
||||||
icon_theme = gtk.icon_theme_get_default()
|
|
||||||
try:
|
|
||||||
pixbuf = icon_theme.load_icon(name, 16, 0)
|
|
||||||
self.insert_pixbuf(it, pixbuf)
|
|
||||||
except gobject.GError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def apply_tag(self, tag_name):
|
|
||||||
self.active_tags.append(tag_name)
|
|
||||||
|
|
||||||
bounds = self.get_selection_bounds()
|
|
||||||
if bounds:
|
|
||||||
[start, end] = bounds
|
|
||||||
self.apply_tag_by_name(tag_name, start, end)
|
|
||||||
|
|
||||||
def unapply_tag(self, tag_name):
|
|
||||||
self.active_tags.remove(tag_name)
|
|
||||||
|
|
||||||
bounds = self.get_selection_bounds()
|
|
||||||
if bounds:
|
|
||||||
[start, end] = bounds
|
|
||||||
self.remove_tag_by_name(tag_name, start, end)
|
|
||||||
|
|
||||||
def __create_tags(self):
|
|
||||||
tag = self.create_tag("icon")
|
|
||||||
|
|
||||||
tag = self.create_tag("link")
|
|
||||||
tag.set_property("underline", pango.UNDERLINE_SINGLE)
|
|
||||||
tag.set_property("foreground", "#0000FF")
|
|
||||||
|
|
||||||
tag = self.create_tag("object-id")
|
|
||||||
tag.set_property("invisible", True)
|
|
||||||
|
|
||||||
tag = self.create_tag("bold")
|
|
||||||
tag.set_property("weight", pango.WEIGHT_BOLD)
|
|
||||||
|
|
||||||
tag = self.create_tag("italic")
|
|
||||||
tag.set_property("style", pango.STYLE_ITALIC)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-xx-small")
|
|
||||||
tag.set_property("scale", pango.SCALE_XX_SMALL)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-x-small")
|
|
||||||
tag.set_property("scale", pango.SCALE_X_SMALL)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-small")
|
|
||||||
tag.set_property("scale", pango.SCALE_SMALL)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-large")
|
|
||||||
tag.set_property("scale", pango.SCALE_LARGE)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-x-large")
|
|
||||||
tag.set_property("scale", pango.SCALE_X_LARGE)
|
|
||||||
|
|
||||||
tag = self.create_tag("font-size-xx-large")
|
|
||||||
tag.set_property("scale", pango.SCALE_XX_LARGE)
|
|
||||||
|
|
||||||
def __insert_text_cb(self, widget, pos, text, length):
|
|
||||||
for tag in self.active_tags:
|
|
||||||
pos_end = pos.copy()
|
|
||||||
pos_end.backward_chars(length)
|
|
||||||
self.apply_tag_by_name(tag, pos, pos_end)
|
|
||||||
|
|
||||||
class RichTextToolbox(gtk.HBox):
|
|
||||||
def __init__(self, buf):
|
|
||||||
gtk.HBox.__init__(self, False, 6)
|
|
||||||
|
|
||||||
self.buf = buf
|
|
||||||
|
|
||||||
self._font_size = "normal"
|
|
||||||
self._font_scales = [ "xx-small", "x-small", "small", \
|
|
||||||
"normal", \
|
|
||||||
"large", "x-large", "xx-large" ]
|
|
||||||
|
|
||||||
image = gtk.Image()
|
|
||||||
image.set_from_stock(gtk.STOCK_BOLD, gtk.ICON_SIZE_SMALL_TOOLBAR)
|
|
||||||
|
|
||||||
item = gtk.ToggleButton()
|
|
||||||
item.set_image(image)
|
|
||||||
item.connect("toggled", self.__toggle_style_cb, "bold")
|
|
||||||
item.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
self.pack_start(item, False)
|
|
||||||
item.show()
|
|
||||||
|
|
||||||
image.show()
|
|
||||||
|
|
||||||
image = gtk.Image()
|
|
||||||
image.set_from_stock(gtk.STOCK_ITALIC, gtk.ICON_SIZE_SMALL_TOOLBAR)
|
|
||||||
|
|
||||||
item = gtk.ToggleButton()
|
|
||||||
item.set_image(image)
|
|
||||||
item.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
item.connect("toggled", self.__toggle_style_cb, "italic")
|
|
||||||
self.pack_start(item, False)
|
|
||||||
item.show()
|
|
||||||
|
|
||||||
image = gtk.Image()
|
|
||||||
image.set_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_SMALL_TOOLBAR)
|
|
||||||
|
|
||||||
self._font_size_up = gtk.Button()
|
|
||||||
self._font_size_up.set_image(image)
|
|
||||||
self._font_size_up.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
self._font_size_up.connect("clicked", self.__font_size_up_cb)
|
|
||||||
self.pack_start(self._font_size_up, False)
|
|
||||||
self._font_size_up.show()
|
|
||||||
|
|
||||||
image.show()
|
|
||||||
|
|
||||||
image = gtk.Image()
|
|
||||||
image.set_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_SMALL_TOOLBAR)
|
|
||||||
|
|
||||||
self._font_size_down = gtk.Button()
|
|
||||||
self._font_size_down.set_image(image)
|
|
||||||
self._font_size_down.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
self._font_size_down.connect("clicked", self.__font_size_down_cb)
|
|
||||||
self.pack_start(self._font_size_down, False)
|
|
||||||
self._font_size_down.show()
|
|
||||||
|
|
||||||
image.show()
|
|
||||||
|
|
||||||
def _get_font_size_index(self):
|
|
||||||
return self._font_scales.index(self._font_size);
|
|
||||||
|
|
||||||
def __toggle_style_cb(self, toggle, tag_name):
|
|
||||||
if toggle.get_active():
|
|
||||||
self.buf.apply_tag(tag_name)
|
|
||||||
else:
|
|
||||||
self.buf.unapply_tag(tag_name)
|
|
||||||
|
|
||||||
def _set_font_size(self, font_size):
|
|
||||||
if self._font_size != "normal":
|
|
||||||
self.buf.unapply_tag("font-size-" + self._font_size)
|
|
||||||
if font_size != "normal":
|
|
||||||
self.buf.apply_tag("font-size-" + font_size)
|
|
||||||
|
|
||||||
self._font_size = font_size
|
|
||||||
|
|
||||||
can_up = self._get_font_size_index() < len(self._font_scales) - 1
|
|
||||||
can_down = self._get_font_size_index() > 0
|
|
||||||
self._font_size_up.set_sensitive(can_up)
|
|
||||||
self._font_size_down.set_sensitive(can_down)
|
|
||||||
|
|
||||||
def __font_size_up_cb(self, button):
|
|
||||||
index = self._get_font_size_index()
|
|
||||||
if index + 1 < len(self._font_scales):
|
|
||||||
self._set_font_size(self._font_scales[index + 1])
|
|
||||||
|
|
||||||
def __font_size_down_cb(self, button):
|
|
||||||
index = self._get_font_size_index()
|
|
||||||
if index > 0:
|
|
||||||
self._set_font_size(self._font_scales[index - 1])
|
|
||||||
|
|
||||||
class RichTextHandler(xml.sax.handler.ContentHandler):
|
|
||||||
def __init__(self, serializer, buf):
|
|
||||||
xml.sax.handler.ContentHandler.__init__(self)
|
|
||||||
self.buf = buf
|
|
||||||
self.serializer = serializer
|
|
||||||
self.tags = []
|
|
||||||
self._in_richtext = False
|
|
||||||
self._done = False
|
|
||||||
|
|
||||||
def startElement(self, name, attrs):
|
|
||||||
# Look for, and only start parsing after 'richtext'
|
|
||||||
if not self._in_richtext and name == "richtext":
|
|
||||||
self._in_richtext = True
|
|
||||||
if not self._in_richtext:
|
|
||||||
return
|
|
||||||
|
|
||||||
if name != "richtext":
|
|
||||||
tag = self.serializer.deserialize_element(name, attrs)
|
|
||||||
self.tags.append(tag)
|
|
||||||
if name == "link":
|
|
||||||
self.href = attrs['href']
|
|
||||||
elif name == "icon":
|
|
||||||
self.buf.append_icon(attrs['name'], self.buf.get_end_iter())
|
|
||||||
|
|
||||||
def characters(self, data):
|
|
||||||
start_it = it = self.buf.get_end_iter()
|
|
||||||
mark = self.buf.create_mark(None, start_it, True)
|
|
||||||
self.buf.insert(it, data)
|
|
||||||
start_it = self.buf.get_iter_at_mark(mark)
|
|
||||||
|
|
||||||
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", "object-id")
|
|
||||||
|
|
||||||
def endElement(self, name):
|
|
||||||
if not self._done and self._in_richtext:
|
|
||||||
if name != "richtext":
|
|
||||||
self.tags.pop()
|
|
||||||
if name == "richtext":
|
|
||||||
self._done = True
|
|
||||||
self._in_richtext = False
|
|
||||||
|
|
||||||
class RichTextSerializer:
|
|
||||||
def __init__(self):
|
|
||||||
self._open_tags = []
|
|
||||||
|
|
||||||
def deserialize_element(self, el_name, attributes):
|
|
||||||
if el_name == "bold":
|
|
||||||
return "bold"
|
|
||||||
elif el_name == "italic":
|
|
||||||
return "italic"
|
|
||||||
elif el_name == "font":
|
|
||||||
return "font-size-" + attributes["size"]
|
|
||||||
elif el_name == "link":
|
|
||||||
return "link"
|
|
||||||
elif el_name == "icon":
|
|
||||||
return "icon"
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _parse_object_id(self, it):
|
|
||||||
object_id_tag = self.buf.get_tag_table().lookup("object-id")
|
|
||||||
end = it.copy()
|
|
||||||
end.forward_to_tag_toggle(object_id_tag)
|
|
||||||
return self.buf.get_text(it, end)
|
|
||||||
|
|
||||||
def serialize_tag_start(self, tag, it):
|
|
||||||
name = tag.get_property("name")
|
|
||||||
if name == "bold":
|
|
||||||
return "<bold>"
|
|
||||||
elif name == "italic":
|
|
||||||
return "<italic>"
|
|
||||||
elif name == "link":
|
|
||||||
address = self._parse_object_id(it)
|
|
||||||
return "<link " + "href=\"" + address + "\">"
|
|
||||||
elif name == "icon":
|
|
||||||
name = self._parse_object_id(it)
|
|
||||||
return "<icon " + "name=\"" + name + "\"/>"
|
|
||||||
elif name == "object-id":
|
|
||||||
return ""
|
|
||||||
elif name.startswith("font-size-"):
|
|
||||||
tag_name = name.replace("font-size-", "", 1)
|
|
||||||
return "<font size=\"" + tag_name + "\">"
|
|
||||||
else:
|
|
||||||
return "<unknown>"
|
|
||||||
|
|
||||||
def serialize_tag_end(self, tag):
|
|
||||||
name = tag.get_property("name")
|
|
||||||
if name == "bold":
|
|
||||||
return "</bold>"
|
|
||||||
elif name == "italic":
|
|
||||||
return "</italic>"
|
|
||||||
elif name == "link":
|
|
||||||
return "</link>"
|
|
||||||
elif name == "icon":
|
|
||||||
return ""
|
|
||||||
elif name == "object-id":
|
|
||||||
return ""
|
|
||||||
elif name.startswith("font-size-"):
|
|
||||||
return "</font>"
|
|
||||||
else:
|
|
||||||
return "</unknown>"
|
|
||||||
|
|
||||||
def serialize(self, buf):
|
|
||||||
self.buf = buf
|
|
||||||
|
|
||||||
res = "<richtext>"
|
|
||||||
|
|
||||||
next_it = buf.get_start_iter()
|
|
||||||
while not next_it.is_end():
|
|
||||||
it = next_it.copy()
|
|
||||||
if not next_it.forward_to_tag_toggle(None):
|
|
||||||
next_it = buf.get_end_iter()
|
|
||||||
|
|
||||||
tags_to_reopen = []
|
|
||||||
|
|
||||||
for tag in it.get_toggled_tags(False):
|
|
||||||
while 1:
|
|
||||||
open_tag = self._open_tags.pop()
|
|
||||||
res += self.serialize_tag_end(tag)
|
|
||||||
if open_tag == tag:
|
|
||||||
break
|
|
||||||
tags_to_reopen.append(open_tag)
|
|
||||||
|
|
||||||
for tag in tags_to_reopen:
|
|
||||||
self._open_tags.append(tag)
|
|
||||||
res += self.serialize_tag_start(tag, it)
|
|
||||||
|
|
||||||
for tag in it.get_toggled_tags(True):
|
|
||||||
self._open_tags.append(tag)
|
|
||||||
res += self.serialize_tag_start(tag, it)
|
|
||||||
|
|
||||||
res += buf.get_text(it, next_it, False)
|
|
||||||
|
|
||||||
if next_it.is_end():
|
|
||||||
self._open_tags.reverse()
|
|
||||||
for tag in self._open_tags:
|
|
||||||
res += self.serialize_tag_end(tag)
|
|
||||||
|
|
||||||
res += "</richtext>"
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def deserialize(self, xml_string, buf):
|
|
||||||
parser = xml.sax.make_parser()
|
|
||||||
handler = RichTextHandler(self, buf)
|
|
||||||
parser.setContentHandler(handler)
|
|
||||||
parser.feed(xml_string)
|
|
||||||
parser.close()
|
|
||||||
|
|
||||||
def test_quit(w, rb):
|
|
||||||
print RichTextSerializer().serialize(rb)
|
|
||||||
gtk.main_quit()
|
|
||||||
|
|
||||||
def link_clicked(v, address):
|
|
||||||
print "Link clicked " + address
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
window = gtk.Window()
|
|
||||||
window.set_default_size(400, 300)
|
|
||||||
|
|
||||||
vbox = gtk.VBox()
|
|
||||||
|
|
||||||
view = RichTextView()
|
|
||||||
view.connect("link-clicked", link_clicked)
|
|
||||||
vbox.pack_start(view)
|
|
||||||
view.show()
|
|
||||||
|
|
||||||
rich_buf = view.get_buffer()
|
|
||||||
|
|
||||||
test_xml = "<richtext>"
|
|
||||||
|
|
||||||
test_xml += "<bold><italic>Test</italic>one</bold>\n"
|
|
||||||
test_xml += "<bold><italic>Test two</italic></bold>"
|
|
||||||
test_xml += "<font size=\"xx-small\">Test three</font>"
|
|
||||||
test_xml += "<link href=\"http://www.gnome.org\">Test link</link>"
|
|
||||||
test_xml += "<icon name=\"stock_help-chat\"/>"
|
|
||||||
test_xml += "</richtext>"
|
|
||||||
|
|
||||||
RichTextSerializer().deserialize(test_xml, rich_buf)
|
|
||||||
|
|
||||||
toolbar = RichTextToolbar(rich_buf)
|
|
||||||
vbox.pack_start(toolbar, False)
|
|
||||||
toolbar.show()
|
|
||||||
|
|
||||||
window.add(vbox)
|
|
||||||
vbox.show()
|
|
||||||
|
|
||||||
window.show()
|
|
||||||
|
|
||||||
window.connect("destroy", test_quit, rich_buf)
|
|
||||||
|
|
||||||
gtk.main()
|
|
@ -1,7 +0,0 @@
|
|||||||
sugardir = $(pythondir)/sugar/chat/sketchpad
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
Sketch.py \
|
|
||||||
SketchPad.py \
|
|
||||||
SVGdraw.py \
|
|
||||||
Toolbox.py
|
|
File diff suppressed because it is too large
Load Diff
@ -1,54 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
from SVGdraw import path
|
|
||||||
|
|
||||||
class Sketch:
|
|
||||||
def __init__(self, rgb):
|
|
||||||
self._points = []
|
|
||||||
self._rgb = (float(rgb[0]), float(rgb[1]), float(rgb[2]))
|
|
||||||
|
|
||||||
def add_point(self, x, y):
|
|
||||||
self._points.append((x, y))
|
|
||||||
|
|
||||||
def get_points(self):
|
|
||||||
return self._points
|
|
||||||
|
|
||||||
def draw(self, ctx):
|
|
||||||
start = True
|
|
||||||
for (x, y) in self._points:
|
|
||||||
if start:
|
|
||||||
ctx.move_to(x, y)
|
|
||||||
start = False
|
|
||||||
else:
|
|
||||||
ctx.line_to(x, y)
|
|
||||||
ctx.set_source_rgb(self._rgb[0], self._rgb[1], self._rgb[2])
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
def draw_to_svg(self):
|
|
||||||
i = 0
|
|
||||||
for (x, y) in self._points:
|
|
||||||
coords = str(x) + ' ' + str(y) + ' '
|
|
||||||
if i == 0:
|
|
||||||
path_data = 'M ' + coords
|
|
||||||
elif i == 1:
|
|
||||||
path_data += 'L ' + coords
|
|
||||||
else:
|
|
||||||
path_data += coords
|
|
||||||
i += 1
|
|
||||||
color = "#%02X%02X%02X" % (255 * self._rgb[0], 255 * self._rgb[1], 255 * self._rgb[2])
|
|
||||||
return path(path_data, fill = 'none', stroke = color)
|
|
@ -1,123 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import gtk, gobject
|
|
||||||
|
|
||||||
from Sketch import Sketch
|
|
||||||
|
|
||||||
from SVGdraw import drawing
|
|
||||||
from SVGdraw import svg
|
|
||||||
|
|
||||||
class SketchPad(gtk.DrawingArea):
|
|
||||||
__gsignals__ = {
|
|
||||||
'new-user-sketch':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, bgcolor=(0.6, 1, 0.4)):
|
|
||||||
gtk.DrawingArea.__init__(self)
|
|
||||||
|
|
||||||
self._active_sketch = None
|
|
||||||
self._rgb = (0.0, 0.0, 0.0)
|
|
||||||
self._bgcolor = bgcolor
|
|
||||||
self._sketches = []
|
|
||||||
|
|
||||||
self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
|
|
||||||
gtk.gdk.BUTTON_RELEASE_MASK |
|
|
||||||
gtk.gdk.BUTTON1_MOTION_MASK)
|
|
||||||
self.connect("button-press-event", self._button_press_cb)
|
|
||||||
self.connect("button-release-event", self._button_release_cb)
|
|
||||||
self.connect("motion-notify-event", self._motion_notify_cb)
|
|
||||||
self.connect('expose_event', self.expose)
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
self._sketches = []
|
|
||||||
self.window.invalidate_rect(None, False)
|
|
||||||
|
|
||||||
def expose(self, widget, event):
|
|
||||||
"""Draw the background of the sketchpad."""
|
|
||||||
rect = self.get_allocation()
|
|
||||||
ctx = widget.window.cairo_create()
|
|
||||||
|
|
||||||
ctx.set_source_rgb(self._bgcolor[0], self._bgcolor[1], self._bgcolor[2])
|
|
||||||
ctx.rectangle(0, 0, rect.width, rect.height)
|
|
||||||
ctx.fill_preserve()
|
|
||||||
|
|
||||||
ctx.set_source_rgb(0, 0.3, 0.2)
|
|
||||||
ctx.stroke()
|
|
||||||
|
|
||||||
for sketch in self._sketches:
|
|
||||||
sketch.draw(ctx)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def set_color(self, color):
|
|
||||||
"""Sets the current drawing color of the sketchpad.
|
|
||||||
color agument should be 3-item tuple of rgb values between 0 and 1."""
|
|
||||||
self._rgb = color
|
|
||||||
|
|
||||||
def add_sketch(self, sketch):
|
|
||||||
"""Add a sketch to the the pad. Mostly for subclasses and clients."""
|
|
||||||
self._sketches.append(sketch)
|
|
||||||
self.window.invalidate_rect(None, False)
|
|
||||||
|
|
||||||
def add_point(self, event):
|
|
||||||
if not self._active_sketch:
|
|
||||||
return
|
|
||||||
self._active_sketch.add_point(event.x, event.y)
|
|
||||||
self.window.invalidate_rect(None, False)
|
|
||||||
|
|
||||||
def _button_press_cb(self, widget, event):
|
|
||||||
self._active_sketch = Sketch(self._rgb)
|
|
||||||
self._sketches.append(self._active_sketch)
|
|
||||||
self.add_point(event)
|
|
||||||
|
|
||||||
def _button_release_cb(self, widget, event):
|
|
||||||
self.add_point(event)
|
|
||||||
self.emit('new-user-sketch', self._active_sketch)
|
|
||||||
self._active_sketch = None
|
|
||||||
|
|
||||||
def _motion_notify_cb(self, widget, event):
|
|
||||||
self.add_point(event)
|
|
||||||
|
|
||||||
def to_svg(self):
|
|
||||||
"""Return a string containing an SVG representation of this sketch."""
|
|
||||||
d = drawing()
|
|
||||||
s = svg()
|
|
||||||
for sketch in self._sketches:
|
|
||||||
s.addElement(sketch.draw_to_svg())
|
|
||||||
d.setSVG(s)
|
|
||||||
return d.toXml()
|
|
||||||
|
|
||||||
def test_quit(w, skpad):
|
|
||||||
print skpad.to_svg()
|
|
||||||
gtk.main_quit()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
window = gtk.Window()
|
|
||||||
window.set_default_size(400, 300)
|
|
||||||
window.connect("destroy", lambda w: gtk.main_quit())
|
|
||||||
|
|
||||||
sketchpad = SketchPad()
|
|
||||||
window.add(sketchpad)
|
|
||||||
sketchpad.show()
|
|
||||||
|
|
||||||
window.show()
|
|
||||||
|
|
||||||
window.connect("destroy", test_quit, sketchpad)
|
|
||||||
|
|
||||||
gtk.main()
|
|
@ -1,77 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import gtk
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
class ColorButton(gtk.RadioButton):
|
|
||||||
def __init__(self, group, rgb):
|
|
||||||
gtk.RadioButton.__init__(self, group)
|
|
||||||
|
|
||||||
self._rgb = rgb
|
|
||||||
|
|
||||||
self.set_mode(False)
|
|
||||||
self.set_relief(gtk.RELIEF_NONE)
|
|
||||||
|
|
||||||
drawing_area = gtk.DrawingArea()
|
|
||||||
drawing_area.set_size_request(24, 24)
|
|
||||||
drawing_area.connect_after('expose_event', self.expose)
|
|
||||||
self.add(drawing_area)
|
|
||||||
drawing_area.show()
|
|
||||||
|
|
||||||
def color(self):
|
|
||||||
return self._rgb
|
|
||||||
|
|
||||||
def expose(self, widget, event):
|
|
||||||
rect = widget.get_allocation()
|
|
||||||
ctx = widget.window.cairo_create()
|
|
||||||
|
|
||||||
ctx.set_source_rgb(self._rgb[0], self._rgb[1] , self._rgb[2])
|
|
||||||
ctx.rectangle(4, 4, rect.width - 8, rect.height - 8)
|
|
||||||
ctx.fill()
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
class Toolbox(gtk.HBox):
|
|
||||||
__gsignals__ = {
|
|
||||||
'color-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
gtk.HBox.__init__(self, False, 6)
|
|
||||||
|
|
||||||
self._colors_group = None
|
|
||||||
|
|
||||||
self._add_color([0, 0, 0])
|
|
||||||
self._add_color([1, 0, 0])
|
|
||||||
self._add_color([0, 1, 0])
|
|
||||||
self._add_color([0, 0, 1])
|
|
||||||
|
|
||||||
def _add_color(self, rgb):
|
|
||||||
color = ColorButton(self._colors_group, rgb)
|
|
||||||
color.unset_flags(gtk.CAN_FOCUS)
|
|
||||||
color.connect('clicked', self.__color_clicked_cb, rgb)
|
|
||||||
self.pack_start(color, False)
|
|
||||||
|
|
||||||
if self._colors_group == None:
|
|
||||||
self._colors_group = color
|
|
||||||
|
|
||||||
color.show()
|
|
||||||
|
|
||||||
def __color_clicked_cb(self, button, rgb):
|
|
||||||
self.emit("color-selected", button.color())
|
|
@ -1,183 +0,0 @@
|
|||||||
# Copyright (C) 2006, Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This library is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU Lesser General Public
|
|
||||||
# License as published by the Free Software Foundation; either
|
|
||||||
# version 2 of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This library 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with this library; if not, write to the
|
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
# Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
import random
|
|
||||||
import copy
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
import dbus
|
|
||||||
|
|
||||||
from sugar.presence import PresenceService
|
|
||||||
from sugar.graphics.xocolor import XoColor
|
|
||||||
from sugar.p2p import Stream
|
|
||||||
from sugar import util
|
|
||||||
|
|
||||||
_nick_names = ['Aba', 'Abebe', 'Abebi', 'Abena', 'Abeni', 'Abeo', 'Abiba', 'Ada', 'Adah', 'Adana', 'Adanna', 'Adanya', 'Aissa', 'Akili', 'Alika', 'Ama', 'Amadi', 'Ameena', 'Ameenah', 'Ami', 'Amina', 'Aminah', 'Arziki', 'Asha', 'Ashia', 'Aziza', 'Baako', 'Binah', 'Binta', 'Bisa', 'Bolanle', 'Bunme', 'Caimile', 'Cataval', 'Chika', 'Chipo', 'Dalia', 'Dalila', 'Dayo', 'Deka', 'Delu', 'Denisha', 'Dore', 'Ebere', 'Fadhila', 'Faizah', 'Falala', 'Fayola', 'Feechi', 'Femi', 'Fisseha', 'Fola', 'Gamada', 'Ghalyela', 'Habika', 'Hada', 'Hadiya', 'Haiba', 'Halima', 'Hanzila', 'Hasina', 'Hija', 'Ilori', 'Iman', 'Imena', 'Iniko', 'Isabis', 'Isoke', 'Jahia', 'Jamelia', 'Jamila', 'Jamilah', 'Jamilia', 'Jamilla', 'Jamille', 'Jemila', 'Jendayi', 'Jina', 'Kabira', 'Kadija', 'Kafi', 'Kainda', 'Kali', 'Kalifa', 'Kanene', 'Kapera', 'Karimah', 'Kasinda', 'Keisha', 'Kesia', 'Lakeesha', 'Lateefah', 'Latrice', 'Latricia', 'Leal', 'Lehana', 'Limber', 'Lulu', 'Maha', 'Maizah', 'Malika', 'Mandisa', 'Mardea', 'Marjani', 'Marka', 'Nabelung', 'Nailah', 'Naima', 'Naja', 'Nakeisha', 'Narkeasha', 'Neda', 'Neema', 'Nichelle', 'Oba', 'Okoth', 'Ontibile', 'Orma', 'Pemba', 'Rabia', 'Rafiya', 'Ramla', 'Rashida', 'Raziya', 'Reta', 'Ridhaa', 'Saada', 'Sabra', 'Safara', 'Saidah', 'Salihah', 'Shasa', 'Shasmecka', 'Sibongile', 'Sika', 'Simbra', 'Sitembile', 'Siyanda', 'Sukutai', 'Tabita', 'Taifa', 'Taja', 'Takiyah', 'Tamala', 'Tamasha', 'Tanesha', 'Tanginika', 'Tanishia', 'Tapanga', 'Tarisai', 'Tayla', 'Tendai', 'Thandiwe', 'Tiesha', 'TinekaJawana', 'Tiombe', 'Wafa', 'Wangari', 'Waseme', 'Xhosa', 'Zabia', 'Zahara', 'Zahra', 'Zalika', 'Zanta', 'Zarina', 'Zina', 'Aba', 'Abebe', 'Abebi', 'Abena', 'Abeni', 'Abeo', 'Abiba', 'Ada', 'Adah', 'Adana', 'Adanna', 'Adanya', 'Aissa', 'Akili', 'Alika', 'Ama', 'Amadi', 'Ameena', 'Ameenah', 'Ami', 'Amina', 'Aminah', 'Amineh', 'Arziki', 'Asha', 'Ashia', 'Aziza', 'Baako', 'Binah', 'Binta', 'Bisa', 'Bolanle', 'Bunme', 'Caimile', 'Cataval', 'Chika', 'Chipo', 'Dalila', 'Dayo', 'Deka', 'Delu', 'Denisha', 'Dore', 'Ebere', 'Fadhila', 'Faizah', 'Falala', 'Fayola', 'Feechi', 'Femi', 'Fisseha', 'Fola', 'Gamada', 'Ghalyela', 'Habika', 'Hada', 'Hadiya', 'Haiba', 'Halima', 'Hanzila', 'Hasina', 'Hija', 'Ilori', 'Iman', 'Imena', 'Iniko', 'Isabis', 'Isoke', 'Jahia', 'Jamelia', 'Jamila', 'Jamilah', 'Jamilia', 'Jamilla', 'Jamille', 'Jemila', 'Jendayi', 'Jina', 'Kabira', 'Kadija', 'Kafi', 'Kainda', 'Kali', 'Kalifa', 'Kanene', 'Kapera', 'Karimah', 'Kasinda', 'Keisha', 'Kesia', 'Lakeesha', 'Lateefah', 'Latrice', 'Leal', 'Lehana', 'Limber', 'Lulu', 'Maha', 'Maizah', 'Malika', 'Mandisa', 'Mandisa', 'Mardea', 'Marjani', 'Marka', 'Nabelung', 'Nailah', 'Naima', 'Naja', 'Nakeisha', 'Narkeasha', 'Neda', 'Neema', 'Nichelle', 'Oba', 'Okoth', 'Ontibile', 'Orma', 'Pemba', 'Rabia', 'Rafiya', 'Ramla', 'Rashida', 'Raziya', 'Reta', 'Ridhaa', 'Saada', 'Sabra', 'Safara', 'Saidah', 'Salihah', 'Shasa', 'Shasmecka', 'Sibongile', 'Sika', 'Simbra', 'Sitembile', 'Siyanda', 'Sukutai', 'Tabita', 'Taifa', 'Taja', 'Takiyah', 'Tale', 'Tale green', 'Tamala', 'Tamasha', 'Tanesha', 'Tanginika', 'Tanishia', 'Tapanga', 'Tarisai', 'Tayla', 'Tendai', 'Thandiwe', 'Tiesha', 'TinekaJawana', 'Tiombe', 'Wafa', 'Wangari', 'Waseme', 'Xhosa', 'Zabia', 'Zahara', 'Zahra', 'Zalika', 'Zanta']
|
|
||||||
|
|
||||||
_PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp"
|
|
||||||
|
|
||||||
_activity_refs = {}
|
|
||||||
|
|
||||||
class _NameCollection(object):
|
|
||||||
def __init__(self):
|
|
||||||
self._names = copy.copy(_nick_names)
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
i = random.randint(0, len(self._names))
|
|
||||||
return self._names.pop(i)
|
|
||||||
|
|
||||||
class _BotService(object):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self._bot = bot
|
|
||||||
|
|
||||||
def announce(self):
|
|
||||||
props = { 'color': self._bot.color.to_string() }
|
|
||||||
pservice = PresenceService.get_instance()
|
|
||||||
self._service = pservice.register_service(self._bot.name,
|
|
||||||
_PRESENCE_SERVICE_TYPE, properties=props)
|
|
||||||
|
|
||||||
self._stream = Stream.Stream.new_from_service(self._service)
|
|
||||||
self._stream.register_reader_handler(
|
|
||||||
self._handle_buddy_icon_request, "get_buddy_icon")
|
|
||||||
self._stream.register_reader_handler(
|
|
||||||
self._handle_invite, "invite")
|
|
||||||
|
|
||||||
def _handle_buddy_icon_request(self):
|
|
||||||
if self._bot.icon:
|
|
||||||
fd = open(self._bot.icon, "r")
|
|
||||||
icon_data = fd.read()
|
|
||||||
fd.close()
|
|
||||||
if icon_data:
|
|
||||||
return base64.b64encode(self._icon)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def _handle_invite(self, issuer, bundle_id, activity_id):
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def set_current_activity(self, activity_id):
|
|
||||||
self._service.set_published_value('curact', dbus.String(activity_id))
|
|
||||||
|
|
||||||
class _JoinActivityAction(object):
|
|
||||||
def __init__(self, bot, named_ref):
|
|
||||||
self._bot = bot
|
|
||||||
self._named_ref = named_ref
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
activity_id = _activity_refs[self._named_ref]
|
|
||||||
|
|
||||||
pservice = PresenceService.get_instance()
|
|
||||||
activity = pservice.get_activity(activity_id)
|
|
||||||
service = activity.get_services()[0]
|
|
||||||
|
|
||||||
name = "%s [%s]" % (self._bot.name, activity_id)
|
|
||||||
properties = { 'title' : service.get_published_value('title'),
|
|
||||||
'color' : service.get_published_value('color') }
|
|
||||||
|
|
||||||
pservice.register_service(name, service.get_type(),
|
|
||||||
properties, service.get_address(),
|
|
||||||
service.get_port())
|
|
||||||
|
|
||||||
self._bot._service.set_current_activity(activity_id)
|
|
||||||
|
|
||||||
class _ChangeActivityAction(object):
|
|
||||||
def __init__(self, bot, named_ref):
|
|
||||||
self._bot = bot
|
|
||||||
self._named_ref = named_ref
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
activity_id = _activity_refs[self._named_ref]
|
|
||||||
self._bot._service.set_current_activity(activity_id)
|
|
||||||
|
|
||||||
class _ShareChatAction(object):
|
|
||||||
def __init__(self, bot, named_ref, title):
|
|
||||||
self._bot = bot
|
|
||||||
self._title = title
|
|
||||||
self._id = util.unique_id()
|
|
||||||
|
|
||||||
_activity_refs[named_ref] = self._id
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
name = "%s [%s]" % (self._bot.name, self._id)
|
|
||||||
stype = '_GroupChatActivity_Sugar_redhat_com._udp'
|
|
||||||
properties = { 'title' : self._title,
|
|
||||||
'color' : self._bot.color.to_string() }
|
|
||||||
address = u"232.%d.%d.%d" % (random.randint(0, 254),
|
|
||||||
random.randint(1, 254),
|
|
||||||
random.randint(1, 254))
|
|
||||||
|
|
||||||
pservice = PresenceService.get_instance()
|
|
||||||
pservice.register_service(name, stype, properties, address)
|
|
||||||
|
|
||||||
class _WaitAction(object):
|
|
||||||
def __init__(self, bot, seconds):
|
|
||||||
self._bot = bot
|
|
||||||
self._seconds = seconds
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
self._bot._pause_queue(self._seconds)
|
|
||||||
|
|
||||||
class Bot(object):
|
|
||||||
_name_collection = _NameCollection()
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.name = Bot._name_collection.get_name()
|
|
||||||
self.color = XoColor()
|
|
||||||
self.icon = None
|
|
||||||
|
|
||||||
self._queue = []
|
|
||||||
|
|
||||||
def wait(self, seconds):
|
|
||||||
action = _WaitAction(self, seconds)
|
|
||||||
self._queue.append(action)
|
|
||||||
|
|
||||||
def share_chat(self, activity_id, title):
|
|
||||||
action = _ShareChatAction(self, activity_id, title)
|
|
||||||
self._queue.append(action)
|
|
||||||
|
|
||||||
def change_activity(self, activity_id):
|
|
||||||
action = _ChangeActivityAction(self, activity_id)
|
|
||||||
self._queue.append(action)
|
|
||||||
|
|
||||||
def join_activity(self, activity_id):
|
|
||||||
action = _JoinActivityAction(self, activity_id)
|
|
||||||
self._queue.append(action)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._service = _BotService(self)
|
|
||||||
self._service.announce()
|
|
||||||
|
|
||||||
self._start_queue()
|
|
||||||
|
|
||||||
def _idle_cb(self):
|
|
||||||
self._next_action()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _pause_done_cb(self):
|
|
||||||
self._start_queue()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _start_queue(self):
|
|
||||||
self._queue_sid = gobject.idle_add(self._idle_cb)
|
|
||||||
|
|
||||||
def _pause_queue(self, seconds):
|
|
||||||
gobject.source_remove(self._queue_sid)
|
|
||||||
gobject.timeout_add(int(seconds * 1000), self._pause_done_cb)
|
|
||||||
|
|
||||||
def _next_action(self):
|
|
||||||
if len(self._queue) > 0:
|
|
||||||
action = self._queue.pop(0)
|
|
||||||
action.execute()
|
|
@ -1,7 +0,0 @@
|
|||||||
This work is licensed under the Creative Commons Attribution 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
|
|
||||||
|
|
||||||
Attributions:
|
|
||||||
|
|
||||||
chaitanya.jpg http://www.flickr.com/photos/meanestindian/166408558/
|
|
||||||
penelope.jpg http://www.flickr.com/photos/gagah/9257515/
|
|
||||||
kiu.jpg http://flickr.com/photos/31072589@N00/139234295/
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
@ -1,24 +0,0 @@
|
|||||||
# 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.simulator import Bot
|
|
||||||
|
|
||||||
bot = Bot()
|
|
||||||
bot.name = 'chaitanya'
|
|
||||||
|
|
||||||
bot.share_chat('giraffes', 'All About Giraffes')
|
|
||||||
|
|
||||||
bot.start()
|
|
@ -1,37 +0,0 @@
|
|||||||
# 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 random
|
|
||||||
|
|
||||||
from sugar.simulator import Bot
|
|
||||||
|
|
||||||
for i in range(0, 8):
|
|
||||||
bot = Bot()
|
|
||||||
|
|
||||||
bot.wait(random.randint(10, 20))
|
|
||||||
bot.join_activity('giraffes')
|
|
||||||
bot.change_activity('giraffes')
|
|
||||||
|
|
||||||
bot.start()
|
|
||||||
|
|
||||||
for i in range(0, 6):
|
|
||||||
bot = Bot()
|
|
||||||
|
|
||||||
bot.wait(random.randint(10, 20))
|
|
||||||
bot.join_activity('nekkhamma')
|
|
||||||
bot.change_activity('nekkhamma')
|
|
||||||
|
|
||||||
bot.start()
|
|
@ -1,24 +0,0 @@
|
|||||||
# 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.simulator import Bot
|
|
||||||
|
|
||||||
bot = Bot()
|
|
||||||
bot.name = 'penelope'
|
|
||||||
|
|
||||||
bot.share_chat('nekkhamma', 'Nekkhamma')
|
|
||||||
|
|
||||||
bot.start()
|
|
@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/env 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
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import gobject
|
|
||||||
|
|
||||||
from sugar.presence import PresenceService
|
|
||||||
|
|
||||||
PresenceService.start()
|
|
||||||
|
|
||||||
base_path = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
|
|
||||||
stage_path = os.path.join(base_path, 'demo')
|
|
||||||
for bot_file in os.listdir(stage_path):
|
|
||||||
if bot_file.endswith('.py'):
|
|
||||||
execfile(os.path.join(stage_path, bot_file))
|
|
||||||
|
|
||||||
mainloop = gobject.MainLoop()
|
|
||||||
mainloop.run()
|
|
@ -1,6 +0,0 @@
|
|||||||
sugardir = $(pkgdatadir)/activities/sketch
|
|
||||||
sugar_PYTHON = \
|
|
||||||
__init__.py \
|
|
||||||
sketchactivity.py
|
|
||||||
|
|
||||||
EXTRA_DIST = sketch.activity
|
|
@ -1,6 +0,0 @@
|
|||||||
[Activity]
|
|
||||||
name = Sketch
|
|
||||||
id = org.laptop.Sketch
|
|
||||||
icon = activity-sketch
|
|
||||||
python_module = sketch.sketchactivity.SketchActivity
|
|
||||||
show_launcher = yes
|
|
@ -1,234 +0,0 @@
|
|||||||
# 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 gobject
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from sugar.p2p import MostlyReliablePipe
|
|
||||||
from sugar.p2p.Stream import Stream
|
|
||||||
|
|
||||||
from sugar.presence import PresenceService
|
|
||||||
from sugar.activity.Activity import Activity
|
|
||||||
from sugar.chat.sketchpad import SketchPad
|
|
||||||
from sugar.chat.sketchpad import Sketch
|
|
||||||
from sugar.graphics.xocolor import XoColor
|
|
||||||
from sugar import profile
|
|
||||||
|
|
||||||
class NetworkController(gobject.GObject):
|
|
||||||
__gsignals__ = {
|
|
||||||
'new-path':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, parent, ps_owner):
|
|
||||||
gobject.GObject.__init__(self)
|
|
||||||
self._parent = parent
|
|
||||||
self._parent.connect('buddy-joined', self._buddy_joined)
|
|
||||||
self._parent.connect('buddy-left', self._buddy_left)
|
|
||||||
self._stream = None
|
|
||||||
self._stream_writer = None
|
|
||||||
self._joined_buddies = {} # IP address -> buddy
|
|
||||||
self._ps_owner = ps_owner
|
|
||||||
|
|
||||||
def init_stream(self, service):
|
|
||||||
self._stream = Stream.new_from_service(service)
|
|
||||||
self._stream.set_data_listener(self._recv_message)
|
|
||||||
self._stream_writer = self._stream.new_writer()
|
|
||||||
|
|
||||||
def _recv_message(self, address, msg):
|
|
||||||
# Ignore multicast messages from ourself
|
|
||||||
if self._ps_owner and address == self._ps_owner.get_ip4_address():
|
|
||||||
return
|
|
||||||
|
|
||||||
# Ensure the message comes from somebody in this activity
|
|
||||||
if not self._joined_buddies.has_key(address):
|
|
||||||
logging.debug("Message from unjoined buddy.")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Convert the points to an array and send to the sketchpad
|
|
||||||
points = []
|
|
||||||
msg = msg.strip()
|
|
||||||
split_coords = msg.split(" ")
|
|
||||||
for item in split_coords:
|
|
||||||
x = 0
|
|
||||||
y = 0
|
|
||||||
try:
|
|
||||||
(x, y) = item.split(",")
|
|
||||||
x = float(x)
|
|
||||||
y = float(y)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
if x < 0 or y < 0:
|
|
||||||
continue
|
|
||||||
points.append((x, y))
|
|
||||||
|
|
||||||
buddy = self._joined_buddies[address]
|
|
||||||
self.emit("new-path", buddy, points)
|
|
||||||
|
|
||||||
def _buddy_joined(self, widget, activity, buddy, activity_type):
|
|
||||||
activity_service = buddy.get_service_of_type(activity_type, activity)
|
|
||||||
if not activity_service:
|
|
||||||
logging.debug("Buddy Joined, but could not get activity service " \
|
|
||||||
"of %s" % activity_type)
|
|
||||||
return
|
|
||||||
|
|
||||||
address = activity_service.get_source_address()
|
|
||||||
port = activity_service.get_port()
|
|
||||||
if not address or not port:
|
|
||||||
logging.debug("Buddy Joined, but could not get address/port from" \
|
|
||||||
" activity service %s" % activity_type)
|
|
||||||
return
|
|
||||||
if not self._joined_buddies.has_key(address):
|
|
||||||
logging.debug("Buddy joined: %s (%s)" % (address, port))
|
|
||||||
self._joined_buddies[address] = buddy
|
|
||||||
|
|
||||||
def _buddy_left(self, widget, activity, buddy, activity_type):
|
|
||||||
buddy_key = None
|
|
||||||
for (key, value) in self._joined_buddies.items():
|
|
||||||
if value == buddy:
|
|
||||||
buddy_key = key
|
|
||||||
break
|
|
||||||
if buddy_key:
|
|
||||||
del self._joined_buddies[buddy_key]
|
|
||||||
|
|
||||||
def new_local_sketch(self, path):
|
|
||||||
""" Receive an array of point tuples the local user created """
|
|
||||||
cmd = ""
|
|
||||||
# Convert points into the wire format
|
|
||||||
for point in path:
|
|
||||||
cmd = cmd + "%d,%d " % (point[0], point[1])
|
|
||||||
|
|
||||||
# If there were no points, or we aren't in a shared activity yet,
|
|
||||||
# don't send anything
|
|
||||||
if not len(cmd) or not self._stream_writer:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Send the points to other buddies
|
|
||||||
self._stream_writer.write(cmd)
|
|
||||||
|
|
||||||
def _html_to_rgb_color(colorstring):
|
|
||||||
""" converts #RRGGBB to cairo-suitable floats"""
|
|
||||||
colorstring = colorstring.strip()
|
|
||||||
while colorstring[0] == '#':
|
|
||||||
colorstring = colorstring[1:]
|
|
||||||
r = int(colorstring[:2], 16)
|
|
||||||
g = int(colorstring[2:4], 16)
|
|
||||||
b = int(colorstring[4:6], 16)
|
|
||||||
color = ((float(r) / 255.0), (float(g) / 255.0), (float(b) / 255.0))
|
|
||||||
return color
|
|
||||||
|
|
||||||
|
|
||||||
class SharedSketchPad(SketchPad.SketchPad):
|
|
||||||
def __init__(self, net_controller, color):
|
|
||||||
SketchPad.SketchPad.__init__(self, bgcolor=(1.0, 0.984313725, 0.560784314))
|
|
||||||
self._net_controller = net_controller
|
|
||||||
self._user_color = _html_to_rgb_color(color)
|
|
||||||
self.set_color(self._user_color)
|
|
||||||
|
|
||||||
# Receive notifications when our buddies send us new sketches
|
|
||||||
self._net_controller.connect('new-path', self._new_buddy_path)
|
|
||||||
|
|
||||||
self.connect('new-user-sketch', self._new_local_sketch_cb)
|
|
||||||
|
|
||||||
def _new_buddy_path(self, net_controller, buddy, path):
|
|
||||||
""" Called whenever a buddy on the mesh sends us a new sketch path """
|
|
||||||
str_color = buddy.get_color()
|
|
||||||
if not str_color:
|
|
||||||
str_color = "#348798" # FIXME
|
|
||||||
color = XoColor(str_color)
|
|
||||||
stroke_color = _html_to_rgb_color(color.get_stroke_color())
|
|
||||||
sketch = Sketch.Sketch(stroke_color)
|
|
||||||
for item in path:
|
|
||||||
sketch.add_point(item[0], item[1])
|
|
||||||
self.add_sketch(sketch)
|
|
||||||
|
|
||||||
def _new_local_sketch_cb(self, widget, sketch):
|
|
||||||
""" Send the sketch the user just made to the network """
|
|
||||||
self._net_controller.new_local_sketch(sketch.get_points())
|
|
||||||
|
|
||||||
|
|
||||||
class SketchActivity(Activity):
|
|
||||||
__gsignals__ = {
|
|
||||||
'buddy-joined':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
|
|
||||||
'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
|
|
||||||
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
Activity.__init__(self)
|
|
||||||
self.connect('destroy', self._cleanup_cb)
|
|
||||||
|
|
||||||
self.set_title("Sketch")
|
|
||||||
|
|
||||||
self._ps = PresenceService.get_instance()
|
|
||||||
self._ps_activity = None
|
|
||||||
self._owner = self._ps.get_owner()
|
|
||||||
|
|
||||||
self._net_controller = NetworkController(self, self._owner)
|
|
||||||
self._sketchpad = SharedSketchPad(self._net_controller,
|
|
||||||
profile.get_color().get_stroke_color())
|
|
||||||
self.add(self._sketchpad)
|
|
||||||
self.show_all()
|
|
||||||
|
|
||||||
def get_ps(self):
|
|
||||||
return self._ps
|
|
||||||
|
|
||||||
def _cleanup_cb(self):
|
|
||||||
del self._net_controller
|
|
||||||
|
|
||||||
def share(self):
|
|
||||||
Activity.share(self)
|
|
||||||
self._net_controller.init_stream(self._service)
|
|
||||||
self._ps.connect('activity-appeared', self._activity_appeared_cb)
|
|
||||||
|
|
||||||
def join(self, activity_ps):
|
|
||||||
Activity.join(self, activity_ps)
|
|
||||||
self._net_controller.init_stream(self._service)
|
|
||||||
self._ps.connect('activity-appeared', self._activity_appeared_cb)
|
|
||||||
self._activity_appeared_cb(self._ps, activity_ps)
|
|
||||||
|
|
||||||
def _activity_appeared_cb(self, ps, activity):
|
|
||||||
# Only care about our own activity
|
|
||||||
if activity.get_id() != self.get_id():
|
|
||||||
return
|
|
||||||
|
|
||||||
# If we already have found our shared activity, do nothing
|
|
||||||
if self._ps_activity:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._ps_activity = activity
|
|
||||||
|
|
||||||
# Connect signals to the shared activity so we are notified when
|
|
||||||
# buddies join and leave
|
|
||||||
self._ps_activity.connect('buddy-joined', self._add_buddy)
|
|
||||||
self._ps_activity.connect('buddy-left', self._remove_buddy)
|
|
||||||
|
|
||||||
# Get the list of buddies already in this shared activity so we can
|
|
||||||
# connect to them
|
|
||||||
buddies = self._ps_activity.get_joined_buddies()
|
|
||||||
for buddy in buddies:
|
|
||||||
self._add_buddy(self._ps_activity, buddy)
|
|
||||||
|
|
||||||
def _add_buddy(self, ps_activity, buddy):
|
|
||||||
service_type = self._ps_activity
|
|
||||||
self.emit('buddy-joined', ps_activity, buddy, self.get_default_type())
|
|
||||||
|
|
||||||
def _remove_buddy(self, ps_activity, buddy):
|
|
||||||
self.emit('buddy-left', ps_activity, buddy, self.get_default_type())
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
VERSION=0.63
|
VERSION=0.63
|
||||||
DATE=`date +%Y%m%d`
|
DATE=`date +%Y%m%d`
|
||||||
RELEASE=2.61
|
RELEASE=2.62
|
||||||
TARBALL=sugar-$VERSION-$RELEASE.${DATE}git.tar.bz2
|
TARBALL=sugar-$VERSION-$RELEASE.${DATE}git.tar.bz2
|
||||||
|
|
||||||
rm sugar-$VERSION.tar.bz2
|
rm sugar-$VERSION.tar.bz2
|
||||||
|
Loading…
Reference in New Issue
Block a user