From 4ad1d9c535a0183a628c92edc6709150f1879cdc Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Wed, 26 Jan 2011 01:04:50 +0000 Subject: Merge git://git.collabora.co.uk/git/user/rgs/turtleart/ --- diff --git a/TurtleArt/collaboration.py b/TurtleArt/collaboration.py new file mode 100644 index 0000000..21cae17 --- /dev/null +++ b/TurtleArt/collaboration.py @@ -0,0 +1,251 @@ + +from dbus.service import signal +from dbus.gobject_service import ExportedGObject +import logging +import telepathy +from sugar import profile +from sugar.presence import presenceservice +from sugar.presence.tubeconn import TubeConnection + +SERVICE = 'org.laptop.TurtleArtActivity' +IFACE = SERVICE +PATH = '/org/laptop/TurtleArtActivity' +_logger = logging.getLogger('turtleart-activity') + +class Collaboration(): + def __init__(self, tw, activity): + """ A simplistic sharing model: the sharer is the master """ + self._tw = tw + self._activity = activity + + def setup(self): + # TODO: hand off role of master is sharer leaves + self.pservice = presenceservice.get_instance() + self.initiating = None # sharing (True) or joining (False) + + # Add my buddy object to the list + owner = self.pservice.get_owner() + self.owner = owner + self.tw.buddies.append(self.owner) + self._share = "" + + self._activity.connect('shared', self._shared_cb) + self._activity.connect('joined', self._joined_cb) + + def _shared_cb(self, activity): + self._shared_activity = self._activity._shared_activity + if self._shared_activity is None: + _logger.error("Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()") + return + + self.initiating = True + self.waiting_for_turtles = False + self.turtle_dictionary = self._get_dictionary() + + _logger.debug('I am sharing...') + + self.conn = self._shared_activity.telepathy_conn + self.tubes_chan = self._shared_activity.telepathy_tubes_chan + self.text_chan = self._shared_activity.telepathy_text_chan + + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( + 'NewTube', self._new_tube_cb) + + _logger.debug('This is my activity: making a tube...') + + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + + def _joined_cb(self, activity): + self._shared_activity = self._activity._shared_activity + if self._shared_activity is None: + _logger.error("Failed to share or join activity ... \ + _shared_activity is null in _shared_cb()") + return + + self.initiating = False + self.conn = self._shared_activity.telepathy_conn + self.tubes_chan = self._shared_activity.telepathy_tubes_chan + self.text_chan = self._shared_activity.telepathy_text_chan + + # call back for "NewTube" signal + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( + 'NewTube', self._new_tube_cb) + + _logger.debug('I am joining an activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + # Joiner should request current state from sharer. + self.waiting_for_turtles = True + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + _logger.error('ListTubes() failed: %s', e) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + """ Create a new tube. """ + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[ \ + telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + + tube_conn = TubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \ + group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + # We'll use a chat tube to send serialized stacks back and forth. + self.chattube = ChatTube(tube_conn, self.initiating, \ + self.event_received_cb) + + # Now that we have the tube, we can ask for the turtle dictionary. + if self.waiting_for_turtles: + _logger.debug("Sending a request for the turtle dictionary") + # we need to send our own nick and colors + colors = self._get_colors() + event = "t|" + data_to_string([self._get_nick(), colors]) + _logger.debug(event) + self.send_event(event) + + def event_received_cb(self, text): + """ + Events are sent as a tuple, nick|cmd, where nick is a turle name + and cmd is a turtle event. Everyone gets the turtle dictionary from + the sharer and watches for 't' events, which indicate that a new + turtle has joined. + """ + if len(text) == 0: + return + # Save active Turtle + save_active_turtle = self.tw.active_turtle + e = text.split("|", 2) + text = e[1] + if e[0] == 't': # request for turtle dictionary + if text > 0: + [nick, colors] = data_from_string(text) + if nick != self.tw.nick: + # There may not be a turtle dictionary. + if hasattr(self, "turtle_dictionary"): + self.turtle_dictionary[nick] = colors + else: + self.turtle_dictionary = {nick: colors} + # Add new turtle for the joiner. + self.tw.canvas.set_turtle(nick, colors) + # Sharer should send turtle dictionary. + if self.initiating: + text = data_to_string(self.turtle_dictionary) + self.send_event("T|" + text) + elif e[0] == 'T': # Receiving the turtle dictionary. + if self.waiting_for_turtles: + if len(text) > 0: + self.turtle_dictionary = data_from_string(text) + for nick in self.turtle_dictionary: + if nick != self.tw.nick: + colors = self.turtle_dictionary[nick] + # add new turtle for the joiner + self.tw.canvas.set_turtle(nick, colors) + self.waiting_for_turtles = False + elif e[0] == 'f': # move a turtle forward + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.forward(x, False) + elif e[0] == 'a': # move a turtle in an arc + if len(text) > 0: + [nick, [a, r]] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.arc(a, r, False) + elif e[0] == 'r': # rotate turtle + if len(text) > 0: + [nick, h] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.seth(h, False) + elif e[0] == 'x': # set turtle xy position + if len(text) > 0: + [nick, [x, y]] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setxy(x, y, False) + elif e[0] == 'c': # set turtle pen color + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setcolor(x, False) + elif e[0] == 'g': # set turtle pen gray level + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setgray(x, False) + elif e[0] == 's': # set turtle pen shade + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setshade(x, False) + elif e[0] == 'w': # set turtle pen width + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setpensize(x, False) + elif e[0] == 'p': # set turtle pen state + if len(text) > 0: + [nick, x] = data_from_string(text) + if nick != self.tw.nick: + self.tw.canvas.set_turtle(nick) + self.tw.canvas.setpen(x, False) + # Restore active Turtle + self.tw.canvas.set_turtle(self.tw.turtles.get_turtle_key( + save_active_turtle)) + + def send_event(self, entry): + """ Send event through the tube. """ + if hasattr(self, 'chattube') and self.chattube is not None: + self.chattube.SendText(entry) + + def _get_dictionary(self): + d = { self._get_nick(): self._get_colors()} + return d + + def _get_nick(self): + return self.tw.nick + + def _get_colors(self): + return profile.get_color().to_string() + +class ChatTube(ExportedGObject): + + def __init__(self, tube, is_initiator, stack_received_cb): + """Class for setting up tube for sharing.""" + super(ChatTube, self).__init__(tube, PATH) + self.tube = tube + self.is_initiator = is_initiator # Are we sharing or joining activity? + self.stack_received_cb = stack_received_cb + self.stack = '' + + self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \ + path=PATH, sender_keyword='sender') + + def send_stack_cb(self, text, sender=None): + if sender == self.tube.get_unique_name(): + return + self.stack = text + self.stack_received_cb(text) + + @signal(dbus_interface=IFACE, signature='s') + def SendText(self, text): + self.stack = text diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py index ed71c4b..45c389c 100755 --- a/TurtleArt/tacanvas.py +++ b/TurtleArt/tacanvas.py @@ -1,5 +1,6 @@ #Copyright (c) 2007-8, Playful Invention Company. #Copyright (c) 2008-10, Walter Bender +#Copyright (c) 2011 Collabora Ltd. #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -219,9 +220,8 @@ class TurtleGraphics: self.height / 2 - self.ycor) self.tw.svg_string += "\"\n" self.tw.svg_string += self.svg.style() - if self.tw.sharing() and share: - self.tw.activity.send_event("f|%s" % \ - (data_to_string([self.tw.nick, int(n)]))) + event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)])) + self._send_event(event, share) def seth(self, n, share=True): """ Set the turtle heading. """ @@ -232,9 +232,8 @@ class TurtleGraphics: return self.heading %= 360 self.turn_turtle() - if self.tw.sharing() and share: - self.tw.activity.send_event("r|%s" % \ - (data_to_string([self.tw.nick, round_int(self.heading)]))) + event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)])) + self._send_event(event, share) def right(self, n, share=True): """ Rotate turtle clockwise """ @@ -245,9 +244,8 @@ class TurtleGraphics: return self.heading %= 360 self.turn_turtle() - if self.tw.sharing() and share: - self.tw.activity.send_event("r|%s" % \ - (data_to_string([self.tw.nick, round_int(self.heading)]))) + event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)])) + self._send_event(event, share) def arc(self, a, r, share=True): """ Draw an arc """ @@ -262,9 +260,8 @@ class TurtleGraphics: _logger.debug("bad value sent to %s" % (__name__)) return self.move_turtle() - if self.tw.sharing() and share: - self.tw.activity.send_event("a|%s" % \ - (data_to_string([self.tw.nick, [round_int(a), round_int(r)]]))) + event = "a|%s" % (data_to_string([self._get_my_nick(), [round_int(a), round_int(r)]])) + self._send_event(event, share) def rarc(self, a, r): """ draw a clockwise arc """ @@ -350,9 +347,8 @@ class TurtleGraphics: self.draw_line(oldx, oldy, self.xcor, self.ycor) self.move_turtle() - if self.tw.sharing() and share: - self.tw.activity.send_event("x|%s" % \ - (data_to_string([self.tw.nick, [round_int(x), round_int(y)]]))) + event = "x|%s" % (data_to_string([self._get_my_nick(), [round_int(x), round_int(y)]])) + self._send_event(event, share) def setpensize(self, ps, share=True): """ Set the pen size """ @@ -367,9 +363,8 @@ class TurtleGraphics: self.gc.set_line_attributes(int(self.pensize * self.tw.coord_scale), gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER) self.svg.set_stroke_width(self.pensize) - if self.tw.sharing() and share: - self.tw.activity.send_event("w|%s" % \ - (data_to_string([self.tw.nick, round_int(ps)]))) + event = "w|%s" % (data_to_string([self._get_my_nick(), round_int(ps)])) + self._send_event(event, share) def setcolor(self, c, share=True): """ Set the pen color """ @@ -382,9 +377,8 @@ class TurtleGraphics: self.tw.active_turtle.set_color(c) self.set_fgcolor() self.set_textcolor() - if self.tw.sharing() and share: - self.tw.activity.send_event("c|%s" % \ - (data_to_string([self.tw.nick, round_int(c)]))) + event = "c|%s" % (data_to_string([self._get_my_nick(), round_int(c)])) + self._send_event(event, share) def setgray(self, g, share=True): """ Set the gray level """ @@ -400,9 +394,8 @@ class TurtleGraphics: self.set_fgcolor() self.set_textcolor() self.tw.active_turtle.set_gray(self.gray) - if self.tw.sharing() and share: - self.tw.activity.send_event("g|%s" % \ - (data_to_string([self.tw.nick, round_int(self.gray)]))) + event = "g|%s" % (data_to_string([self._get_my_nick(), round_int(self.gray)])) + self._send_event(event, share) def settextcolor(self, c): """ Set the text color """ @@ -430,9 +423,8 @@ class TurtleGraphics: self.tw.active_turtle.set_shade(s) self.set_fgcolor() self.set_textcolor() - if self.tw.sharing() and share: - self.tw.activity.send_event("s|%s" % \ - (data_to_string([self.tw.nick, round_int(s)]))) + event = "s|%s" % (data_to_string([self._get_my_nick(), round_int(s)])) + self._send_event(event, share) def fillscreen(self, c, s): """ Fill screen with color/shade and reset to defaults """ @@ -487,9 +479,8 @@ class TurtleGraphics: """ Lower or raise the pen """ self.pendown = bool self.tw.active_turtle.set_pen_state(bool) - if self.tw.sharing() and share: - self.tw.activity.send_event("p|%s" % \ - (data_to_string([self.tw.nick, bool]))) + event = "p|%s" % (data_to_string([self._get_my_nick(), bool])) + self._send_event(event, share) def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path): """ Draw a pixbuf """ @@ -657,3 +648,18 @@ class TurtleGraphics: self.svg.background("#%02x%02x%02x" % \ (self.bgrgb[0], self.bgrgb[1], self.bgrgb[2])), self.tw.svg_string, self.svg.footer()) + + def _get_my_nick(self): + return self.tw.nick + + def _send_event(self, entry, share): + if not share: + return + + if self.tw.sharing(): + if self.tw.activity: + print "Sending: %s" % entry + self.tw.activity.send_event(entry) + elif self.tw.send_event: + print "Sending: %s" % entry + self.tw.send_event(entry) diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py index ba5b5cf..f9c0d4b 100755 --- a/TurtleArt/tawindow.py +++ b/TurtleArt/tawindow.py @@ -3,6 +3,7 @@ #Copyright (c) 2008-10, Walter Bender #Copyright (c) 2009-10 Raúl Gutiérrez Segalés #Copyright (C) 2010 Emiliano Pastorino +#Copyright (c) 2011 Collabora Ltd. #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -100,6 +101,7 @@ class TurtleArtWindow(): self._loaded_project = '' self.win = None self.parent = parent + self.send_event = None # method to send events over the network if type(win) == gtk.DrawingArea: self.interactive_mode = True self.window = win @@ -400,6 +402,10 @@ class TurtleArtWindow(): if self.running_sugar and hasattr(self.activity, 'chattube') and\ self.activity.chattube is not None: return True + + if self.send_event: + return True + return False def is_project_empty(self): diff --git a/TurtleArtActivity.py b/TurtleArtActivity.py index da471cb..a32e56b 100644 --- a/TurtleArtActivity.py +++ b/TurtleArtActivity.py @@ -38,12 +38,6 @@ except ImportError: from sugar.graphics.toolbutton import ToolButton from sugar.datastore import datastore -import telepathy -from dbus.service import signal -from dbus.gobject_service import ExportedGObject -from sugar.presence import presenceservice -from sugar.presence.tubeconn import TubeConnection - from sugar import profile from gettext import gettext as _ import os.path @@ -55,52 +49,7 @@ from TurtleArt.taexportlogo import save_logo from TurtleArt.tautils import data_to_file, data_to_string, data_from_string, \ get_path, chooser from TurtleArt.tawindow import TurtleArtWindow - -SERVICE = 'org.laptop.TurtleArtActivity' -IFACE = SERVICE -PATH = '/org/laptop/TurtleArtActivity' - - -def _add_label(string, toolbar): - """ add a label to a toolbar """ - label = gtk.Label(string) - label.set_line_wrap(True) - label.show() - toolitem = gtk.ToolItem() - toolitem.add(label) - toolbar.insert(toolitem, -1) - toolitem.show() - return label - - -def _add_separator(toolbar, expand=False): - """ add a separator to a toolbar """ - separator = gtk.SeparatorToolItem() - separator.props.draw = True - separator.set_expand(expand) - toolbar.insert(separator, -1) - separator.show() - - -def _add_button(name, tooltip, callback, toolbar, accelerator=None, arg=None): - """ add a button to a toolbar """ - button = ToolButton(name) - button.set_tooltip(tooltip) - if arg is None: - button.connect('clicked', callback) - else: - button.connect('clicked', callback, arg) - if accelerator is not None: - try: - button.props.accelerator = accelerator - except AttributeError: - pass - button.show() - if hasattr(toolbar, 'insert'): # the main toolbar - toolbar.insert(button, -1) - else: # or a secondary toolbar - toolbar.props.page.insert(button, -1) - return button +from TurtleArt.collaboration import Collaboration class TurtleArtActivity(activity.Activity): @@ -476,196 +425,6 @@ class TurtleArtActivity(activity.Activity): tmpfile = None return tmpfile - # Sharing-related callbacks - - def _shared_cb(self, activity): - """ Either set up initial share... """ - if self._shared_activity is None: - _logger.error("Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()") - return - - self.initiating = True - self.waiting_for_turtles = False - self.turtle_dictionary = \ - {profile.get_nick_name(): profile.get_color().to_string()} - _logger.debug('I am sharing...') - - self.conn = self._shared_activity.telepathy_conn - self.tubes_chan = self._shared_activity.telepathy_tubes_chan - self.text_chan = self._shared_activity.telepathy_text_chan - - # call back for "NewTube" signal - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( - 'NewTube', self._new_tube_cb) - - _logger.debug('This is my activity: making a tube...') - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( - SERVICE, {}) - - def _joined_cb(self, activity): - """ ...or join an exisiting share. """ - if self._shared_activity is None: - _logger.error("Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()") - return - - self.initiating = False - self.conn = self._shared_activity.telepathy_conn - self.tubes_chan = self._shared_activity.telepathy_tubes_chan - self.text_chan = self._shared_activity.telepathy_text_chan - - # call back for "NewTube" signal - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( - 'NewTube', self._new_tube_cb) - - _logger.debug('I am joining an activity: waiting for a tube...') - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( - reply_handler=self._list_tubes_reply_cb, - error_handler=self._list_tubes_error_cb) - - # Joiner should request current state from sharer. - self.waiting_for_turtles = True - - def _list_tubes_reply_cb(self, tubes): - for tube_info in tubes: - self._new_tube_cb(*tube_info) - - def _list_tubes_error_cb(self, e): - _logger.error('ListTubes() failed: %s', e) - - def _new_tube_cb(self, id, initiator, type, service, params, state): - """ Create a new tube. """ - _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' - 'params=%r state=%d', id, initiator, type, service, - params, state) - - if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[ \ - telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) - - tube_conn = TubeConnection(self.conn, - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \ - group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - - # We'll use a chat tube to send serialized stacks back and forth. - self.chattube = ChatTube(tube_conn, self.initiating, \ - self.event_received_cb) - - # Now that we have the tube, we can ask for the turtle dictionary. - if self.waiting_for_turtles: - _logger.debug("Sending a request for the turtle dictionary") - # we need to send our own nick and colors - colors = profile.get_color().to_string() - _logger.debug("t|" + data_to_string([self.tw.nick, colors])) - self.send_event("t|%s" % \ - (data_to_string([self.tw.nick, colors]))) - - def event_received_cb(self, text): - """ Handle the receiving of events in share """ - # _logger.debug(text) - - """ - Events are sent as a tuple, nick|cmd, where nick is a turle name - and cmd is a turtle event. Everyone gets the turtle dictionary from - the sharer and watches for 't' events, which indicate that a new - turtle has joined. - """ - if len(text) == 0: - return - # Save active Turtle - save_active_turtle = self.tw.active_turtle - e = text.split("|", 2) - text = e[1] - if e[0] == 't': # request for turtle dictionary - if text > 0: - [nick, colors] = data_from_string(text) - if nick != self.tw.nick: - # There may not be a turtle dictionary. - if hasattr(self, "turtle_dictionary"): - self.turtle_dictionary[nick] = colors - else: - self.turtle_dictionary = {nick: colors} - # Add new turtle for the joiner. - self.tw.canvas.set_turtle(nick, colors) - # Sharer should send turtle dictionary. - if self.initiating: - text = data_to_string(self.turtle_dictionary) - self.send_event("T|" + text) - elif e[0] == 'T': # Receiving the turtle dictionary. - if self.waiting_for_turtles: - if len(text) > 0: - self.turtle_dictionary = data_from_string(text) - for nick in self.turtle_dictionary: - if nick != self.tw.nick: - colors = self.turtle_dictionary[nick] - # add new turtle for the joiner - self.tw.canvas.set_turtle(nick, colors) - self.waiting_for_turtles = False - elif e[0] == 'f': # move a turtle forward - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.forward(x, False) - elif e[0] == 'a': # move a turtle in an arc - if len(text) > 0: - [nick, [a, r]] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.arc(a, r, False) - elif e[0] == 'r': # rotate turtle - if len(text) > 0: - [nick, h] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.seth(h, False) - elif e[0] == 'x': # set turtle xy position - if len(text) > 0: - [nick, [x, y]] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setxy(x, y, False) - elif e[0] == 'c': # set turtle pen color - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setcolor(x, False) - elif e[0] == 'g': # set turtle pen gray level - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setgray(x, False) - elif e[0] == 's': # set turtle pen shade - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setshade(x, False) - elif e[0] == 'w': # set turtle pen width - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setpensize(x, False) - elif e[0] == 'p': # set turtle pen state - if len(text) > 0: - [nick, x] = data_from_string(text) - if nick != self.tw.nick: - self.tw.canvas.set_turtle(nick) - self.tw.canvas.setpen(x, False) - # Restore active Turtle - self.tw.canvas.set_turtle(self.tw.turtles.get_turtle_key( - save_active_turtle)) - - def send_event(self, entry): - """ Send event through the tube. """ - if hasattr(self, 'chattube') and self.chattube is not None: - self.chattube.SendText(entry) - def __visibility_notify_cb(self, window, event): """ Callback method for when the activity's visibility changes. """ if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED: @@ -725,11 +484,11 @@ class TurtleArtActivity(activity.Activity): help_toolbar_button.show() toolbox.toolbar.insert(help_toolbar_button, -1) - _add_separator(toolbox.toolbar) + self._add_separator(toolbox.toolbar) self._make_project_buttons(toolbox.toolbar) - _add_separator(toolbox.toolbar, True) + self._add_separator(toolbox.toolbar, True) stop_button = StopButton(self) stop_button.props.accelerator = 'Q' @@ -759,57 +518,57 @@ class TurtleArtActivity(activity.Activity): self._make_palette_buttons(project_toolbar, palette_button=True) - _add_separator(project_toolbar) + self._add_separator(project_toolbar) self._make_project_buttons(project_toolbar) - self.keep_button = _add_button('filesaveoff', _("Save snapshot"), + self.keep_button = self._add_button('filesaveoff', _("Save snapshot"), self.do_keep_cb, journal_toolbar_button) - self.save_as_html = _add_button('htmloff', _("Save as HTML"), + self.save_as_html = self._add_button('htmloff', _("Save as HTML"), self.do_save_as_html_cb, journal_toolbar_button) - self.save_as_logo = _add_button('logo-saveoff', _("Save as Logo"), + self.save_as_logo = self._add_button('logo-saveoff', _("Save as Logo"), self.do_save_as_logo_cb, journal_toolbar_button) - self.save_as_image = _add_button('image-saveoff', _("Save as image"), + self.save_as_image = self._add_button('image-saveoff', _("Save as image"), self.do_save_as_image_cb, journal_toolbar_button) - self.load_ta_project = _add_button('load-from-journal', + self.load_ta_project = self._add_button('load-from-journal', _("Import project from the Journal"), self.do_load_ta_project_cb, journal_toolbar_button) - _add_separator(journal_toolbar) - self.load_python = _add_button('pippy-openoff', _("Load Python block"), + self._add_separator(journal_toolbar) + self.load_python = self._add_button('pippy-openoff', _("Load Python block"), self.do_load_python_cb, journal_toolbar_button) - self.samples_button = _add_button("ta-open", _('Load example'), + self.samples_button = self._add_button("ta-open", _('Load example'), self.do_samples_cb, journal_toolbar_button) - copy = _add_button('edit-copy', _('Copy'), self._copy_cb, + copy = self._add_button('edit-copy', _('Copy'), self._copy_cb, edit_toolbar_button, 'c') - paste = _add_button('edit-paste', _('Paste'), self._paste_cb, + paste = self._add_button('edit-paste', _('Paste'), self._paste_cb, edit_toolbar_button, 'v') - fullscreen_button = _add_button('view-fullscreen', _("Fullscreen"), + fullscreen_button = self._add_button('view-fullscreen', _("Fullscreen"), self.do_fullscreen_cb, view_toolbar_button, 'Return') - cartesian_button = _add_button('view-Cartesian', + cartesian_button = self._add_button('view-Cartesian', _("Cartesian coordinates"), self.do_cartesian_cb, view_toolbar_button) - polar_button = _add_button('view-polar', _("Polar coordinates"), + polar_button = self._add_button('view-polar', _("Polar coordinates"), self.do_polar_cb, view_toolbar_button) - _add_separator(view_toolbar) - self.coordinates_label = _add_label( + self._add_separator(view_toolbar) + self.coordinates_label = self._add_label( _("xcor") + " = 0 " + _("ycor") + " = 0 " + _("heading") + " = 0", view_toolbar) - _add_separator(view_toolbar, True) - self.rescale_button = _add_button('expand-coordinates', + self._add_separator(view_toolbar, True) + self.rescale_button = self._add_button('expand-coordinates', _("Rescale coordinates up"), self.do_rescale_cb, view_toolbar_button) - self.resize_up_button = _add_button('resize+', _("Grow blocks"), + self.resize_up_button = self._add_button('resize+', _("Grow blocks"), self.do_grow_blocks_cb, view_toolbar_button) - self.resize_down_button = _add_button('resize-', _("Shrink blocks"), + self.resize_down_button = self._add_button('resize-', _("Shrink blocks"), self.do_shrink_blocks_cb, view_toolbar_button) - self.hover_help_label = _add_label( + self.hover_help_label = self._add_label( _("Move the cursor over the orange palette for help."), help_toolbar) @@ -821,10 +580,10 @@ class TurtleArtActivity(activity.Activity): suffix = 'off' else: suffix = 'on' - self.palette_buttons.append(_add_button(name + suffix, + self.palette_buttons.append(self._add_button(name + suffix, HELP_STRINGS[name], self.do_palette_buttons_cb, palette_toolbar_button, None, i)) - _add_separator(palette_toolbar, True) + self._add_separator(palette_toolbar, True) self._make_palette_buttons(palette_toolbar_button) @@ -848,22 +607,22 @@ class TurtleArtActivity(activity.Activity): def _make_palette_buttons(self, toolbar, palette_button=False): """ Creates the palette and block buttons for both toolbar types""" if palette_button: # old-style toolbars need this button - self.palette_button = _add_button("paletteoff", _('Hide palette'), + self.palette_button = self._add_button("paletteoff", _('Hide palette'), self.do_palette_cb, toolbar, _('p')) - self.blocks_button = _add_button("hideshowoff", _('Hide blocks'), + self.blocks_button = self._add_button("hideshowoff", _('Hide blocks'), self.do_hideshow_cb, toolbar, _('b')) def _make_project_buttons(self, toolbar): """ Creates the turtle buttons for both toolbar types""" - self.eraser_button = _add_button("eraseron", _('Clean'), + self.eraser_button = self._add_button("eraseron", _('Clean'), self.do_eraser_cb, toolbar, _('e')) - self.run_button = _add_button("run-fastoff", _('Run'), self.do_run_cb, + self.run_button = self._add_button("run-fastoff", _('Run'), self.do_run_cb, toolbar, _('r')) - self.step_button = _add_button("run-slowoff", _('Step'), + self.step_button = self._add_button("run-slowoff", _('Step'), self.do_step_cb, toolbar, _('w')) - self.debug_button = _add_button("debugoff", _('Debug'), + self.debug_button = self._add_button("debugoff", _('Debug'), self.do_debug_cb, toolbar, _('d')) - self.stop_turtle_button = _add_button("stopitoff", _('Stop turtle'), + self.stop_turtle_button = self._add_button("stopitoff", _('Stop turtle'), self.do_stop_cb, toolbar, _('s')) def _setup_scrolled_window(self): @@ -926,20 +685,8 @@ class TurtleArtActivity(activity.Activity): self.tw.load_start() def _setup_sharing(self): - """ A simplistic sharing model: the sharer is the master """ - # TODO: hand off role of master is sharer leaves - # Get the Presence Service - self.pservice = presenceservice.get_instance() - self.initiating = None # sharing (True) or joining (False) - - # Add my buddy object to the list - owner = self.pservice.get_owner() - self.owner = owner - self.tw.buddies.append(self.owner) - self._share = "" - - self.connect('shared', self._shared_cb) - self.connect('joined', self._joined_cb) + self._collaboration = Collaboration(self.tw) + self._collaboration.setup() def _setup_visibility_handler(self): """ Notify when the visibility state changes """ @@ -1030,26 +777,41 @@ class TurtleArtActivity(activity.Activity): self.tw.paste_offset) self.tw.paste_offset += 20 - -class ChatTube(ExportedGObject): - - def __init__(self, tube, is_initiator, stack_received_cb): - """Class for setting up tube for sharing.""" - super(ChatTube, self).__init__(tube, PATH) - self.tube = tube - self.is_initiator = is_initiator # Are we sharing or joining activity? - self.stack_received_cb = stack_received_cb - self.stack = '' - - self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \ - path=PATH, sender_keyword='sender') - - def send_stack_cb(self, text, sender=None): - if sender == self.tube.get_unique_name(): - return - self.stack = text - self.stack_received_cb(text) - - @signal(dbus_interface=IFACE, signature='s') - def SendText(self, text): - self.stack = text + def _add_label(self, string, toolbar): + """ add a label to a toolbar """ + label = gtk.Label(string) + label.set_line_wrap(True) + label.show() + toolitem = gtk.ToolItem() + toolitem.add(label) + toolbar.insert(toolitem, -1) + toolitem.show() + return label + + def _add_separator(toolbar, expand=False): + """ add a separator to a toolbar """ + separator = gtk.SeparatorToolItem() + separator.props.draw = True + separator.set_expand(expand) + toolbar.insert(separator, -1) + separator.show() + + def _add_button(name, tooltip, callback, toolbar, accelerator=None, arg=None): + """ add a button to a toolbar """ + button = ToolButton(name) + button.set_tooltip(tooltip) + if arg is None: + button.connect('clicked', callback) + else: + button.connect('clicked', callback, arg) + if accelerator is not None: + try: + button.props.accelerator = accelerator + except AttributeError: + pass + button.show() + if hasattr(toolbar, 'insert'): # the main toolbar + toolbar.insert(button, -1) + else: # or a secondary toolbar + toolbar.props.page.insert(button, -1) + return button diff --git a/extra/__init__.py b/extra/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/extra/__init__.py diff --git a/extra/upload.py b/extra/upload.py new file mode 100644 index 0000000..e39d099 --- /dev/null +++ b/extra/upload.py @@ -0,0 +1,170 @@ +#!/usr/bin/python + +try: + import pycurl + import xmlrpclib + _UPLOAD_AVAILABLE = True +except ImportError, e: + print "Import Error: %s. Project upload is disabled." % (e) + _UPLOAD_AVAILABLE = False + +import gtk +from gettext import gettext as _ + +class Uploader(): + MAX_FILE_SIZE = 950000 + UPLOAD_SERVER = 'http://turtleartsite.appspot.com' + + def __init__(self, upload_server = None, max_file_size = 0): + self.uploading = False + + if upload_server is None: + self._upload_server = self.UPLOAD_SERVER + + if max_file_size is None: + self._max_file_size = self.MAX_FILE_SIZE + + def set_tw(self, turtleart_window): + self.tw = turtleart_window + + def enabled(self): + return _UPLOAD_AVAILABLE + + def do_upload_to_web(self, widget = None): + if self.uploading: + return + + self.uploading = False + self.pop_up = gtk.Window() + self.pop_up.set_default_size(600, 400) + self.pop_up.connect('delete_event', self._stop_uploading) + table = gtk.Table(8, 1, False) + self.pop_up.add(table) + + login_label = gtk.Label(_('You must have an account at \ +http://turtleartsite.sugarlabs.org to upload your project.')) + table.attach(login_label, 0, 1, 0, 1) + self.login_message = gtk.Label('') + table.attach(self.login_message, 0, 1, 1, 2) + + self.Hbox1 = gtk.HBox() + table.attach(self.Hbox1, 0, 1, 2, 3, xpadding=5, ypadding=3) + self.username_entry = gtk.Entry() + username_label = gtk.Label(_('Username:') + ' ') + username_label.set_size_request(150, 25) + username_label.set_alignment(1.0, 0.5) + self.username_entry.set_size_request(450, 25) + self.Hbox1.add(username_label) + self.Hbox1.add(self.username_entry) + + self.Hbox2 = gtk.HBox() + table.attach(self.Hbox2, 0, 1, 3, 4, xpadding=5, ypadding=3) + self.password_entry = gtk.Entry() + password_label = gtk.Label(_('Password:') + ' ') + self.password_entry.set_visibility(False) + password_label.set_size_request(150, 25) + password_label.set_alignment(1.0, 0.5) + self.password_entry.set_size_request(450, 25) + self.Hbox2.add(password_label) + self.Hbox2.add(self.password_entry) + + self.Hbox3 = gtk.HBox() + table.attach(self.Hbox3, 0, 1, 4, 5, xpadding=5, ypadding=3) + self.title_entry = gtk.Entry() + title_label = gtk.Label(_('Title:') + ' ') + title_label.set_size_request(150, 25) + title_label.set_alignment(1.0, 0.5) + self.title_entry.set_size_request(450, 25) + self.Hbox3.add(title_label) + self.Hbox3.add(self.title_entry) + + self.Hbox4 = gtk.HBox() + table.attach(self.Hbox4, 0, 1, 5, 6, xpadding=5, ypadding=3) + self.description_entry = gtk.TextView() + description_label = gtk.Label(_('Description:') + ' ') + description_label.set_size_request(150, 25) + description_label.set_alignment(1.0, 0.5) + self.description_entry.set_wrap_mode(gtk.WRAP_WORD) + self.description_entry.set_size_request(450, 50) + self.Hbox4.add(description_label) + self.Hbox4.add(self.description_entry) + + self.Hbox5 = gtk.HBox() + table.attach(self.Hbox5, 0, 1, 6, 7, xpadding=5, ypadding=3) + self.submit_button = gtk.Button(_('Submit to Web')) + self.submit_button.set_size_request(300, 25) + self.submit_button.connect('pressed', self._do_remote_logon) + self.Hbox5.add(self.submit_button) + self.cancel_button = gtk.Button(_('Cancel')) + self.cancel_button.set_size_request(300, 25) + self.cancel_button.connect('pressed', self._stop_uploading) + self.Hbox5.add(self.cancel_button) + + self.pop_up.show_all() + + def _stop_uploading(self, widget, event=None): + """ Hide the popup when the upload is complte """ + self.uploading = False + self.pop_up.hide() + + def _do_remote_logon(self, widget): + """ Log into the upload server """ + username = self.username_entry.get_text() + password = self.password_entry.get_text() + server = xmlrpclib.ServerProxy(self._upload_server + '/call/xmlrpc') + logged_in = server.login_remote(username, password) + if logged_in: + upload_key = logged_in + self._do_submit_to_web(upload_key) + else: + self.login_message.set_text(_('Login failed')) + + def _do_submit_to_web(self, key): + """ Submit project to the server """ + title = self.title_entry.get_text() + description = self.description_entry.get_buffer().get_text( + *self.description_entry.get_buffer().get_bounds()) + tafile, imagefile = self.tw.save_for_upload(title) + + # Set a maximum file size for image to be uploaded. + if int(os.path.getsize(imagefile)) > self.max_file_size: + import Image + while int(os.path.getsize(imagefile)) > self._max_file_size: + big_file = Image.open(imagefile) + smaller_file = big_file.resize(int(0.9 * big_file.size[0]), + int(0.9 * big_file.size[1]), + Image.ANTIALIAS) + smaller_file.save(imagefile, quality = 100) + + c = pycurl.Curl() + c.setopt(c.POST, 1) + c.setopt(c.FOLLOWLOCATION, 1) + c.setopt(c.URL, self._upload_server + '/upload') + c.setopt(c.HTTPHEADER, ["Expect:"]) + c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, tafile)), + ('newimage', (c.FORM_FILE, imagefile)), + ('small_image', (c.FORM_FILE, imagefile)), + ('title', title), + ('description', description), + ('upload_key', key), ('_formname', + 'image_create')]) + c.perform() + error_code = c.getinfo(c.HTTP_CODE) + c.close + os.remove(imagefile) + os.remove(tafile) + if error_code == 400: + self.login_message.set_text(_('Failed to upload!')) + else: + self.pop_up.hide() + self.uploading = False + +if __name__ == "__main__": + # TODO: create test data... + u = Uploader(None) + if u.enabled(): + print "Uploader is enabled... trying to upload" + u.do_upload_to_web() + gtk.main() + else: + print "Uploader is not enabled... exiting" diff --git a/turtleart.py b/turtleart.py index d3a0f70..996e94a 100755 --- a/turtleart.py +++ b/turtleart.py @@ -32,14 +32,6 @@ import cStringIO import errno try: - import pycurl - import xmlrpclib - _UPLOAD_AVAILABLE = True -except ImportError, e: - print "Import Error: %s. Project upload is disabled." % (e) - _UPLOAD_AVAILABLE = False - -try: # Try to use XDG Base Directory standard for config files import xdg.BaseDirectory CONFIG_HOME = os.path.join(xdg.BaseDirectory.xdg_config_home, 'turtleart') @@ -57,86 +49,128 @@ from TurtleArt.tautils import data_to_string, data_from_string, get_save_name from TurtleArt.tawindow import TurtleArtWindow from TurtleArt.taexporthtml import save_html from TurtleArt.taexportlogo import save_logo +from extra.upload import Uploader + -_HELP_MSG = 'turtleart.py: ' + _('usage is') + """ +class TurtleMain(): + """ Launch Turtle Art from outside of Sugar """ + + _HELP_MSG = 'turtleart.py: ' + _('usage is') + """ \tturtleart.py \tturtleart.py project.ta \tturtleart.py --output_png project.ta \tturtleart.py -o project""" + _INSTALL_PATH = '/usr/share/turtleart' + _ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart' + _ICON_SUBPATH = 'images/turtle.png' -_MAX_FILE_SIZE = 950000 - -_INSTALL_PATH = '/usr/share/turtleart' -_ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart' -_ICON_SUBPATH = 'images/turtle.png' -_UPLOAD_SERVER = 'http://turtleartsite.appspot.com' - + def __init__(self): + self._init_vars() + self._parse_command_line() + self._ensure_sugar_paths() -def mkdir_p(path): - '''Create a directory in a fashion similar to `mkdir -p`''' - try: - os.makedirs(path) - except OSError as exc: - if exc.errno == errno.EEXIST: - pass + if self.output_png: + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, + gtk.gdk.screen_width(), + gtk.gdk.screen_height()) + self.canvas, mask = pixbuf.render_pixmap_and_mask() + self._build_window() + self._draw_and_quit() else: - raise - - -def _make_sub_menu(menu, name): - """ add a new submenu to the toolbar """ - sub_menu = gtk.MenuItem(name) - sub_menu.show() - sub_menu.set_submenu(menu) - return sub_menu - - -def _make_menu_item(menu, tooltip, callback, arg=None): - """ add a new item to the submenu """ - menu_items = gtk.MenuItem(tooltip) - menu.append(menu_items) - if arg is None: - menu_items.connect('activate', callback) - else: - menu_items.connect('activate', callback, arg) - menu_items.show() - - -def makepath(path): - """ Make a path if it doesn't previously exist """ - from os import makedirs - from os.path import normpath, dirname, exists - - dpath = normpath(dirname(path)) - if not exists(dpath): - makedirs(dpath) + self._read_initial_pos() + self._setup_gtk() + self._build_window() + self._uploader.set_tw(self.tw) + self._start_gtk() + + def _mkdir_p(path): + '''Create a directory in a fashion similar to `mkdir -p`''' + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST: + pass + else: + raise + + def _make_sub_menu(self, menu, name): + """ add a new submenu to the toolbar """ + sub_menu = gtk.MenuItem(name) + sub_menu.show() + sub_menu.set_submenu(menu) + return sub_menu + + + def _make_menu_item(self, menu, tooltip, callback, arg=None): + """ add a new item to the submenu """ + menu_items = gtk.MenuItem(tooltip) + menu.append(menu_items) + if arg is None: + menu_items.connect('activate', callback) + else: + menu_items.connect('activate', callback, arg) + menu_items.show() + + def _makepath(self, path): + """ Make a path if it doesn't previously exist """ + from os import makedirs + from os.path import normpath, dirname, exists + + dpath = normpath(dirname(path)) + if not exists(dpath): + makedirs(dpath) + + def _start_gtk(self): + self.win.connect('configure_event', self.tw.update_overlay_position) + self.tw.win = self.win + if self.ta_file is None: + self.tw.load_start() + else: + print self.ta_file + self.tw.load_start(self.ta_file) + self.tw.lc.trace = 0 + self.tw.run_button(0) + gtk.main() + def _draw_and_quit(self): + self.tw.load_start(self.ta_file) + self.tw.lc.trace = 0 + self.tw.run_button(0) + self.tw.save_as_image(self.ta_file, self.canvas) -class TurtleMain(): - """ Launch Turtle Art from outside of Sugar """ + def _build_window(self): + if os.path.exists(self._INSTALL_PATH): + self.tw = TurtleArtWindow(self.canvas, self._INSTALL_PATH) + elif os.path.exists(self._ALTERNATE_INSTALL_PATH): + self.tw = TurtleArtWindow(self.canvas, self._ALTERNATE_INSTALL_PATH) + else: + self.tw = TurtleArtWindow(self.canvas, os.path.abspath('.')) - def __init__(self): - """ Parse command-line options and initialize class """ + self.tw.save_folder = os.path.expanduser('~') + def _init_vars(self): # If we are invoked to start a project from Gnome, we should make # sure our current directory is TA's source dir. os.chdir(os.path.dirname(__file__)) + self._uploader = Uploader() self.ta_file = None self.output_png = False - self.uploading = False + self.i = 0 # FIXME: use a better name for this variable + self.scale = 2.0 + self.tw = None - # Parse command line + def _parse_command_line(self): try: opts, args = getopt.getopt(argv[1:], 'ho', ['help', 'output_png']) except getopt.GetoptError, err: print str(err) - print _HELP_MSG + print self._HELP_MSG sys.exit(2) for o, a in opts: if o in ('-h', '--help'): - print _HELP_MSG + print self._HELP_MSG sys.exit() if o in ('-o', '--output_png'): self.output_png = True @@ -146,7 +180,7 @@ class TurtleMain(): self.ta_file = args[0] if len(args) > 1 or self.output_png and self.ta_file is None: - print _HELP_MSG + print self._HELP_MSG sys.exit() if self.ta_file is not None: @@ -155,163 +189,134 @@ class TurtleMain(): if not os.path.exists(self.ta_file): assert False, ('%s: %s' % (self.ta_file, _('File not found'))) - self.i = 0 - self.scale = 2.0 - self.tw = None - - # make sure Sugar paths are present + """ + make sure Sugar paths are present + """ + def _ensure_sugar_paths(self): tapath = os.path.join(os.environ['HOME'], '.sugar', 'default', 'org.laptop.TurtleArtActivity') - map(makepath, (os.path.join(tapath, 'data/'), + map(self._makepath, (os.path.join(tapath, 'data/'), os.path.join(tapath, 'instance/'))) - if self.output_png: - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - gtk.gdk.screen_width(), - gtk.gdk.screen_height()) - canvas, mask = pixbuf.render_pixmap_and_mask() + def _read_initial_pos(self): + try: + data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), 'r') + except IOError: + # Opening the config file failed + # We'll assume it needs to be created + try: + self._mkdir_p(CONFIG_HOME) + data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), + 'a+') + except IOError, e: + # We can't write to the configuration file, use + # a faux file that will persist for the length of + # the session. + print _('Configuration directory not writable: %s') % (e) + data_file = cStringIO.StringIO() + data_file.write(str(50) + '\n') + data_file.write(str(50) + '\n') + data_file.write(str(800) + '\n') + data_file.write(str(550) + '\n') + data_file.seek(0) + self.x = int(data_file.readline()) + self.y = int(data_file.readline()) + self.width = int(data_file.readline()) + self.height = int(data_file.readline()) + + def _setup_gtk(self): + win = gtk.Window(gtk.WINDOW_TOPLEVEL) + win.set_default_size(self.width, self.height) + win.move(self.x, self.y) + win.maximize() + win.set_title(_('Turtle Art')) + if os.path.exists(os.path.join(self._INSTALL_PATH, self._ICON_SUBPATH)): + win.set_icon_from_file(os.path.join(self._INSTALL_PATH, + self._ICON_SUBPATH)) else: - win = gtk.Window(gtk.WINDOW_TOPLEVEL) - try: - data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), 'r') + win.set_icon_from_file(self._ICON_SUBPATH) except IOError: - # Opening the config file failed - # We'll assume it needs to be created - try: - mkdir_p(CONFIG_HOME) - data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), - 'a+') - except IOError, e: - # We can't write to the configuration file, use - # a faux file that will persist for the length of - # the session. - print _('Configuration directory not writable: %s') % (e) - data_file = cStringIO.StringIO() - data_file.write(str(50) + '\n') - data_file.write(str(50) + '\n') - data_file.write(str(800) + '\n') - data_file.write(str(550) + '\n') - data_file.seek(0) - self.x = int(data_file.readline()) - self.y = int(data_file.readline()) - self.width = int(data_file.readline()) - self.height = int(data_file.readline()) - - win.set_default_size(self.width, self.height) - win.move(self.x, self.y) - win.maximize() - win.set_title(_('Turtle Art')) - if os.path.exists(os.path.join(_INSTALL_PATH, _ICON_SUBPATH)): - win.set_icon_from_file(os.path.join(_INSTALL_PATH, - _ICON_SUBPATH)) - else: - try: - win.set_icon_from_file(_ICON_SUBPATH) - except IOError: - pass - win.connect('delete_event', self._quit_ta) - - menu = gtk.Menu() - _make_menu_item(menu, _('New'), self._do_new_cb) - _make_menu_item(menu, _('Open'), self._do_open_cb) - _make_menu_item(menu, _('Save'), self._do_save_cb) - _make_menu_item(menu, _('Save As'), self._do_save_as_cb) - _make_menu_item(menu, _('Save as image'), self._do_save_picture_cb) - _make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb) - _make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb) - if _UPLOAD_AVAILABLE: - _make_menu_item(menu, _('Upload to Web'), - self._do_upload_to_web) - _make_menu_item(menu, _('Quit'), self.destroy) - activity_menu = _make_sub_menu(menu, _('File')) - - menu = gtk.Menu() - _make_menu_item(menu, _('Cartesian coordinates'), - self._do_cartesian_cb) - _make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb) - _make_menu_item(menu, _('Rescale coordinates'), - self._do_rescale_cb) - _make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5) - _make_menu_item(menu, _('Shrink blocks'), - self._do_resize_cb, 0.667) - _make_menu_item(menu, _('Reset block size'), - self._do_resize_cb, -1) - view_menu = _make_sub_menu(menu, _('View')) - - menu = gtk.Menu() - _make_menu_item(menu, _('Copy'), self._do_copy_cb) - _make_menu_item(menu, _('Paste'), self._do_paste_cb) - edit_menu = _make_sub_menu(menu, _('Edit')) - - menu = gtk.Menu() - _make_menu_item(menu, _('Show palette'), self._do_palette_cb) - _make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb) - _make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb) - tool_menu = _make_sub_menu(menu, _('Tools')) - - menu = gtk.Menu() - _make_menu_item(menu, _('Clean'), self._do_eraser_cb) - _make_menu_item(menu, _('Run'), self._do_run_cb) - _make_menu_item(menu, _('Step'), self._do_step_cb) - _make_menu_item(menu, _('Debug'), self._do_trace_cb) - _make_menu_item(menu, _('Stop'), self._do_stop_cb) - turtle_menu = _make_sub_menu(menu, _('Turtle')) - - vbox = gtk.VBox(False, 0) - win.add(vbox) - vbox.show() - - menu_bar = gtk.MenuBar() - vbox.pack_start(menu_bar, False, False, 2) - menu_bar.show() - - sw = gtk.ScrolledWindow() - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.show() - canvas = gtk.DrawingArea() - width = gtk.gdk.screen_width() * 2 - height = gtk.gdk.screen_height() * 2 - canvas.set_size_request(width, height) - sw.add_with_viewport(canvas) - canvas.show() - vbox.pack_end(sw, True, True) - - menu_bar.append(activity_menu) - menu_bar.append(edit_menu) - menu_bar.append(view_menu) - menu_bar.append(tool_menu) - menu_bar.append(turtle_menu) - - win.show_all() - - if os.path.exists(_INSTALL_PATH): - self.tw = TurtleArtWindow(canvas, _INSTALL_PATH) - elif os.path.exists(_ALTERNATE_INSTALL_PATH): - self.tw = TurtleArtWindow(canvas, _ALTERNATE_INSTALL_PATH) - else: - self.tw = TurtleArtWindow(canvas, os.path.abspath('.')) - - self.tw.save_folder = os.path.expanduser('~') - - if not self.output_png: - win.connect('configure_event', self.tw.update_overlay_position) - self.tw.win = win - if self.ta_file is None: - self.tw.load_start() - else: - print self.ta_file - self.tw.load_start(self.ta_file) - self.tw.lc.trace = 0 - self.tw.run_button(0) - - gtk.main() - - else: - self.tw.load_start(self.ta_file) - self.tw.lc.trace = 0 - self.tw.run_button(0) - self.tw.save_as_image(self.ta_file, canvas) + pass + win.connect('delete_event', self._quit_ta) + + vbox = gtk.VBox(False, 0) + win.add(vbox) + vbox.show() + + menu_bar = self._get_menu_bar() + vbox.pack_start(menu_bar, False, False, 2) + menu_bar.show() + + sw = gtk.ScrolledWindow() + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.show() + canvas = gtk.DrawingArea() + width = gtk.gdk.screen_width() * 2 + height = gtk.gdk.screen_height() * 2 + canvas.set_size_request(width, height) + sw.add_with_viewport(canvas) + canvas.show() + vbox.pack_end(sw, True, True) + + win.show_all() + self.win = win + self.canvas = canvas + + def _get_menu_bar(self): + menu = gtk.Menu() + self._make_menu_item(menu, _('New'), self._do_new_cb) + self._make_menu_item(menu, _('Open'), self._do_open_cb) + self._make_menu_item(menu, _('Save'), self._do_save_cb) + self._make_menu_item(menu, _('Save As'), self._do_save_as_cb) + self._make_menu_item(menu, _('Save as image'), self._do_save_picture_cb) + self._make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb) + self._make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb) + if self._uploader.enabled(): + self._make_menu_item(menu, _('Upload to Web'), + self._uploader.do_upload_to_web) + self._make_menu_item(menu, _('Quit'), self.destroy) + activity_menu = self._make_sub_menu(menu, _('File')) + + menu = gtk.Menu() + self._make_menu_item(menu, _('Cartesian coordinates'), + self._do_cartesian_cb) + self._make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb) + self._make_menu_item(menu, _('Rescale coordinates'), + self._do_rescale_cb) + self._make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5) + self._make_menu_item(menu, _('Shrink blocks'), + self._do_resize_cb, 0.667) + self._make_menu_item(menu, _('Reset block size'), + self._do_resize_cb, -1) + view_menu = self._make_sub_menu(menu, _('View')) + + menu = gtk.Menu() + self._make_menu_item(menu, _('Copy'), self._do_copy_cb) + self._make_menu_item(menu, _('Paste'), self._do_paste_cb) + edit_menu = self._make_sub_menu(menu, _('Edit')) + + menu = gtk.Menu() + self._make_menu_item(menu, _('Show palette'), self._do_palette_cb) + self._make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb) + self._make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb) + tool_menu = self._make_sub_menu(menu, _('Tools')) + + menu = gtk.Menu() + self._make_menu_item(menu, _('Clean'), self._do_eraser_cb) + self._make_menu_item(menu, _('Run'), self._do_run_cb) + self._make_menu_item(menu, _('Step'), self._do_step_cb) + self._make_menu_item(menu, _('Debug'), self._do_trace_cb) + self._make_menu_item(menu, _('Stop'), self._do_stop_cb) + turtle_menu = self._make_sub_menu(menu, _('Turtle')) + + menu_bar = gtk.MenuBar() + menu_bar.append(activity_menu) + menu_bar.append(edit_menu) + menu_bar.append(view_menu) + menu_bar.append(tool_menu) + menu_bar.append(turtle_menu) + return menu_bar def _quit_ta(self, widget=None, e=None): """ Save changes on exit """ @@ -528,134 +533,5 @@ class TurtleMain(): """ Callback for destroy event. """ gtk.main_quit() - def _do_upload_to_web(self, widget): - """ Create dialog for uploading current project to the Web """ - if not self.uploading: - self.uploading = True - self.pop_up = gtk.Window() - self.pop_up.set_default_size(600, 400) - self.pop_up.connect('delete_event', self._stop_uploading) - table = gtk.Table(8, 1, False) - self.pop_up.add(table) - - login_label = gtk.Label(_('You must have an account at \ -http://turtleartsite.sugarlabs.org to upload your project.')) - table.attach(login_label, 0, 1, 0, 1) - self.login_message = gtk.Label('') - table.attach(self.login_message, 0, 1, 1, 2) - - self.Hbox1 = gtk.HBox() - table.attach(self.Hbox1, 0, 1, 2, 3, xpadding=5, ypadding=3) - self.username_entry = gtk.Entry() - username_label = gtk.Label(_('Username:') + ' ') - username_label.set_size_request(150, 25) - username_label.set_alignment(1.0, 0.5) - self.username_entry.set_size_request(450, 25) - self.Hbox1.add(username_label) - self.Hbox1.add(self.username_entry) - - self.Hbox2 = gtk.HBox() - table.attach(self.Hbox2, 0, 1, 3, 4, xpadding=5, ypadding=3) - self.password_entry = gtk.Entry() - password_label = gtk.Label(_('Password:') + ' ') - self.password_entry.set_visibility(False) - password_label.set_size_request(150, 25) - password_label.set_alignment(1.0, 0.5) - self.password_entry.set_size_request(450, 25) - self.Hbox2.add(password_label) - self.Hbox2.add(self.password_entry) - - self.Hbox3 = gtk.HBox() - table.attach(self.Hbox3, 0, 1, 4, 5, xpadding=5, ypadding=3) - self.title_entry = gtk.Entry() - title_label = gtk.Label(_('Title:') + ' ') - title_label.set_size_request(150, 25) - title_label.set_alignment(1.0, 0.5) - self.title_entry.set_size_request(450, 25) - self.Hbox3.add(title_label) - self.Hbox3.add(self.title_entry) - - self.Hbox4 = gtk.HBox() - table.attach(self.Hbox4, 0, 1, 5, 6, xpadding=5, ypadding=3) - self.description_entry = gtk.TextView() - description_label = gtk.Label(_('Description:') + ' ') - description_label.set_size_request(150, 25) - description_label.set_alignment(1.0, 0.5) - self.description_entry.set_wrap_mode(gtk.WRAP_WORD) - self.description_entry.set_size_request(450, 50) - self.Hbox4.add(description_label) - self.Hbox4.add(self.description_entry) - - self.Hbox5 = gtk.HBox() - table.attach(self.Hbox5, 0, 1, 6, 7, xpadding=5, ypadding=3) - self.submit_button = gtk.Button(_('Submit to Web')) - self.submit_button.set_size_request(300, 25) - self.submit_button.connect('pressed', self._do_remote_logon) - self.Hbox5.add(self.submit_button) - self.cancel_button = gtk.Button(_('Cancel')) - self.cancel_button.set_size_request(300, 25) - self.cancel_button.connect('pressed', self._stop_uploading) - self.Hbox5.add(self.cancel_button) - - self.pop_up.show_all() - - def _stop_uploading(self, widget, event=None): - """ Hide the popup when the upload is complte """ - self.uploading = False - self.pop_up.hide() - - def _do_remote_logon(self, widget): - """ Log into the upload server """ - username = self.username_entry.get_text() - password = self.password_entry.get_text() - server = xmlrpclib.ServerProxy(_UPLOAD_SERVER + '/call/xmlrpc') - logged_in = server.login_remote(username, password) - if logged_in: - upload_key = logged_in - self._do_submit_to_web(upload_key) - else: - self.login_message.set_text(_('Login failed')) - - def _do_submit_to_web(self, key): - """ Submit project to the server """ - title = self.title_entry.get_text() - description = self.description_entry.get_buffer().get_text( - *self.description_entry.get_buffer().get_bounds()) - tafile, imagefile = self.tw.save_for_upload(title) - - # Set a maximum file size for image to be uploaded. - if int(os.path.getsize(imagefile)) > _MAX_FILE_SIZE: - import Image - while int(os.path.getsize(imagefile)) > _MAX_FILE_SIZE: - big_file = Image.open(imagefile) - smaller_file = big_file.resize(int(0.9 * big_file.size[0]), - int(0.9 * big_file.size[1]), - Image.ANTIALIAS) - smaller_file.save(imagefile, quality = 100) - - c = pycurl.Curl() - c.setopt(c.POST, 1) - c.setopt(c.FOLLOWLOCATION, 1) - c.setopt(c.URL, _UPLOAD_SERVER + '/upload') - c.setopt(c.HTTPHEADER, ["Expect:"]) - c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, tafile)), - ('newimage', (c.FORM_FILE, imagefile)), - ('small_image', (c.FORM_FILE, imagefile)), - ('title', title), - ('description', description), - ('upload_key', key), ('_formname', - 'image_create')]) - c.perform() - error_code = c.getinfo(c.HTTP_CODE) - c.close - os.remove(imagefile) - os.remove(tafile) - if error_code == 400: - self.login_message.set_text(_('Failed to upload!')) - else: - self.pop_up.hide() - self.uploading = False - - if __name__ == "__main__": TurtleMain() -- cgit v0.9.1