Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tawindow.py
diff options
context:
space:
mode:
Diffstat (limited to 'tawindow.py')
-rw-r--r--tawindow.py2983
1 files changed, 2132 insertions, 851 deletions
diff --git a/tawindow.py b/tawindow.py
index 7b25335..57224f4 100644
--- a/tawindow.py
+++ b/tawindow.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#Copyright (c) 2007, Playful Invention Company
-#Copyright (c) 2008-9, Walter Bender
-#Copyright (c) 2009, Raúl Gutiérrez Segalés
+#Copyright (c) 2008-10, Walter Bender
+#Copyright (c) 2009-10 Raúl Gutiérrez Segalés
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
@@ -24,895 +24,2176 @@
import pygtk
pygtk.require('2.0')
import gtk
-import pango
import gobject
import os
import os.path
-import time
-
-# Import from Journal for these blocks
-importblocks = ['audiooff', 'descriptionoff','journal']
-
-class taWindow: pass
-
from math import atan2, pi
DEGTOR = 2*pi/360
+from gettext import gettext as _
-from tasetup import *
-from tasprites import *
-from talogo import *
-from taturtle import *
-from taproject import *
try:
from sugar.graphics.objectchooser import ObjectChooser
-except:
+ from sugar.datastore import datastore
+ from sugar import profile
+except ImportError:
pass
-from tahoverhelp import *
-from gettext import gettext as _
+from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
+ PALETTE_NAMES, TITLEXY, MEDIA_SHAPES, STATUS_SHAPES, \
+ OVERLAY_SHAPES, TOOLBAR_SHAPES, TAB_LAYER, \
+ OVERLAY_LAYER, CATEGORY_LAYER, BLOCKS_WITH_SKIN, \
+ ICON_SIZE, PALETTES, PALETTE_SCALE, BOX_STYLE_MEDIA, \
+ PALETTE_WIDTH, MACROS, TOP_LAYER, BLOCK_LAYER, \
+ CONTENT_BLOCKS, DEFAULTS, SPECIAL_NAMES, HELP_STRINGS, \
+ CURSOR, EXPANDABLE, COLLAPSIBLE, RETURN, \
+ DEAD_DICTS, DEAD_KEYS, TEMPLATES, PYTHON_SKIN, \
+ PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, OLD_NAMES, \
+ BOOLEAN_STYLE, BLOCK_NAMES
+from talogo import LogoCode, stop_logo
+from tacanvas import TurtleGraphics
+from tablock import Blocks, Block
+from taturtle import Turtles, Turtle
+from tautils import magnitude, get_load_name, get_save_name, data_from_file, \
+ data_to_file, round_int, get_id, get_pixbuf_from_journal, \
+ movie_media_type, audio_media_type, image_media_type, \
+ save_picture, save_svg, calc_image_size, get_path, \
+ reset_stack_arm, grow_stack_arm, find_sandwich_top, \
+ find_sandwich_bottom, restore_stack, collapse_stack, \
+ collapsed, collapsible, hide_button_hit, show_button_hit, \
+ arithmetic_check, xy, find_block_to_run, find_top_block, \
+ find_start_stack, find_group, find_blk_below, olpc_xo_1, \
+ dock_dx_dy
+from tasprite_factory import SVG, svg_str_to_pixbuf, svg_from_file
+from sprites import Sprites, Sprite
+
+class TurtleArtWindow():
+ """ TurtleArt Window class abstraction """
+ timeout_tag = [0]
+
+ def __init__(self, win, path, parent=None, mycolors=None):
+ self.win = None
+ self.window = win
+ self.path = path
+ self.load_save_folder = os.path.join(path, 'samples')
+ self.save_folder = None
+ self.save_file_name = None
+ self.window.set_flags(gtk.CAN_FOCUS)
+ self.width = gtk.gdk.screen_width()
+ self.height = gtk.gdk.screen_height()
+ if parent is not None:
+ parent.show_all()
+ self.running_sugar = True
+ self.activity = parent
+ self.nick = profile.get_nick_name()
+ else:
+ self.window.show_all()
+ self.running_sugar = False
+ self.activity = None
+ self.nick = None
+ self._setup_events()
+ self.keypress = ""
+ self.keyvalue = 0
+ self.dead_key = ""
+ self.area = self.window.window
+ self.gc = self.area.new_gc()
+ self.orientation = HORIZONTAL_PALETTE
+ if olpc_xo_1():
+ self.lead = 1.0
+ self.scale = 0.67
+ if self.running_sugar and not self.activity.new_sugar_system:
+ self.orientation = VERTICAL_PALETTE
+ else:
+ self.lead = 1.0
+ self.scale = 1.0
+ self.block_scale = BLOCK_SCALE
+ self.trash_scale = 0.5
+ self.myblock = None
+ self.nop = 'nop'
+ self.loaded = 0
+ self.step_time = 0
+ self.hide = False
+ self.palette = True
+ self.coord_scale = 1
+ self.buddies = []
+ self.saved_string = ''
+ self.dx = 0
+ self.dy = 0
+ self.media_shapes = {}
+ self.cartesian = False
+ self.polar = False
+ self.overlay_shapes = {}
+ self.toolbar_shapes = {}
+ self.toolbar_offset = 0
+ self.status_spr = None
+ self.status_shapes = {}
+ self.toolbar_spr = None
+ self.palette_sprs = []
+ self.palettes = []
+ self.palette_button = []
+ self.trash_index = PALETTE_NAMES.index('trash')
+ self.trash_stack = []
+ self.selected_palette = None
+ self.previous_palette = None
+ self.selectors = []
+ self.selected_selector = None
+ self.previous_selector = None
+ self.selector_shapes = []
+ self.selected_blk = None
+ self.selected_spr = None
+ self.drag_group = None
+ self.drag_turtle = 'move', 0, 0
+ self.drag_pos = 0, 0
+ self.block_list = Blocks(self.scale)
+ self.sprite_list = Sprites(self.window, self.area, self.gc)
+ self.turtles = Turtles(self.sprite_list)
+ if mycolors == None:
+ Turtle(self.turtles, 1)
+ else:
+ Turtle(self.turtles, 1, mycolors.split(','))
+ self.active_turtle = self.turtles.get_turtle(1)
+ self.saving_svg = False
+ self.svg_string = ''
+ self.selected_turtle = None
+ self.canvas = TurtleGraphics(self, self.width, self.height)
+ self.titlex = -(self.canvas.width*TITLEXY[0])/(self.coord_scale*2)
+ self.leftx = -(self.canvas.width*TITLEXY[0])/(self.coord_scale*2)
+ self.rightx = 0
+ self.titley = (self.canvas.height*TITLEXY[1])/(self.coord_scale*2)
+ self.topy = (self.canvas.height*(TITLEXY[1]-0.125))/(self.coord_scale*2)
+ self.bottomy = 0
+ self.lc = LogoCode(self)
+ self.saved_pictures = []
+
+ self._setup_misc()
+ self._show_toolbar_palette(0, False)
+ self.block_operation = ''
+
+ def _setup_events(self):
+ """ Register the events we listen to. """
+ self.window.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self.window.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
+ self.window.add_events(gtk.gdk.POINTER_MOTION_MASK)
+ self.window.add_events(gtk.gdk.KEY_PRESS_MASK)
+ self.window.connect("expose-event", self._expose_cb)
+ self.window.connect("button-press-event", self._buttonpress_cb)
+ self.window.connect("button-release-event", self._buttonrelease_cb)
+ self.window.connect("motion-notify-event", self._move_cb)
+ self.window.connect("key_press_event", self._keypress_cb)
+
+ def _setup_misc(self):
+ """ Misc. sprites for status, overlays, etc. """
+ # media blocks get positioned into other blocks
+ for _name in MEDIA_SHAPES:
+ if _name[0:7] == 'journal' and not self.running_sugar:
+ file_name = 'file'+_name[7:]
+ else:
+ file_name = _name
+ self.media_shapes[_name] = svg_str_to_pixbuf(svg_from_file(
+ "%s/images/%s.svg" % (self.path, file_name)))
+
+ for i, _name in enumerate(STATUS_SHAPES):
+ self.status_shapes[_name] = svg_str_to_pixbuf(svg_from_file(
+ "%s/images/%s.svg" % (self.path, _name)))
+ self.status_spr = Sprite(self.sprite_list, 0, self.height-200,
+ self.status_shapes['status'])
+ self.status_spr.hide()
+ self.status_spr.type = 'status'
+
+ for _name in OVERLAY_SHAPES:
+ self.overlay_shapes[_name] = Sprite(self.sprite_list,
+ int(self.width/2-600), int(self.height/2-450),
+ svg_str_to_pixbuf(svg_from_file(
+ "%s/images/%s.svg" % (self.path, _name))))
+ self.overlay_shapes[_name].hide()
+ self.overlay_shapes[_name].type = 'overlay'
+
+ if not self.running_sugar:
+ offset = self.width-55*len(TOOLBAR_SHAPES)
+ for i, _name in enumerate(TOOLBAR_SHAPES):
+ self.toolbar_shapes[_name] = Sprite(self.sprite_list,
+ i*55+offset, 0,
+ svg_str_to_pixbuf(
+ svg_from_file("%s/icons/%s.svg" % (self.path, _name))))
+ self.toolbar_shapes[_name].set_layer(TAB_LAYER)
+ self.toolbar_shapes[_name].name = _name
+ self.toolbar_shapes[_name].type = 'toolbar'
+ self.toolbar_shapes['stopiton'].hide()
+
+ def sharing(self):
+ """ Is a chattube available for sharing? """
+ if self.running_sugar and hasattr(self.activity, 'chattube') and\
+ self.activity.chattube is not None:
+ return True
+ return False
-# dead key dictionaries
-dead_grave = {'A':192,'E':200,'I':204,'O':210,'U':217,'a':224,'e':232,'i':236,\
- 'o':242,'u':249}
-dead_acute = {'A':193,'E':201,'I':205,'O':211,'U':218,'a':225,'e':233,'i':237,\
- 'o':243,'u':250}
-dead_circumflex = {'A':194,'E':202,'I':206,'O':212,'U':219,'a':226,'e':234,\
- 'i':238,'o':244,'u':251}
-dead_tilde = {'A':195,'O':211,'N':209,'U':360,'a':227,'o':245,'n':241,'u':361}
-dead_diaeresis = {'A':196,'E':203,'I':207,'O':211,'U':218,'a':228,'e':235,\
- 'i':239,'o':245,'u':252}
-dead_abovering = {'A':197,'a':229}
-
-# Time out for triggering help
-timeout_tag = [0]
-
-
-#
-# Setup
-#
-
-def twNew(win, path, lang, parent=None):
- tw = taWindow()
- tw.window = win
- tw.path = os.path.join(path,'images')
- tw.path_lang = os.path.join(path,'images',lang)
- tw.path_en = os.path.join(path,'images/en') # en as fallback
- tw.load_save_folder = os.path.join(path,'samples')
- tw.save_folder = None
- tw.save_file_name = None
- win.set_flags(gtk.CAN_FOCUS)
- tw.width = gtk.gdk.screen_width()
- tw.height = gtk.gdk.screen_height()
- # starting from command line
- if parent is None:
- win.show_all()
- # starting from Sugar
- else:
- parent.show_all()
- win.add_events(gtk.gdk.BUTTON_PRESS_MASK)
- win.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
- win.add_events(gtk.gdk.POINTER_MOTION_MASK)
- win.add_events(gtk.gdk.KEY_PRESS_MASK)
- win.connect("expose-event", expose_cb, tw)
- win.connect("button-press-event", buttonpress_cb, tw)
- win.connect("button-release-event", buttonrelease_cb, tw)
- win.connect("motion-notify-event", move_cb, tw)
- win.connect("key_press_event", keypress_cb, tw)
- tw.keypress = ""
- tw.keyvalue = 0
- tw.dead_key = ""
- tw.area = win.window
- tw.gc = tw.area.new_gc()
- # on an OLPC-XO-1, there is a scaling factor
- if os.path.exists('/etc/olpc-release') or \
- os.path.exists('/sys/power/olpc-pm'):
- tw.lead = 1.6
- tw.scale = 1.0
- else:
- tw.lead = 1.0
- tw.scale = 1.6
- tw.cm = tw.gc.get_colormap()
- tw.rgb = [255,0,0]
- tw.bgcolor = tw.cm.alloc_color('#fff8de')
- tw.msgcolor = tw.cm.alloc_color('black')
- tw.fgcolor = tw.cm.alloc_color('red')
- tw.textcolor = tw.cm.alloc_color('blue')
- tw.textsize = 32
- tw.sprites = []
- tw.selected_block = None
- tw.draggroup = None
- prep_selectors(tw)
- tw.myblock = None
- tw.nop = 'nop'
- tw.loaded = 0
- for s in selectors:
- setup_selectors(tw,s)
- setup_misc(tw)
- tw.step_time = 0
- tw.hide = False
- tw.palette = True
- select_category(tw, tw.selbuttons[0])
- tw.coord_scale = 1
- tw.turtle = tNew(tw,tw.width,tw.height)
- tw.lc = lcNew(tw)
- tw.buddies = []
- tw.dx = 0
- tw.dy = 0
- tw.cartesian = False
- tw.polar = False
- tw.spr = None # "currently selected spr"
- return tw
-
-#
-# Button Press
-#
-
-def buttonpress_cb(win, event, tw):
- win.grab_focus()
- x, y = xy(event)
- button_press(tw, event.get_state()&gtk.gdk.CONTROL_MASK, x, y)
- # if sharing, send button press
- if hasattr(tw, 'activity') and \
- hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None:
- # print "sending button pressed"
- if event.get_state()&gtk.gdk.CONTROL_MASK is True:
- tw.activity._send_event("p:"+str(x)+":"+str(y)+":"+'T')
- else:
- tw.activity._send_event("p:"+str(x)+":"+str(y)+":"+'F')
- return True
-
-def button_press(tw, mask, x, y, verbose=False):
- if verbose:
- print "processing remote button press: " + str(x) + " " + str(y)
- tw.block_operation = 'click'
- if tw.selected_block!=None:
- unselect(tw)
- else:
- setlayer(tw.status_spr,400)
- spr = findsprite(tw,(x,y))
- tw.x, tw.y = x,y
- tw.dx = 0
- tw.dy = 0
- if spr is None:
- return True
- if spr.type == "canvas":
+ def _expose_cb(self, win, event):
+ """ Repaint """
+ self.sprite_list.redraw_sprites()
+ # self.canvas.cr_expose(event)
return True
- elif spr.type == 'selbutton':
- select_category(tw,spr)
- elif spr.type == 'category':
- block_selector_pressed(tw,x,y)
- elif spr.type == 'block':
- block_pressed(tw,mask,x,y,spr)
- elif spr.type == 'turtle':
- turtle_pressed(tw,x,y)
- tw.spr = spr
-
-def block_selector_pressed(tw,x,y):
- proto = get_proto_from_category(tw,x,y)
- if proto==None:
+
+ def eraser_button(self):
+ """ Eraser_button (hide status block when clearing the screen.) """
+ if self.status_spr is not None:
+ self.status_spr.hide()
+ self.lc.prim_clear()
+ self.display_coordinates()
+
+ def run_button(self, time):
+ """ Run turtle! """
+ if self.running_sugar:
+ self.activity.recenter()
+ # Look for a 'start' block
+ for blk in self.just_blocks():
+ if find_start_stack(blk):
+ self.step_time = time
+ print "running stack starting from %s" % (blk.name)
+ self._run_stack(blk)
+ return
+ # If there is no 'start' block, run stacks that aren't 'def action'
+ for blk in self.just_blocks():
+ if find_block_to_run(blk):
+ self.step_time = time
+ print "running stack starting from %s" % (blk.name)
+ self._run_stack(blk)
return
- if proto is not 'hide':
- new_block_from_category(tw,proto,x,y)
- else:
- hideshow_palette(tw,False)
-
-def hideshow_palette(tw,state):
- if state is False:
- tw.palette == False
- if hasattr(tw,'activity'):
- # Use new toolbar design
- tw.activity.do_hidepalette()
- hide_palette(tw)
- else:
- tw.palette == True
- if hasattr(tw,'activity'):
- # Use new toolbar design
- tw.activity.do_showpalette()
- show_palette(tw)
-
-def show_palette(tw):
- for i in tw.selbuttons: setlayer(i,800)
- select_category(tw,tw.selbuttons[0])
- tw.palette = True
-
-def hide_palette(tw):
- for i in tw.selbuttons: hide(i)
- setshape(tw.category_spr, tw.hidden_palette_icon)
- tw.palette = False
-
-def get_proto_from_category(tw,x,y):
- dx,dy = x-tw.category_spr.x, y-tw.category_spr.y,
- pixel = getpixel(tw.current_category.mask,dx,dy)
- index = ((pixel%256)>>3)-1
- if index==0:
- return 'hide'
- index-=1
- if index>len(tw.current_category.blockprotos):
- return None
- return tw.current_category.blockprotos[index]
-
-def select_category(tw, spr):
- if hasattr(tw, 'current_category'):
- setshape(tw.current_category, tw.current_category.offshape)
- setshape(spr, spr.onshape)
- tw.current_category = spr
- setshape(tw.category_spr,spr.group)
-
-def new_block_from_category(tw,proto,x,y):
- if proto is None:
+
+ def stop_button(self):
+ """ Stop button """
+ stop_logo(self)
+
+ def set_userdefined(self):
+ """ Change icon for user-defined blocks after Python code is loaded. """
+ for blk in self.just_blocks():
+ if blk.name in PYTHON_SKIN:
+ x, y = self._calc_image_offset('pythonon', blk.spr)
+ blk.set_image(self.media_shapes['pythonon'], x, y)
+ self._resize_skin(blk)
+ self.nop = 'pythonloaded'
+
+ def set_fullscreen(self):
+ """ Enter fullscreen mode """
+ if self.running_sugar:
+ self.activity.fullscreen()
+ self.activity.recenter()
+
+ def set_cartesian(self, flag):
+ """ Turn on/off Cartesian coordinates """
+ if flag:
+ if self.coord_scale == 1:
+ self.overlay_shapes['Cartesian_labeled'].set_layer(
+ OVERLAY_LAYER)
+ else:
+ self.overlay_shapes['Cartesian'].set_layer(OVERLAY_LAYER)
+ self.cartesian = True
+ else:
+ if self.coord_scale == 1:
+ self.overlay_shapes['Cartesian_labeled'].hide()
+ else:
+ self.overlay_shapes['Cartesian'].hide()
+ self.cartesian = False
+
+ def set_polar(self, flag):
+ """ Turn on/off polar coordinates """
+ if flag:
+ self.overlay_shapes['polar'].set_layer(OVERLAY_LAYER)
+ self.polar = True
+ else:
+ self.overlay_shapes['polar'].hide()
+ self.polar = False
+
+ def hideshow_button(self):
+ """ Hide/show button """
+ if not self.hide:
+ for blk in self.just_blocks():
+ blk.spr.hide()
+ self.hide_palette()
+ self.hide = True
+ else:
+ for blk in self.just_blocks():
+ if blk.status != 'collapsed':
+ blk.spr.set_layer(BLOCK_LAYER)
+ self.show_palette()
+ if self.activity is not None and self.activity.new_sugar_system:
+ self.activity.palette_buttons[0].set_icon(
+ PALETTE_NAMES[0] + 'on')
+ self.hide = False
+ self.canvas.canvas.inval()
+
+ def hideshow_palette(self, state):
+ """ Hide or show palette """
+ if not state:
+ self.palette = False
+ if self.running_sugar:
+ self.activity.do_hidepalette()
+ self.hide_palette()
+ else:
+ self.palette = True
+ if self.running_sugar:
+ self.activity.do_showpalette()
+ self.show_palette()
+
+ def show_palette(self, n=0):
+ """ Show palette """
+ self._show_toolbar_palette(n)
+ self.palette_button[self.orientation].set_layer(TAB_LAYER)
+ self.palette_button[2].set_layer(TAB_LAYER)
+ if self.activity is None or not self.activity.new_sugar_system:
+ self.toolbar_spr.set_layer(CATEGORY_LAYER)
+ self.palette = True
+
+ def hide_palette(self):
+ """ Hide the palette. """
+ self._hide_toolbar_palette()
+ self.palette_button[self.orientation].hide()
+ self.palette_button[2].hide()
+ if self.activity is None or not self.activity.new_sugar_system:
+ self.toolbar_spr.hide()
+ self.palette = False
+
+ def hideblocks(self):
+ """ Callback from 'hide blocks' block """
+ self.hide = False
+ self.hideshow_button()
+ if self.running_sugar:
+ self.activity.do_hide()
+
+ def showblocks(self):
+ """ Callback from 'show blocks' block """
+ self.hide = True
+ self.hideshow_button()
+ if self.running_sugar:
+ self.activity.do_show()
+
+ def resize_blocks(self):
+ """ Resize all of the blocks """
+ # We need to restore collapsed stacks before resizing.
+ for blk in self.just_blocks():
+ if blk.status == 'collapsed':
+ bot = find_sandwich_bottom(blk)
+ if collapsed(bot):
+ dy = bot.values[0]
+ restore_stack(find_sandwich_top(blk))
+ bot.values[0] = dy
+
+ # Do the resizing.
+ for blk in self.just_blocks():
+ blk.rescale(self.block_scale)
+ for blk in self.just_blocks():
+ self._adjust_dock_positions(blk)
+
+ # Re-collapsed stacks after resizing.
+ for blk in self.just_blocks():
+ if collapsed(blk):
+ collapse_stack(find_sandwich_top(blk))
+ for blk in self.just_blocks():
+ if blk.name == 'sandwichtop':
+ grow_stack_arm(blk)
+
+ # Resize the skins on some blocks: media content and Python
+ for blk in self.just_blocks():
+ if blk.name in BLOCKS_WITH_SKIN:
+ self._resize_skin(blk)
+
+ def _show_toolbar_palette(self, n, init_only=False):
+ """ Show the toolbar palettes, creating them on init_only """
+ if (self.activity is None or not self.activity.new_sugar_system) and\
+ self.selectors == []:
+ # Create the selectors
+ svg = SVG()
+ x, y = 50, 0
+ for i, name in enumerate(PALETTE_NAMES):
+ a = svg_str_to_pixbuf(svg_from_file("%s/icons/%soff.svg" % (
+ self.path, name)))
+ b = svg_str_to_pixbuf(svg_from_file("%s/icons/%son.svg" % (
+ self.path, name)))
+ self.selector_shapes.append([a, b])
+ self.selectors.append(Sprite(self.sprite_list, x, y, a))
+ self.selectors[i].type = 'selector'
+ self.selectors[i].name = name
+ self.selectors[i].set_layer(TAB_LAYER)
+ w = self.selectors[i].get_dimensions()[0]
+ x += int(w)
+
+ # Create the toolbar background
+ self.toolbar_offset = ICON_SIZE
+ self.toolbar_spr = Sprite(self.sprite_list, 0, 0,
+ svg_str_to_pixbuf(svg.toolbar(self.width, ICON_SIZE)))
+ self.toolbar_spr.type = 'toolbar'
+ self.toolbar_spr.set_layer(CATEGORY_LAYER)
+
+
+ if self.palette_sprs == []:
+ # Create the empty palettes
+ if len(self.palettes) == 0:
+ for i in range(len(PALETTES)):
+ self.palettes.append([])
+
+ # Create empty palette backgrounds
+ for i in PALETTE_NAMES:
+ self.palette_sprs.append([None, None])
+
+ # Create the palette orientation button
+ self.palette_button.append(Sprite(self.sprite_list, 0,
+ self.toolbar_offset, svg_str_to_pixbuf(svg_from_file(
+ "%s/images/palettehorizontal.svg" %(self.path)))))
+ self.palette_button.append(Sprite(self.sprite_list, 0,
+ self.toolbar_offset, svg_str_to_pixbuf(svg_from_file(
+ "%s/images/palettevertical.svg" % (self.path)))))
+ self.palette_button[0].name = _('orientation')
+ self.palette_button[1].name = _('orientation')
+ self.palette_button[0].type = 'palette'
+ self.palette_button[1].type = 'palette'
+ self.palette_button[self.orientation].set_layer(TAB_LAYER)
+ self.palette_button[1-self.orientation].hide()
+
+ # Create the palette next button
+ self.palette_button.append(Sprite(self.sprite_list, 16,
+ self.toolbar_offset, svg_str_to_pixbuf(svg_from_file(
+ "%s/images/palettenext.svg" %(self.path)))))
+ self.palette_button[2].name = _('next')
+ self.palette_button[2].type = 'palette'
+ self.palette_button[2].set_layer(TAB_LAYER)
+
+ if init_only:
+ return
+
+ # Hide the previously displayed palette
+ self._hide_previous_palette()
+
+ self.selected_palette = n
+ self.previous_palette = self.selected_palette
+
+ if self.activity is None or not self.activity.new_sugar_system:
+ self.selected_selector = self.selectors[n]
+ # Make sure all of the selectors are visible.
+ self.selectors[n].set_shape(self.selector_shapes[n][1])
+ for i in range(len(PALETTES)):
+ self.selectors[i].set_layer(TAB_LAYER)
+
+ # Show the palette with the current orientation.
+ if self.palette_sprs[n][self.orientation] is not None:
+ self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+
+ if self.palettes[n] == []:
+ # Create 'proto' blocks for each palette entry
+ for i, name in enumerate(PALETTES[n]):
+ self.palettes[n].append(Block(self.block_list,
+ self.sprite_list, name,
+ 0, 0, 'proto', [], PALETTE_SCALE))
+ self.palettes[n][i].spr.set_layer(TAB_LAYER)
+ self.palettes[n][i].unhighlight()
+
+ # Some proto blocks get a skin.
+ if name in BOX_STYLE_MEDIA:
+ self._proto_skin(name+'small', n, i)
+ elif name[:8] == 'template':
+ self._proto_skin(name[8:], n, i)
+ elif name[:7] == 'picture':
+ self._proto_skin(name[7:], n, i)
+ elif name in PYTHON_SKIN:
+ self._proto_skin('pythonsmall', n, i)
+
+ self._layout_palette(n)
+ for blk in self.palettes[n]:
+ blk.spr.set_layer(TAB_LAYER)
+ if n == self.trash_index:
+ for blk in self.trash_stack:
+ for gblk in find_group(blk):
+ if gblk.status != 'collapsed':
+ gblk.spr.set_layer(TAB_LAYER)
+
+ def _hide_toolbar_palette(self):
+ """ Hide the toolbar palettes """
+ self._hide_previous_palette()
+ if self.activity is None or not self.activity.new_sugar_system:
+ # Hide the selectors
+ for i in range(len(PALETTES)):
+ self.selectors[i].hide()
+ elif self.selected_palette is not None:
+ self.activity.palette_buttons[self.selected_palette].set_icon(
+ PALETTE_NAMES[self.selected_palette] + 'off')
+ self.selected_palette = None
+ self.previous_palette = None
+
+ def _hide_previous_palette(self):
+ """ Hide just the previously viewed toolbar palette """
+ # Hide previous palette
+ if self.previous_palette is not None:
+ for i in range(len(PALETTES[self.previous_palette])):
+ self.palettes[self.previous_palette][i].spr.hide()
+ self.palette_sprs[self.previous_palette][
+ self.orientation].hide()
+ if self.activity is None or not self.activity.new_sugar_system:
+ self.selectors[self.previous_palette].set_shape(
+ self.selector_shapes[self.previous_palette][0])
+ elif self.previous_palette is not None and \
+ self.previous_palette != self.selected_palette:
+ self.activity.palette_buttons[self.previous_palette].set_icon(
+ PALETTE_NAMES[self.previous_palette] + 'off')
+ if self.previous_palette == self.trash_index:
+ for blk in self.trash_stack:
+ for gblk in find_group(blk):
+ gblk.spr.hide()
+
+ def _horizontal_layout(self, x, y, blocks):
+ """ Position prototypes in a horizontal palette. """
+ _max_w = 0
+ for blk in blocks:
+ _w, _h = self._width_and_height(blk)
+ if y + _h > PALETTE_HEIGHT + self.toolbar_offset:
+ x += int(_max_w+3)
+ y = self.toolbar_offset + 3
+ _max_w = 0
+ (_bx, _by) = blk.spr.get_xy()
+ _dx = x-_bx
+ _dy = y-_by
+ for g in find_group(blk):
+ g.spr.move_relative((int(_dx), int(_dy)))
+ y += int(_h+3)
+ if _w > _max_w:
+ _max_w = _w
+ return x, y, _max_w
+
+ def _vertical_layout(self, x, y, blocks):
+ """ Position prototypes in a vertical palette. """
+ _row = []
+ _row_w = 0
+ _max_h = 0
+ for _b in blocks:
+ _w, _h = self._width_and_height(_b)
+ if x + _w > PALETTE_WIDTH:
+ # Recenter row.
+ _dx = int((PALETTE_WIDTH-_row_w)/2)
+ for _r in _row:
+ for _g in find_group(_r):
+ _g.spr.move_relative((_dx, 0))
+ _row = []
+ _row_w = 0
+ x = 4
+ y += int(_max_h+3)
+ _max_h = 0
+ _row.append(_b)
+ _row_w += (4 + _w)
+ (_bx, _by) = _b.spr.get_xy()
+ _dx = int(x - _bx)
+ _dy = int(y - _by)
+ for _g in find_group(_b):
+ _g.spr.move_relative((_dx, _dy))
+ x += int(_w + 4)
+ if _h > _max_h:
+ _max_h = _h
+ # Recenter last row.
+ _dx = int((PALETTE_WIDTH - _row_w)/2)
+ for _r in _row:
+ for _g in find_group(_r):
+ _g.spr.move_relative((_dx, 0))
+ return x, y, _max_h
+
+ def _layout_palette(self, n):
+ """ Layout prototypes in a palette. """
+ if n is not None:
+ if self.orientation == HORIZONTAL_PALETTE:
+ _x, _y = 20, self.toolbar_offset + 5
+ _x, _y, _max = self._horizontal_layout(_x, _y, self.palettes[n])
+ if n == self.trash_index:
+ _x, _y, _max = self._horizontal_layout(_x+_max, _y,
+ self.trash_stack)
+ _w = _x + _max + 25
+ if self.palette_sprs[n][self.orientation] is None:
+ svg = SVG()
+ self.palette_sprs[n][self.orientation] = Sprite(
+ self.sprite_list, 0, self.toolbar_offset,
+ svg_str_to_pixbuf(svg.palette(_w, PALETTE_HEIGHT)))
+ self.palette_sprs[n][self.orientation].type = 'category'
+ if n == PALETTE_NAMES.index('trash'):
+ svg = SVG()
+ self.palette_sprs[n][self.orientation].set_shape(
+ svg_str_to_pixbuf(svg.palette(_w, PALETTE_HEIGHT)))
+ self.palette_button[2].move((_w-20, self.toolbar_offset))
+ else:
+ _x, _y = 5, self.toolbar_offset + 15
+ _x, _y, _max = self._vertical_layout(_x, _y, self.palettes[n])
+ if n == PALETTE_NAMES.index('trash'):
+ _x, _y, _max = self._vertical_layout(_x, _y + _max,
+ self.trash_stack)
+ _h = _y + _max + 25 - self.toolbar_offset
+ if self.palette_sprs[n][self.orientation] is None:
+ svg = SVG()
+ self.palette_sprs[n][self.orientation] = \
+ Sprite(self.sprite_list, 0, self.toolbar_offset,
+ svg_str_to_pixbuf(svg.palette(PALETTE_WIDTH, _h)))
+ self.palette_sprs[n][self.orientation].type = 'category'
+ if n == PALETTE_NAMES.index('trash'):
+ svg = SVG()
+ self.palette_sprs[n][self.orientation].set_shape(
+ svg_str_to_pixbuf(svg.palette(PALETTE_WIDTH, _h)))
+ self.palette_button[2].move((PALETTE_WIDTH-20,
+ self.toolbar_offset))
+ self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+
+ def _buttonpress_cb(self, win, event):
+ """ Button press """
+ self.window.grab_focus()
+ x, y = xy(event)
+ self.button_press(event.get_state()&gtk.gdk.CONTROL_MASK, x, y)
return True
- # load alternative image of nop block if python code is loaded
- if proto.name == 'nop' and tw.nop == 'pythonloaded':
- newspr = sprNew(tw,x-20,y-20,tw.media_shapes['pythonloaded'])
- else:
- newspr = sprNew(tw,x-20,y-20,proto.image)
- setlayer(newspr,2000)
- tw.dragpos = 20,20
- newspr.type = 'block'
- newspr.proto = proto
- if tw.defdict.has_key(newspr.proto.name):
- newspr.label=tw.defdict[newspr.proto.name]
- newspr.connections = [None]*len(proto.docks)
- for i in range(len(proto.defaults)):
- dock = proto.docks[i+1]
- argproto = tw.protodict[tw.valdict[dock[0]]]
- argdock = argproto.docks[0]
- nx,ny = newspr.x+dock[2]-argdock[2],newspr.y+dock[3]-argdock[3]
- argspr = sprNew(tw,nx,ny,argproto.image)
- argspr.type = 'block'
- argspr.proto = argproto
- argspr.label = str(proto.defaults[i])
- setlayer(argspr,2000)
- argspr.connections = [newspr,None]
- newspr.connections[i+1] = argspr
- tw.draggroup = findgroup(newspr)
- tw.block_operation = 'new'
-
-def block_pressed(tw,mask,x,y,spr):
- if spr is not None:
- tw.draggroup = findgroup(spr)
- for b in tw.draggroup: setlayer(b,2000)
- if spr.connections[0] != None and spr.proto.name == 'lock':
- b = find_top_block(spr)
- tw.dragpos = x-b.x,y-b.y
- else:
- tw.dragpos = x-spr.x,y-spr.y
- disconnect(spr)
-
-def turtle_pressed(tw,x,y):
- dx,dy = x-tw.turtle.spr.x-30,y-tw.turtle.spr.y-30
- if dx*dx+dy*dy > 200:
- tw.dragpos = ('turn', \
- tw.turtle.heading-atan2(dy,dx)/DEGTOR,0)
- else:
- tw.dragpos = ('move', x-tw.turtle.spr.x,y-tw.turtle.spr.y)
- tw.draggroup = [tw.turtle.spr]
-
-#
-# Mouse move
-#
-
-def move_cb(win, event, tw):
- x,y = xy(event)
- mouse_move(tw, x, y)
- return True
-
-def mouse_move(tw, x, y, verbose=False, mdx=0, mdy=0):
- if verbose:
- print "processing remote mouse move: " + str(x) + " " + str(y)
- if tw.draggroup is None:
- # popup help from RGS
- spr = findsprite(tw,(x,y))
- if spr and spr.type == 'category':
- proto = get_proto_from_category(tw,x,y)
- if proto and proto!='hide':
- if timeout_tag[0] == 0:
- timeout_tag[0] = showPopup(proto.name,tw)
- tw.spr = spr
- return
- else:
- if timeout_tag[0] > 0:
- try:
- gobject.source_remove(timeout_tag[0])
- timeout_tag[0] = 0
- except:
- timeout_tag[0] = 0
- elif spr and spr.type == 'selbutton':
- if timeout_tag[0] == 0:
- timeout_tag[0] = showPopup(spr.name,tw)
- tw.spr = spr
+
+ def button_press(self, mask, x, y, verbose=False):
+ if verbose:
+ print "processing remote button press: %d, %d" % (x, y)
+ self.block_operation = 'click'
+
+ # Unselect things that may have been selected earlier
+ if self.selected_blk is not None:
+ self._unselect_block()
+ self.selected_turtle = None
+ # Always hide the status layer on a click
+ if self.status_spr is not None:
+ self.status_spr.hide()
+
+ # Find out what was clicked
+ spr = self.sprite_list.find_sprite((x, y))
+ self.dx = 0
+ self.dy = 0
+ if spr is None:
+ return True
+ self.selected_spr = spr
+
+ # From the sprite at x, y, look for a corresponding block
+ blk = self.block_list.spr_to_block(spr)
+ if blk is not None:
+ if blk.type == 'block':
+ self.selected_blk = blk
+ self._block_pressed(x, y, blk)
+ elif blk.type == 'trash':
+ self._restore_from_trash(find_top_block(blk))
+ elif blk.type == 'proto':
+ if blk.name == 'restoreall':
+ self._restore_all_from_trash()
+ elif blk.name == 'restore':
+ self._restore_latest_from_trash()
+ elif blk.name == 'empty':
+ self._empty_trash()
+ elif MACROS.has_key(blk.name):
+ self._new_macro(blk.name, x + 20, y + 20)
+ else:
+ blk.highlight()
+ self._new_block(blk.name, x, y)
+ blk.unhighlight()
+ return True
+
+ # Next, look for a turtle
+ t = self.turtles.spr_to_turtle(spr)
+ if t is not None:
+ self.selected_turtle = t
+ self.canvas.set_turtle(self.turtles.get_turtle_key(t))
+ self._turtle_pressed(x, y)
+ return True
+
+ # Finally, check for anything else
+ if hasattr(spr, 'type'):
+ if spr.type == "canvas":
+ pass
+ # spr.set_layer(CANVAS_LAYER)
+ elif spr.type == 'selector':
+ self._select_category(spr)
+ elif spr.type == 'category':
+ if hide_button_hit(spr, x, y):
+ self.hideshow_palette(False)
+ elif spr.type == 'palette':
+ if spr.name == _('next'):
+ i = self.selected_palette + 1
+ if i == len(PALETTE_NAMES):
+ i = 0
+ if self.activity is None or \
+ not self.activity.new_sugar_system:
+ self._select_category(self.selectors[i])
+ else:
+ if self.selected_palette is not None:
+ self.activity.palette_buttons[
+ self.selected_palette].set_icon(
+ PALETTE_NAMES[self.selected_palette] + 'off')
+ self.activity.palette_buttons[i].set_icon(
+ PALETTE_NAMES[i] + 'on')
+ self.show_palette(i)
+ else:
+ self.orientation = 1 - self.orientation
+ self.palette_button[self.orientation].set_layer(TAB_LAYER)
+ self.palette_button[1 - self.orientation].hide()
+ self.palette_sprs[self.selected_palette][
+ 1 - self.orientation].hide()
+ self._layout_palette(self.selected_palette)
+ self.show_palette(self.selected_palette)
+ elif spr.type == 'toolbar':
+ self._select_toolbar_button(spr)
+ return True
+
+ def _select_category(self, spr):
+ """ Select a category from the toolbar (old Sugar systems only). """
+ i = self.selectors.index(spr)
+ spr.set_shape(self.selector_shapes[i][1])
+ if self.selected_selector is not None:
+ j = self.selectors.index(self.selected_selector)
+ if i == j:
+ return
+ self.selected_selector.set_shape(self.selector_shapes[j][0])
+ self.previous_selector = self.selected_selector
+ self.selected_selector = spr
+ self.show_palette(i)
+
+ def _select_toolbar_button(self, spr):
+ """ Select a toolbar button (Used when not running Sugar). """
+ if not hasattr(spr, 'name'):
+ return
+ if spr.name == 'run-fastoff':
+ self.lc.trace = 0
+ self.run_button(0)
+ elif spr.name == 'run-slowoff':
+ self.lc.trace = 0
+ self.run_button(3)
+ elif spr.name == 'debugoff':
+ self.lc.trace = 1
+ self.run_button(6)
+ elif spr.name == 'stopiton':
+ self.stop_button()
+ self.toolbar_shapes['stopiton'].hide()
+ elif spr.name == 'eraseron':
+ self.eraser_button()
+ elif spr.name == 'hideshowoff':
+ self.hideshow_button()
+
+ def _put_in_trash(self, blk, x=0, y=0):
+ """ Put a group of blocks into the trash. """
+ self.trash_stack.append(blk)
+ group = find_group(blk)
+ for gblk in group:
+ if gblk.status == 'collapsed':
+ # Collapsed stacks are restored for rescaling
+ # and then recollapsed after they are moved to the trash.
+ bot = find_sandwich_bottom(gblk)
+ if collapsed(bot):
+ dy = bot.values[0]
+ restore_stack(find_sandwich_top(gblk))
+ bot.values[0] = dy
+ gblk.type = 'trash'
+ gblk.rescale(self.trash_scale)
+ blk.spr.move((x, y))
+ for gblk in group:
+ self._adjust_dock_positions(gblk)
+
+ # Re-collapsing any stacks we had restored for scaling
+ for gblk in group:
+ if collapsed(gblk):
+ collapse_stack(find_sandwich_top(gblk))
+
+ # And resize any skins.
+ for gblk in group:
+ if gblk.name in BLOCKS_WITH_SKIN:
+ self._resize_skin(gblk)
+
+ # self.show_palette(self.trash_index)
+ if self.selected_palette != self.trash_index:
+ for gblk in group:
+ gblk.spr.hide()
+
+ def _restore_all_from_trash(self):
+ """ Restore all the blocks in the trash can. """
+ for blk in self.block_list.list:
+ if blk.type == 'trash':
+ self._restore_from_trash(blk)
+
+ def _restore_latest_from_trash(self):
+ """ Restore most recent blocks from the trash can. """
+ if len(self.trash_stack) == 0:
+ return
+ self._restore_from_trash(self.trash_stack[len(self.trash_stack) - 1])
+
+ def _restore_from_trash(self, blk):
+ group = find_group(blk)
+ for gblk in group:
+ gblk.rescale(self.block_scale)
+ gblk.spr.set_layer(BLOCK_LAYER)
+ x, y = gblk.spr.get_xy()
+ if self.orientation == 0:
+ gblk.spr.move((x, y + PALETTE_HEIGHT + self.toolbar_offset))
+ else:
+ gblk.spr.move((x + PALETTE_WIDTH, y))
+ gblk.type = 'block'
+ for gblk in group:
+ self._adjust_dock_positions(gblk)
+ # If the stack had been collapsed before going into the trash,
+ # collapse it again now.
+ for gblk in group:
+ if collapsed(gblk):
+ collapse_stack(find_sandwich_top(gblk))
+ # And resize any skins.
+ for gblk in group:
+ if gblk.name in BLOCKS_WITH_SKIN:
+ self._resize_skin(gblk)
+
+ self.trash_stack.remove(blk)
+
+ def _empty_trash(self):
+ """ Permanently remove all blocks presently in the trash can. """
+ for blk in self.block_list.list:
+ if blk.type == 'trash':
+ blk.type = 'deleted'
+ blk.spr.hide()
+ self.trash_stack = []
+
+ def _in_the_trash(self, x, y):
+ """ Is x, y over the trash can? """
+ """
+ if self.selected_palette == self.trash_index and \
+ self.palette_sprs[self.trash_index][self.orientation].hit((x, y)):
+ return True
+ """
+ if self.selected_palette is not None and\
+ self.palette_sprs[self.selected_palette][self.orientation].hit(
+ (x, y)):
+ return True
+ return False
+
+ def _block_pressed(self, x, y, blk):
+ """ Block pressed """
+ if blk is not None:
+ blk.highlight()
+ self._disconnect(blk)
+ self.drag_group = find_group(blk)
+ (sx, sy) = blk.spr.get_xy()
+ self.drag_pos = x-sx, y-sy
+ for blk in self.drag_group:
+ if blk.status != 'collapsed':
+ blk.spr.set_layer(TOP_LAYER)
+ self.saved_string = blk.spr.labels[0]
+
+ def _unselect_block(self):
+ """ Unselect block """
+ # After unselecting a 'number' block, we need to check its value
+ if self.selected_blk.name == 'number':
+ self._number_check()
+ elif self.selected_blk.name == 'string':
+ self._string_check()
+ self.selected_blk.unhighlight()
+ self.selected_blk = None
+
+ def _new_block(self, name, x, y):
+ """ Make a new block. """
+ if name in CONTENT_BLOCKS:
+ newblk = Block(self.block_list, self.sprite_list, name, x - 20,
+ y - 20, 'block', DEFAULTS[name], self.block_scale)
+ else:
+ newblk = Block(self.block_list, self.sprite_list, name, x - 20,
+ y - 20, 'block', [], self.block_scale)
+
+ # Add a 'skin' to some blocks
+ if name in PYTHON_SKIN:
+ if self.nop == 'pythonloaded':
+ self._block_skin('pythonon', newblk)
else:
- if timeout_tag[0] > 0:
+ self._block_skin('pythonoff', newblk)
+ elif name in BOX_STYLE_MEDIA:
+ self._block_skin(name+'off', newblk)
+
+ newspr = newblk.spr
+ newspr.set_layer(TOP_LAYER)
+ self.drag_pos = 20, 20
+ newblk.connections = [None]*len(newblk.docks)
+ if DEFAULTS.has_key(newblk.name):
+ for i, argvalue in enumerate(DEFAULTS[newblk.name]):
+ # skip the first dock position since it is always a connector
+ dock = newblk.docks[i + 1]
+ argname = dock[0]
+ if argname == 'unavailable':
+ continue
+ if argname == 'media':
+ argname = 'journal'
+ elif argname == 'number' and \
+ (type(argvalue) is str or type(argvalue) is unicode):
+ argname = 'string'
+ elif argname == 'bool':
+ argname = argvalue
+ elif argname == 'flow':
+ argname = argvalue
+ (sx, sy) = newspr.get_xy()
+ if argname is not None:
+ if argname in CONTENT_BLOCKS:
+ argblk = Block(self.block_list, self.sprite_list,
+ argname, 0, 0, 'block', [argvalue],
+ self.block_scale)
+ else:
+ argblk = Block(self.block_list, self.sprite_list,
+ argname, 0, 0, 'block', [],
+ self.block_scale)
+ argdock = argblk.docks[0]
+ nx = sx + dock[2] - argdock[2]
+ ny = sy + dock[3] - argdock[3]
+ if argname == 'journal':
+ self._block_skin('journaloff', argblk)
+ argblk.spr.move((nx, ny))
+ argblk.spr.set_layer(TOP_LAYER)
+ argblk.connections = [newblk, None]
+ newblk.connections[i + 1] = argblk
+ self.drag_group = find_group(newblk)
+ self.block_operation = 'new'
+
+ def _new_macro(self, name, x, y):
+ """ Create a "macro" (predefined stack of blocks). """
+ macro = MACROS[name]
+ macro[0][2] = x
+ macro[0][3] = y
+ top = self.process_data(macro)
+ self.block_operation = 'new'
+ self._check_collapsibles(top)
+ self.drag_group = find_group(top)
+
+ def process_data(self, data):
+ """ Process data (from a macro, a file, or the clipboard). """
+ # Create the blocks (or turtle).
+ blocks = []
+ for blk in data:
+ if not self._found_a_turtle(blk):
+ blocks.append(self.load_block(blk))
+
+ # Make the connections.
+ for i in range(len(blocks)):
+ cons = []
+ # Normally, it is simply a matter of copying the connections.
+ if blocks[i].connections == None:
+ for c in data[i][4]:
+ if c is None:
+ cons.append(None)
+ else:
+ cons.append(blocks[c])
+ elif blocks[i].connections == 'check':
+ # Corner case to convert old-style boolean and arithmetic blocks
+ cons.append(None) # Add an extra connection.
+ for c in data[i][4]:
+ if c is None:
+ cons.append(None)
+ else:
+ cons.append(blocks[c])
+ # If the boolean op was connected, readjust the plumbing.
+ if blocks[i].name in BOOLEAN_STYLE:
+ if data[i][4][0] is not None:
+ c = data[i][4][0]
+ cons[0] = blocks[data[c][4][0]]
+ c0 = data[c][4][0]
+ for j, cj in enumerate(data[c0][4]):
+ if cj == c:
+ blocks[c0].connections[j] = blocks[i]
+ if c < i:
+ blocks[c].connections[0] = blocks[i]
+ blocks[c].connections[3] = None
+ else:
+ # Connection was to a block we haven't seen yet.
+ print "WARNING: dock check couldn't see the future"
+ else:
+ if data[i][4][0] is not None:
+ c = data[i][4][0]
+ cons[0] = blocks[data[c][4][0]]
+ c0 = data[c][4][0]
+ for j, cj in enumerate(data[c0][4]):
+ if cj == c:
+ blocks[c0].connections[j] = blocks[i]
+ if c < i:
+ blocks[c].connections[0] = blocks[i]
+ blocks[c].connections[1] = None
+ else:
+ # Connection was to a block we haven't seen yet.
+ print "WARNING: dock check couldn't see the future"
+ else:
+ print "WARNING: unknown connection state %s" % \
+ (str(blocks[i].connections))
+ blocks[i].connections = cons[:]
+
+ # Block sizes and shapes may have changed.
+ for blk in blocks:
+ self._adjust_dock_positions(blk)
+
+ # Look for any stacks that need to be collapsed or sandwiched
+ for blk in blocks:
+ if collapsed(blk):
+ collapse_stack(find_sandwich_top(blk))
+ elif blk.name == 'sandwichbottom' and collapsible(blk):
+ blk.svg.set_hide(True)
+ blk.svg.set_show(False)
+ blk.refresh()
+ grow_stack_arm(find_sandwich_top(blk))
+
+ if len(blocks) > 0:
+ return blocks[0]
+ else:
+ return None
+
+ def _adjust_dock_positions(self, blk):
+ """ Adjust the dock x, y positions """
+ (sx, sy) = blk.spr.get_xy()
+ for i, c in enumerate(blk.connections):
+ if i > 0 and c is not None:
+ bdock = blk.docks[i]
+ for j in range(len(c.docks)):
+ if c.connections[j] == blk:
+ cdock = c.docks[j]
+ nx = sx + bdock[2] - cdock[2]
+ ny = sy + bdock[3] - cdock[3]
+ c.spr.move((nx, ny))
+ self._adjust_dock_positions(c)
+
+ def _turtle_pressed(self, x, y):
+ """ Turtle pressed """
+ (tx, ty) = self.selected_turtle.get_xy()
+ dx = x - tx - 30
+ dy = y - ty - 30
+ if (dx * dx) + (dy * dy) > 200:
+ self.drag_turtle = ('turn',
+ self.canvas.heading - atan2(dy, dx)/DEGTOR, 0)
+ else:
+ self.drag_turtle = ('move', x - tx, y - ty)
+
+ def _move_cb(self, win, event):
+ """ Mouse move """
+ x, y = xy(event)
+ self._mouse_move(x, y)
+ return True
+
+ def _mouse_move(self, x, y, verbose=False, mdx=0, mdy=0):
+ if verbose:
+ print "processing remote mouse move: %d, %d" % (x, y)
+
+ self.block_operation = 'move'
+ # First, check to see if we are dragging or rotating a turtle.
+ if self.selected_turtle is not None:
+ dtype, dragx, dragy = self.drag_turtle
+ (sx, sy) = self.selected_turtle.get_xy()
+ if dtype == 'move':
+ if mdx != 0 or mdy != 0:
+ dx, dy = mdx, mdy
+ else:
+ dx = x - dragx - sx
+ dy = y - dragy - sy
+ self.selected_turtle.move((sx + dx, sy + dy))
+ else:
+ if mdx != 0 or mdy != 0:
+ dx = mdx
+ dy = mdy
+ else:
+ dx = x - sx - 30
+ dy = y - sy - 30
+ self.canvas.seth(int(dragx + atan2(dy, dx)/DEGTOR + 5)/10 * 10)
+ # If we are hoving, show popup help.
+ elif self.drag_group is None:
+ self._show_popup(x, y)
+ return
+ # If we have a stack of blocks selected, move them.
+ elif self.drag_group[0] is not None:
+ blk = self.drag_group[0]
+ # Don't move a bottom blk is the stack is collapsed
+ if collapsed(blk):
+ return
+
+ self.selected_spr = blk.spr
+ dragx, dragy = self.drag_pos
+ if mdx != 0 or mdy != 0:
+ dx = mdx
+ dy = mdy
+ else:
+ (sx, sy) = blk.spr.get_xy()
+ dx = x - dragx - sx
+ dy = y - dragy - sy
+
+ # Take no action if there was a move of 0,0.
+ if dx == 0 and dy == 0:
+ return
+ self.drag_group = find_group(blk)
+
+ # Prevent blocks from ending up with a negative x...
+ for gblk in self.drag_group:
+ (bx, by) = gblk.spr.get_xy()
+ if bx + dx < 0:
+ dx += -(bx + dx)
+ """
+ # ...or under the palette.
+ if self.selected_palette is not None and\
+ self.selected_palette != self.trash_index:
+ w, h = self.palette_sprs[self.selected_palette][
+ self.orientation].get_dimensions()
+ if self.orientation == HORIZONTAL_PALETTE:
+ if bx < w and\
+ by+dy < self.toolbar_offset+PALETTE_HEIGHT:
+ dy += -(by+dy)+self.toolbar_offset+PALETTE_HEIGHT
+ else:
+ if by < h+self.toolbar_offset and bx+dx < PALETTE_WIDTH:
+ dx += -(bx+dx)+PALETTE_WIDTH
+ """
+
+ # Move the stack.
+ for gblk in self.drag_group:
+ (bx, by) = gblk.spr.get_xy()
+ gblk.spr.move((bx + dx, by + dy))
+ if mdx != 0 or mdy != 0:
+ dx = 0
+ dy = 0
+ else:
+ self.dx += dx
+ self.dy += dy
+
+ def _show_popup(self, x, y):
+ """ Let's help our users by displaying a little help. """
+ spr = self.sprite_list.find_sprite((x, y))
+ blk = self.block_list.spr_to_block(spr)
+ if spr and blk is not None:
+ if self.timeout_tag[0] == 0:
+ self.timeout_tag[0] = self._do_show_popup(blk.name)
+ self.selected_spr = spr
+ else:
+ if self.timeout_tag[0] > 0:
try:
- gobject.source_remove(timeout_tag[0])
- timeout_tag[0] = 0
+ gobject.source_remove(self.timeout_tag[0])
+ self.timeout_tag[0] = 0
except:
- timeout_tag[0] = 0
- elif spr and spr.type == 'block':
- if timeout_tag[0] == 0:
- timeout_tag[0] = showPopup(spr.proto.name,tw)
- tw.spr = spr
+ self.timeout_tag[0] = 0
+ elif spr and hasattr(spr,'type') and (spr.type == 'selector' or\
+ spr.type == 'palette' or\
+ spr.type == 'toolbar'):
+ if self.timeout_tag[0] == 0 and hasattr(spr, 'name'):
+ self.timeout_tag[0] = self._do_show_popup(spr.name)
+ self.selected_spr = spr
else:
- if timeout_tag[0] > 0:
+ if self.timeout_tag[0] > 0:
try:
- gobject.source_remove(timeout_tag[0])
- timeout_tag[0] = 0
+ gobject.source_remove(self.timeout_tag[0])
+ self.timeout_tag[0] = 0
except:
- timeout_tag[0] = 0
+ self.timeout_tag[0] = 0
else:
- if timeout_tag[0] > 0:
+ if self.timeout_tag[0] > 0:
try:
- gobject.source_remove(timeout_tag[0])
- timeout_tag[0] = 0
+ gobject.source_remove(self.timeout_tag[0])
+ self.timeout_tag[0] = 0
except:
- timeout_tag[0] = 0
- return
- tw.block_operation = 'move'
- spr = tw.draggroup[0]
- if spr.type == 'block':
- tw.spr = spr
- dragx, dragy = tw.dragpos
- if mdx != 0 or mdy != 0:
- dx,dy = mdx,mdy
+ self.timeout_tag[0] = 0
+
+ def _do_show_popup(self, block_name):
+ """ Fetch the help text and display it. """
+ if SPECIAL_NAMES.has_key(block_name):
+ block_name_s = SPECIAL_NAMES[block_name]
+ elif BLOCK_NAMES.has_key(block_name):
+ block_name_s = BLOCK_NAMES[block_name][0]
+ elif block_name in TOOLBAR_SHAPES:
+ block_name_s = ''
else:
- dx,dy = x-dragx-spr.x,y-dragy-spr.y
- # skip if there was a move of 0,0
- if dx == 0 and dy == 0:
- return
- # drag entire stack if moving lock block
- if spr.proto.name == 'lock':
- tw.draggroup = findgroup(find_top_block(spr))
+ block_name_s = _(block_name)
+ if HELP_STRINGS.has_key(block_name):
+ if block_name_s == '':
+ label = HELP_STRINGS[block_name]
+ else:
+ label = block_name_s + ": " + HELP_STRINGS[block_name]
else:
- tw.draggroup = findgroup(spr)
- # check to see if any block ends up with a negative x
- for b in tw.draggroup:
- if b.x+dx < 0:
- dx += -(b.x+dx)
- # move the stack
- for b in tw.draggroup:
- move(b,(b.x+dx, b.y+dy))
- elif spr.type=='turtle':
- type,dragx,dragy = tw.dragpos
- if type == 'move':
- if mdx != 0 or mdy != 0:
- dx,dy = mdx,mdy
+ label = block_name_s
+ if self.running_sugar:
+ self.activity.hover_help_label.set_text(label)
+ self.activity.hover_help_label.show()
+ else:
+ self.win.set_title(_("Turtle Art") + " — " + label)
+ return 0
+
+ def _buttonrelease_cb(self, win, event):
+ """ Button release """
+ x, y = xy(event)
+ self.button_release(x, y)
+ return True
+
+ def button_release(self, x, y, verbose=False):
+ # We may have been moving the turtle
+ if self.selected_turtle is not None:
+ (tx, ty) = self.selected_turtle.get_xy()
+ (cx, cy) = self.canvas.canvas.get_xy()
+ # self.canvas.xcor = tx - self.canvas.canvas._width/2 + 30 - cx
+ # self.canvas.ycor = self.canvas.canvas._height/2 - ty - 30 + cy
+ self.canvas.xcor = tx - self.canvas.width/2 + 30 - cx
+ self.canvas.ycor = self.canvas.height/2 - ty - 30 + cy
+ self.canvas.move_turtle()
+ if self.running_sugar:
+ self.display_coordinates()
+ self.selected_turtle = None
+ return
+
+ # If we don't have a group of blocks, then there is nothing to do.
+ if self.drag_group == None:
+ return
+
+ blk = self.drag_group[0]
+ # Remove blocks by dragging them onto the trash palette.
+ if self.block_operation == 'move' and self._in_the_trash(x, y):
+ self._put_in_trash(blk, x, y)
+ self.drag_group = None
+ return
+
+ # Pull a stack of new blocks off of the category palette.
+ if self.block_operation == 'new':
+ for gblk in self.drag_group:
+ (bx, by) = gblk.spr.get_xy()
+ if self.orientation == 0:
+ gblk.spr.move((bx+20,
+ by+PALETTE_HEIGHT+self.toolbar_offset))
+ else:
+ gblk.spr.move((bx+PALETTE_WIDTH, by+20))
+
+ # Look to see if we can dock the current stack.
+ self._snap_to_dock()
+ self._check_collapsibles(blk)
+ for gblk in self.drag_group:
+ if gblk.status != 'collapsed':
+ gblk.spr.set_layer(BLOCK_LAYER)
+ self.drag_group = None
+
+ # Find the block we clicked on and process it.
+ if self.block_operation == 'click':
+ self._click_block(x, y)
+
+ def _click_block(self, x, y):
+ """ Click block """
+ blk = self.block_list.spr_to_block(self.selected_spr)
+ if blk is None:
+ return
+ self.selected_blk = blk
+ if blk.name == 'number' or blk.name == 'string':
+ self.saved_string = blk.spr.labels[0]
+ blk.spr.labels[0] += CURSOR
+ elif blk.name in BOX_STYLE_MEDIA:
+ self._import_from_journal(self.selected_blk)
+ if blk.name == 'journal' and self.running_sugar:
+ self._load_description_block(blk)
+ elif blk.name == 'identity2' or blk.name == 'hspace':
+ group = find_group(blk)
+ if hide_button_hit(blk.spr, x, y):
+ dx = blk.reset_x()
+ elif show_button_hit(blk.spr, x, y):
+ dx = 20
+ blk.expand_in_x(dx)
+ else:
+ dx = 0
+ for gblk in group:
+ if gblk != blk:
+ gblk.spr.move_relative((dx * blk.scale, 0))
+ elif blk.name == 'vspace':
+ group = find_group(blk)
+ if hide_button_hit(blk.spr, x, y):
+ dy = blk.reset_y()
+ elif show_button_hit(blk.spr, x, y):
+ dy = 20
+ blk.expand_in_y(dy)
+ else:
+ dy = 0
+ for gblk in group:
+ if gblk != blk:
+ gblk.spr.move_relative((0, dy * blk.scale))
+ grow_stack_arm(find_sandwich_top(blk))
+ elif blk.name in EXPANDABLE or blk.name == 'nop':
+ if show_button_hit(blk.spr, x, y):
+ n = len(blk.connections)
+ group = find_group(blk.connections[n-1])
+ if blk.name == 'myfunc1arg':
+ blk.spr.labels[1] = 'f(x, y)'
+ blk.spr.labels[2] = ' '
+ dy = blk.add_arg()
+ blk.primitive = 'myfunction2'
+ blk.name = 'myfunc2arg'
+ elif blk.name == 'myfunc2arg':
+ blk.spr.labels[1] = 'f(x, y, z)'
+ dy = blk.add_arg(False)
+ blk.primitive = 'myfunction3'
+ blk.name = 'myfunc3arg'
+ elif blk.name == 'userdefined':
+ dy = blk.add_arg()
+ blk.primitive = 'userdefined2'
+ blk.name = 'userdefined2args'
+ elif blk.name == 'userdefined2args':
+ dy = blk.add_arg(False)
+ blk.primitive = 'userdefined3'
+ blk.name = 'userdefined3args'
+ else:
+ dy = blk.add_arg()
+ for gblk in group:
+ gblk.spr.move_relative((0, dy))
+ blk.connections.append(blk.connections[n-1])
+ argname = blk.docks[n-1][0]
+ argvalue = DEFAULTS[blk.name][len(DEFAULTS[blk.name])-1]
+ argblk = Block(self.block_list, self.sprite_list, argname,
+ 0, 0, 'block', [argvalue], self.block_scale)
+ argdock = argblk.docks[0]
+ (bx, by) = blk.spr.get_xy()
+ nx = bx + blk.docks[n - 1][2] - argdock[2]
+ ny = by + blk.docks[n - 1][3] - argdock[3]
+ argblk.spr.move((nx, ny))
+ argblk.spr.set_layer(TOP_LAYER)
+ argblk.connections = [blk, None]
+ blk.connections[n - 1] = argblk
+ grow_stack_arm(find_sandwich_top(blk))
+ elif blk.name in PYTHON_SKIN and self.myblock == None:
+ self._import_py()
else:
- dx,dy = x-dragx-spr.x,y-dragy-spr.y
- move(spr, (spr.x+dx, spr.y+dy))
+ self._run_stack(blk)
+ elif blk.name in COLLAPSIBLE:
+ top = find_sandwich_top(blk)
+ if collapsed(blk):
+ restore_stack(top)
+ elif top is not None:
+ collapse_stack(top)
else:
- if mdx != 0 or mdy != 0:
- dx,dy = mdx,mdy
- else:
- dx,dy = x-spr.x-30,y-spr.y-30
- seth(tw.turtle, int(dragx+atan2(dy,dx)/DEGTOR+5)/10*10)
- if mdx != 0 or mdy != 0:
- dx,dy = 0,0
- else:
- tw.dx += dx
- tw.dy += dy
-
-#
-# Button release
-#
-
-def buttonrelease_cb(win, event, tw):
- x,y = xy(event)
- button_release(tw, x, y)
- if hasattr(tw, 'activity') and \
- hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None:
- # print "sending release button"
- tw.activity._send_event("r:"+str(x)+":"+str(y))
- return True
-
-def button_release(tw, x, y, verbose=False):
- if tw.dx != 0 or tw.dy != 0:
- if hasattr(tw, 'activity') and \
- hasattr(tw.activity, 'chattube') and \
- tw.activity.chattube is not None:
- if verbose:
- print "processing move: " + str(tw.dx) + " " + str(tw.dy)
- tw.activity._send_event("m:"+str(tw.dx)+":"+str(tw.dy))
- tw.dx = 0
- tw.dy = 0
- if verbose:
- print "processing remote button release: " + str(x) + " " + str(y)
- if tw.draggroup == None:
- return
- spr = tw.draggroup[0]
- if spr.type == 'turtle':
- tw.turtle.xcor = tw.turtle.spr.x-tw.turtle.canvas.x- \
- tw.turtle.canvas.width/2+30
- tw.turtle.ycor = tw.turtle.canvas.height/2-tw.turtle.spr.y+ \
- tw.turtle.canvas.y-30
- move_turtle(tw.turtle)
- display_coordinates(tw)
- tw.draggroup = None
- return
- if tw.block_operation=='move' and hit(tw.category_spr, (x,y)):
- for b in tw.draggroup: hide(b)
- tw.draggroup = None
- return
- if tw.block_operation=='new':
- for b in tw.draggroup:
- move(b, (b.x+200, b.y))
- snap_to_dock(tw)
- for b in tw.draggroup: setlayer(b,650)
- tw.draggroup = None
- if tw.block_operation=='click':
- if tw.spr.proto.name=='number':
- tw.selected_block = spr
- move(tw.select_mask, (spr.x-5,spr.y-5))
- setlayer(tw.select_mask, 660)
- tw.firstkey = True
- elif tw.defdict.has_key(spr.proto.name):
- tw.selected_block = spr
- if tw.spr.proto.name=='string':
- move(tw.select_mask_string, (spr.x-5,spr.y-5))
- setlayer(tw.select_mask_string, 660)
- tw.firstkey = True
- elif tw.spr.proto.name in importblocks:
- import_from_journal(tw, spr)
- elif tw.spr.proto.name=='nop' and tw.myblock==None:
- tw.activity.import_py()
- else: run_stack(tw, spr)
-
-def import_from_journal(tw, spr):
- if hasattr(tw,"activity"):
- chooser = ObjectChooser('Choose image', None,\
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
- try:
- result = chooser.run()
- if result == gtk.RESPONSE_ACCEPT:
- dsobject = chooser.get_selected_object()
- # change block graphic to indicate that object is "loaded"
- if spr.proto.name == 'journal':
- load_image(tw, dsobject, spr)
- elif spr.proto.name == 'audiooff':
- setimage(spr,tw.media_shapes['audioon'])
+ self._run_stack(blk)
+
+ def _check_collapsibles(self, blk):
+ """ Check the state of collapsible blocks upon change in dock state. """
+ group = find_group(blk)
+ for gblk in group:
+ if gblk.name in COLLAPSIBLE:
+ if collapsed(gblk):
+ gblk.svg.set_show(True)
+ gblk.svg.set_hide(False)
+ reset_stack_arm(find_sandwich_top(gblk))
+ elif collapsible(gblk):
+ gblk.svg.set_hide(True)
+ gblk.svg.set_show(False)
+ grow_stack_arm(find_sandwich_top(gblk))
else:
- setimage(spr, tw.media_shapes['decson'])
- spr.ds_id = dsobject.object_id
- dsobject.destroy()
- finally:
- chooser.destroy()
- del chooser
- else:
- print "Journal Object Chooser unavailable from outside of Sugar"
-
-# Replace Journal block graphic with preview image
-def load_image(tw, picture, spr):
- from talogo import get_pixbuf_from_journal
- pixbuf = get_pixbuf_from_journal(picture,spr.width,spr.height)
- if pixbuf is not None:
- setimage(spr, pixbuf)
- else:
- setimage(spr, tw.media_shapes['texton'])
-
-# change the icon for user-defined blocks after Python code is loaded
-def set_userdefined(tw):
- list = tw.sprites[:]
- for spr in list:
- if hasattr(spr,'proto') and spr.proto.name == 'nop':
- setimage(spr,tw.media_shapes['pythonloaded'])
- tw.nop = 'pythonloaded'
-
-def snap_to_dock(tw):
- d=200
- me = tw.draggroup[0]
- for mydockn in range(len(me.proto.docks)):
- for you in blocks(tw):
- if you in tw.draggroup:
- continue
- for yourdockn in range(len(you.proto.docks)):
- thisxy = dock_dx_dy(you,yourdockn,me,mydockn)
- if magnitude(thisxy)>d:
+ gblk.svg.set_hide(False)
+ gblk.svg.set_show(False)
+ # Ouch: When you tear off the sandwich bottom, you
+ # no longer have access to the group with the sandwich top
+ # so check them all.
+ for b in self.just_blocks():
+ if b.name == 'sandwichtop':
+ if find_sandwich_bottom(b) is None:
+ reset_stack_arm(b)
+ gblk.refresh()
+
+ def _run_stack(self, blk):
+ """ Run a stack of blocks. """
+ if blk is None:
+ return
+ self.lc.ag = None
+ top = find_top_block(blk)
+ self.lc.run_blocks(top, self.just_blocks(), True)
+ gobject.idle_add(self.lc.doevalstep)
+
+ def _snap_to_dock(self):
+ """ Snap a block to the dock of another block. """
+ my_block = self.drag_group[0]
+ d = 200
+ for my_dockn in range(len(my_block.docks)):
+ for your_block in self.just_blocks():
+ # don't link to a block to which you're already connected
+ if your_block in self.drag_group:
continue
- d=magnitude(thisxy)
- bestxy=thisxy
- bestyou=you
- bestyourdockn=yourdockn
- bestmydockn=mydockn
- if d<200:
- for b in tw.draggroup:
- move(b,(b.x+bestxy[0],b.y+bestxy[1]))
- blockindock=bestyou.connections[bestyourdockn]
- if blockindock!=None:
- for b in findgroup(blockindock):
- hide(b)
- bestyou.connections[bestyourdockn]=me
- me.connections[bestmydockn]=bestyou
-
-def dock_dx_dy(block1,dock1n,block2,dock2n):
- dock1 = block1.proto.docks[dock1n]
- dock2 = block2.proto.docks[dock2n]
- d1type,d1dir,d1x,d1y=dock1[0:4]
- d2type,d2dir,d2x,d2y=dock2[0:4]
- if (d2type!='num') or (dock2n!=0):
- if block1.connections[dock1n] != None:
- return (100,100)
- if block2.connections[dock2n] != None:
- return (100,100)
- if block1==block2: return (100,100)
- if d1type!=d2type:
- # some blocks can take strings or nums
- if block1.proto.name in ('write', 'plus2', 'equal', 'less', 'greater', \
- 'template1', 'template2', 'template3', \
- 'template4', 'template6', 'template7', 'nop', \
- 'print', 'stack'):
- if block1.proto.name == 'write' and d1type == 'string':
- if d2type == 'num' or d2type == 'string':
- pass
- else:
- if d2type == 'num' or d2type == 'string':
- pass
- # some blocks can take strings, nums, or Journal
- elif block1.proto.name in ('show', 'push', 'storein', 'storeinbox1', \
- 'storeinbox2'):
- if d2type == 'num' or d2type == 'string' or d2type == 'journal':
- pass
- # some blocks can take media, audio, movies, of descriptions
- elif block1.proto.name in ('containter'):
- if d1type == 'audiooff' or d1type == 'journal':
- pass
+ # check each dock of your_block for a possible connection
+ for your_dockn in range(len(your_block.docks)):
+ this_xy = dock_dx_dy(your_block, your_dockn,
+ my_block, my_dockn)
+ if magnitude(this_xy) > d:
+ continue
+ d = magnitude(this_xy)
+ best_xy = this_xy
+ best_you = your_block
+ best_your_dockn = your_dockn
+ best_my_dockn = my_dockn
+ if d < 200:
+ if not arithmetic_check(my_block, best_you, best_my_dockn,
+ best_your_dockn):
+ return
+ for blk in self.drag_group:
+ (sx, sy) = blk.spr.get_xy()
+ blk.spr.move((sx + best_xy[0], sy + best_xy[1]))
+
+ # If there was already a block docked there, move it to the trash.
+ blk_in_dock = best_you.connections[best_your_dockn]
+ if blk_in_dock is not None:
+ blk_in_dock.connections[0] = None
+ self._put_in_trash(blk_in_dock)
+
+ best_you.connections[best_your_dockn] = my_block
+ if my_block.connections is not None:
+ my_block.connections[best_my_dockn] = best_you
+
+ def _import_from_journal(self, blk):
+ """ Import a file from the Sugar Journal """
+ if self.running_sugar:
+ chooser = ObjectChooser('Choose image', None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
+ try:
+ result = chooser.run()
+ if result == gtk.RESPONSE_ACCEPT:
+ dsobject = chooser.get_selected_object()
+ self._update_media_icon(blk, dsobject, dsobject.object_id)
+ dsobject.destroy()
+ finally:
+ chooser.destroy()
+ del chooser
+ else:
+ fname, self.load_save_folder = \
+ get_load_name('.*', self.load_save_folder)
+ if fname is None:
+ return
+ self._update_media_icon(blk, fname)
+
+ def _load_description_block(self, blk):
+ """ Look for a corresponding description block """
+ if blk == None or blk.name != 'journal' or len(blk.values) == 0 or\
+ blk.connections[0] is None:
+ return
+ _blk = blk.connections[0]
+ dblk = find_blk_below(_blk, 'description')
+ # Autoupdate the block if it is empty
+ if dblk != None and (len(dblk.values) == 0 or dblk.values[0] == None):
+ self._update_media_icon(dblk, None, blk.values[0])
+
+ def _update_media_icon(self, blk, name, value=''):
+ """ Update the icon on a 'loaded' media block. """
+ if blk.name == 'journal':
+ self._load_image_thumb(name, blk)
+ elif blk.name == 'audio':
+ self._block_skin('audioon', blk)
+ else:
+ self._block_skin('descriptionon', blk)
+ if value == '':
+ value = name
+ if len(blk.values) > 0:
+ blk.values[0] = value
+ else:
+ blk.values.append(value)
+ blk.spr.set_label(' ')
+
+ def _load_image_thumb(self, picture, blk):
+ """ Replace icon with a preview image. """
+ pixbuf = None
+ self._block_skin('descriptionon', blk)
+
+ if self.running_sugar:
+ w, h = calc_image_size(blk.spr)
+ pixbuf = get_pixbuf_from_journal(picture, w, h)
+ else:
+ if movie_media_type(picture):
+ self._block_skin('journalon', blk)
+ elif audio_media_type(picture):
+ self._block_skin('audioon', blk)
+ blk.name = 'audio'
+ elif image_media_type(picture):
+ w, h = calc_image_size(blk.spr)
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(picture, w, h)
+ else:
+ blk.name = 'description'
+ if pixbuf is not None:
+ x, y = self._calc_image_offset('', blk.spr)
+ blk.set_image(pixbuf, x, y)
+ self._resize_skin(blk)
+
+ def _disconnect(self, blk):
+ """ Disconnect block from stack above it. """
+ if blk.connections[0] == None:
+ return
+ if collapsed(blk):
+ return
+ blk2 = blk.connections[0]
+ blk2.connections[blk2.connections.index(blk)] = None
+ blk.connections[0] = None
+
+ def _keypress_cb(self, area, event):
+ """ Keyboard """
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ keyunicode = gtk.gdk.keyval_to_unicode(event.keyval)
+
+ if event.get_state()&gtk.gdk.MOD1_MASK:
+ alt_mask = True
+ alt_flag = 'T'
else:
- return (100,100)
- if d1dir==d2dir:
- return (100,100)
- return (block1.x+d1x)-(block2.x+d2x),(block1.y+d1y)-(block2.y+d2y)
-
-def magnitude(pos):
- x,y = pos
- return x*x+y*y
-
-#
-# Repaint
-#
-
-def expose_cb(win, event, tw):
- redrawsprites(tw)
- return True
-
-#
-# Keyboard
-#
-
-def keypress_cb(area, event, tw):
- keyname = gtk.gdk.keyval_name(event.keyval)
-# keyunicode = unichr(gtk.gdk.keyval_to_unicode(event.keyval)).replace("\x00","")
- keyunicode = gtk.gdk.keyval_to_unicode(event.keyval)
-# print keyname
-# if keyunicode > 0:
-# print unichr(keyunicode)
-
- if event.get_state()&gtk.gdk.MOD1_MASK:
- alt_mask = True
- else:
- alt_mask = False
- results = key_press(tw, alt_mask, keyname, keyunicode)
- if keyname is not None and hasattr(tw,"activity") and \
- hasattr(tw.activity, 'chattube') and tw.activity.chattube is not None:
- # print "key press"
- if alt_mask:
- tw.activity._send_event("k:"+'T'+":"+keyname+":"+str(keyunicode))
- else:
- tw.activity._send_event("k:"+'F'+":"+keyname+":"+str(keyunicode))
- return keyname
-'''
- if len(keyname)>1:
- # print "(" + keyunicode.encode("utf-8") + ")"
+ alt_mask = False
+ alt_flag = 'F'
+ self._key_press(alt_mask, keyname, keyunicode)
return keyname
- else:
- # print "[" + keyunicode.encode("utf-8") + "]"
- return keyunicode.encode("utf-8")
-'''
-def key_press(tw, alt_mask, keyname, keyunicode, verbose=False):
- if keyname is None:
- return False
- if verbose:
- print "processing remote key press: " + keyname
- tw.keypress = keyname
- if alt_mask is True and tw.selected_block==None:
- if keyname=="i" and hasattr(tw, 'activity'):
- tw.activity.waiting_for_blocks = True
- tw.activity._send_event("i") # request sync for sharing
- elif keyname=="p":
- hideshow_button(tw)
- elif keyname=='q':
- exit()
- return True
- if tw.selected_block is not None and \
- tw.selected_block.proto.name == 'number':
- if keyname in ['minus', 'period']:
- keyname = {'minus': '-', 'period': '.'}[keyname]
- oldnum = tw.selected_block.label
- selblock=tw.selected_block.proto
- if keyname == 'BackSpace':
- if len(oldnum) > 1:
+
+ def _key_press(self, alt_mask, keyname, keyunicode, verbose=False):
+ if keyname is None:
+ return False
+ if verbose:
+ print "processing remote key press: %s" % (keyname)
+
+ self.keypress = keyname
+
+ # First, process Alt keys.
+ if alt_mask and self.selected_blk is not None:
+ if keyname == "p":
+ self.hideshow_button()
+ elif keyname == 'q':
+ exit()
+ return True
+ # Process keyboard input for 'number' blocks
+ if self.selected_blk is not None and \
+ self.selected_blk.name == 'number':
+ self._process_numeric_input(keyname)
+ return True
+ # Process keyboard input for 'string' blocks
+ elif self.selected_blk is not None and \
+ self.selected_blk.name == 'string':
+ self.process_alphanumeric_input(keyname, keyunicode)
+ if self.selected_blk is not None:
+ self.selected_blk.resize()
+ return True
+ # Otherwise, use keyboard input to move blocks or turtles
+ else:
+ self._process_keyboard_commands(keyname)
+ if self.selected_blk is None:
+ return False
+
+ def _process_numeric_input(self, keyname):
+ ''' Make sure numeric input is valid. '''
+ oldnum = self.selected_blk.spr.labels[0].replace(CURSOR, '')
+ if len(oldnum) == 0:
+ oldnum = '0'
+ if keyname == 'minus':
+ if oldnum == '0':
+ newnum = '-'
+ elif oldnum[0] != '-':
+ newnum = '-' + oldnum
+ else:
+ newnum = oldnum
+ elif keyname == 'period' and '.' not in oldnum:
+ newnum = oldnum + '.'
+ elif keyname == 'BackSpace':
+ if len(oldnum) > 0:
newnum = oldnum[:len(oldnum)-1]
else:
newnum = ''
- setlabel(tw.selected_block, selblock.check(newnum,oldnum))
- if len(newnum) > 0:
- tw.firstkey = False
+ elif keyname in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
+ if oldnum == '0':
+ newnum = keyname
+ else:
+ newnum = oldnum + keyname
+ elif keyname == 'Return':
+ self._unselect_block()
+ return
+ else:
+ newnum = oldnum
+ if newnum == '.':
+ newnum = '0.'
+ if len(newnum) > 0 and newnum != '-':
+ try:
+ float(newnum)
+ except ValueError, e:
+ newnum = oldnum
+ self.selected_blk.spr.set_label(newnum + CURSOR)
+
+ def process_alphanumeric_input(self, keyname, keyunicode):
+ """ Make sure alphanumeric input is properly parsed. """
+ if len(self.selected_blk.spr.labels[0]) > 0:
+ c = self.selected_blk.spr.labels[0].count(CURSOR)
+ if c == 0:
+ oldleft = self.selected_blk.spr.labels[0]
+ oldright = ''
+ elif len(self.selected_blk.spr.labels[0]) == 1:
+ oldleft = ''
+ oldright = ''
else:
- tw.firstkey = True
- if len(keyname)>1:
+ try: # Why are getting a ValueError on occasion?
+ oldleft, oldright = \
+ self.selected_blk.spr.labels[0].split(CURSOR)
+ except ValueError:
+ print "[%s]" % self.selected_blk.spr.labels[0]
+ oldleft = self.selected_blk.spr.labels[0]
+ oldright = ''
+ else:
+ oldleft = ''
+ oldright = ''
+ newleft = oldleft
+ if keyname in ['Shift_L', 'Shift_R', 'Control_L', 'Caps_Lock', \
+ 'Alt_L', 'Alt_R', 'KP_Enter', 'ISO_Level3_Shift']:
+ keyname = ''
+ keyunicode = 0
+ # Hack until I sort out input and unicode and dead keys,
+ if keyname[0:5] == 'dead_':
+ self.dead_key = keyname
+ keyname = ''
+ keyunicode = 0
+ if keyname == 'space':
+ keyunicode = 32
+ elif keyname == 'Tab':
+ keyunicode = 9
+ if keyname == 'BackSpace':
+ if len(oldleft) > 1:
+ newleft = oldleft[:len(oldleft)-1]
+ else:
+ newleft = ''
+ elif keyname == 'Home':
+ oldright = oldleft+oldright
+ newleft = ''
+ elif keyname == 'Left':
+ if len(oldleft) > 0:
+ oldright = oldleft[len(oldleft)-1:]+oldright
+ newleft = oldleft[:len(oldleft)-1]
+ elif keyname == 'Right':
+ if len(oldright) > 0:
+ newleft = oldleft + oldright[0]
+ oldright = oldright[1:]
+ elif keyname == 'End':
+ newleft = oldleft+oldright
+ oldright = ''
+ elif keyname == 'Return':
+ newleft = oldleft+RETURN
+ elif keyname == 'Down':
+ self._unselect_block()
+ return
+ elif keyname == 'Up' or keyname == 'Escape': # Restore previous state
+ self.selected_blk.spr.set_label(self.saved_string)
+ self._unselect_block()
+ return
+ else:
+ if self.dead_key is not '':
+ keyunicode = \
+ DEAD_DICTS[DEAD_KEYS.index(self.dead_key[5:])][keyname]
+ self.dead_key = ''
+ if keyunicode > 0:
+ if unichr(keyunicode) != '\x00':
+ newleft = oldleft+unichr(keyunicode)
+ else:
+ newleft = oldleft
+ elif keyunicode == -1: # clipboard text
+ if keyname == '\n':
+ newleft = oldleft+RETURN
+ else:
+ newleft = oldleft+keyname
+ self.selected_blk.spr.set_label("%s%s%s" % (newleft, CURSOR, oldright))
+
+ def _process_keyboard_commands(self, keyname):
+ """ Use the keyboard to move blocks and turtle """
+ mov_dict = {'KP_Up':[0, 10], 'j':[0, 10], 'Up':[0, 10],
+ 'KP_Down':[0, -10], 'k':[0, -10], 'Down':[0, -10],
+ 'KP_Left':[-10, 0], 'h':[-10, 0], 'Left':[-10, 0],
+ 'KP_Right':[10, 0], 'l':[10, 0], 'Right':[10, 0],
+ 'KP_Page_Down':[0, 0], 'KP_Page_Up':[0, 0], 'KP_End':[0, 0],
+ 'KP_Home':[-1, -1], 'Return':[-1, -1], 'Esc':[0, 0]}
+ if not mov_dict.has_key(keyname):
+ return
+ if keyname == 'KP_End':
+ self.run_button(0)
+ elif self.selected_spr is not None:
+ blk = self.block_list.spr_to_block(self.selected_spr)
+ tur = self.turtles.spr_to_turtle(self.selected_spr)
+ if not self.lc.running and blk is not None:
+ if keyname == 'Return' or keyname == 'KP_Page_Up':
+ (x, y) = blk.spr.get_xy()
+ self._click_block(x, y)
+ elif keyname == 'KP_Page_Down':
+ if self.drag_group == None:
+ self.drag_group = find_group(blk)
+ for gblk in self.drag_group:
+ gblk.spr.hide()
+ self.drag_group = None
+ else:
+ self._jog_block(blk, mov_dict[keyname][0],
+ mov_dict[keyname][1])
+ elif tur is not None:
+ self._jog_turtle(mov_dict[keyname][0], mov_dict[keyname][1])
+ return True
+
+ def _jog_turtle(self, dx, dy):
+ """ Jog turtle """
+ if dx == -1 and dy == -1:
+ self.canvas.xcor = 0
+ self.canvas.ycor = 0
+ else:
+ self.canvas.xcor += dx
+ self.canvas.ycor += dy
+ self.canvas.move_turtle()
+ self.display_coordinates()
+ self.selected_turtle = None
+
+ def _jog_block(self, blk, dx, dy):
+ """ Jog block """
+ if collapsed(blk):
+ return
+ self.drag_group = find_group(blk)
+ # check to see if any block ends up with a negative x
+ for blk in self.drag_group:
+ (sx, sy) = blk.spr.get_xy()
+ if sx+dx < 0:
+ dx += -(sx + dx)
+ # move the stack
+ for blk in self.drag_group:
+ (sx, sy) = blk.spr.get_xy()
+ blk.spr.move((sx + dx, sy - dy))
+ self._snap_to_dock()
+ self.drag_group = None
+
+ def _number_check(self):
+ """ Make sure a 'number' block contains a number. """
+ n = self.selected_blk.spr.labels[0].replace(CURSOR, '')
+ if n in ['-', '.', '-.']:
+ n = 0
+ if n is not None:
+ try:
+ f = float(n)
+ if f > 1000000:
+ n = 1
+ self.showlabel("#overflowerror")
+ elif f < -1000000:
+ n = -1
+ self.showlabel("#overflowerror")
+ except ValueError:
+ n = 0
+ self.showlabel("#notanumber")
+ else:
+ n = 0
+ self.selected_blk.spr.set_label(n)
+ self.selected_blk.values[0] = n
+
+ def _string_check(self):
+ s = self.selected_blk.spr.labels[0].replace(CURSOR, '')
+ self.selected_blk.spr.set_label(s)
+ self.selected_blk.values[0] = s.replace(RETURN, "\12")
+
+ def load_python_code(self):
+ """ Load Python code from a file """
+ fname, self.load_save_folder = get_load_name('.py',
+ self.load_save_folder)
+ if fname == None:
+ return
+ f = open(fname, 'r')
+ self.myblock = f.read()
+ f.close()
+
+ def _import_py(self):
+ """ Import Python code into a block """
+ if self.running_sugar:
+ self.activity.import_py()
+ else:
+ self.load_python_code()
+ self.set_userdefined()
+
+ def new_project(self):
+ """ Start a new project """
+ stop_logo(self)
+ # Put current project in the trash.
+ while len(self.just_blocks()) > 0:
+ blk = self.just_blocks()[0]
+ top = find_top_block(blk)
+ self._put_in_trash(top)
+ self.canvas.clearscreen()
+ self.save_file_name = None
+
+ def load_files(self, ta_file, create_new_project=True):
+ """ Load a project from a file """
+ if create_new_project:
+ self.new_project()
+ self._check_collapsibles(self.process_data(data_from_file(ta_file)))
+
+ def load_file(self, create_new_project=True):
+ _file_name, self.load_save_folder = get_load_name('.ta',
+ self.load_save_folder)
+ if _file_name == None:
+ return
+ if _file_name[-3:] == '.ta':
+ _file_name = _file_name[0:-3]
+ self.load_files(_file_name+'.ta', create_new_project)
+ if create_new_project:
+ self.save_file_name = os.path.basename(_file_name)
+ if self.running_sugar:
+ self.activity.metadata['title'] = os.path.split(_file_name)[1]
+
+ def _found_a_turtle(self, blk):
+ """ Either [-1, 'turtle', ...] or [-1, ['turtle', key], ...] """
+ if blk[1] == 'turtle':
+ self.load_turtle(blk)
return True
- else: # gtk.keysyms.Left ...
- if keyname in ['Escape', 'Return', 'KP_Page_Up',
- 'Up', 'Down', 'Left', 'Right', 'KP_Home', 'KP_End',
- 'KP_Up', 'KP_Down', 'KP_Left', 'KP_Right',
- 'KP_Page_Down']:
- # move blocks (except number and text blocks only with arrows)
- # or click with Return
- if keyname == 'KP_End':
- run_button(tw, 0)
- elif tw.spr is not None:
- if tw.spr.type == 'turtle': # jog turtle with arrow keys
- if keyname == 'KP_Up' or keyname == 'Up':
- jog_turtle(tw,0,10)
- elif keyname == 'KP_Down' or keyname == 'Down':
- jog_turtle(tw,0,-10)
- elif keyname == 'KP_Left' or keyname == 'Left':
- jog_turtle(tw,-10,0)
- elif keyname == 'KP_Right' or keyname == 'Right':
- jog_turtle(tw,10,0)
- elif keyname == 'KP_Home':
- jog_turtle(tw,-1,-1)
- elif tw.spr.type == 'block':
- if keyname == 'Return' or keyname == 'KP_Page_Up':
- click_block(tw)
- elif keyname == 'KP_Up' or keyname == 'Up':
- jog_block(tw,0,10)
- elif keyname == 'KP_Down' or keyname == 'Down':
- jog_block(tw,0,-10)
- elif keyname == 'KP_Left' or keyname == 'Left':
- jog_block(tw,-10,0)
- elif keyname == 'KP_Right' or keyname == 'Right':
- jog_block(tw,10,0)
- elif keyname == 'KP_Page_Down':
- if tw.draggroup == None:
- tw.draggroup = findgroup(tw.spr)
- for b in tw.draggroup: hide(b)
- tw.draggroup = None
- elif tw.spr.type == 'selbutton':
- if keyname == 'Return' or keyname == 'KP_Page_Up':
- select_category(tw,tw.spr)
- elif tw.spr.type == 'category':
- if keyname == 'Return' or keyname == 'KP_Page_Up':
- (x,y) = tw.window.get_pointer()
- block_selector_pressed(tw,x,y)
- for b in tw.draggroup:
- move(b, (b.x+200, b.y))
- tw.draggroup = None
+ elif type(blk[1]) == list and blk[1][0] == 'turtle':
+ self.load_turtle(blk, blk[1][1])
return True
- if tw.selected_block is None:
+ elif type(blk[1]) == tuple:
+ _btype, _key = blk[1]
+ if _btype == 'turtle':
+ self.load_turtle(blk, _key)
+ return True
return False
- if keyname in ['Shift_L', 'Shift_R', 'Control_L', 'Caps_Lock', \
- 'Alt_L', 'Alt_R', 'KP_Enter', 'ISO_Level3_Shift']:
- keyname = ''
- keyunicode = 0
- # Hack until I sort out input and unicode + dead keys
- if keyname[0:5] == 'dead_':
- tw.dead_key = keyname
- keyname = ''
- keyunicode = 0
- if keyname == 'Tab':
- keyunicode = 32 # substitute a space for a tab
- oldnum = tw.selected_block.label
- selblock=tw.selected_block.proto
- if keyname == 'BackSpace':
- if len(oldnum) > 1:
- newnum = oldnum[:len(oldnum)-1]
- else:
- newnum = ''
- setlabel(tw.selected_block, selblock.check(newnum,oldnum))
- if len(newnum) > 0:
- tw.firstkey = False
- else:
- tw.firstkey = True
- elif keyname is not '':
- # Hack until I sort out input and unicode + dead keys
- if tw.dead_key == 'dead_grave':
- keyunicode = dead_grave[keyname]
- elif tw.dead_key == 'dead_acute':
- keyunicode = dead_acute[keyname]
- elif tw.dead_key == 'dead_circumflex':
- keyunicode = dead_circumflex[keyname]
- elif tw.dead_key == 'dead_tilde':
- keyunicode = dead_tilde[keyname]
- elif tw.dead_key == 'dead_diaeresis':
- keyunicode = dead_diaeresis[keyname]
- elif tw.dead_key == 'dead_abovering':
- keyunicode = dead_abovering[keyname]
- tw.dead_key = ""
- if tw.firstkey:
- newnum = selblock.check(unichr(keyunicode), \
- tw.defdict[selblock.name])
- elif keyunicode > 0:
- if unichr(keyunicode) is not '\x00':
- newnum = oldnum+unichr(keyunicode)
+
+ def load_turtle(self, blk, key=1):
+ """ Restore a turtle from its saved state """
+ tid, name, xcor, ycor, heading, color, shade, pensize = blk
+ self.canvas.set_turtle(key)
+ self.canvas.setxy(xcor, ycor)
+ self.canvas.seth(heading)
+ self.canvas.setcolor(color)
+ self.canvas.setshade(shade)
+ self.canvas.setpensize(pensize)
+
+ def load_block(self, b):
+ """ Restore individual blocks from saved state """
+ # A block is saved as: (i, (btype, value), x, y, (c0,... cn))
+ # The x, y position is saved/loaded for backward compatibility
+ btype, value = b[1], None
+ if type(btype) == tuple:
+ btype, value = btype
+ elif type(btype) == list:
+ btype, value = btype[0], btype[1]
+ if btype in CONTENT_BLOCKS or btype in COLLAPSIBLE:
+ if btype == 'number':
+ try:
+ values = [int(value)]
+ except ValueError:
+ values = [float(value)]
+ elif btype in COLLAPSIBLE:
+ if value is not None:
+ values = [int(value)]
+ else:
+ values = []
else:
- newnum = oldnum
+ values = [value]
else:
- newnum = ""
- setlabel(tw.selected_block, selblock.check(newnum,oldnum))
- tw.firstkey = False
- return True
-
-def unselect(tw):
- if tw.selected_block.label in ['-', '.', '-.']:
- setlabel(tw.selected_block,'0')
-
- # put an upper and lower bound on numbers to prevent OverflowError
- if tw.selected_block.proto.name == 'number' and \
- tw.selected_block.label is not None:
- try:
- i = float(tw.selected_block.label)
- if i > 1000000:
- setlabel(tw.selected_block,'1')
- showlabel(tw.lc,"#overflowerror")
- elif i < -1000000:
- setlabel(tw.selected_block,'-1')
- showlabel(tw.lc,"#overflowerror")
- except ValueError:
- pass
-
- hide(tw.select_mask)
- hide(tw.select_mask_string)
- tw.selected_block = None
-
-def jog_turtle(tw,dx,dy):
- if dx == -1 and dy == -1:
- tw.turtle.xcor = 0
- tw.turtle.ycor = 0
- else:
- tw.turtle.xcor += dx
- tw.turtle.ycor += dy
- move_turtle(tw.turtle)
- display_coordinates(tw)
- tw.draggroup = None
-
-def jog_block(tw,dx,dy):
- # drag entire stack if moving lock block
- if tw.spr.proto.name == 'lock':
- tw.draggroup = findgroup(find_top_block(tw.spr))
- else:
- tw.draggroup = findgroup(tw.spr)
- # check to see if any block ends up with a negative x
- for b in tw.draggroup:
- if b.x+dx < 0:
- dx += -(b.x+dx)
- # move the stack
- for b in tw.draggroup:
- move(b,(b.x+dx, b.y-dy))
- snap_to_dock(tw)
- tw.draggroup = None
-
-def click_block(tw):
- if tw.spr.proto.name=='number':
- tw.selected_block = tw.spr
- move(tw.select_mask, (tw.spr.x-5,tw.spr.y-5))
- setlayer(tw.select_mask, 660)
- tw.firstkey = True
- elif tw.defdict.has_key(tw.spr.proto.name):
- tw.selected_block = tw.spr
- if tw.spr.proto.name=='string':
- move(tw.select_mask_string, (tw.spr.x-5,tw.spr.y-5))
- setlayer(tw.select_mask_string, 660)
- tw.firstkey = True
- elif tw.spr.proto.name in importblocks:
- import_from_journal(tw, tw.spr)
- elif tw.spr.proto.name=='nop' and tw.myblock==None:
- tw.activity.import_py()
- else: run_stack(tw, tw.spr)
-
-#
-# Block utilities
-#
-
-def disconnect(b):
- if b.connections[0]==None:
- return
- b2=b.connections[0]
- b2.connections[b2.connections.index(b)] = None
- b.connections[0] = None
-
-def run_stack(tw,spr):
- tw.lc.ag = None
- top = find_top_block(spr)
- run_blocks(tw.lc, top, blocks(tw), True)
- gobject.idle_add(doevalstep, tw.lc)
-
-def findgroup(b):
- group=[b]
- for b2 in b.connections[1:]:
- if b2!=None: group.extend(findgroup(b2))
- return group
-
-def find_top_block(spr):
- b = spr
- while b.connections[0]!=None:
- b=b.connections[0]
- return b
-
-def runtool(tw, spr, cmd, *args):
- cmd(*(args))
-
-def eraser_button(tw):
- # hide status block
- setlayer(tw.status_spr,400)
- clear(tw.lc)
- display_coordinates(tw)
-
-def stop_button(tw):
- stop_logo(tw)
-
-def run_button(tw, time):
- print "you better run, turtle, run!!"
- # look for the start block
- for b in blocks(tw):
- if find_start_stack(tw, b):
- tw.step_time = time
- if hasattr(tw,'activity'):
- tw.activity.recenter()
- run_stack(tw, b)
+ values = []
+
+ if btype in OLD_DOCK:
+ check_dock = True
+ else:
+ check_dock = False
+ if OLD_NAMES.has_key(btype):
+ btype = OLD_NAMES[btype]
+ blk = Block(self.block_list, self.sprite_list,
+ btype, b[2] + self.canvas.cx, b[3] + self.canvas.cy,
+ 'block', values, self.block_scale)
+ # Some blocks get transformed.
+ if btype == 'string':
+ blk.spr.set_label(blk.values[0].replace('\n', RETURN))
+ elif btype in EXPANDABLE or btype == 'nop':
+ if btype == 'vspace':
+ if value is not None:
+ blk.expand_in_y(value)
+ elif btype == 'hspace' or btype == 'identity2':
+ if value is not None:
+ blk.expand_in_x(value)
+ elif btype == 'templatelist' or btype == 'list':
+ for i in range(len(b[4])-4):
+ blk.add_arg()
+ elif btype == 'myfunc2arg' or btype == 'myfunc3arg' or\
+ btype == 'userdefined2args' or btype == 'userdefined3args':
+ blk.add_arg()
+ if btype == 'myfunc3arg' or btype == 'userdefined3args':
+ blk.add_arg(False)
+ if btype in PYTHON_SKIN:
+ if self.nop == 'pythonloaded':
+ self._block_skin('pythonon', blk)
+ else:
+ self._block_skin('pythonoff', blk)
+ elif btype in BOX_STYLE_MEDIA:
+ if len(blk.values) == 0 or blk.values[0] == 'None' or\
+ blk.values[0] == None:
+ self._block_skin(btype+'off', blk)
+ elif btype == 'audio' or btype == 'description':
+ self._block_skin(btype+'on', blk)
+ elif self.running_sugar:
+ try:
+ dsobject = datastore.get(blk.values[0])
+ if not movie_media_type(dsobject.file_path[-4:]):
+ w, h, = calc_image_size(blk.spr)
+ pixbuf = get_pixbuf_from_journal(dsobject, w, h)
+ if pixbuf is not None:
+ x, y = self._calc_image_offset('', blk.spr)
+ blk.set_image(pixbuf, x, y)
+ else:
+ self._block_skin('journalon', blk)
+ dsobject.destroy()
+ except:
+ try:
+ w, h, = calc_image_size(blk.spr)
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
+ blk.values[0], w, h)
+ x, y = self._calc_image_offset('', blk.spr)
+ blk.set_image(pixbuf, x, y)
+ except:
+ print "Warning: Couldn't open dsobject (%s)" % \
+ (blk.values[0])
+ self._block_skin('journaloff', blk)
+ else:
+ if not movie_media_type(blk.values[0][-4:]):
+ try:
+ w, h, = calc_image_size(blk.spr)
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
+ blk.values[0], w, h)
+ x, y = self._calc_image_offset('', blk.spr)
+ blk.set_image(pixbuf, x, y)
+ except:
+ self._block_skin('journaloff', blk)
+ else:
+ self._block_skin('journalon', blk)
+ blk.spr.set_label(' ')
+ blk.resize()
+
+ blk.spr.set_layer(BLOCK_LAYER)
+ if check_dock:
+ blk.connections = 'check'
+ return blk
+
+ def load_start(self):
+ """ Start a new project with a 'start' brick """
+ self.process_data([[0, "start", PALETTE_WIDTH + 20,
+ self.toolbar_offset+PALETTE_HEIGHT + 20,
+ [None, None]]])
+
+ def save_file(self):
+ """ Start a project to a file """
+ if self.save_folder is not None:
+ self.load_save_folder = self.save_folder
+ _file_name, self.load_save_folder = get_save_name('.ta',
+ self.load_save_folder,
+ self.save_file_name)
+ if _file_name is None:
+ return
+ if _file_name[-3:] == '.ta':
+ _file_name = _file_name[0:-3]
+ data_to_file(self.assemble_data_to_save(), _file_name + '.ta')
+ self.save_file_name = os.path.basename(_file_name)
+ if not self.running_sugar:
+ self.save_folder = self.load_save_folder
+
+ def assemble_data_to_save(self, save_turtle=True, save_project=True):
+ """ Pack the project (or stack) into a data stream to be serialized """
+ _data = []
+ _blks = []
+
+ if save_project:
+ _blks = self.just_blocks()
+ else:
+ _blks = find_group(find_top_block(self.selected_blk))
+
+ for _i, _blk in enumerate(_blks):
+ _blk.id = _i
+ for _blk in _blks:
+ if _blk.name in CONTENT_BLOCKS or _blk.name in COLLAPSIBLE:
+ if len(_blk.values) > 0:
+ _name = (_blk.name, _blk.values[0])
+ else:
+ _name = (_blk.name)
+ elif _blk.name in EXPANDABLE:
+ _ex, _ey = _blk.get_expand_x_y()
+ if _ex > 0:
+ _name = (_blk.name, _ex)
+ elif _ey > 0:
+ _name = (_blk.name, _ey)
+ else:
+ _name = (_blk.name, 0)
+ else:
+ _name = (_blk.name)
+ if hasattr(_blk, 'connections'):
+ connections = [get_id(_cblk) for _cblk in _blk.connections]
+ else:
+ connections = None
+ (_sx, _sy) = _blk.spr.get_xy()
+ # Add a slight offset for copy/paste
+ if not save_project:
+ _sx += 20
+ _sy += 20
+ _data.append((_blk.id, _name, _sx-self.canvas.cx,
+ _sy-self.canvas.cy, connections))
+ if save_turtle:
+ for _turtle in iter(self.turtles.dict):
+ self.canvas.set_turtle(_turtle)
+ _data.append((-1, ['turtle', _turtle],
+ self.canvas.xcor, self.canvas.ycor,
+ self.canvas.heading,
+ self.canvas.color, self.canvas.shade,
+ self.canvas.pensize))
+ return _data
+
+ def display_coordinates(self):
+ """ Display the coordinates of the current turtle on the toolbar """
+ x = round_int(self.canvas.xcor/self.coord_scale)
+ y = round_int(self.canvas.ycor/self.coord_scale)
+ h = round_int(self.canvas.heading)
+ if self.running_sugar:
+ self.activity.coordinates_label.set_text("%s: %d %s: %d %s: %d" % (
+ _("xcor"), x, _("ycor"), y, _("heading"), h))
+ self.activity.coordinates_label.show()
+ else:
+ self.win.set_title("%s — %s: %d %s: %d %s: %d" % (_("Turtle Art"),
+ _("xcor"), x, _("ycor"), y, _("heading"), h))
+
+ def showlabel(self, shp, label = ''):
+ """ Display a message on a status block """
+ if shp == 'syntaxerror' and str(label) != '':
+ if self.status_shapes.has_key(str(label)[1:]):
+ shp = str(label)[1:]
+ label = ''
+ else:
+ shp = 'status'
+ elif shp[0] == '#':
+ shp = shp[1:]
+ label = ''
+ if shp == 'notanumber':
+ shp = 'overflowerror'
+ self.status_spr.set_shape(self.status_shapes[shp])
+ self.status_spr.set_label(str(label))
+ self.status_spr.set_layer(STATUS_LAYER)
+ if shp == 'info':
+ self.status_spr.move((PALETTE_WIDTH, self.height-400))
+ else:
+ self.status_spr.move((PALETTE_WIDTH, self.height-200))
+
+ def calc_position(self, template):
+ """ Relative placement of portfolio objects (depreciated) """
+ w, h, x, y, dx, dy = TEMPLATES[template]
+ x *= self.canvas.width
+ y *= self.canvas.height
+ w *= (self.canvas.width-x)
+ h *= (self.canvas.height-y)
+ dx *= w
+ dy *= h
+ return(w, h, x, y, dx, dy)
+
+ def save_as_image(self, name="", svg=False):
+ """ Grab the current canvas and save it. """
+
+ if self.running_sugar:
+ if svg:
+ if len(name) == 0:
+ filename = "ta.svg"
+ else:
+ filename = name+".svg"
+ else:
+ if len(name) == 0:
+ filename = "ta.png"
+ else:
+ filename = name+".png"
+ datapath = get_path(self.activity, 'instance')
+ elif len(name) == 0:
+ name = "ta"
+ if self.save_folder is not None:
+ self.load_save_folder = self.save_folder
+ if svg:
+ filename, self.load_save_folder = get_save_name('.svg',
+ self.load_save_folder,
+ name)
+ else:
+ filename, self.load_save_folder = get_save_name('.png',
+ self.load_save_folder,
+ name)
+ datapath = self.load_save_folder
+ else:
+ datapath = os.getcwd()
+ if svg:
+ filename = name+".svg"
+ else:
+ filename = name+".png"
+ if filename is None:
return
- # no start block, so run a stack that isn't a hat
- for b in blocks(tw):
- if find_block_to_run(tw, b):
- print "running " + b.proto.name
- tw.step_time = time
- run_stack(tw, b)
- return
-
-def hideshow_button(tw):
- if tw.hide is False:
- for b in blocks(tw): setlayer(b,100)
- hide_palette(tw)
- hide(tw.select_mask)
- hide(tw.select_mask_string)
- tw.hide = True
- else:
- for b in blocks(tw): setlayer(b,650)
- show_palette(tw)
- tw.hide = False
- inval(tw.turtle.canvas)
-
-# find start stack
-def find_start_stack(tw, spr):
- top = find_top_block(spr)
- if spr.proto.name == 'start':
- return True
- else:
- return False
-# find a stack to run (any stack without a hat)
-def find_block_to_run(tw, spr):
- top = find_top_block(spr)
- if spr == top and spr.proto.name[0:3] != 'hat':
- return True
- else:
- return False
+ file_path = os.path.join(datapath, filename)
+ if svg:
+ if self.svg_string == '':
+ return
+ save_svg(self.svg_string, file_path)
+ self.svg_string = ''
+ else:
+ save_picture(self.canvas, file_path)
-def blocks(tw):
- return [spr for spr in tw.sprites if spr.type == 'block']
-
-def xy(event):
- return map(int, event.get_coords())
-
-def showPopup(block_name,tw):
- if blocks_dict.has_key(block_name):
- block_name_s = _(blocks_dict[block_name])
- else:
- block_name_s = _(block_name)
- if hover_dict.has_key(block_name):
- label = block_name_s + ": " + hover_dict[block_name]
- else:
- label = block_name_s
- if hasattr(tw, "activity"):
- tw.activity.hover_help_label.set_text(label)
- tw.activity.hover_help_label.show()
- elif hasattr(tw, "win"):
- tw.win.set_title(_("Turtle Art") + " — " + label)
- return 0
+ # keep a log of the saved pictures for export to HTML
+ self.saved_pictures.append(file_path)
+
+ if self.running_sugar:
+ dsobject = datastore.create()
+ if len(name) == 0:
+ dsobject.metadata['title'] = "%s %s" % (
+ self.activity.metadata['title'], _("image"))
+ else:
+ dsobject.metadata['title'] = name
+ dsobject.metadata['icon-color'] = profile.get_color().to_string()
+ if svg:
+ dsobject.metadata['mime_type'] = 'image/svg+xml'
+ else:
+ dsobject.metadata['mime_type'] = 'image/png'
+ dsobject.set_file_path(file_path)
+ datastore.write(dsobject)
+ dsobject.destroy()
+
+ def just_blocks(self):
+ """ Filter out 'proto', 'trash', and 'deleted' blocks """
+ just_blocks_list = []
+ for _blk in self.block_list.list:
+ if _blk.type == 'block':
+ just_blocks_list.append(_blk)
+ return just_blocks_list
+
+ def _width_and_height(self, blk):
+ """ What are the width and height of a stack? """
+ minx = 10000
+ miny = 10000
+ maxx = -10000
+ maxy = -10000
+ for gblk in find_group(blk):
+ (x, y) = gblk.spr.get_xy()
+ w, h = gblk.spr.get_dimensions()
+ if x < minx:
+ minx = x
+ if y < miny:
+ miny = y
+ if x + w > maxx:
+ maxx = x + w
+ if y + h > maxy:
+ maxy = y + h
+ return(maxx - minx, maxy - miny)
+
+ # Utilities related to putting a image 'skin' on a block
+
+ def _calc_image_offset(self, name, spr, iw=0, ih=0):
+ """ Calculate the postion for placing an image onto a sprite. """
+ _l, _t = spr.label_left_top()
+ if name == '':
+ return _l, _t
+ _w = spr.label_safe_width()
+ _h = spr.label_safe_height()
+ if iw == 0:
+ iw = self.media_shapes[name].get_width()
+ ih = self.media_shapes[name].get_height()
+ return int(_l + (_w - iw)/2), int(_t + (_h - ih)/2)
+
+ def _calc_w_h(self, name, spr):
+ """ Calculate new image size """
+ target_w = spr.label_safe_width()
+ target_h = spr.label_safe_height()
+ if name == '':
+ return target_w, target_h
+ image_w = self.media_shapes[name].get_width()
+ image_h = self.media_shapes[name].get_height()
+ scale_factor = float(target_w)/image_w
+ new_w = target_w
+ new_h = image_h*scale_factor
+ if new_h > target_h:
+ scale_factor = float(target_h)/new_h
+ new_h = target_h
+ new_w = target_w*scale_factor
+ return int(new_w), int(new_h)
+
+ def _proto_skin(self, name, n, i):
+ """ Utility for calculating proto skin images """
+ x, y = self._calc_image_offset(name, self.palettes[n][i].spr)
+ self.palettes[n][i].spr.set_image(self.media_shapes[name], 1, x, y)
+
+ def _block_skin(self, name, blk):
+ """ Some blocks get a skin """
+ x, y = self._calc_image_offset(name, blk.spr)
+ blk.set_image(self.media_shapes[name], x, y)
+ self._resize_skin(blk)
+
+ def _resize_skin(self, blk):
+ """ Resize the 'skin' when block scale changes. """
+ if blk.name in PYTHON_SKIN:
+ w, h = self._calc_w_h('pythonoff', blk.spr)
+ x, y = self._calc_image_offset('pythonoff', blk.spr, w, h)
+ elif blk.name == 'journal':
+ if len(blk.values) == 1 and blk.values[0] is not None:
+ w, h = self._calc_w_h('', blk.spr)
+ x, y = self._calc_image_offset('journaloff', blk.spr, w, h)
+ else:
+ w, h = self._calc_w_h('journaloff', blk.spr)
+ x, y = self._calc_image_offset('journaloff', blk.spr, w, h)
+ else:
+ w, h = self._calc_w_h('descriptionoff', blk.spr)
+ x, y = self._calc_image_offset('descriptionoff', blk.spr, w, h)
+ blk.scale_image(x, y, w, h)