Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tautils.py
diff options
context:
space:
mode:
Diffstat (limited to 'tautils.py')
-rw-r--r--tautils.py705
1 files changed, 0 insertions, 705 deletions
diff --git a/tautils.py b/tautils.py
deleted file mode 100644
index e7888ea..0000000
--- a/tautils.py
+++ /dev/null
@@ -1,705 +0,0 @@
-#Copyright (c) 2007-8, Playful Invention Company.
-#Copyright (c) 2008-10, Walter Bender
-
-#Permission is hereby granted, free of charge, to any person obtaining a copy
-#of this software and associated documentation files (the "Software"), to deal
-#in the Software without restriction, including without limitation the rights
-#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-#copies of the Software, and to permit persons to whom the Software is
-#furnished to do so, subject to the following conditions:
-
-#The above copyright notice and this permission notice shall be included in
-#all copies or substantial portions of the Software.
-
-#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-#THE SOFTWARE.
-
-import gtk
-import pickle
-import subprocess
-try:
- OLD_SUGAR_SYSTEM = False
- import json
- json.dumps
- from json import load as jload
- from json import dump as jdump
-except (ImportError, AttributeError):
- try:
- import simplejson as json
- from simplejson import load as jload
- from simplejson import dump as jdump
- except:
- OLD_SUGAR_SYSTEM = True
-from taconstants import STRING_OR_NUMBER_ARGS, HIDE_LAYER, CONTENT_ARGS, \
- COLLAPSIBLE, BLOCK_LAYER, CONTENT_BLOCKS
-from StringIO import StringIO
-import os.path
-from gettext import gettext as _
-import logging
-_logger = logging.getLogger('turtleart-activity')
-
-class pythonerror(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-
-'''
-The strategy for mixing numbers and strings is to first try
-converting the string to a float; then if the string is a single
-character, try converting it to an ord; finally, just treat it as a
-string. Numbers appended to strings are first trreated as ints, then
-floats.
-'''
-def convert(x, fn, try_ord=True):
- try:
- return fn(x)
- except ValueError:
- if try_ord:
- xx, flag = chr_to_ord(x)
- if flag:
- return fn(xx)
- return x
-
-def chr_to_ord(x):
- """ Try to comvert a string to an ord """
- if strtype(x) and len(x) == 1:
- try:
- return ord(x[0]), True
- except ValueError:
- return x, False
- return x, False
-
-def strtype(x):
- """ Is x a string type? """
- if type(x) == str:
- return True
- if type(x) == unicode:
- return True
- return False
-
-def magnitude(pos):
- """ Calculate the magnitude of the distance between to blocks. """
- x, y = pos
- return x*x+y*y
-
-def json_load(text):
- """ Load JSON data using what ever resources are available. """
- if OLD_SUGAR_SYSTEM is True:
- _listdata = json.read(text)
- else:
- # strip out leading and trailing whitespace, nulls, and newlines
- text = text.lstrip()
- text = text.replace('\12','')
- text = text.replace('\00','')
- _io = StringIO(text.rstrip())
- _listdata = jload(_io)
- # json converts tuples to lists, so we need to convert back,
- return _tuplify(_listdata)
-
-def _tuplify(tup):
- """ Convert to tuples """
- if type(tup) is not list:
- return tup
- return tuple(map(_tuplify, tup))
-
-def get_id(connection):
- """ Get a connection block ID. """
- if connection is None:
- return None
- return connection.id
-
-def json_dump(data):
- """ Save data using available JSON tools. """
- if OLD_SUGAR_SYSTEM is True:
- return json.write(data)
- else:
- _io = StringIO()
- jdump(data, _io)
- return _io.getvalue()
-
-def get_load_name(suffix, load_save_folder):
- """ Open a load file dialog. """
- _dialog = gtk.FileChooserDialog("Load...", None,
- gtk.FILE_CHOOSER_ACTION_OPEN,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN, gtk.RESPONSE_OK))
- _dialog.set_default_response(gtk.RESPONSE_OK)
- return do_dialog(_dialog, suffix, load_save_folder)
-
-def get_save_name(suffix, load_save_folder, save_file_name):
- """ Open a save file dialog. """
- _dialog = gtk.FileChooserDialog("Save...", None,
- gtk.FILE_CHOOSER_ACTION_SAVE,
- (gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL,
- gtk.STOCK_SAVE,
- gtk.RESPONSE_OK))
- _dialog.set_default_response(gtk.RESPONSE_OK)
- if save_file_name is not None:
- _dialog.set_current_name(save_file_name+suffix)
- return do_dialog(_dialog, suffix, load_save_folder)
-
-#
-# We try to maintain read-compatibility with all versions of Turtle Art.
-# Try pickle first; then different versions of json.
-#
-def data_from_file(ta_file):
- """ Open the .ta file, ignoring any .png file that might be present. """
- file_handle = open(ta_file, "r")
- try:
- _data = pickle.load(file_handle)
- except:
- # Rewind necessary because of failed pickle.load attempt
- file_handle.seek(0)
- _text = file_handle.read()
- _data = data_from_string(_text)
- file_handle.close()
- return _data
-
-def data_from_string(text):
- """ JSON load data from a string. """
- return json_load(text)
-
-def data_to_file(data, ta_file):
- """ Write data to a file. """
- file_handle = file(ta_file, "w")
- file_handle.write(data_to_string(data))
- file_handle.close()
-
-def data_to_string(data):
- """ JSON dump a string. """
- return json_dump(data)
-
-def do_dialog(dialog, suffix, load_save_folder):
- """ Open a file dialog. """
- _result = None
- file_filter = gtk.FileFilter()
- file_filter.add_pattern('*'+suffix)
- file_filter.set_name("Turtle Art")
- dialog.add_filter(file_filter)
- dialog.set_current_folder(load_save_folder)
- _response = dialog.run()
- if _response == gtk.RESPONSE_OK:
- _result = dialog.get_filename()
- load_save_folder = dialog.get_current_folder()
- dialog.destroy()
- return _result, load_save_folder
-
-def save_picture(canvas, file_name=''):
- """ Save the canvas to a file. """
- _pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, canvas.width,
- canvas.height)
- _pixbuf.get_from_drawable(canvas.canvas.images[0],
- canvas.canvas.images[0].get_colormap(),
- 0, 0, 0, 0, canvas.width, canvas.height)
- if file_name != '':
- _pixbuf.save(file_name, 'png')
- return _pixbuf
-
-def save_svg(string, file_name):
- """ Write a string to a file. """
- file_handle = file(file_name, "w")
- file_handle.write(string)
- file_handle.close()
-
-def get_pixbuf_from_journal(dsobject, w, h):
- """ Load a pixbuf from a Journal object. """
- try:
- _pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(dsobject.file_path,
- int(w), int(h))
- except:
- try:
- _pixbufloader = \
- gtk.gdk.pixbuf_loader_new_with_mime_type('image/png')
- _pixbufloader.set_size(min(300, int(w)), min(225, int(h)))
- _pixbufloader.write(dsobject.metadata['preview'])
- _pixbufloader.close()
- _pixbuf = _pixbufloader.get_pixbuf()
- except:
- _pixbuf = None
- return _pixbuf
-
-def get_path(activity, subpath ):
- """ Find a Rainbow-approved place for temporary files. """
- try:
- return(os.path.join(activity.get_activity_root(), subpath))
- except:
- # Early versions of Sugar didn't support get_activity_root()
- return(os.path.join(os.environ['HOME'], ".sugar/default",
- "org.laptop.TurtleArtActivity", subpath))
-
-def image_to_base64(pixbuf, activity):
- """ Convert an image to base64 """
- _file_name = os.path.join(get_path(activity, 'instance'), 'imagetmp.png')
- if pixbuf != None:
- pixbuf.save(_file_name, "png")
- _base64 = os.path.join(get_path(activity, 'instance'), 'base64tmp')
- _cmd = "base64 <" + _file_name + " >" + _base64
- subprocess.check_call(_cmd, shell=True)
- _file_handle = open(_base64, 'r')
- _data = _file_handle.read()
- _file_handle.close()
- return _data
-
-def movie_media_type(name):
- """ Is it movie media? """
- return name.endswith(('.ogv', '.vob', '.mp4', '.wmv', '.mov', '.mpeg'))
-
-def audio_media_type(name):
- """ Is it audio media? """
- return name.endswith(('.ogg', '.oga', '.m4a'))
-
-def image_media_type(name):
- """ Is it image media? """
- return name.endswith(('.png', '.jpg', '.jpeg', '.gif', '.tiff', '.tif',
- '.svg'))
-def text_media_type(name):
- """ Is it text media? """
- return name.endswith(('.txt', '.py', '.lg', '.doc', '.rtf'))
-
-def round_int(num):
- """ Remove trailing decimal places if number is an int """
- try:
- float(num)
- except TypeError:
- _logger.debug("error trying to convert %s to number" % (str(num)))
- raise pythonerror("#syntaxerror")
-
- if int(float(num)) == num:
- return int(num)
- else:
- if float(num)<0:
- _nn = int((float(num)-0.005)*100)/100.
- else:
- _nn = int((float(num)+0.005)*100)/100.
- if int(float(_nn)) == _nn:
- return int(_nn)
- return _nn
-
-def calc_image_size(spr):
- """ Calculate the maximum size for placing an image onto a sprite. """
- return spr.label_safe_width(), spr.label_safe_height()
-
-
-
-# Collapsible stacks live between 'sandwichtop' and 'sandwichbottom' blocks
-
-def reset_stack_arm(top):
- """ When we undock, retract the 'arm' that extends from 'sandwichtop'. """
- if top is not None and top.name == 'sandwichtop':
- if top.ey > 0:
- top.reset_y()
-
-def grow_stack_arm(top):
- """ When we dock, grow an 'arm' from 'sandwichtop'. """
- if top is not None and top.name == 'sandwichtop':
- _bot = find_sandwich_bottom(top)
- if _bot is None:
- return
- if top.ey > 0:
- top.reset_y()
- _ty = top.spr.get_xy()[1]
- _th = top.spr.get_dimensions()[1]
- _by = _bot.spr.get_xy()[1]
- _dy = _by-(_ty + _th)
- if _dy > 0:
- top.expand_in_y(_dy/top.scale)
- top.refresh()
-
-def find_sandwich_top(blk):
- """ Find the sandwich top above this block. """
- # Always follow the main branch of a flow: the first connection.
- _blk = blk.connections[0]
- while _blk is not None:
- if _blk.name in COLLAPSIBLE:
- return None
- if _blk.name in ['repeat', 'if', 'ifelse', 'forever', 'while']:
- if blk != _blk.connections[len(_blk.connections) - 1]:
- return None
- if _blk.name == 'sandwichtop' or _blk.name == 'sandwichtop2':
- return _blk
- blk = _blk
- _blk = _blk.connections[0]
- return None
-
-def find_sandwich_bottom(blk):
- """ Find the sandwich bottom below this block. """
- # Always follow the main branch of a flow: the last connection.
- _blk = blk.connections[len(blk.connections) - 1]
- while _blk is not None:
- if _blk.name == 'sandwichtop' or _blk.name == 'sandwichtop2':
- return None
- if _blk.name in COLLAPSIBLE:
- return _blk
- _blk = _blk.connections[len(_blk.connections) - 1]
- return None
-
-def find_sandwich_top_below(blk):
- """ Find the sandwich top below this block. """
- if blk.name == 'sandwichtop' or blk.name == 'sandwichtop2':
- return blk
- # Always follow the main branch of a flow: the last connection.
- _blk = blk.connections[len(blk.connections) - 1]
- while _blk is not None:
- if _blk.name == 'sandwichtop' or _blk.name == 'sandwichtop2':
- return _blk
- _blk = _blk.connections[len(_blk.connections) - 1]
- return None
-
-def restore_stack(top):
- """ Restore the blocks between the sandwich top and sandwich bottom. """
- _group = find_group(top.connections[len(top.connections) - 1])
- _hit_bottom = False
- _bot = find_sandwich_bottom(top)
- for _blk in _group:
- if not _hit_bottom and _blk == _bot:
- _hit_bottom = True
- if len(_blk.values) == 0:
- _blk.values.append(0)
- else:
- _blk.values[0] = 0
- _olddx = _blk.docks[1][2]
- _olddy = _blk.docks[1][3]
- # Replace 'sandwichcollapsed' shape with 'sandwichbottom' shape
- _blk.name = 'sandwichbottom'
- _blk.spr.set_label(' ')
- _blk.svg.set_show(False)
- _blk.svg.set_hide(True)
- _blk.refresh()
- # Redock to previous block in group
- _you = _blk.connections[0]
- (_yx, _yy) = _you.spr.get_xy()
- _yd = _you.docks[len(_you.docks) - 1]
- (_bx, _by) = _blk.spr.get_xy()
- _dx = _yx + _yd[2] - _blk.docks[0][2] - _bx
- _dy = _yy + _yd[3] - _blk.docks[0][3] - _by
- _blk.spr.move_relative((_dx, _dy))
- # Since the shapes have changed, the dock positions have too.
- _newdx = _blk.docks[1][2]
- _newdy = _blk.docks[1][3]
- _dx += _newdx - _olddx
- _dy += _newdy - _olddy
- else:
- if not _hit_bottom:
- _blk.spr.set_layer(BLOCK_LAYER)
- _blk.status = None
- else:
- _blk.spr.move_relative((_dx, _dy))
- # Add 'sandwichtop' arm
- top.name = 'sandwichtop'
- top.refresh()
- grow_stack_arm(top)
-
-def uncollapse_forks(top, looping=False):
- """ From the top, find and restore any collapsible stacks on forks. """
- if top == None:
- return
- if looping and top.name == 'sandwichtop' or top.name == 'sandwichtop2':
- restore_stack(top)
- return
- if len(top.connections) == 0:
- return
- _blk = top.connections[len(top.connections) - 1]
- while _blk is not None:
- if _blk.name in COLLAPSIBLE:
- return
- if _blk.name == 'sandwichtop' or _blk.name == 'sandwichtop2':
- restore_stack(_blk)
- return
- # Follow a fork
- if _blk.name in ['repeat', 'if', 'ifelse', 'forever', 'while', 'until']:
- top = find_sandwich_top_below(
- _blk.connections[len(_blk.connections) - 2])
- if top is not None:
- uncollapse_forks(top, True)
- if _blk.name == 'ifelse':
- top = find_sandwich_top_below(
- _blk.connections[len(_blk.connections) - 3])
- if top is not None:
- uncollapse_forks(top, True)
- _blk = _blk.connections[len(_blk.connections) - 1]
- return
-
-def collapse_stack(top):
- """ Hide all the blocks between the sandwich top and sandwich bottom. """
- # First uncollapse any nested stacks
- if top == None or top.spr == None:
- return
- uncollapse_forks(top)
- _hit_bottom = False
- _bot = find_sandwich_bottom(top)
- _group = find_group(top.connections[len(top.connections) - 1])
- for _blk in _group:
- if not _hit_bottom and _blk == _bot:
- _hit_bottom = True
- # Replace 'sandwichbottom' shape with 'sandwichcollapsed' shape
- if len(_blk.values) == 0:
- _blk.values.append(1)
- else:
- _blk.values[0] = 1
- _olddx = _blk.docks[1][2]
- _olddy = _blk.docks[1][3]
- _blk.name = 'sandwichcollapsed'
- _blk.svg.set_show(True)
- _blk.svg.set_hide(False)
- _blk._dx = 0
- _blk._ey = 0
- _blk.spr.set_label(' ')
- _blk.resize()
- _blk.spr.set_label(_('click to open'))
- _blk.resize()
- # Redock to sandwich top in group
- _you = find_sandwich_top(_blk)
- (_yx, _yy) = _you.spr.get_xy()
- _yd = _you.docks[len(_you.docks) - 1]
- (_bx, _by) = _blk.spr.get_xy()
- _dx = _yx + _yd[2] - _blk.docks[0][2] - _bx
- _dy = _yy + _yd[3] - _blk.docks[0][3] - _by
- _blk.spr.move_relative((_dx, _dy))
- # Since the shapes have changed, the dock positions have too.
- _newdx = _blk.docks[1][2]
- _newdy = _blk.docks[1][3]
- _dx += _newdx - _olddx
- _dy += _newdy - _olddy
- else:
- if not _hit_bottom:
- _blk.spr.set_layer(HIDE_LAYER)
- _blk.status = 'collapsed'
- else:
- _blk.spr.move_relative((_dx, _dy))
- # Remove 'sandwichtop' arm
- top.name = 'sandwichtop2'
- top.refresh()
-
-def collapsed(blk):
- """ Is this stack collapsed? """
- if blk is not None and blk.name in COLLAPSIBLE and\
- len(blk.values) == 1 and blk.values[0] != 0:
- return True
- return False
-
-def collapsible(blk):
- """ Can this stack be collapsed? """
- if blk is None or blk.name not in COLLAPSIBLE:
- return False
- if find_sandwich_top(blk) is None:
- return False
- return True
-
-def hide_button_hit(spr, x, y):
- """ Did the sprite's hide (contract) button get hit? """
- _red, _green, _blue, _alpha = spr.get_pixel((x, y))
- if (_red == 255 and _green == 0) or _green == 255:
- return True
- else:
- return False
-
-def show_button_hit(spr, x, y):
- """ Did the sprite's show (expand) button get hit? """
- _red, _green, _blue, _alpha = spr.get_pixel((x, y))
- if _green == 254:
- return True
- else:
- return False
-
-def numeric_arg(value):
- """ Dock test: looking for a numeric value """
- if type(convert(value, float)) is float:
- return True
- return False
-
-def zero_arg(value):
- """ Dock test: looking for a zero argument """
- if numeric_arg(value):
- if convert(value, float) == 0:
- return True
- return False
-
-def neg_arg(value):
- """ Dock test: looking for a negative argument """
- if numeric_arg(value):
- if convert(value, float) < 0:
- return True
- return False
-
-def dock_dx_dy(block1, dock1n, block2, dock2n):
- """ Find the distance between the dock points of two blocks. """
- _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:
- return (100, 100)
- if _d1dir == _d2dir:
- 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':
- pass
- elif block1.name in CONTENT_ARGS:
- if _d2type in CONTENT_BLOCKS:
- 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))
-
-def arithmetic_check(blk1, blk2, dock1, dock2):
- """ Dock strings only if they convert to numbers. Avoid /0 and root(-1)"""
- if blk1 == None or blk2 == None:
- return True
- if blk1.name in ['sqrt', 'number', 'string'] and\
- blk2.name in ['sqrt', 'number', 'string']:
- if blk1.name == 'number' or blk1.name == 'string':
- if not numeric_arg(blk1.values[0]) or neg_arg(blk1.values[0]):
- return False
- elif blk2.name == 'number' or blk2.name == 'string':
- if not numeric_arg(blk2.values[0]) or neg_arg(blk2.values[0]):
- return False
- elif blk1.name in ['division2', 'number', 'string'] and\
- blk2.name in ['division2', 'number', 'string']:
- if blk1.name == 'number' or blk1.name == 'string':
- if not numeric_arg(blk1.values[0]):
- return False
- if dock2 == 2 and zero_arg(blk1.values[0]):
- return False
- elif blk2.name == 'number' or blk2.name == 'string':
- if not numeric_arg(blk2.values[0]):
- return False
- if dock1 == 2 and zero_arg(blk2.values[0]):
- return False
- elif blk1.name in ['product2', 'minus2', 'random', 'remainder2',
- 'string'] and\
- blk2.name in ['product2', 'minus2', 'random', 'remainder2',
- 'string']:
- if blk1.name == 'string':
- if not numeric_arg(blk1.values[0]):
- return False
- elif blk1.name == 'string':
- if not numeric_arg(blk2.values[0]):
- return False
- elif blk1.name in ['greater2', 'less2'] and blk2.name == 'string':
- # Non-numeric stings are OK if only both args are strings;
- # Lots of test conditions...
- if dock1 == 1 and blk1.connections[2] is not None:
- if blk1.connections[2].name == 'number':
- if not numeric_arg(blk2.values[0]):
- return False
- elif dock1 == 2 and blk1.connections[1] is not None:
- if blk1.connections[1].name == 'number':
- if not numeric_arg(blk2.values[0]):
- return False
- elif blk2.name in ['greater2', 'less2'] and blk1.name == 'string':
- if dock2 == 1 and blk2.connections[2] is not None:
- if blk2.connections[2].name == 'number':
- if not numeric_arg(blk1.values[0]):
- return False
- elif dock2 == 2 and blk2.connections[1] is not None:
- if blk2.connections[1].name == 'number':
- if not numeric_arg(blk1.values[0]):
- return False
- elif blk1.name in ['greater2', 'less2'] and blk2.name == 'number':
- if dock1 == 1 and blk1.connections[2] is not None:
- if blk1.connections[2].name == 'string':
- if not numeric_arg(blk1.connections[2].values[0]):
- return False
- elif dock1 == 2 and blk1.connections[1] is not None:
- if blk1.connections[1].name == 'string':
- if not numeric_arg(blk1.connections[1].values[0]):
- return False
- elif blk2.name in ['greater2', 'less2'] and blk1.name == 'number':
- if dock2 == 1 and blk2.connections[2] is not None:
- if blk2.connections[2].name == 'string':
- if not numeric_arg(blk2.connections[2].values[0]):
- return False
- elif dock2 == 2 and blk2.connections[1] is not None:
- if blk2.connections[1].name == 'string':
- if not numeric_arg(blk2.connections[1].values[0]):
- return False
- return True
-
-def xy(event):
- """ Where is the mouse event? """
- return map(int, event.get_coords())
-
-"""
-Utilities related to finding blocks in stacks.
-"""
-
-def find_block_to_run(blk):
- """ Find a stack to run (any stack without a 'def action'on the top). """
- _top = find_top_block(blk)
- if blk == _top and blk.name[0:3] is not 'def':
- return True
- else:
- return False
-
-def find_top_block(blk):
- """ Find the top block in a stack. """
- if blk is None:
- return None
- if len(blk.connections) == 0:
- return blk
- while blk.connections[0] is not None:
- blk = blk.connections[0]
- return blk
-
-def find_start_stack(blk):
- """ Find a stack with a 'start' block on top. """
- if blk is None:
- return False
- if find_top_block(blk).name == 'start':
- return True
- else:
- return False
-
-def find_group(blk):
- """ Find the connected group of block in a stack. """
- 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(find_group(_blk2))
- return _group
-
-def find_blk_below(blk, name):
- """ Find a specific block below this block. """
- if blk == None or len(blk.connections) == 0:
- return
- _group = find_group(blk)
- for _gblk in _group:
- if _gblk.name == name:
- return _gblk
- return None
-
-def olpc_xo_1():
- """ Is the an OLPC XO-1 or XO-1.5? """
- return os.path.exists('/etc/olpc-release') or \
- os.path.exists('/sys/power/olpc-pm')
-
-def walk_stack(tw, blk):
- """ Convert blocks to logo psuedocode. """
- top = find_top_block(blk)
- if blk == top:
- code = tw.lc.run_blocks(top, tw.block_list.list, False)
- return code
- else:
- return []