diff options
Diffstat (limited to 'tawindow.py')
-rw-r--r-- | tawindow.py | 3343 |
1 files changed, 844 insertions, 2499 deletions
diff --git a/tawindow.py b/tawindow.py index f33d8b6..c626fb9 100644 --- a/tawindow.py +++ b/tawindow.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- #Copyright (c) 2007, Playful Invention Company -#Copyright (c) 2008-10, Walter Bender -#Copyright (c) 2009-10 Raúl Gutiérrez Segalés +#Copyright (c) 2008-9, Walter Bender +#Copyright (c) 2009, 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 @@ -21,13 +21,6 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. - -# TODO: -# - better comments! -# - many methods could have their logic simplified! -# - verbose flag should be in the scope of the object instance - - import pygtk pygtk.require('2.0') import gtk @@ -36,2540 +29,892 @@ 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 taarduino import * +from tasetup import * +from tasprites import * +from talogo import * +from taturtle import * +from taproject import * try: from sugar.graphics.objectchooser import ObjectChooser - from sugar.datastore import datastore - from sugar import profile -except ImportError: +except: pass -from taconstants import * -from talogo import LogoCode, stop_logo, convert, str_to_num -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, calc_image_size -from tasprite_factory import SVG, svg_str_to_pixbuf, svg_from_file -from sprites import Sprites, Sprite - -""" -Dock tests -""" - -def numeric_arg(value): - if type(convert(value, float)) is float: - return True - return False - -def zero_arg(value): - if numeric_arg(value): - if convert(value, float) == 0: - return True - return False - -def neg_arg(value): - if numeric_arg(value): - if convert(value, float) < 0: - return True - return False - -""" -TurtleArt Window class abstraction -""" -class TurtleArtWindow(): - - # Time out for triggering help - timeout_tag = [0] - - def __init__(self, win, path, lang, parent=None, mycolors=None): - self._setup_initial_values(win, path, lang, parent, mycolors) - self._setup_misc() - self._show_toolbar_palette(0, False) - - def _setup_initial_values(self, win, path, lang, parent, mycolors): - self.window = win - self.path = os.path.join(path, 'images') - 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 - else: - self.window.show_all() - self.running_sugar = False - self._setup_events() - self.keypress = "" - self.keyvalue = 0 - self.dead_key = "" - self.area = self.window.window - self.gc = self.area.new_gc() - if self._OLPC_XO_1(): - self.lead = 1.0 - self.scale = 0.67 - else: - self.lead = 1.0 - self.scale = 1.0 - self.block_scale = BLOCK_SCALE - self.trash_scale = 0.5 - self.cm = self.gc.get_colormap() - self.rgb = [255,0,0] - self.bgcolor = self.cm.alloc_color('#fff8de') - self.msgcolor = self.cm.alloc_color('black') - self.fgcolor = self.cm.alloc_color('red') - self.textcolor = self.cm.alloc_color('blue') - self.textsize = 32 - 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.status_spr = None - self.status_shapes = {} - self.toolbar_spr = None - self.palette_sprs = [] - self.palettes = [] - self.palette_button = [] - self.orientation = 0 - 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.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) - - """ - Register the events we listen to. - """ - def _setup_events(self): - 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) - - """ - Misc. sprites for status, overlays, etc. - """ - def _setup_misc(self): - # media blocks get positioned into other blocks - for name in MEDIA_SHAPES: - if name[0:7] == 'journal' and not self.running_sugar: - filename = 'file'+name[7:] - else: - filename = name - self.media_shapes[name] = \ - svg_str_to_pixbuf(svg_from_file("%s/%s.svg" % ( - self.path, filename))) - - for i, name in enumerate(STATUS_SHAPES): - self.status_shapes[name] = svg_str_to_pixbuf(svg_from_file( - "%s/%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/%s.svg" % (self.path, name)))) - self.overlay_shapes[name].hide() - self.overlay_shapes[name].type = 'overlay' - - """ - Is a chattube available for sharing? - """ - def _sharing(self): - if self.running_sugar and hasattr(self.activity, 'chattube') and\ - self.activity.chattube is not None: - return True - return False +from tahoverhelp import * +from gettext import gettext as _ - """ - Is the an OLPC XO-1? - """ - def _OLPC_XO_1(self): - return os.path.exists('/etc/olpc-release') or \ - os.path.exists('/sys/power/olpc-pm') - - """ - Repaint - """ - def _expose_cb(self, win, event): - self.sprite_list.redraw_sprites() +# 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" + tw.arduino = TAArduino() + return tw + +# +# Button Press +# + +def buttonpress_cb(win, event, tw): + win.grab_focus() + x, y = xy(event) + button_press(tw, event.get_state()>k.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()>k.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 - - - """ - Eraser_button (Always hide status block when clearing the screen.) - """ - def eraser_button(self): - if self.status_spr is not None: - self.status_spr.hide() - self.lc.prim_clear() - self.display_coordinates() - - """ - Run turtle! - """ - def run_button(self, time): - if self.running_sugar: - self.activity.recenter() - # Look for a 'start' block - for blk in self.just_blocks(): - if self._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 self._find_block_to_run(blk): - self.step_time = time - print "running stack starting from %s" % (blk.name) - self._run_stack(blk) + if spr.type == "canvas": + 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: 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] - """ - Stop button - """ - def stop_button(self): - stop_logo(self) - - """ - Change the icon for user-defined blocks after Python code is loaded. - """ - def set_userdefined(self): - for blk in self.just_blocks(): - if blk.name == 'nop': - 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' - - """ - Enter fulscreen mode - """ - def set_fullscreen(self): - if self.running_sugar: - self.activity.fullscreen() - self.activity.recenter() - - """ - Hide/show button - """ - def hideshow_button(self): - if self.hide is False: - 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() - self.hide = False - self.canvas.canvas.inval() - - """ - Hide or show palette - """ - def hideshow_palette(self, state): - if state is False: - 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() - - """ - Show palette - """ - def show_palette(self, n=0): - self._show_toolbar_palette(n) - self.palette_button[self.orientation].set_layer(TAB_LAYER) - self.toolbar_spr.set_layer(CATEGORY_LAYER) - self.palette = True - - """ - Hide the palette. - """ - def hide_palette(self): - self._hide_toolbar_palette() - self.palette_button[self.orientation].hide() - self.toolbar_spr.hide() - self.palette = False - - """ - Callback from 'hide blocks' block - """ - def hideblocks(self): - self.hide = False - self.hideshow_button() - if self.running_sugar: - self.activity.do_hide() - - """ - Callback from 'show blocks' block - """ - def showblocks(self): - self.hide = True - self.hideshow_button() - if self.running_sugar: - self.activity.do_show() - - """ - Resize all of the blocks - """ - def resize_blocks(self): - # We need to restore collapsed stacks before resizing. - for b in self.just_blocks(): - if b.status == 'collapsed': - bot = self._find_sandwich_bottom(b) - if self._collapsed(bot): - dy = bot.values[0] - self._restore_stack(self._find_sandwich_top(b)) - bot.values[0] = dy - - # Do the resizing. - for b in self.just_blocks(): - b.rescale(self.block_scale) - for b in self.just_blocks(): - self._adjust_dock_positions(b) - - # Re-collapsed stacks after resizing. - for b in self.just_blocks(): - if self._collapsed(b): - self._collapse_stack(self._find_sandwich_top(b)) - for b in self.just_blocks(): - if b.name == 'sandwichtop': - self._grow_stack_arm(b) - - # Resize the skins on some blocks: media content and Python - for b in self.just_blocks(): - if b.name in BLOCKS_WITH_SKIN: - self._resize_skin(b) - - """ - Show the toolbar palettes, creating them on init_only - """ - def _show_toolbar_palette(self, n, init_only=False): - # Create the selectors the first time through. - if self.selectors == []: - svg = SVG() - x, y = 50, 0 - for i, name in enumerate(PALETTE_NAMES): - a = svg_str_to_pixbuf(svg_from_file("%s/%soff.svg" % ( - self.path, name))) - b = svg_str_to_pixbuf(svg_from_file("%s/%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, h = self.selectors[i].get_dimensions() - x += int(w) - self.palette_sprs.append([None,None]) - - # Create the palette orientation button - self.palette_button.append(Sprite(self.sprite_list, 0, ICON_SIZE, - svg_str_to_pixbuf(svg_from_file( - "%s/palettehorizontal.svg" %(self.path))))) - self.palette_button.append(Sprite(self.sprite_list, 0, ICON_SIZE, - svg_str_to_pixbuf(svg_from_file( - "%s/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 toolbar background - 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) - - # Create the empty palettes - if len(self.palettes) == 0: - for i in range(len(PALETTES)): - self.palettes.append([]); - - if init_only is True: - return +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) - # Hide the previously displayed palette - self._hide_previous_palette() - - self.selected_palette = n - self.previous_palette = self.selected_palette - 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 == 'nop': - self._proto_skin('pythonsmall', n, i) - - self._layout_palette(n) - for blk in self.palettes[n]: - blk.spr.set_layer(TAB_LAYER) - if n == PALETTE_NAMES.index('trash'): - for blk in self.trash_stack: - for b in self._find_group(blk): - if b.status != 'collapsed': - b.spr.set_layer(TAB_LAYER) - - """ - Hide the toolbar palettes - """ - def _hide_toolbar_palette(self): - self._hide_previous_palette() - # Hide the selectors - for i in range(len(PALETTES)): - self.selectors[i].hide() - self.selected_palette = None - self.previous_palette = None - - """ - Hide just the previously viewed toolbar palette - """ - def _hide_previous_palette(self): - # 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() - self.selectors[self.previous_palette].set_shape( - self.selector_shapes[self.previous_palette][0]) - if self.previous_palette == PALETTE_NAMES.index('trash'): - for b in self.trash_stack: - for bb in self._find_group(b): - bb.spr.hide() - - """ - Position prototypes in a horizontal palette. - """ - def _horizontal_layout(self, x, y, blocks): - _max_w = 0 - for b in blocks: - _w, _h = self._width_and_height(b) - if y+_h > PALETTE_HEIGHT+ICON_SIZE: - x += int(_max_w+5) - y = ICON_SIZE+5 - _max_w = 0 - (_bx, _by) = b.spr.get_xy() - _dx = x-_bx - _dy = y-_by - for g in self._find_group(b): - g.spr.move_relative((int(_dx), int(_dy))) - y += int(_h+5) - if _w > _max_w: - _max_w = _w - return x, y, _max_w - - """ - Position prototypes in a vertical palette. - """ - def _vertical_layout(self, x, y, blocks): - _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 self._find_group(_r): - _g.spr.move_relative((_dx, 0)) - _row = [] - _row_w = 0 - x = 5 - y += int(_max_h+5) - _max_h = 0 - _row.append(_b) - _row_w += (5+_w) - (_bx, _by) = _b.spr.get_xy() - _dx = int(x-_bx) - _dy = int(y-_by) - for _g in self._find_group(_b): - _g.spr.move_relative((_dx, _dy)) - x += int(_w+5) - if _h > _max_h: - _max_h = _h - # Recenter last row. - _dx = int((PALETTE_WIDTH-_row_w)/2) - for _r in _row: - for _g in self._find_group(_r): - _g.spr.move_relative((_dx, 0)) - return x, y, _max_h - - """ - Layout prototypes in a palette. - """ - def _layout_palette(self, n): - if n is not None: - if self.orientation == 0: - _x, _y = 20, ICON_SIZE+5 - _x, _y, _max = self._horizontal_layout(_x, _y, self.palettes[n]) - if n == PALETTE_NAMES.index('trash'): - _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, ICON_SIZE, - 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))) - else: - _x, _y = 5, ICON_SIZE+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-ICON_SIZE - if self.palette_sprs[n][self.orientation] is None: - svg = SVG() - self.palette_sprs[n][self.orientation] =\ - Sprite(self.sprite_list, 0, ICON_SIZE, - 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_sprs[n][self.orientation].set_layer(CATEGORY_LAYER) - - """ - Button press - """ - def _buttonpress_cb(self, win, event): - self.window.grab_focus() - x, y = self._xy(event) - self.button_press(event.get_state()>k.gdk.CONTROL_MASK, x, y) - if self._sharing(): - if event.get_state()>k.gdk.CONTROL_MASK is True: - self.activity._send_event("p:%d:%d:T" % (x, y)) - else: - self.activity._send_event("p:%d:%d:F" % (x, y)) +def new_block_from_category(tw,proto,x,y): + if proto is None: return True - - 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(mask, x, y, blk) - elif blk.type == 'trash': - self._restore_from_trash(self.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 self._hide_button_hit(spr, x, y): - self.hideshow_palette(False) - elif spr.type == 'palette': - 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) - return True - - """ - Select a category from the toolbar. - """ - def _select_category(self, spr): - 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) - - """ - Put a group of blocks into the trash. - """ - def _put_in_trash(self, blk, x=0, y=0): - self.trash_stack.append(blk) - group = self._find_group(blk) - for b in group: - if b.status == 'collapsed': - # Collapsed stacks are restored for rescaling - # and then recollapsed after they are moved to the trash. - bot = self._find_sandwich_bottom(b) - if self._collapsed(bot): - dy = bot.values[0] - self._restore_stack(self._find_sandwich_top(b)) - bot.values[0] = dy - b.type = 'trash' - b.rescale(self.trash_scale) - blk.spr.move((x,y)) - for b in group: - self._adjust_dock_positions(b) - - # Re-collapsing any stacks we had restored for scaling - for b in group: - if self._collapsed(b): - self._collapse_stack(self._find_sandwich_top(b)) - - # And resize any skins. - for b in group: - if b.name in BLOCKS_WITH_SKIN: - self._resize_skin(b) - - self.show_palette(PALETTE_NAMES.index('trash')) - - """ - Restore all the blocks in the trash can - """ - def _restore_all_from_trash(self): - for b in self.block_list.list: - if b.type == 'trash': - self._restore_from_trash(b) - - """ - Restore latest blocks from the trash can - """ - def _restore_latest_from_trash(self): - 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 = self._find_group(blk) - for b in group: - b.rescale(self.block_scale) - b.spr.set_layer(BLOCK_LAYER) - x,y = b.spr.get_xy() - if self.orientation == 0: - b.spr.move((x,y+PALETTE_HEIGHT+ICON_SIZE)) - else: - b.spr.move((x+PALETTE_WIDTH,y)) - b.type = 'block' - for b in group: - self._adjust_dock_positions(b) - # If the stack had been collapsed before going into the trash, - # collapse it again now. - for b in group: - if self._collapsed(b): - self._collapse_stack(self._find_sandwich_top(b)) - # And resize any skins. - for b in group: - if b.name in BLOCKS_WITH_SKIN: - self._resize_skin(b) - - self.trash_stack.remove(blk) - - """ - Permanently remove blocks in the trash can - """ - def _empty_trash(self): - for b in self.block_list.list: - if b.type == 'trash': - b.type = 'deleted' - b.spr.hide() - self.trash_stack = [] - - """ - Is x,y over the trash can? - """ - def _in_the_trash(self, x, y): - if self.selected_palette == self.trash_index and \ - self.palette_sprs[self.trash_index][self.orientation].hit((x,y)): - return True - return False - - """ - Block pressed - """ - def _block_pressed(self, mask, x, y, blk): - if blk is not None: - blk.highlight() - self._disconnect(blk) - self.drag_group = self._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] - - """ - Unselect block - """ - def _unselect_block(self): - # 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 - - """ - Make a new block. - """ - def _new_block(self, name, x, y): - 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 == 'nop': - if self.nop == 'pythonloaded': - self._block_skin('pythonon', newblk) - else: - 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, ny = sx+dock[2]-argdock[2], 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 = self._find_group(newblk) - self.block_operation = 'new' - - """ - Create a "macro" (predefined stack of blocks) - """ - def _new_macro(self, name, x, y): - 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 = self._find_group(top) - - """ - Process data (from a macro, a file, or the clipboard) into blocks. - """ - def process_data(self, data): - # Create the blocks (or turtle). - blocks = [] - for b in data: - if self._found_a_turtle(b) is False: - blk = self.load_block(b) - blocks.append(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" + # 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: - print "WARNING: unknown connection state %s" %\ - (str(blocks[i].connections)) - blocks[i].connections = cons[:] - - # Block sizes and shapes may have changed. - for b in blocks: - self._adjust_dock_positions(b) - - # Look for any stacks that need to be collapsed. - for b in blocks: - if self._collapsed(b): - self._collapse_stack(self._find_sandwich_top(b)) - return blocks[0] - - """ - Adjust the dock x,y positions - """ - def _adjust_dock_positions(self, blk): - (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, ny = sx+bdock[2]-cdock[2], sy+bdock[3]-cdock[3] - c.spr.move((nx, ny)) - self._adjust_dock_positions(c) - - """ - Turtle pressed - """ - def _turtle_pressed(self, x, y): - (tx, ty) = self.selected_turtle.get_xy() - dx, dy = x-tx-30, 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) - - """ - Mouse move - """ - def _move_cb(self, win, event): - x, y = self._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, dy = x-dragx-sx, y-dragy-sy - self.selected_turtle.move((sx+dx, sy+dy)) - else: - if mdx != 0 or mdy != 0: - dx, dy = mdx, mdy - else: - dx, dy = x-sx-30, 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 self._collapsed(blk): - return - - self.selected_spr = blk.spr - dragx, dragy = self.drag_pos - if mdx != 0 or mdy != 0: - dx, dy = mdx, mdy - else: - (sx,sy) = blk.spr.get_xy() - dx, dy = x-dragx-sx, y-dragy-sy - - # Take no action if there was a move of 0,0. - if dx == 0 and dy == 0: - return - self.drag_group = self._find_group(blk) - - # Prevent blocks from ending up with a negative x... - for b in self.drag_group: - (bx, by) = b.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 == 0: - if bx < w and by+dy < ICON_SIZE+PALETTE_HEIGHT: - dy += -(by+dy)+ICON_SIZE+PALETTE_HEIGHT - else: - if by < h+ICON_SIZE and bx+dx < PALETTE_WIDTH: - dx += -(bx+dx)+PALETTE_WIDTH - - # Move the stack. - for b in self.drag_group: - (bx, by) = b.spr.get_xy() - b.spr.move((bx+dx, by+dy)) - if mdx != 0 or mdy != 0: - dx, dy = 0, 0 - else: - self.dx += dx - self.dy += dy - - """ - Let's help our users by displaying a little help. - """ - def _show_popup(self, x, y): - 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 + 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 else: - if self.timeout_tag[0] > 0: + if timeout_tag[0] > 0: try: - gobject.source_remove(self.timeout_tag[0]) - self.timeout_tag[0] = 0 + gobject.source_remove(timeout_tag[0]) + timeout_tag[0] = 0 except: - self.timeout_tag[0] = 0 - elif spr and hasattr(spr,'type') and (spr.type == 'selector' or\ - spr.type == 'palette'): - if self.timeout_tag[0] == 0: - self.timeout_tag[0] = self._do_show_popup(spr.name) - self.selected_spr = spr + 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 else: - if self.timeout_tag[0] > 0: + if timeout_tag[0] > 0: try: - gobject.source_remove(self.timeout_tag[0]) - self.timeout_tag[0] = 0 + gobject.source_remove(timeout_tag[0]) + timeout_tag[0] = 0 except: - self.timeout_tag[0] = 0 + timeout_tag[0] = 0 else: - if self.timeout_tag[0] > 0: + if timeout_tag[0] > 0: try: - gobject.source_remove(self.timeout_tag[0]) - self.timeout_tag[0] = 0 + gobject.source_remove(timeout_tag[0]) + timeout_tag[0] = 0 except: - self.timeout_tag[0] = 0 - - """ - Fetch the help text and display it. - """ - def _do_show_popup(self, block_name): - 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] - else: - block_name_s = _(block_name) - if HELP_STRINGS.has_key(block_name): - label = block_name_s + ": " + HELP_STRINGS[block_name] - else: - label = block_name_s - if self.running_sugar: - self.activity.hover_help_label.set_text(label) - self.activity.hover_help_label.show() + 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 else: - self.win.set_title(_("Turtle Art") + " — " + label) - return 0 - - """ - Button release - """ - def _buttonrelease_cb(self, win, event): - x, y = self._xy(event) - self.button_release(x, y) - if self._sharing(): - self.activity._send_event("r:"+str(x)+":"+str(y)) - return True - - def button_release(self, x, y, verbose=False): - if self.dx != 0 or self.dy != 0: - if self._sharing(): - if verbose: - print "processing move: %d %d" % (self.dx, self.dy) - self.activity._send_event("m:%d:%d" % (self.dx, self.dy)) - self.dx = 0 - self.dy = 0 - if verbose: - print "processing remote button release: %d, %d" % (x, y) - - # 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.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 b in self.drag_group: - (bx, by) = b.spr.get_xy() - if self.orientation == 0: - b.spr.move((bx+20, by+PALETTE_HEIGHT+ICON_SIZE)) - else: - b.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 b in self.drag_group: - if b.status != 'collapsed': - b.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) - - """ - Click block - """ - def _click_block(self, x, y): - blk = self.block_list.spr_to_block(self.selected_spr) - if blk is None: + 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 - 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) - elif blk.name=='identity2' or blk.name=='hspace': - group = self._find_group(blk) - if self._hide_button_hit(blk.spr, x, y): - dx = blk.reset_x() - elif self._show_button_hit(blk.spr, x, y): - dx = 20 - blk.expand_in_x(dx) - for b in group: - if b != blk: - b.spr.move_relative((dx*blk.scale, 0)) - elif blk.name=='vspace': - group = self._find_group(blk) - if self._hide_button_hit(blk.spr, x, y): - dy = blk.reset_y() - elif self._show_button_hit(blk.spr, x, y): - dy = 20 - blk.expand_in_y(dy) - else: - dy = 0 - for b in group: - if b != blk: - b.spr.move_relative((0, dy*blk.scale)) - self._grow_stack_arm(self._find_sandwich_top(blk)) - elif blk.name in EXPANDABLE: - if self._show_button_hit(blk.spr, x, y): - n = len(blk.connections) - group = self._find_group(blk.connections[n-1]) - if blk.name == 'myfunc' and n == 4: - blk.spr.labels[1] = 'f(x,y)' - blk.spr.labels[2] = ' ' - if blk.name == 'myfunc' and n == 5: - blk.spr.labels[1] = 'f(x,y,z)' - dy = blk.add_arg(False) - else: - dy = blk.add_arg() - for b in group: - b.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 - self._grow_stack_arm(self._find_sandwich_top(blk)) - else: - self._run_stack(blk) - elif blk.name in COLLAPSIBLE: - top = self._find_sandwich_top(blk) - if self._collapsed(blk): - self._restore_stack(top) - elif top is not None: - self._collapse_stack(top) - elif blk.name=='nop' and self.myblock==None: - self._import_py() + # drag entire stack if moving lock block + if spr.proto.name == 'lock': + tw.draggroup = findgroup(find_top_block(spr)) else: - self._run_stack(blk) - - """ - Collapsible stacks live between 'sandwichtop' and 'sandwichbottom' blocks - What follows are a number of utilities for managing collapsible stacks. - Alas, corner-cases abound. - """ - - """ - From the top, find and restore any collapsible stacks on forks. - """ - def _uncollapse_forks(self, top, looping=False): - if top == None: - return - if looping and top.name == 'sandwichtop': - self._restore_stack(top) - return - if len(top.connections) == 0: - return - b = top.connections[len(top.connections)-1] - while b is not None: - if b.name in COLLAPSIBLE: - return - if b.name == 'sandwichtop': - self._restore_stack(b) - return - # Follow a fork - if b.name in ['repeat', 'if', 'ifelse', 'forever', 'while']: - top = self._find_sandwich_top_below( - b.connections[len(b.connections)-2]) - if top is not None: - self._uncollapse_forks(top, True) - if b.name == 'ifelse': - top = self._find_sandwich_top_below( - b.connections[len(b.connections)-3]) - if top is not None: - self._uncollapse_forks(top, True) - b = b.connections[len(b.connections)-1] - return - - """ - Find the sandwich top above this block. - """ - def _find_sandwich_top(self, blk): - # Always follow the main branch of a flow: the first connection. - b = blk.connections[0] - while b is not None: - if b.name in COLLAPSIBLE: - return None - if b.name in ['repeat', 'if', 'ifelse', 'forever', 'while']: - if blk != b.connections[len(b.connections)-1]: - return None - if b.name == 'sandwichtop': - return b - blk = b - b = b.connections[0] - return None - - """ - Find the sandwich bottom below this block. - """ - def _find_sandwich_bottom(self, blk): - # Always follow the main branch of a flow: the last connection. - b = blk.connections[len(blk.connections)-1] - while b is not None: - if b.name == 'sandwichtop': - return None - if b.name in COLLAPSIBLE: - return b - b = b.connections[len(b.connections)-1] - return None - - """ - Find the sandwich top below this block. - """ - def _find_sandwich_top_below(self, blk): - if blk.name == 'sandwichtop': - return blk - # Always follow the main branch of a flow: the last connection. - b = blk.connections[len(blk.connections)-1] - while b is not None: - if b.name == 'sandwichtop': - return b - b = b.connections[len(b.connections)-1] - return None - - """ - Hide all the blocks between the sandwich top and sandwich bottom. - """ - def _collapse_stack(self, top): - # First uncollapse any nested stacks - self._uncollapse_forks(top) - hit_bottom = False - bot = self._find_sandwich_bottom(top) - group = self._find_group(top.connections[len(top.connections)-1]) - for b in group: - if not hit_bottom and b == bot: - hit_bottom = True - - # Replace 'sandwichbottom' shape with 'sandwichcollapsed' shape - if len(b.values) == 0: - b.values.append(1) - else: - b.values[0] = 1 - olddx = b.docks[1][2] - olddy = b.docks[1][3] - b.name = 'sandwichcollapsed' - b.svg.set_show(True) - b.svg.set_hide(False) - b._dx = 0 - b._ey = 0 - b.spr.set_label(_('click to open')) - b.resize() - - # Redock to sandwich top in group - you = self._find_sandwich_top(b) - (yx, yy) = you.spr.get_xy() - yd = you.docks[len(you.docks)-1] - (bx, by) = b.spr.get_xy() - dx = yx+yd[2]-b.docks[0][2]-bx - dy = yy+yd[3]-b.docks[0][3]-by - b.spr.move_relative((dx, dy)) - - # Since the shapes have changed, the dock positions have too. - newdx = b.docks[1][2] - newdy = b.docks[1][3] - dx += newdx-olddx - dy += newdy-olddy + 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 else: - if not hit_bottom: - b.spr.set_layer(HIDE_LAYER) - b.status = 'collapsed' - else: - b.spr.move_relative((dx, dy)) - self._reset_stack_arm(top) - - """ - Restore all the blocks between the sandwich top and sandwich bottom. - """ - def _restore_stack(self, top): - group = self._find_group(top.connections[len(top.connections)-1]) - hit_bottom = False - bot = self._find_sandwich_bottom(top) - for b in group: - if not hit_bottom and b == bot: - hit_bottom = True - if len(b.values) == 0: - b.values.append(0) - else: - b.values[0] = 0 - olddx = b.docks[1][2] - olddy = b.docks[1][3] - # Replace 'sandwichcollapsed' shape with 'sandwichbottom' shape - b.name = 'sandwichbottom' - b.spr.set_label(' ') - b.svg.set_show(False) - b.svg.set_hide(True) - b.refresh() - - # Redock to previous block in group - you = b.connections[0] - (yx, yy) = you.spr.get_xy() - yd = you.docks[len(you.docks)-1] - (bx, by) = b.spr.get_xy() - dx = yx+yd[2]-b.docks[0][2]-bx - dy = yy+yd[3]-b.docks[0][3]-by - b.spr.move_relative((dx, dy)) - - # Since the shapes have changed, the dock positions have too. - newdx = b.docks[1][2] - newdy = b.docks[1][3] - dx += newdx-olddx - dy += newdy-olddy + dx,dy = x-dragx-spr.x,y-dragy-spr.y + move(spr, (spr.x+dx, spr.y+dy)) + else: + if mdx != 0 or mdy != 0: + dx,dy = mdx,mdy else: - if not hit_bottom: - b.spr.set_layer(BLOCK_LAYER) - b.status = None - else: - b.spr.move_relative((dx, dy)) - self._grow_stack_arm(top) - - """ - When we undock, retract the 'arm' that extends down from 'sandwichtop'. - """ - def _reset_stack_arm(self, top): - if top is not None and top.name == 'sandwichtop': - if top.ey > 0: - top.reset_y() - - """ - When we dock, grow an 'arm' the length of the stack from 'sandwichtop'. - """ - def _grow_stack_arm(self, top): - if top is not None and top.name == 'sandwichtop': - bot = self._find_sandwich_bottom(top) - if bot is None: - return - if top.ey > 0: - top.reset_y() - (tx, ty) = top.spr.get_xy() - (tw, th) = top.spr.get_dimensions() - (bx, by) = bot.spr.get_xy() - dy = by-(ty+th) - if dy > 0: - top.expand_in_y(dy/top.scale) - - """ - Check the state of collapsible blocks upon change in dock state. - """ - def _check_collapsibles(self, blk): - group = self._find_group(blk) - for b in group: - if b.name in COLLAPSIBLE: - if self._collapsed(b): - b.svg.set_show(True) - b.svg.set_hide(False) - self._reset_stack_arm(self._find_sandwich_top(b)) - elif self._collapsible(b): - b.svg.set_hide(True) - b.svg.set_show(False) - self._grow_stack_arm(self._find_sandwich_top(b)) + 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']) else: - b.svg.set_hide(False) - b.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 bb in self.just_blocks(): - if bb.name == 'sandwichtop': - if self._find_sandwich_bottom(bb) is None: - self._reset_stack_arm(bb) - b.refresh() - - """ - Is this stack collapsed? - """ - def _collapsed(self, blk): - if blk is not None and blk.name in COLLAPSIBLE and\ - len(blk.values) == 1 and blk.values[0] != 0: - return True - return False - - """ - Can this stack be collapsed? - """ - def _collapsible(self, blk): - if blk is None or blk.name not in COLLAPSIBLE: - return False - if self._find_sandwich_top(blk) is None: - return False - return True - - """ - Run a stack of blocks. - """ - def _run_stack(self, blk): - if blk is None: - return - self.lc.ag = None - top = self.find_top_block(blk) - self.lc.run_blocks(top, self.just_blocks(), True) - gobject.idle_add(self.lc.doevalstep) - - """ - Did the sprite's hide (contract) button get hit? - """ - def _hide_button_hit(self, spr, x, y): - r,g,b,a = spr.get_pixel((x, y)) - if (r == 255 and g == 0) or g == 255: - return True - else: - return False - - """ - Did the sprite's show (expand) button get hit? - """ - def _show_button_hit(self, spr, x, y): - r,g,b,a = spr.get_pixel((x, y)) - if g == 254: - return True - else: - return False - - """ - Snap a block to the dock of another block. - """ - def _snap_to_dock(self): - my_block = self.drag_group[0] - d = 200 - for my_dockn in range(len(my_block.docks)): - for i, your_block in enumerate(self.just_blocks()): - # don't link to a block to which you're already connected - if your_block in self.drag_group: + 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: continue - # check each dock of your_block for a possible connection - for your_dockn in range(len(your_block.docks)): - this_xy = self._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 self._arithmetic_check(my_block, best_you, best_my_dockn, - best_your_dockn) is False: - 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 - - """ - Additional docking check for arithmetic blocks: dock strings only if they - convert to numbers. Also, avoid /0 and root(-1) - """ - def _arithmetic_check(self, b1, b2, d1, d2): - if b1 == None or b2 == None: - return True - if b1.name in ['sqrt', 'number', 'string'] and\ - b2.name in ['sqrt', 'number', 'string']: - if b1.name == 'number' or b1.name == 'string': - if not numeric_arg(b1.values[0]) or neg_arg(b1.values[0]): - return False - elif b2.name == 'number' or b2.name == 'string': - if not numeric_arg(b2.values[0]) or neg_arg(b2.values[0]): - return False - elif b1.name in ['division2', 'number', 'string'] and\ - b2.name in ['division2', 'number', 'string']: - if b1.name == 'number' or b1.name == 'string': - if not numeric_arg(b1.values[0]): - return False - if d2 == 2 and zero_arg(b1.values[0]): - return False - elif b2.name == 'number' or b2.name == 'string': - if not numeric_arg(b2.values[0]): - return False - if d1 == 2 and zero_arg(b2.values[0]): - return False - elif b1.name in ['product2', 'minus2', 'random', 'remainder2', - 'string'] and\ - b2.name in ['product2', 'minus2', 'random', 'remainder2', - 'string']: - if b1.name == 'string': - if not numeric_arg(b1.values[0]): - return False - elif b1.name == 'string': - if not numeric_arg(b2.values[0]): - return False - elif b1.name in ['greater2', 'less2'] and b2.name == 'string': - # Non-numeric stings are OK if only both args are strings; - # Lots of test conditions... - if d1 == 1 and b1.connections[2] is not None: - if b1.connections[2].name == 'number': - if not numeric_arg(b2.values[0]): - return False - elif d1 == 2 and b1.connections[1] is not None: - if b1.connections[1].name == 'number': - if not numeric_arg(b2.values[0]): - return False - elif b2.name in ['greater2', 'less2'] and b1.name == 'string': - if d2 == 1 and b2.connections[2] is not None: - if b2.connections[2].name == 'number': - if not numeric_arg(b1.values[0]): - return False - elif d2 == 2 and b2.connections[1] is not None: - if b2.connections[1].name == 'number': - if not numeric_arg(b1.values[0]): - return False - elif b1.name in ['greater2', 'less2'] and b2.name == 'number': - if d1 == 1 and b1.connections[2] is not None: - if b1.connections[2].name == 'string': - if not numeric_arg(b1.connections[2].values[0]): - return False - elif d1 == 2 and b1.connections[1] is not None: - if b1.connections[1].name == 'string': - if not numeric_arg(b1.connections[1].values[0]): - return False - elif b2.name in ['greater2', 'less2'] and b1.name == 'number': - if d2 == 1 and b2.connections[2] is not None: - if b2.connections[2].name == 'string': - if not numeric_arg(b2.connections[2].values[0]): - return False - elif d2 == 2 and b2.connections[1] is not None: - if b2.connections[1].name == 'string': - if not numeric_arg(b2.connections[1].values[0]): - return False - return True - - """ - Import a file from the Sugar Journal - """ - def _import_from_journal(self, blk): - 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) - - """ - Update the icon on a 'loaded' media block. - """ - def _update_media_icon(self, blk, name, value=''): - 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(' ') - - """ - Replace icon with a preview image. - """ - def _load_image_thumb(self, picture, blk): - 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) - - """ - Disconnect block from stack above it. - """ - def _disconnect(self, blk): - if blk.connections[0]==None: - return - if self._collapsed(blk): - return - blk2=blk.connections[0] - blk2.connections[blk2.connections.index(blk)] = None - blk.connections[0] = None - - """ - Find the distance between the dock points of two blocks. - """ - def _dock_dx_dy(self, block1, dock1n, block2, dock2n): - dock1 = block1.docks[dock1n] - dock2 = block2.docks[dock2n] - d1type, d1dir, d1x, d1y = dock1[0:4] - d2type, d2dir, d2x, d2y = dock2[0:4] - if block1 == block2: + 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 d1dir == d2dir: + if block2.connections[dock2n] != None: return (100,100) - if (d2type is not 'number') or (dock2n is not 0): - if block1.connections is not None and \ - dock1n < len(block1.connections) and \ - block1.connections[dock1n] is not None: - return (100,100) - if block2.connections is not None and \ - dock2n < len(block2.connections) and \ - block2.connections[dock2n] is not None: - return (100,100) - if d1type != d2type: - if block1.name in STRING_OR_NUMBER_ARGS: - if d2type == 'number' or d2type == 'string': + 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 - elif block1.name in CONTENT_ARGS: - if d2type in CONTENT_BLOCKS: + else: + if d2type == 'num' or d2type == 'string': pass - else: - return (100,100) - (b1x, b1y) = block1.spr.get_xy() - (b2x, b2y) = block2.spr.get_xy() - return ((b1x+d1x)-(b2x+d2x), (b1y+d1y)-(b2y+d2y)) - - - """ - Keyboard - """ - def _keypress_cb(self, area, event): - keyname = gtk.gdk.keyval_name(event.keyval) - keyunicode = gtk.gdk.keyval_to_unicode(event.keyval) - - if event.get_state()>k.gdk.MOD1_MASK: - alt_mask = True - alt_flag = 'T' + # 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 else: - alt_mask = False - alt_flag = 'F' - results = self._key_press(alt_mask, keyname, keyunicode) - if keyname is not None and self._sharing(): - self.activity._send_event("k:%s:%s:%s" % (alt_flag, keyname, - str(keyunicode))) + 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()>k.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") + ")" return keyname - - 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 is True and self.selected_blk==None: - if keyname=="i" and self._sharing(): - self.activity.waiting_for_blocks = True - self.activity._send_event("i") # request sync for sharing - elif 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 - - ''' - Make sure numeric input is valid. - ''' - def _process_numeric_input(self, keyname): - 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: + 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: newnum = oldnum[:len(oldnum)-1] else: newnum = '' - elif keyname in ['0','1','2','3','4','5','6','7','8','9']: - if oldnum == '0': - newnum = keyname + setlabel(tw.selected_block, selblock.check(newnum,oldnum)) + if len(newnum) > 0: + tw.firstkey = False 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) - - """ - Make sure alphanumeric input is properly parsed. - """ - def process_alphanumeric_input(self, keyname, keyunicode): - 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: - 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 in WHITE_SPACE: - keyunicode = 32 - 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' or 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) is not '\x00': - newleft = oldleft+unichr(keyunicode) - else: - newleft = oldleft - elif keyunicode == -1: # clipboard text - newleft = oldleft+keyname - self.selected_blk.spr.set_label("%s%s%s" % (newleft, CURSOR, oldright)) - - """ - Use the keyboard to move blocks and turtle - """ - def _process_keyboard_commands(self, keyname): - 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 = self._find_group(blk) - for b in self.drag_group: b.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 - - """ - Jog turtle - """ - def _jog_turtle(self, dx, dy): - 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 - - """ - Jog block - """ - def _jog_block(self, blk, dx, dy): - if self._collapsed(blk): - return - self.drag_group = self._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 - - """ - Make sure a 'number' block contains a number. - """ - def _number_check(self): - 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 - - """ - Load Python code from a file - """ - def load_python_code(self): - 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() - - """ - Import Python code into a block - """ - def _import_py(self): - if self.running_sugar: - self.activity.import_py() - else: - self.load_python_code() - self.set_userdefined() - - """ - Start a new project - """ - def new_project(self): - stop_logo(self) - # Put current project in the trash. - while len(self.just_blocks()) > 0: - b = self.just_blocks()[0] - top = self.find_top_block(b) - self._put_in_trash(top) - self.canvas.clearscreen() - self.save_file_name = None - - """ - Load a project from a file - """ - def load_files(self, ta_file, create_new_project=True): - if create_new_project is True: - self.new_project() - top = self.process_data(data_from_file(ta_file)) - self._check_collapsibles(top) - - def load_file(self, create_new_project=True): - fname, self.load_save_folder = get_load_name('.ta', - self.load_save_folder) - if fname==None: - return - if fname[-3:] == '.ta': - fname=fname[0:-3] - self.load_files(fname+'.ta', create_new_project) - if create_new_project is True: - self.save_file_name = os.path.basename(fname) - - """ - Turtles are either [-1, 'turtle', ...] or [-1, ['turtle', key], ...] - """ - def _found_a_turtle(self, b): - if b[1] == 'turtle': - self.load_turtle(b) + tw.firstkey = True + if len(keyname)>1: return True - elif type(b[1]) == list and b[1][0] == 'turtle': - self.load_turtle(b, b[1][1]) + 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 return True - elif type(b[1]) == tuple: - btype, key = b[1] - if btype == 'turtle': - self.load_turtle(b, key) - return True + if tw.selected_block is None: return False - - """ - Restore a turtle from its saved state - """ - def load_turtle(self, b, key=1): - id, name, xcor, ycor, heading, color, shade, pensize = b - 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) - - """ - Restore individual blocks from saved state - """ - def load_block(self, b): - # 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 = [] + 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) else: - values = [value] - else: - values = [] - - if btype in OLD_DOCK: - check_dock = True + newnum = oldnum 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 == 'nop': - if self.nop == 'pythonloaded': - self._block_skin('pythonon', blk) - else: - self._block_skin('pythonoff', blk) - elif btype in EXPANDABLE: - 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): - dy = blk.add_arg() - 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 is True: - blk.connections = 'check' - return blk - - """ - Start a new project with a 'start' brick - """ - def load_start(self): - top = self.process_data([[0, "start", PALETTE_WIDTH+20, - ICON_SIZE+PALETTE_HEIGHT+20, [None, None]]]) - - """ - Start a project to a file - """ - def save_file(self): - if self.save_folder is not None: - self.load_save_folder = self.save_folder - fname, self.load_save_folder = get_save_name('.ta', - self.load_save_folder, - self.save_file_name) - if fname is None: + 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) return - if fname[-3:]=='.ta': - fname=fname[0:-3] - data = self.assemble_data_to_save() - data_to_file(data, fname+'.ta') - self.save_file_name = os.path.basename(fname) - - """ - Pack the project (or stack) into a data stream to be serialized - """ - def assemble_data_to_save(self, save_turtle=True, save_project=True): - data = [] - blks = [] - - if save_project is True: - blks = self.just_blocks() - else: - blks = self._find_group(self.find_top_block(self.selected_blk)) - - for i, b in enumerate(blks): - b.id = i - for b in blks: - if b.name in CONTENT_BLOCKS or b.name in COLLAPSIBLE: - if len(b.values)>0: - name = (b.name, b.values[0]) - else: - name = (b.name) - elif b.name in EXPANDABLE: - ex, ey = b.get_expand_x_y() - if ex > 0: - name = (b.name, ex) - elif ey > 0: - name = (b.name, ey) - else: - name = (b.name, 0) - else: - name = (b.name) - if hasattr(b, 'connections'): - connections = [get_id(c) for c in b.connections] - else: - connections = None - (sx, sy) = b.spr.get_xy() - # Add a slight offset for copy/paste - if save_project is False: - sx+=20 - sy+=20 - data.append((b.id, name, sx-self.canvas.cx, sy-self.canvas.cy, - connections)) - if save_turtle is True: - for k in iter(self.turtles.dict): - self.canvas.set_turtle(k) - data.append((-1,['turtle', k], - self.canvas.xcor, self.canvas.ycor, - self.canvas.heading, - self.canvas.color, self.canvas.shade, - self.canvas.pensize)) - return data - - """ - Display the coordinates of the current turtle on the toolbar - """ - def display_coordinates(self): - 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)) - - """ - Display a message on a status block - """ - def showlabel(self, shp, label=''): - 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-300)) - else: - self.status_spr.move((PALETTE_WIDTH, self.height-200)) - - """ - Relative placement of portfolio objects (used by depreciated blocks) - """ - def calc_position(self, t): - w,h,x,y,dx,dy = TEMPLATES[t] - 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) - - """ - Grab the current canvas and save it. - """ - def save_as_image(self, name=""): - if len(name) == 0: - filename = "ta.png" - else: - filename = name+".png" + # 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 - if self.running_sugar: - datapath = os.path.join(self.activity.get_activity_root(), - "instance") - else: - datapath = os.getcwd() - file_path = os.path.join(datapath, filename) - save_picture(self.canvas, file_path) - - if self.running_sugar: - dsobject = datastore.create() - if len(name) == 0: - dsobject.metadata['title'] = "%s %s" % (self.metadata['title'], - _("image")) - else: - dsobject.metadata['title'] = name - dsobject.metadata['icon-color'] = profile.get_color().to_string() - dsobject.metadata['mime_type'] = 'image/png' - dsobject.set_file_path(file_path) - datastore.write(dsobject) - dsobject.destroy() - - """ - Where is the mouse event? - """ - def _xy(self, event): - return map(int, event.get_coords()) - - """ - Utilities related to finding blocks in stacks. - """ - - """ - Find a stack to run (any stack without a 'def action'on the top). - """ - def _find_block_to_run(self, blk): - top = self.find_top_block(blk) - if blk == top and blk.name[0:3] is not 'def': - return True - else: - return False - - """ - Find the top block in a stack. - """ - def find_top_block(self, blk): - if len(blk.connections) == 0: - return blk - while blk.connections[0] is not None: - blk = blk.connections[0] - return blk - - """ - Find a stack with a 'start' block on top. - """ - def _find_start_stack(self, blk): - top = self.find_top_block(blk) - if top.name == 'start': - return True - else: - return False - - """ - Find the connected group of block in a stack. - """ - def _find_group(self, blk): - if blk is None: - return [] - group=[blk] - if blk.connections is not None: - for blk2 in blk.connections[1:]: - if blk2 is not None: - group.extend(self._find_group(blk2)) - return group - - """ - Filter out 'proto', 'trash', and 'deleted' blocks - """ - def just_blocks(self): - just_blocks_list = [] - for b in self.block_list.list: - if b.type == 'block': - just_blocks_list.append(b) - return just_blocks_list - - """ - What are the width and height of a stack? - """ - def _width_and_height(self, blk): - minx = 10000 - miny = 10000 - maxx = -10000 - maxy = -10000 - for b in self._find_group(blk): - (x, y) = b.spr.get_xy() - w, h = b.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 - """ - - """ - Calculate the postion for placing an image onto a sprite. - """ - def _calc_image_offset(self, name, spr, iw=0, ih=0): - _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) - - """ - Calculate new image size - """ - def _calc_w_h(self, name, spr): - 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() - if image_w > target_w or image_h > target_h: - 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) - return int(target_w), int(target_h) - - """ - Utility for calculating proto skin images - """ - def _proto_skin(self, name, n, i): - 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) - - """ - Some blocks get a skin - """ - def _block_skin(self, name, blk): - x, y = self._calc_image_offset(name, blk.spr) - blk.set_image(self.media_shapes[name], x, y) - self._resize_skin(blk) - - """ - Resize the 'skin' when block scale changes. - """ - def _resize_skin(self, b): - if b.name == 'nop': - w, h = self._calc_w_h('pythonoff', b.spr) - x, y = self._calc_image_offset('pythonoff', b.spr, w, h) - elif b.name == 'journal': - if len(b.values) == 1 and b.values[0] is not None: - w, h = self._calc_w_h('', b.spr) - x, y = self._calc_image_offset('journaloff', b.spr, w, h) - else: - w, h = self._calc_w_h('journaloff', b.spr) - x, y = self._calc_image_offset('journaloff', b.spr, w, h) - else: - w, h = self._calc_w_h('descriptionoff', b.spr) - x, y = self._calc_image_offset('descriptionoff', b.spr, w, h) - b.scale_image(x, y, w, h) +# 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 + +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 |