Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2012-07-08 12:33:14 (GMT)
committer Walter Bender <walter.bender@gmail.com>2012-07-08 12:33:14 (GMT)
commit298aa030cc8aa99012fe5d7bc80f323b94bf0cda (patch)
treedd0a5e8f8548bdb3fd70139768a321256a534066
parent8d371d53196c046f1b2b86b57cee5faa1eedf484 (diff)
resync with TB 149
-rw-r--r--TurtleArt/sprites.py16
-rw-r--r--TurtleArt/tabasics.py164
-rw-r--r--TurtleArt/tablock.py275
-rw-r--r--TurtleArt/tacanvas.py3
-rw-r--r--TurtleArt/tacollaboration.py40
-rw-r--r--TurtleArt/taconstants.py11
-rw-r--r--TurtleArt/tagplay.py52
-rw-r--r--TurtleArt/talogo.py78
-rw-r--r--TurtleArt/tapalette.py36
-rwxr-xr-xTurtleArt/tasprite_factory.py134
-rw-r--r--TurtleArt/tautils.py310
-rw-r--r--TurtleArt/tawindow.py1250
12 files changed, 1473 insertions, 896 deletions
diff --git a/TurtleArt/sprites.py b/TurtleArt/sprites.py
index c400d88..ce456eb 100644
--- a/TurtleArt/sprites.py
+++ b/TurtleArt/sprites.py
@@ -171,6 +171,8 @@ class Sprite:
self._rescale = [True]
self._horiz_align = ["center"]
self._vert_align = ["middle"]
+ self._x_pos = [None]
+ self._y_pos = [None]
self._fd = None
self._bold = False
self._italic = False
@@ -289,6 +291,8 @@ class Sprite:
self._rescale.append(self._rescale[0])
self._horiz_align.append(self._horiz_align[0])
self._vert_align.append(self._vert_align[0])
+ self._x_pos.append(self._x_pos[0])
+ self._y_pos.append(self._y_pos[0])
def set_font(self, font):
''' Set the font for a label '''
@@ -310,13 +314,15 @@ class Sprite:
return
def set_label_attributes(self, scale, rescale=True, horiz_align="center",
- vert_align="middle", i=0):
+ vert_align="middle", x_pos=None, y_pos=None, i=0):
''' Set the various label attributes '''
self._extend_labels_array(i)
self._scale[i] = scale
self._rescale[i] = rescale
self._horiz_align[i] = horiz_align
self._vert_align[i] = vert_align
+ self._x_pos[i] = x_pos
+ self._y_pos[i] = y_pos
def hide(self):
''' Hide a sprite '''
@@ -399,14 +405,18 @@ class Sprite:
pl.set_font_description(self._fd)
w = pl.get_size()[0] / pango.SCALE
j -= 1
- if self._horiz_align[i] == "center":
+ if self._x_pos[i] is not None:
+ x = int(self.rect.x + self._x_pos[i])
+ elif self._horiz_align[i] == "center":
x = int(self.rect.x + self._margins[0] + (my_width - w) / 2)
elif self._horiz_align[i] == 'left':
x = int(self.rect.x + self._margins[0])
else: # right
x = int(self.rect.x + self.rect.width - w - self._margins[2])
h = pl.get_size()[1] / pango.SCALE
- if self._vert_align[i] == "middle":
+ if self._y_pos[i] is not None:
+ y = int(self.rect.y + self._y_pos[i])
+ elif self._vert_align[i] == "middle":
y = int(self.rect.y + self._margins[1] + (my_height - h) / 2)
elif self._vert_align[i] == "top":
y = int(self.rect.y + self._margins[1])
diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py
index 1704224..6bff0dc 100644
--- a/TurtleArt/tabasics.py
+++ b/TurtleArt/tabasics.py
@@ -69,6 +69,7 @@ from talogo import primitive_dictionary, logoerror
from tautils import convert, chr_to_ord, round_int, strtype
from taconstants import BLACK, WHITE, CONSTANTS, XO30
+
def _num_type(x):
""" Is x a number type? """
if type(x) == int:
@@ -137,7 +138,7 @@ class Palettes():
lambda self, x: primitive_dictionary['move'](
self.tw.canvas.forward, -x))
- primitive_dictionary['clean'] = self._prim_clean
+ primitive_dictionary['clean'] = self.tw.lc.prim_clear
palette.add_block('clean',
style='basic-style-extended-vertical',
label=_('clean'),
@@ -292,7 +293,6 @@ setxy :x :y\rpendown\rend\r')
lambda self: self.tw.canvas.setpen(True))
palette.add_block('setpensize',
- hidden=True,
style='basic-style-1arg',
label=_('set pen size'),
prim_name='setpensize',
@@ -307,7 +307,6 @@ turtle'))
round :a\rend\r')
palette.add_block('fillscreen',
- hidden=True,
style='basic-style-2arg',
label=[_('fill screen'), _('color'), _('shade')],
prim_name='fillscreen',
@@ -321,7 +320,6 @@ shade)'))
:shade\rtasetshade :shade\rsetbackground :color\rend\r')
palette.add_block('pensize',
- hidden=True,
style='box-style',
label=_('pen size'),
help_string=_('holds current pen size (can be used \
@@ -333,38 +331,6 @@ in place of a number block)'),
define_logo_function('tapensize', 'to tapensize\routput first round \
pensize\rend\r')
- palette.add_block('startfill',
- hidden=True,
- style='basic-style-extended-vertical',
- label=_('start fill'),
- prim_name='startfill',
- help_string=_('starts filled polygon (used with end \
-fill block)'))
- self.tw.lc.def_prim('startfill', 0,
- lambda self: self.tw.canvas.start_fill())
-
- palette.add_block('stopfill',
- hidden=True,
- style='basic-style-extended-vertical',
- label=_('end fill'),
- prim_name='stopfill',
- help_string=_('completes filled polygon (used with \
-start fill block)'))
- self.tw.lc.def_prim('stopfill', 0,
- lambda self: self.tw.canvas.stop_fill())
-
- def _color_palette(self):
- """ The basic Turtle Art color palette """
-
- if self.tw.hw == XO30:
- palette = make_palette('pen',
- colors=["#00FFFF", "#00A0A0"],
- help_string=_('Palette of pen colors'))
- else:
- palette = make_palette('colors',
- colors=["#00FFFF", "#00A0A0"],
- help_string=_('Palette of pen colors'))
-
palette.add_block('setcolor',
style='basic-style-1arg',
label=_('set color'),
@@ -378,7 +344,6 @@ turtle'))
'color', self.tw.canvas.setcolor, x))
palette.add_block('setshade',
- hidden=True,
style='basic-style-1arg',
label=_('set shade'),
prim_name='setshade',
@@ -391,7 +356,6 @@ turtle'))
'shade', self.tw.canvas.setshade, x))
palette.add_block('setgray',
- hidden=True,
style='basic-style-1arg',
label=_('set gray'),
prim_name='setgray',
@@ -413,7 +377,6 @@ in place of a number block)'),
self.tw.lc.def_prim('color', 0, lambda self: self.tw.canvas.color)
palette.add_block('shade',
- hidden=True,
style='box-style',
label=_('shade'),
help_string=_('holds current pen shade'),
@@ -423,15 +386,44 @@ in place of a number block)'),
self.tw.lc.def_prim('shade', 0, lambda self: self.tw.canvas.shade)
palette.add_block('gray',
- hidden=True,
style='box-style',
label=_('gray'),
- help_string=_('holds current gray level (can be used \
-in place of a number block)'),
+ help_string=_('holds current gray level (can be \
+used in place of a number block)'),
value_block=True,
prim_name='gray')
self.tw.lc.def_prim('gray', 0, lambda self: self.tw.canvas.gray)
+ palette.add_block('startfill',
+ style='basic-style-extended-vertical',
+ label=_('start fill'),
+ prim_name='startfill',
+ help_string=_('starts filled polygon (used with end \
+fill block)'))
+ self.tw.lc.def_prim('startfill', 0,
+ lambda self: self.tw.canvas.start_fill())
+
+ palette.add_block('stopfill',
+ style='basic-style-extended-vertical',
+ label=_('end fill'),
+ prim_name='stopfill',
+ help_string=_('completes filled polygon (used with \
+start fill block)'))
+ self.tw.lc.def_prim('stopfill', 0,
+ lambda self: self.tw.canvas.stop_fill())
+
+ def _color_palette(self):
+ """ The basic Turtle Art color palette """
+
+ if self.tw.hw == XO30:
+ palette = make_palette('pen',
+ colors=["#00FFFF", "#00A0A0"],
+ help_string=_('Palette of pen colors'))
+ else:
+ palette = make_palette('colors',
+ colors=["#00FFFF", "#00A0A0"],
+ help_string=_('Palette of pen colors'))
+
self._make_constant(palette, 'red', _('red'), CONSTANTS['red'])
self._make_constant(palette, 'orange', _('orange'),
CONSTANTS['orange'])
@@ -571,8 +563,8 @@ tasetshade :shade \r')
top numeric input'))
self.tw.lc.def_prim(
'minus', 2, lambda self, x, y: primitive_dictionary['minus'](x, y))
- define_logo_function('taminus', 'to taminus :y :x\routput sum :x minus \
-:y\rend\r')
+ define_logo_function('taminus', 'to taminus :y :x\routput sum :x \
+minus :y\rend\r')
primitive_dictionary['product'] = self._prim_product
palette.add_block('product2',
@@ -593,8 +585,8 @@ top numeric input'))
special_name=_('divide'),
prim_name='division',
logo_command='quotient',
- help_string=_('divides top numeric input (numerator) \
-by bottom numeric input (denominator)'))
+ help_string=_('divides top numeric input \
+(numerator) by bottom numeric input (denominator)'))
self.tw.lc.def_prim(
'division', 2,
lambda self, x, y: primitive_dictionary['division'](x, y))
@@ -623,7 +615,6 @@ blocks'))
primitive_dictionary['sqrt'] = self._prim_sqrt
palette.add_block('sqrt',
- hidden=True,
style='number-style-1arg',
label=_('√'),
special_name=_('square root'),
@@ -641,8 +632,8 @@ blocks'))
default=[0, 100],
prim_name='random',
logo_command='tarandom',
- help_string=_('returns random number between minimum \
-(top) and maximum (bottom) values'))
+ help_string=_('returns random number between \
+minimum (top) and maximum (bottom) values'))
self.tw.lc.def_prim(
'random', 2, lambda self, x, y: primitive_dictionary['random'](
x, y))
@@ -662,8 +653,8 @@ operators'))
hidden=True,
style='compare-porch-style',
label='>',
- special_name=_('greater than'),
string_or_number=True,
+ special_name=_('greater than'),
prim_name='greater?',
logo_command='greater?',
help_string=_('logical greater-than operator'))
@@ -671,9 +662,6 @@ operators'))
'greater?', 2,
lambda self, x, y: primitive_dictionary['more'](x, y))
- if self.tw.canvas.width > 1024:
- self._make_constant(palette, 'true', _('True'), 1)
-
primitive_dictionary['less'] = self._prim_less
palette.add_block('less2',
hidden=True,
@@ -687,21 +675,18 @@ operators'))
self.tw.lc.def_prim(
'less?', 2, lambda self, x, y: primitive_dictionary['less'](x, y))
- if self.tw.canvas.width > 1024:
- self._make_constant(palette, 'false', _('False'), 0)
-
primitive_dictionary['equal'] = self._prim_equal
palette.add_block('equal2',
hidden=True,
style='compare-style',
label='=',
- string_or_number=True,
special_name=_('equal'),
+ string_or_number=True,
prim_name='equal?',
logo_command='equal?',
help_string=_('logical equal-to operator'))
- self.tw.lc.def_prim(
- 'equal?', 2, lambda self, x, y: primitive_dictionary['equal'](x, y))
+ self.tw.lc.def_prim('equal?', 2,
+ lambda self, x, y: primitive_dictionary['equal'](x, y))
palette.add_block('not',
hidden=True,
@@ -745,7 +730,6 @@ operators'))
primitive_dictionary['wait'] = self._prim_wait
palette.add_block('wait',
- hidden=True,
style='basic-style-1arg',
label=_('wait'),
prim_name='wait',
@@ -758,20 +742,21 @@ number of seconds'))
primitive_dictionary['forever'] = self._prim_forever
palette.add_block('forever',
hidden=True,
- style='flow-style',
+ style='clamp-style',
label=_('forever'),
prim_name='forever',
- default=[None, 'vspace'],
+ default=[None, None],
logo_command='forever',
help_string=_('loops forever'))
- self.tw.lc.def_prim('forever', 1, primitive_dictionary['forever'], True)
+ self.tw.lc.def_prim('forever', 1, primitive_dictionary['forever'],
+ True)
primitive_dictionary['repeat'] = self._prim_repeat
palette.add_block('repeat',
- style='flow-style-1arg',
- label=[' ', _('repeat')],
+ style='clamp-style-1arg',
+ label=_('repeat'),
prim_name='repeat',
- default=[4, None, 'vspace'],
+ default=[4, None, None],
logo_command='repeat',
special_name=_('repeat'),
help_string=_('loops specified number of times'))
@@ -780,10 +765,10 @@ number of seconds'))
primitive_dictionary['if'] = self._prim_if
palette.add_block('if',
hidden=True,
- style='flow-style-boolean',
- label=[_('if'), ' ', _('then')],
+ style='clamp-style-boolean',
+ label=[_('if'), _('then'), ''],
prim_name='if',
- default=[None, None, 'vspace'],
+ default=[None, None, None],
special_name=_('if then'),
logo_command='if',
help_string=_('if-then operator that uses boolean \
@@ -792,20 +777,30 @@ operators from Numbers palette'))
primitive_dictionary['ifelse'] = self._prim_ifelse
palette.add_block('ifelse',
- hidden=True,
- style='flow-style-else',
- label=[_('if'), ' ', _('then else')],
+ hidden=True, # Too big to fit palette
+ style='clamp-style-else',
+ label=[_('if'), _('then'), _('else')],
prim_name='ifelse',
- default=[None, 'vspace', None, 'vspace'],
+ default=[None, None, None, None],
logo_command='ifelse',
special_name=_('if then else'),
help_string=_('if-then-else operator that uses \
boolean operators from Numbers palette'))
self.tw.lc.def_prim('ifelse', 3, primitive_dictionary['ifelse'], True)
+ # macro
+ palette.add_block('ifthenelse',
+ hidden=True,
+ style='basic-style-extended-vertical',
+ label=_('if then else'),
+ help_string=_('if-then-else operator that uses \
+boolean operators from Numbers palette'))
+
+ # Deprecated
palette.add_block('hspace',
style='flow-style-tail',
- label=' ',
+ hidden=True,
+ label='',
prim_name='nop',
special_name=_('horizontal space'),
help_string=_('jogs stack right'))
@@ -813,7 +808,7 @@ boolean operators from Numbers palette'))
palette.add_block('vspace',
style='basic-style-extended-vertical',
- label=' ',
+ label='',
prim_name='nop',
special_name=_('vertical space'),
help_string=_('jogs stack down'))
@@ -852,9 +847,9 @@ buttons'))
palette.add_block('storeinbox1',
style='basic-style-1arg',
label=_('store in box 1'),
- string_or_number=True,
prim_name='storeinbox1',
default=100,
+ string_or_number=True,
logo_command='make "box1',
help_string=_('stores numeric value in Variable 1'))
self.tw.lc.def_prim('storeinbox1', 1,
@@ -864,9 +859,9 @@ buttons'))
palette.add_block('storeinbox2',
style='basic-style-1arg',
label=_('store in box 2'),
- string_or_number=True,
prim_name='storeinbox2',
default=100,
+ string_or_number=True,
logo_command='make "box2',
help_string=_('stores numeric value in Variable 2'))
self.tw.lc.def_prim('storeinbox2', 1,
@@ -874,7 +869,6 @@ buttons'))
'box2', None, x))
palette.add_block('string',
- hidden=True,
style='box-style',
label=_('text'),
default=_('text'),
@@ -901,11 +895,10 @@ buttons'))
primitive_dictionary['box'] = self._prim_box
palette.add_block('box',
- hidden=True,
style='number-style-1strarg',
label=_('box'),
- prim_name='box',
string_or_number=True,
+ prim_name='box',
default=_('my box'),
logo_command='box',
help_string=_('named variable (numeric value)'))
@@ -913,7 +906,6 @@ buttons'))
lambda self, x: primitive_dictionary['box'](x))
palette.add_block('storein',
- hidden=True,
style='basic-style-2arg',
label=[_('store in'), _('box'), _('value')],
string_or_number=True,
@@ -927,7 +919,6 @@ variable'))
'box3', x, y))
palette.add_block('hat',
- hidden=True,
style='basic-style-head-1arg',
label=_('action'),
prim_name='nop3',
@@ -955,7 +946,6 @@ variable'))
primitive_dictionary['stack'] = self._prim_stack
palette.add_block('stack',
- hidden=True,
style='basic-style-1arg',
label=_('action'),
string_or_number=True,
@@ -1007,12 +997,6 @@ variable'))
# Block primitives
- def _prim_clean(self):
- ''' Clean block '''
- self.tw.lc.prim_clear
- self.tw.display_coordinates()
- self.tw.parent.restore_challenge()
-
def _prim_and(self, x, y):
""" Logical and """
return x & y
@@ -1217,6 +1201,8 @@ variable'))
def _prim_less(self, x, y):
""" Compare numbers and strings """
+ if type(x) == list or type(y) == list:
+ raise logoerror("#syntaxerror")
try:
return float(x) < float(y)
except ValueError:
diff --git a/TurtleArt/tablock.py b/TurtleArt/tablock.py
index f835994..7e48d43 100644
--- a/TurtleArt/tablock.py
+++ b/TurtleArt/tablock.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-#Copyright (c) 2010,11 Walter Bender
+#Copyright (c) 2010-2012 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
@@ -23,7 +23,8 @@ import gtk
from gettext import gettext as _
from taconstants import EXPANDABLE, EXPANDABLE_ARGS, OLD_NAMES, CONSTANTS, \
- STANDARD_STROKE_WIDTH, BLOCK_SCALE, BOX_COLORS, GRADIENT_COLOR
+ STANDARD_STROKE_WIDTH, BLOCK_SCALE, BOX_COLORS, GRADIENT_COLOR, \
+ EXPANDABLE_FLOW
from tapalette import palette_blocks, block_colors, expandable_blocks, \
content_blocks, block_names, block_primitives, block_styles, \
special_block_colors
@@ -139,6 +140,7 @@ class Block:
self.dx = 0
self.ex = 0
self.ey = 0
+ self.ey2 = 0
self._ei = 0
self._font_size = [6.0, 4.5]
self._image = None
@@ -156,7 +158,6 @@ class Block:
'basic-style-2arg': self._make_basic_style_2arg,
'basic-style-3arg': self._make_basic_style_3arg,
'basic-style-var-arg': self._make_basic_style_var_arg,
- 'invisible': self._make_invisible_style,
'bullet-style': self._make_bullet_style,
'box-style': self._make_box_style,
'box-style-media': self._make_media_style,
@@ -170,19 +171,13 @@ class Block:
'compare-porch-style': self._make_compare_porch_style,
'boolean-style': self._make_boolean_style,
'not-style': self._make_not_style,
- 'flow-style': self._make_flow_style,
+ 'clamp-style': self._make_clamp_style,
+ 'clamp-style-collapsible': self._make_clamp_style_collapsible,
+ 'clamp-style-collapsed': self._make_clamp_style_collapsed,
+ 'clamp-style-1arg': self._make_clamp_style_1arg,
+ 'clamp-style-boolean': self._make_clamp_style_boolean,
+ 'clamp-style-else': self._make_clamp_style_else,
'flow-style-tail': self._make_flow_style_tail,
- 'flow-style-1arg': self._make_flow_style_1arg,
- 'flow-style-boolean': self._make_flow_style_boolean,
- 'flow-style-else': self._make_flow_style_else,
- 'collapsible-top': [self._make_collapsible_style_top, True, True],
- 'collapsible-top-no-arm': [self._make_collapsible_style_top,
- False, True],
- 'collapsible-top-no-label': [self._make_collapsible_style_top,
- True, False],
- 'collapsible-top-no-arm-no-label': [
- self._make_collapsible_style_top, False, False],
- 'collapsible-bottom': self._make_collapsible_style_bottom,
'portfolio-style-2x2': self._make_portfolio_style_2x2,
'portfolio-style-1x1': self._make_portfolio_style_1x1,
'portfolio-style-2x1': self._make_portfolio_style_2x1,
@@ -232,6 +227,8 @@ class Block:
return True
if self.name in EXPANDABLE_ARGS:
return True
+ if self.name in EXPANDABLE_FLOW:
+ return True
return False
def cloneable(self):
@@ -246,12 +243,12 @@ class Block:
def highlight(self):
""" We may want to highlight a block... """
- if self.spr is not None:
+ if self.spr is not None and self.status is not 'collapsed':
self.spr.set_shape(self.shapes[1])
def unhighlight(self):
""" Or unhighlight it. """
- if self.spr is not None:
+ if self.spr is not None and self.status is not 'collapsed':
self.spr.set_shape(self.shapes[0])
def resize(self):
@@ -290,7 +287,6 @@ class Block:
self.scale = scale
for i in range(len(self._font_size)):
self._font_size[i] *= self.scale
- self._set_label_attributes()
self.svg.set_scale(self.scale)
self.refresh()
self.spr.inval()
@@ -305,6 +301,7 @@ class Block:
return
self._make_block(self.svg)
self._set_margins()
+ self._set_label_attributes()
self.spr.set_shape(self.shapes[0])
def add_arg(self, keep_expanding=True):
@@ -336,6 +333,22 @@ class Block:
self.svg.set_show(False)
self.refresh()
+ def expand_in_y2(self, dy):
+ """ We may want to grow a block vertically. """
+ if self.spr is None:
+ return
+ self.ey2 += dy
+ if self.type == 'block':
+ if self.ey2 > 0:
+ self.svg.set_hide(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(False)
+ self.refresh()
+
def expand_in_x(self, dx):
""" We may want to grow a block horizontally. """
if self.spr is None:
@@ -349,6 +362,42 @@ class Block:
self.svg.set_show(False)
self.refresh()
+ def contract_in_y(self, dy):
+ """ We may want to shrink a block veritcally. """
+ if self.spr is None:
+ return
+ self.ey -= dy
+ if self.ey < 0:
+ self.ey = 0
+ if self.type == 'block':
+ if self.ey > 0:
+ self.svg.set_hide(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(False)
+ self.refresh()
+
+ def contract_in_x(self, dx):
+ """ We may want to shrink a block horizontally. """
+ if self.spr is None:
+ return
+ self.ex -= dx
+ if self.ex < 0:
+ self.ex = 0
+ if self.type == 'block':
+ if self.ex > 0:
+ self.svg.set_hide(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(True)
+ else:
+ self.svg.set_hide(False)
+ self.svg.set_show(False)
+ self.refresh()
+
def reset_x(self):
if self.spr is None:
return 0
@@ -375,10 +424,23 @@ class Block:
self.refresh()
return dy
+ def reset_y2(self):
+ if self.spr is None:
+ return 0
+ dy = -self.ey2
+ self.ey2 = 0
+ self.svg.set_hide(False)
+ if self.type == 'block':
+ self.svg.set_show(True)
+ else: # 'proto'
+ self.svg.set_show(False)
+ self.refresh()
+ return dy
+
def get_expand_x_y(self):
if self.spr is None:
return(0, 0)
- return (self.ex, self.ey)
+ return (self.ex, self.ey, self.ey2)
def _new_block_from_factory(self, sprite_list, x, y, copy_block=None):
self.svg = SVG()
@@ -459,28 +521,51 @@ class Block:
for i in range(n):
if self.name in block_styles['compare-porch-style']:
self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
- True, 'center', 'bottom', i)
+ True, 'center', 'bottom', i=i)
elif self.name in block_styles['number-style-porch']:
self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
- True, 'right', 'bottom', i)
-
- elif self.name in block_styles['flow-style-boolean'] or \
- self.name in block_styles['flow-style-else']:
- self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
- True, 'left', 'middle', 0)
- self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
- True, 'right', 'top', 1)
- self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
- True, 'right', 'bottom', 2)
+ True, 'right', 'bottom', i=i)
+ elif self.name in EXPANDABLE_FLOW:
+ self._calc_moving_labels(i)
elif i == 1: # top
self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
- True, 'right', 'top', i)
+ True, 'right', 'top', i=i)
elif i == 2: # bottom
self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
- True, 'right', 'bottom', i)
+ True, 'right', 'bottom', i=i)
else:
self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
- True, 'center', 'middle', i)
+ True, 'center', 'middle', i=i)
+
+ def _calc_moving_labels(self, i):
+ ''' Some labels move as blocks change shape/size '''
+ if self.name in block_styles['clamp-style'] or \
+ self.name in block_styles['clamp-style-collapsible']:
+ y = int((self.docks[0][3] + self.docks[1][3]) / 3)
+ self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
+ True, 'right', y_pos=y, i=0)
+ elif self.name in block_styles['clamp-style-1arg']:
+ y = self.docks[1][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
+ True, 'right', y_pos=y, i=0)
+ elif self.name in block_styles['clamp-style-boolean']:
+ y = self.docks[1][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
+ True, 'right', y_pos=y, i=0)
+ y = self.docks[2][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
+ True, 'right', y_pos=y, i=1)
+ elif self.name in block_styles['clamp-style-else']:
+ y = self.docks[1][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
+ True, 'right', y_pos=y, i=0)
+ y = self.docks[2][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
+ True, 'right', y_pos=y, i=1)
+ y = self.docks[3][3] - int(int(self._font_size[0] * 1.3))
+ self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
+ True, 'right', y_pos=y, i=2)
+
def _set_labels(self, i, label):
if self.spr is None:
@@ -502,8 +587,8 @@ class Block:
else:
self.block_methods[k](svg)
return
- error_output('block type not found %s' % (self.name))
- self.block_methods['basic-style'](svg)
+ error_output('ERROR: block type not found %s' % (self.name))
+ self.block_methods['blank-style'](svg)
def _set_colors(self, svg):
if self._custom_colors:
@@ -778,107 +863,109 @@ class Block:
['bool', False, self.svg.docks[1][0],
self.svg.docks[1][1]]]
- def _make_flow_style(self, svg):
- self.svg.expand(10 + self.dx + self.ex, self.ey)
+ def _make_clamp_style(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y)
self.svg.set_slot(True)
self.svg.set_tab(True)
- self._make_block_graphics(svg, self.svg.basic_flow)
+ self.svg.second_clamp(False)
+ self._make_block_graphics(svg, self.svg.clamp)
self.docks = [['flow', True, self.svg.docks[0][0],
self.svg.docks[0][1]],
['flow', False, self.svg.docks[1][0],
self.svg.docks[1][1], '['],
- ['flow', False, self.svg.docks[2][0],
- self.svg.docks[2][1], ']']]
+ # Skip bottom of clamp
+ ['flow', False, self.svg.docks[3][0],
+ self.svg.docks[3][1], ']']]
- def _make_flow_style_tail(self, svg):
- self.svg.expand(10 + self.dx + self.ex, self.ey)
+ def _make_clamp_style_collapsible(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y)
self.svg.set_slot(True)
- self.svg.set_tab(False)
- self._make_block_graphics(svg, self.svg.basic_flow)
+ self.svg.set_tab(True)
+ self.svg.set_collapsible(True)
+ self.svg.second_clamp(False)
+ self._make_block_graphics(svg, self.svg.clamp)
self.docks = [['flow', True, self.svg.docks[0][0],
self.svg.docks[0][1]],
['flow', False, self.svg.docks[1][0],
- self.svg.docks[1][1]]]
+ self.svg.docks[1][1], '['],
+ # Skip bottom of clamp
+ ['flow', False, self.svg.docks[3][0],
+ self.svg.docks[3][1], ']']]
- def _make_flow_style_1arg(self, svg):
- self.svg.expand(self.dx + self.ex, self.ey)
+ def _make_clamp_style_collapsed(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y)
+ self.svg.set_show(True)
+ self._make_block_graphics(svg, self.svg.basic_block)
+ self.docks = [['flow', True, self.svg.docks[0][0],
+ self.svg.docks[0][1]],
+ ['unavailable', True, 0, self.svg.docks[0][1] + 10, '['],
+ ['flow', False, self.svg.docks[1][0],
+ self.svg.docks[1][1], ']']]
+
+ def _make_clamp_style_1arg(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y)
self.svg.set_slot(True)
self.svg.set_tab(True)
self.svg.set_innie([True])
- self._make_block_graphics(svg, self.svg.basic_flow)
+ self.svg.second_clamp(False)
+ self._make_block_graphics(svg, self.svg.clamp)
self.docks = [['flow', True, self.svg.docks[0][0],
self.svg.docks[0][1]],
['number', False, self.svg.docks[1][0],
self.svg.docks[1][1]],
['flow', False, self.svg.docks[2][0],
self.svg.docks[2][1], '['],
- ['flow', False, self.svg.docks[3][0],
- self.svg.docks[3][1], ']']]
+ # Skip bottom of clamp
+ ['flow', False, self.svg.docks[4][0],
+ self.svg.docks[4][1], ']']]
- def _make_flow_style_boolean(self, svg):
- self.svg.expand(self.dx + self.ex, self.ey)
+ def _make_clamp_style_boolean(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y)
self.svg.set_slot(True)
self.svg.set_tab(True)
self.svg.set_boolean(True)
- self._make_block_graphics(svg, self.svg.basic_flow)
+ self.svg.second_clamp(False)
+ self._make_block_graphics(svg, self.svg.clamp)
self.docks = [['flow', True, self.svg.docks[0][0],
self.svg.docks[0][1]],
['bool', False, self.svg.docks[1][0],
- self.svg.docks[1][1]],
+ self.svg.docks[1][1]],
['flow', False, self.svg.docks[2][0],
self.svg.docks[2][1], '['],
- ['flow', False, self.svg.docks[3][0],
- self.svg.docks[3][1], ']']]
+ # Skip bottom of clamp
+ ['flow', False, self.svg.docks[4][0],
+ self.svg.docks[4][1], ']']]
- def _make_flow_style_else(self, svg):
- self.svg.expand(self.dx + self.ex, self.ey)
+ def _make_clamp_style_else(self, svg, extend_x=0, extend_y=4):
+ self.svg.expand(self.dx + self.ex + extend_x, self.ey + extend_y,
+ self.dx + self.ex + extend_x, self.ey2 + extend_y)
self.svg.set_slot(True)
self.svg.set_tab(True)
- self.svg.set_else(True)
self.svg.set_boolean(True)
- self._make_block_graphics(svg, self.svg.basic_flow)
+ self.svg.second_clamp(True)
+ self._make_block_graphics(svg, self.svg.clamp)
self.docks = [['flow', True, self.svg.docks[0][0],
self.svg.docks[0][1]],
['bool', False, self.svg.docks[1][0],
self.svg.docks[1][1]],
- ['flow', False, self.svg.docks[3][0],
- self.svg.docks[3][1], '['],
['flow', False, self.svg.docks[2][0],
- self.svg.docks[2][1], ']['],
+ self.svg.docks[2][1], '['],
+ # Skip bottom of clamp
['flow', False, self.svg.docks[4][0],
- self.svg.docks[4][1], ']']]
+ self.svg.docks[4][1], ']['],
+ # Skip bottom of clamp
+ ['flow', False, self.svg.docks[6][0],
+ self.svg.docks[6][1], ']']]
- def _make_collapsible_style_top(self, svg, arm=True, label=True):
- self.svg.expand(self.dx + self.ex, self.ey)
- self.svg.set_arm(arm)
- self.svg.set_show(not arm)
- self._make_block_graphics(svg, self.svg.sandwich_top, label)
- if label:
- self.docks = [['flow', True, self.svg.docks[0][0],
- self.svg.docks[0][1]],
- ['number', False, self.svg.docks[1][0],
- self.svg.docks[1][1]],
- ['flow', False, self.svg.docks[2][0],
- self.svg.docks[2][1]]]
- else:
- self.docks = [['flow', True, self.svg.docks[0][0],
- self.svg.docks[0][1]],
- ['flow', False, self.svg.docks[1][0],
- self.svg.docks[1][1]]]
-
- def _make_collapsible_style_bottom(self, svg):
- self.svg.expand(self.dx + self.ex, self.ey)
- self._make_block_graphics(svg, self.svg.sandwich_bottom)
- self.docks = [['flow', True, self.svg.docks[0][0],
- self.svg.docks[0][1]], ['flow', False,
- self.svg.docks[1][0], self.svg.docks[1][1]]]
-
- def _make_invisible_style(self, svg):
- self._make_block_graphics(svg, self.svg.invisible)
- # force dock positions to be the same
+ def _make_flow_style_tail(self, svg):
+ self.svg.expand(10 + self.dx + self.ex, self.ey)
+ self.svg.set_slot(True)
+ self.svg.set_tab(False)
+ self._make_block_graphics(svg, self.svg.basic_flow)
self.docks = [['flow', True, self.svg.docks[0][0],
- self.svg.docks[0][1]], ['flow', False,
- self.svg.docks[0][0], self.svg.docks[0][1]]]
+ self.svg.docks[0][1]],
+ ['flow', False, self.svg.docks[1][0],
+ self.svg.docks[1][1]]]
# Depreciated block styles
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index 5bd0326..878d40d 100644
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -21,6 +21,7 @@
#THE SOFTWARE.
import gtk
+import gobject
from math import sin, cos, atan, pi, sqrt
import os
import pango
@@ -592,7 +593,7 @@ class TurtleGraphics:
round_int(width),
round_int(height),
data]]))
- self.tw.send_event(event)
+ gobject.idle_add(self.tw.send_event, event)
os.remove(tmp_file)
def draw_text(self, label, x, y, size, w, share=True):
diff --git a/TurtleArt/tacollaboration.py b/TurtleArt/tacollaboration.py
index 8316c7a..a025074 100644
--- a/TurtleArt/tacollaboration.py
+++ b/TurtleArt/tacollaboration.py
@@ -1,4 +1,4 @@
-#Copyright (c) 2011, Walter Bender
+#Copyright (c) 2011-12 Walter Bender
#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
#Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,10 +22,12 @@
from dbus.service import signal
from dbus.gobject_service import ExportedGObject
import telepathy
-
+import os
import gtk
import base64
+from gettext import gettext as _
+
from TurtleArt.tautils import data_to_string, data_from_string, get_path, \
base64_to_image, debug_output, error_output
from TurtleArt.taconstants import DEFAULT_TURTLE_COLORS
@@ -54,7 +56,7 @@ class Collaboration():
self._setup_dispatch_table()
def setup(self):
- # TODO: hand off role of master is sharer leaves
+ # TODO: hand off role of master if sharer leaves
self.pservice = presenceservice.get_instance()
self.initiating = None # sharing (True) or joining (False)
@@ -81,7 +83,9 @@ class Collaboration():
'w': self._set_pen_width,
'p': self._set_pen_state,
'F': self._fill_polygon,
- 'P': self._draw_pixbuf
+ 'P': self._draw_pixbuf,
+ 'B': self._paste,
+ 'S': self._speak
}
def _shared_cb(self, activity):
@@ -112,6 +116,7 @@ class Collaboration():
id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
SERVICE, {})
+ self._enable_share_button()
def _joined_cb(self, activity):
self._shared_activity = self._activity._shared_activity
@@ -140,6 +145,11 @@ class Collaboration():
# Joiner should request current state from sharer.
self.waiting_for_turtles = True
+ self._enable_share_button()
+
+ def _enable_share_button(self):
+ self._activity.share_button.set_icon('shareon')
+ self._activity.share_button.set_tooltip(_('Share selected blocks'))
def _list_tubes_reply_cb(self, tubes):
for tube_info in tubes:
@@ -192,11 +202,12 @@ class Collaboration():
try:
command, payload = event_message.split('|', 2)
- self._processing_methods[command](payload)
except ValueError:
- debug_output('Could not split event message.',
+ debug_output('Could not split event message [%s]' % event_message,
self._tw.running_sugar)
+ self._processing_methods[command](payload)
+
# Restore active Turtle
self._tw.canvas.set_turtle(self._tw.turtles.get_turtle_key(
save_active_turtle))
@@ -376,6 +387,23 @@ class Collaboration():
poly_points[i][0], poly_points[i][1])))
self._tw.canvas.fill_polygon(shared_poly_points)
+ def _speak(self, payload):
+ if len(payload) > 0:
+ [nick, language_option, text] = data_from_string(payload)
+ if language_option == 'None':
+ language_option = ''
+ if text is not None:
+ os.system('espeak %s "%s" --stdout | aplay' % (
+ language_option, str(text)))
+
+ def _paste(self, payload):
+ if len(payload) > 0:
+ [nick, text] = data_from_string(payload)
+ if text is not None:
+ self._tw.process_data(data_from_string(text),
+ self._tw.paste_offset)
+ self._tw.paste_offset += 20
+
def _get_dictionary(self):
return {self._get_nick(): self._get_colors()}
diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py
index 7a82a42..8cba7c2 100644
--- a/TurtleArt/taconstants.py
+++ b/TurtleArt/taconstants.py
@@ -90,14 +90,13 @@ EXPANDABLE_STYLE = ['boolean-style', 'compare-porch-style', 'compare-style',
'number-style-porch', 'number-style', 'basic-style-2arg',
'number-style-block', 'box-style-media']
+# These are defined in add_block based on block style
+EXPANDABLE_FLOW = []
+
EXPANDABLE = ['vspace', 'hspace', 'identity2']
EXPANDABLE_ARGS = ['list', 'myfunc1arg', 'myfunc2arg', 'myfunc3arg',
'userdefined', 'userdefined2args', 'userdefined3args']
-#
-# Blocks that are 'collapsible'
-#
-COLLAPSIBLE = ['sandwichbottom', 'sandwichcollapsed']
#
# Deprecated block styles that need dock adjustments
@@ -138,7 +137,7 @@ STATUS_SHAPES = ['status', 'info', 'nostack', 'dupstack', 'noinput',
# Emulate Sugar toolbar when running from outside of Sugar
#
TOOLBAR_SHAPES = ['hideshowoff', 'eraseron', 'run-fastoff',
- 'run-slowoff', 'debugoff', 'stopiton']
+ 'run-slowoff', 'stopiton']
#
# Legacy names
@@ -218,6 +217,8 @@ VOICES = {'af': 'afrikaans', 'cy': 'welsh-test', 'el': 'greek',
# Macros (groups of blocks)
#
MACROS = {
+ 'ifthenelse': # Because it is too big to fit on the palette
+ [[0, 'ifelse', 0, 0, [None, None, None, None, None]]],
'kbinput':
[[0, 'until', 0, 0, [None, 1, 4, None]],
[1, 'greater2', 0, 0, [0, 2, 3, None]],
diff --git a/TurtleArt/tagplay.py b/TurtleArt/tagplay.py
index 72379c6..505f6f1 100644
--- a/TurtleArt/tagplay.py
+++ b/TurtleArt/tagplay.py
@@ -37,8 +37,6 @@ import gst
import gst.interfaces
import gtk
-import urllib
-
def play_audio_from_file(lc, file_path):
""" Called from Show block of audio media """
@@ -77,6 +75,24 @@ def stop_media(lc):
lc.gplay = None
+def pause_media(lc):
+ """ From pause media block """
+ if lc.gplay == None:
+ return False
+
+ if lc.gplay.player is not None:
+ lc.gplay.player.pause()
+
+
+def play_media(lc):
+ """ From play media block """
+ if lc.gplay == None:
+ return False
+
+ if lc.gplay.player is not None:
+ lc.gplay.player.play()
+
+
def media_playing(lc):
if lc.gplay == None:
return False
@@ -106,7 +122,7 @@ class Gplay():
if lc.tw.running_sugar:
self.bin.set_transient_for(lc.tw.activity)
- self.bin.move(x, y + 108)
+ self.bin.move(x, y)
self.bin.resize(w, h)
self.bin.show_all()
@@ -114,6 +130,8 @@ class Gplay():
def _player_eos_cb(self, widget):
logging.debug('end of stream')
+ # Make sure player is stopped after EOS
+ self.player.stop()
def _player_error_cb(self, widget, message, detail):
self.player.stop()
@@ -133,12 +151,12 @@ class Gplay():
self.only_audio = only_audio
self.got_stream_info = True
- def start(self, uri=None):
+ def start(self, file_path=None):
self._want_document = False
- self.playpath = os.path.dirname(uri)
- if not uri:
+ self.playpath = os.path.dirname(file_path)
+ if not file_path:
return False
- self.playlist.append('file://' + urllib.quote(os.path.abspath(uri)))
+ self.playlist.append('file://' + os.path.abspath(file_path))
if not self.player:
# lazy init the player so that videowidget is realized
# and has a valid widget allocation
@@ -149,15 +167,13 @@ class Gplay():
try:
if not self.currentplaying:
- logging.info('Playing: ' + self.playlist[0])
+ logging.info('Playing: %s' % (self.playlist[0]))
self.player.set_uri(self.playlist[0])
self.currentplaying = 0
self.play_toggled()
self.show_all()
- else:
- pass
except:
- pass
+ logging.error('Error playing %s' % (self.playlist[0]))
return False
def play_toggled(self):
@@ -184,7 +200,9 @@ class GstPlayer(gobject.GObject):
self.player = gst.element_factory_make('playbin', 'player')
+ videowidget.realize()
self.videowidget = videowidget
+ self.videowidget_xid = videowidget.window.xid
self._init_video_sink()
bus = self.player.get_bus()
@@ -200,7 +218,7 @@ class GstPlayer(gobject.GObject):
if message.structure is None:
return
if message.structure.get_name() == 'prepare-xwindow-id':
- self.videowidget.set_sink(message.src)
+ self.videowidget.set_sink(message.src, self.videowidget_xid)
message.src.set_property('force-aspect-ratio', True)
def on_message(self, bus, message):
@@ -220,6 +238,8 @@ class GstPlayer(gobject.GObject):
if old == gst.STATE_READY and new == gst.STATE_PAUSED:
self.emit('stream-info',
self.player.props.stream_info_value_array)
+ # else:
+ # logging.debug(message.type)
def _init_video_sink(self):
self.bin = gst.Bin()
@@ -241,7 +261,6 @@ class GstPlayer(gobject.GObject):
caps_string += 'width=%d, height=%d' % (w, h)
else:
caps_string += 'width=480, height=360'
-
caps = gst.Caps(caps_string)
self.filter = gst.element_factory_make('capsfilter', 'filter')
self.bin.add(self.filter)
@@ -269,7 +288,7 @@ class GstPlayer(gobject.GObject):
self.player.set_state(gst.STATE_NULL)
self.playing = False
logging.debug('stopped player')
- return False
+ # return False
def get_state(self, timeout=1):
return self.player.get_state(timeout=timeout)
@@ -294,7 +313,6 @@ class VideoWidget(gtk.DrawingArea):
else:
return True
- def set_sink(self, sink):
- assert self.window.xid
+ def set_sink(self, sink, xid):
self.imagesink = sink
- self.imagesink.set_xwindow_id(self.window.xid)
+ self.imagesink.set_xwindow_id(xid)
diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py
index 1e5bf07..3e8fb0f 100644
--- a/TurtleArt/talogo.py
+++ b/TurtleArt/talogo.py
@@ -27,6 +27,8 @@ from time import time
from operator import isNumberType
from UserDict import UserDict
+from sugar.graphics import style
+
from taconstants import TAB_LAYER, DEFAULT_SCALE, PREFIX_DICTIONARY
from tapalette import block_names, value_blocks
from tautils import get_pixbuf_from_journal, convert, data_from_file, \
@@ -62,7 +64,6 @@ class symbol:
class logoerror(Exception):
def __init__(self, value):
- print value
self.value = value
def __str__(self):
@@ -149,7 +150,6 @@ class LogoCode:
def stop_logo(self):
""" Stop logo is called from the Stop button on the toolbar """
- self.tw.step_time = 0
self.step = _just_stop()
for plugin in self.tw._plugins:
plugin.stop()
@@ -157,6 +157,7 @@ class LogoCode:
from tagplay import stop_media
stop_media(self)
self.tw.active_turtle.show()
+ self.tw.running_blocks = False
def def_prim(self, name, args, fcn, rprim=False):
""" Define the primitives associated with the blocks """
@@ -192,9 +193,15 @@ class LogoCode:
for b in blocks:
b.unhighlight()
+ # Hidden macro expansions
+ for b in blocks:
+ if b.name in ['while', 'until']:
+ action_blk, new_blocks = self._expand_forever(b, blk, blocks)
+ blocks = new_blocks[:]
+ if b == blk:
+ blk = action_blk
for b in blocks:
- # Hidden macro expansions
- if b.name in ['while', 'until', 'forever']:
+ if b.name in ['forever']:
action_blk, new_blocks = self._expand_forever(b, blk, blocks)
blocks = new_blocks[:]
if b == blk:
@@ -216,6 +223,7 @@ class LogoCode:
except IndexError:
self.tw.showlabel('#nostack')
self.tw.showblocks()
+ self.tw.running_blocks = False
return None
if type(convert(x, float, False)) == float:
if int(float(x)) == x:
@@ -252,11 +260,10 @@ class LogoCode:
if len(dock) > 4: # There could be a '(', ')', '[' or ']'.
code.append(dock[4])
if blk.primitive is not None: # make a tuple (prim, blk)
- # special case: expand 'while' and 'until' primitives
- try:
+ if blk in self.tw.block_list.list:
code.append((blk.primitive,
self.tw.block_list.list.index(blk)))
- except ValueError:
+ else:
code.append(blk.primitive) # Hidden block
elif len(blk.values) > 0: # Extract the value from content blocks.
if blk.name == 'number':
@@ -415,6 +422,7 @@ class LogoCode:
if self.bindex is not None:
self.tw.block_list.list[self.bindex].highlight()
self.tw.showblocks()
+ self.tw.display_coordinates()
raise logoerror(str(self.iresult))
self.iline = oldiline
self.ireturn()
@@ -454,6 +462,7 @@ class LogoCode:
if token.nargs == None:
self.tw.showblocks()
+ self.tw.display_coordinates()
raise logoerror("#noinput")
for i in range(token.nargs):
self._no_args_check()
@@ -497,6 +506,7 @@ class LogoCode:
except ValueError:
debug_output('generator already executing',
self.tw.running_sugar)
+ self.tw.running_blocks = False
return False
else:
return False
@@ -507,11 +517,14 @@ class LogoCode:
self.hidden_turtle = None
else:
self.tw.active_turtle.show()
+ self.tw.running_blocks = False
return False
except logoerror, e:
self.tw.showblocks()
+ self.tw.display_coordinates()
self.tw.showlabel('syntaxerror', str(e))
self.tw.turtles.show_all()
+ self.tw.running_blocks = False
return False
return True
@@ -540,6 +553,7 @@ class LogoCode:
if self.iline and self.iline[0] is not self.symnothing:
return
self.tw.showblocks()
+ self.tw.display_coordinates()
raise logoerror("#noinput")
#
@@ -562,7 +576,7 @@ class LogoCode:
self.tw.clear_plugins()
if self.tw.gst_available:
from tagplay import stop_media
- # stop_media(self) # TODO: gplay variable
+ stop_media(self)
self.tw.canvas.clearscreen()
self.scale = DEFAULT_SCALE
self.hidden_turtle = None
@@ -731,6 +745,30 @@ class LogoCode:
self.ireturn()
yield True
+ def media_stop(self):
+ """ Stop playing media"""
+ if self.tw.gst_available:
+ from tagplay import stop_media
+ stop_media(self)
+ self.ireturn()
+ yield True
+
+ def media_pause(self):
+ """ Pause media"""
+ if self.tw.gst_available:
+ from tagplay import pause_media
+ pause_media(self)
+ self.ireturn()
+ yield True
+
+ def media_play(self):
+ """ Play media"""
+ if self.tw.gst_available:
+ from tagplay import play_media
+ play_media(self)
+ self.ireturn()
+ yield True
+
def play_sound(self):
""" Sound file from Journal """
if self.tw.gst_available:
@@ -744,8 +782,17 @@ class LogoCode:
return
if self.tw.gst_available:
from tagplay import play_movie_from_file
- play_movie_from_file(self, self.filepath, self.x2tx(), self.y2ty(),
- w, h)
+ # The video window is an overlay, so we need to know where
+ # the canvas is relative to the window, e.g., which
+ # toolbars, if any are open.
+ yoffset = 0
+ if self.tw.running_sugar:
+ if not self.tw.activity.is_fullscreen():
+ yoffset += style.GRID_CELL_SIZE
+ if self.tw.activity.toolbars_expanded():
+ yoffset += style.GRID_CELL_SIZE
+ play_movie_from_file(self, self.filepath, self.x2tx(),
+ self.y2ty() + yoffset, w, h)
def _expand_forever(self, b, blk, blocks):
""" Expand a while or until block into: forever, ifelse, stopstack
@@ -755,6 +802,9 @@ class LogoCode:
# manage the connections and flows locally means we may run
# into trouble if any of these block types (forever, while,
# until. ifelse, stopstack, or stack) is changed in tablock.py
+
+ # TODO: Detect nesting, e.g., forever while
+
if b.name == 'while':
while_blk = True
else:
@@ -804,11 +854,11 @@ class LogoCode:
inflow.connections[i] = action_blk
else:
i = None
+ j = None
if outflow is not None:
- j = outflow.connections.index(b)
- outflow.connections[j] = action_blk
- else:
- j = None
+ if b in outflow.connections:
+ j = outflow.connections.index(b)
+ outflow.connections[j] = action_blk
if until_blk and whileflow is not None:
action_first.connections.append(inflow)
diff --git a/TurtleArt/tapalette.py b/TurtleArt/tapalette.py
index ad96103..b14c207 100644
--- a/TurtleArt/tapalette.py
+++ b/TurtleArt/tapalette.py
@@ -22,6 +22,7 @@
help_palettes = {}
help_windows = {}
palette_names = []
+palette_init_on_start = []
palette_blocks = []
block_colors = []
expandable_blocks = []
@@ -49,7 +50,6 @@ block_styles = {'basic-style': [],
'basic-style-3arg': [],
'basic-style-var-arg': [],
'bullet-style': [],
- 'invisible': [],
'box-style': [],
'box-style-media': [],
'number-style': [],
@@ -62,17 +62,13 @@ block_styles = {'basic-style': [],
'compare-porch-style': [],
'boolean-style': [],
'not-style': [],
- 'flow-style': [],
'flow-style-tail': [],
- 'flow-style-1arg': [],
- 'flow-style-boolean': [],
- 'flow-style-while': [],
- 'flow-style-else': [],
- 'collapsible-top': [],
- 'collapsible-top-no-arm': [],
- 'collapsible-top-no-label': [],
- 'collapsible-top-no-arm-no-label': [],
- 'collapsible-bottom': [],
+ 'clamp-style': [],
+ 'clamp-style-collapsible': [],
+ 'clamp-style-collapsed': [],
+ 'clamp-style-1arg': [],
+ 'clamp-style-boolean': [],
+ 'clamp-style-else': [],
'portfolio-style-2x2': [],
'portfolio-style-1x1': [],
'portfolio-style-2x1': [],
@@ -84,7 +80,7 @@ import gtk
from sugar.graphics.icon import Icon
from sugar.graphics import style
-from taconstants import EXPANDABLE_STYLE
+from taconstants import EXPANDABLE_STYLE, EXPANDABLE_FLOW
from tautils import debug_output
from util.helpbutton import add_section, add_paragraph
@@ -125,7 +121,7 @@ class Palette():
self._help_box = help_palettes[self._name]
self._help = 'deja vu'
- def add_palette(self, position=None):
+ def add_palette(self, position=None, init_on_start=False):
if self._name is None:
debug_output('You must specify a name for your palette')
return
@@ -143,6 +139,9 @@ class Palette():
palette_names.insert(i, self._name)
palette_blocks.insert(i, [])
block_colors.insert(i, self._colors)
+ if init_on_start:
+ if not self._name in palette_init_on_start:
+ palette_init_on_start.append(self._name)
else:
return
@@ -211,7 +210,8 @@ class Palette():
block.add_block()
-def make_palette(palette_name, colors=None, help_string=None, position=None):
+def make_palette(palette_name, colors=None, help_string=None, position=None,
+ init_on_start=False):
""" Palette helper function """
if colors is None:
palette = Palette(palette_name)
@@ -219,7 +219,7 @@ def make_palette(palette_name, colors=None, help_string=None, position=None):
palette = Palette(palette_name, colors)
if help_string is not None:
palette.set_help(help_string)
- palette.add_palette(position)
+ palette.add_palette(position, init_on_start=init_on_start)
return palette
@@ -271,6 +271,12 @@ class Block():
return
else:
block_styles[self._style].append(self._name)
+ if self._style in ['clamp-style',
+ 'clamp-style-collapsible',
+ 'clamp-style-1arg',
+ 'clamp-style-boolean',
+ 'clamp-style-else']:
+ EXPANDABLE_FLOW.append(self._name)
if self._label is not None:
block_names[self._name] = self._label
diff --git a/TurtleArt/tasprite_factory.py b/TurtleArt/tasprite_factory.py
index 2b0e922..2c403b6 100755
--- a/TurtleArt/tasprite_factory.py
+++ b/TurtleArt/tasprite_factory.py
@@ -64,12 +64,16 @@ class SVG:
4 * self._stroke_width
self._porch_y = self._innie_y2
self._expand_x = 0
+ self._expand_x2 = 0
self._expand_y = 0
+ self._expand_y2 = 0
+ self._second_clamp = False
self._arm = True
self._else = False
self._draw_innies = True
self._hide = False
self._show = False
+ self._collapsible = False
self._show_x = 0
self._show_y = 0
self._hide_x = 0
@@ -426,76 +430,69 @@ class SVG:
svg = "%s%s%s%s%s%s%s%s" % (" <path d=\"M 27.5 48.3 ",
"C 26.9 48.3 26.4 48.2 25.9 48.2 L 27.2 50.5 L 28.6 48.2 ",
- "C 28.2 48.2 27.9 48.3 27.5 48.3 Z\" stroke_width=\"3.5\" ",
+ "C 28.2 48.2 27.9 48.3 27.5 48.3 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke,
"\" />\n")
svg += "%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 40.2 11.7 ",
"C 38.0 11.7 36.2 13.3 35.8 15.3 ",
"C 37.7 16.7 39.3 18.4 40.5 20.5 ",
"C 42.8 20.4 44.6 18.5 44.6 16.2 ",
- "C 44.6 13.7 42.6 11.7 40.2 11.7 Z\" stroke_width=\"3.5\" ",
+ "C 44.6 13.7 42.6 11.7 40.2 11.7 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
svg += "%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 40.7 39.9 ",
"C 39.5 42.1 37.9 44.0 35.9 45.4 ",
"C 36.4 47.3 38.1 48.7 40.2 48.7 ",
"C 42.6 48.7 44.6 46.7 44.6 44.3 ",
- "C 44.6 42.0 42.9 40.2 40.7 39.9 Z\" stroke_width=\"3.5\" ",
+ "C 44.6 42.0 42.9 40.2 40.7 39.9 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
svg += "%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 14.3 39.9 ",
"C 12.0 40.1 10.2 42.0 10.2 44.3 ",
"C 10.2 46.7 12.2 48.7 14.7 48.7 ",
"C 16.7 48.7 18.5 47.3 18.9 45.4 ",
- "C 17.1 43.9 15.5 42.1 14.3 39.9 Z\" stroke_width=\"3.5\" ",
+ "C 17.1 43.9 15.5 42.1 14.3 39.9 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
svg += "%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 19.0 15.4 ",
"C 18.7 13.3 16.9 11.7 14.7 11.7 ",
"C 12.2 11.7 10.2 13.7 10.2 16.2 ",
"C 10.2 18.5 12.1 20.5 14.5 20.6 ",
- "C 15.7 18.5 17.2 16.8 19.0 15.4 Z\" stroke_width=\"3.5\" ",
- "fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
- svg += "%s%s%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 27.5 12.6 ",
- "C 29.4 12.6 31.2 13.0 32.9 13.7 ",
- "C 33.7 12.6 34.1 11.3 34.1 9.9 ",
- "C 34.1 6.2 31.1 3.2 27.4 3.2 ",
- "C 23.7 3.2 20.7 6.2 20.7 9.9 ",
- "C 20.7 11.3 21.2 12.7 22.0 13.7 ",
- "C 23.7 13.0 25.5 12.6 27.5 12.6 Z\" stroke_width=\"3.5\" ",
+ "C 15.7 18.5 17.2 16.8 19.0 15.4 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
+ svg += '<path d="m 27.497,12.563 c 1.908,0 3.728,0.411 5.418,1.128 C 33.656,12.615 34.847526,11.272713 34.25,10 32.953704,7.2389259 31.1875,2.305074 27.5,2.305074 c -3.6875,0 -5.083333,4.9338519 -6.75,7.694926 -0.73796,1.222538 0.442,2.657 1.206,3.742 1.724,-0.749 3.587,-1.179 5.541,-1.179 z" style="fill:%s;stroke:%s;stroke-width:3.5" />' % (self._fill, self._stroke)
svg += "%s%s%s%s%s%s%s%s%s%s%s%s" % (" <path d=\"M 43.1 30.4 ",
"C 43.1 35.2 41.5 39.7 38.5 43.0 ",
"C 35.6 46.4 31.6 48.3 27.5 48.3 ",
"C 23.4 48.3 19.4 46.4 16.5 43.0 ",
"C 13.5 39.7 11.9 35.2 11.9 30.4 ",
"C 11.9 20.6 18.9 12.6 27.5 12.6 ",
- "C 36.1 12.6 43.1 20.6 43.1 30.4 Z\" stroke_width=\"3.5\" ",
+ "C 36.1 12.6 43.1 20.6 43.1 30.4 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._fill, ";\" stroke=\"", self._stroke, "\" />\n")
svg += "%s%s%s%s%s" % (" <path d=\"M 25.9 33.8 L 24.3 29.1 ",
- "L 27.5 26.5 L 31.1 29.2 L 29.6 33.8 Z\" stroke_width=\"3.5\" ",
+ "L 27.5 26.5 L 31.1 29.2 L 29.6 33.8 Z\" stroke-width=\"3.5\" ",
"fill=\"", self._stroke, ";\" stroke=\"none\" />\n")
svg += "%s%s%s%s%s%s" % (" <path d=\"M 27.5 41.6 ",
"C 23.5 41.4 22.0 39.5 22.0 39.5 L 25.5 35.4 L 30.0 35.5 ",
"L 33.1 39.7 C 33.1 39.7 30.2 41.7 27.5 41.6 Z\" ",
- "stroke_width=\"3.5\" fill=\"", self._stroke,
+ "stroke-width=\"3.5\" fill=\"", self._stroke,
";\" stroke=\"none\" />\n")
svg += "%s%s%s%s%s%s" % (" <path d=\"M 18.5 33.8 ",
"C 17.6 30.9 18.6 27.0 18.6 27.0 L 22.6 29.1 L 24.1 33.8 ",
"L 20.5 38.0 C 20.5 38.0 19.1 36.0 18.4 33.8 Z\" ",
- "stroke_width=\"3.5\" fill=\"", self._stroke,
+ "stroke-width=\"3.5\" fill=\"", self._stroke,
";\" stroke=\"none\" />\n")
svg += "%s%s%s%s%s%s" % (" <path d=\"M 19.5 25.1 ",
"C 19.5 25.1 20.0 23.2 22.5 21.3 ",
"C 24.7 19.7 27.0 19.6 27.0 19.6 L 26.9 24.6 L 23.4 27.3 ",
- "L 19.5 25.1 Z\" stroke_width=\"3.5\" fill=\"", self._stroke,
+ "L 19.5 25.1 Z\" stroke-width=\"3.5\" fill=\"", self._stroke,
";\" stroke=\"none\" />\n")
svg += "%s%s%s%s%s%s" % (" <path d=\"M 32.1 27.8 L 28.6 25.0 ",
"L 29 19.8 C 29 19.8 30.8 19.7 33.0 21.4 ",
"C 35.2 23.2 36.3 26.4 36.3 26.4 L 32.1 27.8 Z\" ",
- "stroke_width=\"3.5\" fill=\"", self._stroke,
+ "stroke-width=\"3.5\" fill=\"", self._stroke,
";\" stroke=\"none\" />\n")
svg += "%s%s%s%s%s%s" % (" <path d=\"M 31.3 34.0 L 32.6 29.6 ",
"L 36.8 28.0 C 36.8 28.0 37.5 30.7 36.8 33.7 ",
"C 36.2 36.0 34.7 38.1 34.7 38.1 L 31.3 34.0 Z\" ",
- "stroke_width=\"3.5\" fill=\"", self._stroke,
+ "stroke-width=\"3.5\" fill=\"", self._stroke,
";\" stroke=\"none\" />\n")
self._width, self._height = 55, 55
svg += self.footer()
@@ -522,9 +519,10 @@ class SVG:
svg += self.footer()
return self.header() + svg
- def sandwich_top(self, innie_flag=True):
- ''' Special block for the top of a collapsible stack; includes
- an 'arm" that extends down the left side of a stack '''
+ def clamp(self):
+ ''' Special block for collapsible stacks; includes an 'arm"
+ that extends down the left side of a stack and a bottom jaw to
+ clamp the blocks. '''
self.reset_min_max()
x = self._stroke_width / 2.0
y = self._stroke_width / 2.0 + self._radius
@@ -534,64 +532,46 @@ class SVG:
self.margins[3] = 0
svg = self.new_path(x, y)
svg += self._corner(1, -1)
- svg += self._rline_to(self._radius + self._stroke_width, 0)
svg += self._do_slot()
+ svg += self._rline_to(self._radius + self._stroke_width, 0)
svg += self._rline_to(self._expand_x, 0)
xx = self._x
svg += self._corner(1, 1)
- if innie_flag:
+ if self._innie[0] is True:
svg += self._do_innie()
+ else:
+ self.margins[2] = \
+ int((self._x - self._stroke_width + 0.5) * self._scale)
+ if self._bool is True:
+ svg += self._do_boolean()
svg += self._corner(-1, 1)
svg += self.line_to(xx, self._y)
svg += self._rline_to(-self._expand_x, 0)
svg += self._do_tab()
- if self._arm:
- svg += self._inverse_corner(-1, 1, 90, 0, 0)
- svg += self._rline_to(0, self._expand_y)
- svg += self._rline_to(-self._radius, 0)
- else:
- svg += self._rline_to(-self._radius - self._stroke_width, 0)
- svg += self._corner(-1, -1)
- svg += self._close_path()
- self.calc_w_h()
- svg += self.style()
- if self._show is True:
- svg += self._show_dot()
- if self._hide is True:
- svg += self._hide_dot()
- svg += self.footer()
- return self.header() + svg
-
- def sandwich_bottom(self):
- ''' Special block for the bottom of a collapsible stack;
- includes a connection to the 'arm" that extends down the left
- side of a stack '''
- self.reset_min_max()
- x = self._stroke_width / 2.0
- y = self._stroke_width / 2.0
- self.margins[0] = int((x + self._stroke_width + 0.5) * self._scale)
- self.margins[1] = int((self._stroke_width + 0.5) * self._scale)
- self.margins[2] = 0
- self.margins[3] = 0
- svg = self.new_path(x, y)
- svg += self._rline_to(self._radius, 0)
+ svg += self._inverse_corner(-1, 1, 90, 0, 0)
svg += self._rline_to(0, self._expand_y)
svg += self._inverse_corner(1, 1, 90, 0, 0)
svg += self._do_slot()
svg += self._rline_to(self._radius, 0)
+ if self._second_clamp:
+ svg += self._corner(-1, 1)
+ svg += self.line_to(xx, self._y)
+ svg += self._rline_to(-self._expand_x, 0)
+ svg += self._do_tab()
+ svg += self._inverse_corner(-1, 1, 90, 0, 0)
+ svg += self._rline_to(0, self._expand_y2)
+ svg += self._inverse_corner(1, 1, 90, 0, 0)
+ svg += self._do_slot()
+ svg += self._rline_to(self._radius, 0)
svg += self._corner(-1, 1)
- svg += self._do_tab()
svg += self._rline_to(-self._radius - self._stroke_width, 0)
+ svg += self._do_tab()
svg += self._corner(-1, -1)
svg += self._close_path()
self.calc_w_h()
svg += self.style()
- self._hide_x = x + self._radius / 2
- self._hide_y = y + self._radius / 2
- if self._hide is True:
+ if self._collapsible:
svg += self._hide_dot()
- if self._show is True:
- svg += self._show_dot()
svg += self.footer()
return self.header() + svg
@@ -635,6 +615,9 @@ class SVG:
def set_show(self, flag=False):
self._show = flag
+ def set_collapsible(self, flag=False):
+ self._collapsible = flag
+
def get_width(self):
return self._width
@@ -656,9 +639,14 @@ class SVG:
def set_orientation(self, orientation=0):
self._orientation = orientation
- def expand(self, w=0, h=0):
+ def second_clamp(self, flag=False):
+ self._second_clamp = flag
+
+ def expand(self, w=0, h=0, w2=0, h2=0):
self._expand_x = w
self._expand_y = h
+ self._expand_x2 = w2
+ self._expand_y2 = h2
def set_stroke_width(self, stroke_width=1.5):
self._stroke_width = stroke_width
@@ -1011,8 +999,8 @@ class SVG:
self._rline_to(0, -self._slot_y))
elif self._cap is True:
return "%s%s" % (
- self._rline_to(self._slot_x / 2.0, -self._slot_y * 2.0),
- self._rline_to(self._slot_x / 2.0, self._slot_y * 2.0))
+ self._rline_to(self._slot_x / 2.0, -self._slot_y * 3.0),
+ self._rline_to(self._slot_x / 2.0, self._slot_y * 3.0))
else:
return self._rline_to(self._slot_x, 0)
@@ -1021,8 +1009,8 @@ class SVG:
return self._rline_to(-self._slot_x, 0)
elif self._tail:
return "%s%s" % (
- self._rline_to(-self._slot_x / 2.0, self._slot_y * 2.0),
- self._rline_to(-self._slot_x / 2.0, -self._slot_y * 2.0))
+ self._rline_to(-self._slot_x / 2.0, self._slot_y * 3.0),
+ self._rline_to(-self._slot_x / 2.0, -self._slot_y * 3.0))
else:
return self._rline_to(-self._slot_x, 0)
@@ -1167,8 +1155,8 @@ class SVG:
x += self._innie_x1 + self._innie_x2
self.margins[0] += self._innie_x1 + self._innie_x2
if self._cap is True:
- y += self._slot_y * 2.0
- self.margins[1] += self._slot_y * 2.0
+ y += self._slot_y * 3.0
+ self.margins[1] += self._slot_y * 3.0
elif self._slot is True:
self.margins[1] += self._slot_y
self.margins[0] *= self._scale
@@ -1190,13 +1178,13 @@ def close_file(f):
def generator(datapath):
svg0 = SVG()
- f = open_file(datapath, "basic.svg")
- svg0.set_innie([True, True])
+ f = open_file(datapath, "clamp.svg")
svg0.set_scale(2)
- svg0.set_tab(True)
- svg0.set_slot(True)
svg0.set_arm(True)
- svg_str = svg0.basic_block()
+ svg0.expand(0, 0, 0, 21)
+ svg0.set_collapsible(True)
+ svg0.set_hide(True)
+ svg_str = svg0.clamp()
f.write(svg_str)
close_file(f)
diff --git a/TurtleArt/tautils.py b/TurtleArt/tautils.py
index 40c39e9..bd7309f 100644
--- a/TurtleArt/tautils.py
+++ b/TurtleArt/tautils.py
@@ -1,4 +1,4 @@
-#Copyright (c) 2007-8, Playful Invention Company.
+#copyright (c) 2007-8, Playful Invention Company.
#Copyright (c) 2008-12, Walter Bender
#Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -41,11 +41,7 @@ except (ImportError, AttributeError):
OLD_SUGAR_SYSTEM = True
from StringIO import StringIO
-from taconstants import COLLAPSIBLE, HIT_HIDE, HIT_SHOW, XO1, XO15, XO175, \
- XO30, UNKNOWN
-
-SANDWICHES = ['sandwichtop', 'sandwichtop_no_label', 'sandwichtop_no_arm',
- 'sandwichtop_no_arm_no_label']
+from taconstants import HIT_HIDE, HIT_SHOW, XO1, XO15, XO175, XO30, UNKNOWN
import logging
_logger = logging.getLogger('turtleart-activity')
@@ -235,8 +231,14 @@ def data_from_file(ta_file):
def data_from_string(text):
''' JSON load data from a string. '''
- return json_load(text.replace(']],\n', ']], '))
-
+ if type(text) == str:
+ return json_load(text.replace(']],\n', ']], '))
+ elif type(text) == unicode:
+ text = text.encode('ascii', 'replace')
+ return json_load(text.replace(']],\n', ']], '))
+ else:
+ print 'type error (%s) in data_from_string' % (type(text))
+ return None
def data_to_file(data, ta_file):
''' Write data to a file. '''
@@ -390,226 +392,77 @@ def calc_image_size(spr):
int(max(spr.label_safe_height(), 1))
-# 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 in ['sandwichtop', 'sandwichtop_no_label']:
- if top.ey > 0:
- top.reset_y()
- top.svg.set_hide(False)
- top.refresh()
-
-
-def grow_stack_arm(top):
- ''' When we dock, grow an 'arm' from 'sandwichtop'. '''
- if top is not None and top.name in ['sandwichtop', 'sandwichtop_no_label']:
- 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.svg.set_hide(True)
- 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.
- cblk = blk.connections[0]
- while cblk is not None:
- if cblk.name in COLLAPSIBLE:
- return None
- if cblk.name in ['repeat', 'if', 'ifelse', 'forever', 'while']:
- if blk != cblk.connections[len(cblk.connections) - 1]:
- return None
- if cblk.name in SANDWICHES:
- return cblk
- blk = cblk
- cblk = cblk.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.
- cblk = blk.connections[len(blk.connections) - 1]
- while cblk is not None:
- if cblk.name in SANDWICHES:
- return None
- if cblk.name in COLLAPSIBLE:
- return cblk
- cblk = cblk.connections[len(cblk.connections) - 1]
- return None
-
-
-def find_sandwich_top_below(blk):
- ''' Find the sandwich top below this block. '''
- if blk.name in SANDWICHES:
- return blk
- # Always follow the main branch of a flow: the last connection.
- cblk = blk.connections[len(blk.connections) - 1]
- while cblk is not None:
- if cblk.name in SANDWICHES:
- return cblk
- cblk = cblk.connections[len(cblk.connections) - 1]
- return None
-
+def restore_clamp(top):
+ ''' Restore the blocks in a sandwich clamp. '''
+ if top is None:
+ return
-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 gblk in group:
- if not hit_bottom and gblk == bot:
- hit_bottom = True
- if len(gblk.values) == 0:
- gblk.values.append(0)
- else:
- gblk.values[0] = 0
- olddx = gblk.docks[1][2]
- olddy = gblk.docks[1][3]
- # Replace 'sandwichcollapsed' shape with 'sandwichbottom' shape
- gblk.name = 'sandwichbottom'
- gblk.spr.set_label(' ', 1)
- gblk.svg.set_show(False)
- gblk.svg.set_hide(True)
- gblk.refresh()
- # Redock to previous block in group
- you = gblk.connections[0]
- (yx, yy) = you.spr.get_xy()
- yd = you.docks[len(you.docks) - 1]
- (bx, by) = gblk.spr.get_xy()
- dx = yx + yd[2] - gblk.docks[0][2] - bx
- dy = yy + yd[3] - gblk.docks[0][3] - by
- gblk.spr.move_relative((dx, dy))
- # Since the shapes have changed, the dock positions have too.
- dx += gblk.docks[1][2] - olddx
- dy += gblk.docks[1][3] - olddy
- else:
- if not hit_bottom:
- gblk.spr.restore()
- gblk.status = None
- else:
- gblk.spr.move_relative((dx, dy))
- # Add 'sandwichtop' arm
- if top.name == 'sandwichtop_no_arm':
- top.name = 'sandwichtop'
- else:
- top.name = 'sandwichtop_no_label'
- top.spr.set_label(' ', 1)
+ if top.name == 'sandwichclampcollapsed':
+ y1 = top.docks[2][3]
+ if top.connections[1] is not None:
+ for blk in find_group(top.connections[1]):
+ blk.spr.restore()
+ blk.status = None
+
+ # If you come across a 'sandwichclampcollapsed', do not
+ # restore its clamp
+ for blk in find_group(top.connections[1]):
+ if blk.name == 'sandwichclampcollapsed':
+ if blk.connections[1] is not None:
+ for b in find_group(blk.connections[1]):
+ b.spr.hide()
+ b.status = 'collapsed'
+
+ bot = top.connections[2]
+ top.name = 'sandwichclamp'
+ top.spr.set_label('')
top.resize()
- top.refresh()
- grow_stack_arm(top)
+ top.svg.set_hide(True)
+ top.refresh()
+ y2 = top.docks[2][3]
+ dy = y2 - y1
+ if bot is not None:
+ for blk in find_group(bot):
+ blk.spr.move_relative((0, dy))
+ if top.connections[1] is not None:
+ # Make sure stack is aligned to dock
+ x1, y1 = top.connections[1].spr.get_xy()
+ x1 += top.connections[1].docks[0][2]
+ y1 += top.connections[1].docks[0][3]
+ x2, y2 = top.spr.get_xy()
+ x2 += top.docks[1][2]
+ y2 += top.docks[1][3]
+ if x1 != x2 or y1 != y2:
+ for blk in find_group(top.connections[1]):
+ blk.spr.move_relative((x2 - x1, y2 - y1))
+ return
-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 in ['sandwichtop_no_arm',
- 'sandwichtop_no_arm_no_label']:
- restore_stack(top)
- return
- if len(top.connections) == 0:
- return
- cblk = top.connections[len(top.connections) - 1]
- while cblk is not None:
- if cblk.name in COLLAPSIBLE:
- return
- if cblk.name in ['sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
- restore_stack(cblk)
- return
- # Follow a fork
- if cblk.name in ['repeat', 'if', 'ifelse', 'forever', 'while',
- 'until']:
- top = find_sandwich_top_below(
- cblk.connections[len(cblk.connections) - 2])
- if top is not None:
- uncollapse_forks(top, True)
- if cblk.name == 'ifelse':
- top = find_sandwich_top_below(
- cblk.connections[len(cblk.connections) - 3])
- if top is not None:
- uncollapse_forks(top, True)
- cblk = cblk.connections[len(cblk.connections) - 1]
- return
-
-
-def collapse_stack(top):
- ''' Hide all the blocks between the sandwich top and sandwich bottom. '''
- # First uncollapse any nested stacks
+def collapse_clamp(top, transform=True):
+ ''' Hide all the blocks in the clamp. '''
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 gblk in group:
- if not hit_bottom and gblk == bot:
- hit_bottom = True
- # Replace 'sandwichbottom' with invisible 'sandwichcollapsed'
- if len(gblk.values) == 0:
- gblk.values.append(1)
- else:
- gblk.values[0] = 1
- olddx = gblk.docks[1][2]
- olddy = gblk.docks[1][3]
- gblk.name = 'sandwichcollapsed'
- gblk.resize()
- you = find_sandwich_top(gblk)
- (yx, yy) = you.spr.get_xy()
- yd = you.docks[len(you.docks) - 1]
- (bx, by) = gblk.spr.get_xy()
- dx = yx + yd[2] - gblk.docks[0][2] - bx
- dy = yy + yd[3] - gblk.docks[0][3] - by
- gblk.spr.move_relative((dx, dy))
- # Since the shapes have changed, the dock positions have too.
- dx += gblk.docks[1][2] - olddx
- dy += gblk.docks[1][3] - olddy
- else:
- if not hit_bottom:
- gblk.spr.hide()
- gblk.status = 'collapsed'
- else:
- gblk.spr.move_relative((dx, dy))
- # Remove 'sandwichtop' arm
- if top.name == 'sandwichtop' or top.name == 'sandwichtop_no_arm':
- top.name = 'sandwichtop_no_arm'
- else:
- top.name = 'sandwichtop_no_arm_no_label'
- top.spr.set_label(' ')
- top.spr.set_label(' ', 1)
- top.resize()
- top.spr.set_label(_('click to open'), 1)
- top.resize()
- top.svg.set_hide(False)
- 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
+ if top.name in ['sandwichclampcollapsed', 'sandwichclamp']:
+ y1 = top.docks[2][3]
+ if top.connections[1] is not None:
+ for blk in find_group(top.connections[1]):
+ blk.spr.hide()
+ blk.status = 'collapsed'
+ if transform:
+ bot = top.connections[2]
+ top.name = 'sandwichclampcollapsed'
+ top.spr.set_label(_('click to open'))
+ top.reset_y()
+ top.resize()
+ top.svg.set_hide(False)
+ top.refresh()
+ y2 = top.docks[2][3]
+ dy = y2 - y1
+ if bot is not None:
+ for blk in find_group(bot):
+ blk.spr.move_relative((0, dy))
+ return
def hide_button_hit(spr, x, y):
@@ -760,6 +613,8 @@ def find_top_block(blk):
''' Find the top block in a stack. '''
if blk is None:
return None
+ if blk.connections is None:
+ return blk
if len(blk.connections) == 0:
return blk
while blk.connections[0] is not None:
@@ -767,6 +622,19 @@ def find_top_block(blk):
return blk
+def find_bot_block(blk):
+ ''' Find the bottom block in a stack. '''
+ if blk is None:
+ return None
+ if blk.connections is None:
+ return blk
+ if len(blk.connections) == 0:
+ return blk
+ while blk.connections[-1] is not None:
+ blk = blk.connections[-1]
+ return blk
+
+
def find_start_stack(blk):
''' Find a stack with a 'start' block on top. '''
if blk is None:
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index 44e95a6..baf2212 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -48,26 +48,26 @@ from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
TOOLBAR_SHAPES, TAB_LAYER, RETURN, OVERLAY_LAYER, CATEGORY_LAYER, \
BLOCKS_WITH_SKIN, ICON_SIZE, PALETTE_SCALE, PALETTE_WIDTH, SKIN_PATHS, \
MACROS, TOP_LAYER, BLOCK_LAYER, OLD_NAMES, DEFAULT_TURTLE, TURTLE_LAYER, \
- CURSOR, EXPANDABLE, COLLAPSIBLE, DEAD_DICTS, DEAD_KEYS, NO_IMPORT, \
+ CURSOR, EXPANDABLE, DEAD_DICTS, DEAD_KEYS, NO_IMPORT, \
TEMPLATES, PYTHON_SKIN, PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, \
EXPANDABLE_ARGS, XO1, XO15, XO175, XO30, TITLEXY, CONTENT_ARGS, \
- CONSTANTS, EXPAND_SKIN, PROTO_LAYER
+ CONSTANTS, EXPAND_SKIN, PROTO_LAYER, EXPANDABLE_FLOW
from tapalette import palette_names, palette_blocks, expandable_blocks, \
block_names, content_blocks, default_values, special_names, block_styles, \
- help_strings, hidden_proto_blocks, string_or_number_args
-from talogo import LogoCode
+ help_strings, hidden_proto_blocks, string_or_number_args, \
+ make_palette, palette_name_to_index, palette_init_on_start
+from talogo import LogoCode, primitive_dictionary, logoerror
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, get_path, reset_stack_arm, grow_stack_arm, \
- find_sandwich_top, find_sandwich_bottom, restore_stack, collapse_stack, \
- collapsed, collapsible, hide_button_hit, show_button_hit, chooser, \
+ calc_image_size, get_path, hide_button_hit, show_button_hit, chooser, \
arithmetic_check, xy, find_block_to_run, find_top_block, journal_check, \
find_group, find_blk_below, data_to_string, find_start_stack, \
- get_hardware, debug_output, error_output, data_to_string
+ get_hardware, debug_output, error_output, data_to_string, convert, \
+ find_bot_block, restore_clamp, collapse_clamp
from tasprite_factory import SVG, svg_str_to_pixbuf, svg_from_file
from sprites import Sprites, Sprite
@@ -76,10 +76,11 @@ if GST_AVAILABLE:
MOTION_THRESHOLD = 6
SNAP_THRESHOLD = 200
+NO_DOCK = (100, 100) # Blocks cannot be docked
class TurtleArtWindow():
- """ TurtleArt Window class abstraction """
+ ''' TurtleArt Window class abstraction '''
timeout_tag = [0]
_PLUGIN_SUBPATH = 'plugins'
@@ -88,6 +89,7 @@ class TurtleArtWindow():
self._loaded_project = ''
self._sharing = False
self.parent = parent
+ self.window_init_complete = False
self.turtle_canvas = turtle_canvas
self.send_event = None # method to send events over the network
self.gst_available = GST_AVAILABLE
@@ -135,6 +137,7 @@ class TurtleArtWindow():
self.mouse_flag = 0
self.mouse_x = 0
self.mouse_y = 0
+ self.running_blocks = False
try:
locale.setlocale(locale.LC_NUMERIC, '')
@@ -172,6 +175,8 @@ class TurtleArtWindow():
self.coord_scale = 1
self.buddies = []
self.saved_string = ''
+ self._saved_action_name = ''
+ self._saved_box_name = ''
self.dx = 0
self.dy = 0
self.media_shapes = {}
@@ -262,13 +267,19 @@ class TurtleArtWindow():
if self.interactive_mode:
self._setup_misc()
- self.show_toolbar_palette(0, False)
-
+ for name in palette_init_on_start:
+ debug_output('initing palette %s' % (name), self.running_sugar)
+ self.show_toolbar_palette(palette_names.index(name),
+ init_only=False, regenerate=True,
+ show=False)
+ self.show_toolbar_palette(0, init_only=False, regenerate=True,
+ show=True)
self.saved_pictures = []
self.block_operation = ''
+ self.window_init_complete = True
def _get_plugin_home(self):
- """ Look in the execution directory """
+ ''' Look in the execution directory '''
path = os.path.join(self.path, self._PLUGIN_SUBPATH)
if os.path.exists(path):
return path
@@ -276,7 +287,7 @@ class TurtleArtWindow():
return None
def _get_plugins_from_plugins_dir(self, path):
- """ Look for plugin files in plugin dir. """
+ ''' Look for plugin files in plugin dir. '''
plugin_files = []
if path is not None:
candidates = os.listdir(path)
@@ -287,13 +298,13 @@ class TurtleArtWindow():
return plugin_files
def _init_plugins(self):
- """ Try importing plugin files from the plugin dir. """
+ ''' Try importing plugin files from the plugin dir. '''
for plugin_dir in self._get_plugins_from_plugins_dir(
self._get_plugin_home()):
self.init_plugin(plugin_dir)
def init_plugin(self, plugin_dir):
- """ Initialize plugin in plugin_dir """
+ ''' Initialize plugin in plugin_dir '''
plugin_class = plugin_dir.capitalize()
f = "def f(self): from plugins.%s.%s import %s; return %s(self)" \
% (plugin_dir, plugin_dir, plugin_class, plugin_class)
@@ -319,7 +330,7 @@ class TurtleArtWindow():
self._icon_paths.append(icon_path)
def _get_plugin_instance(self, plugin_name):
- """ Returns the plugin 'plugin_name' instance """
+ ''' Returns the plugin 'plugin_name' instance '''
list_plugins = self._get_plugins_from_plugins_dir(
self._get_plugin_home())
if plugin_name in list_plugins:
@@ -329,48 +340,47 @@ class TurtleArtWindow():
return None
def _setup_plugins(self):
- """ Initial setup -- called just once. """
+ ''' Initial setup -- called just once. '''
for plugin in self._plugins:
plugin.setup()
def _start_plugins(self):
- """ Start is called everytime we execute blocks. """
+ ''' Start is called everytime we execute blocks. '''
for plugin in self._plugins:
plugin.start()
def _stop_plugins(self):
- """ Stop is called whenever we stop execution. """
+ ''' Stop is called whenever we stop execution. '''
for plugin in self._plugins:
plugin.stop()
def clear_plugins(self):
- """ Clear is called from the clean block and erase button. """
+ ''' Clear is called from the clean block and erase button. '''
for plugin in self._plugins:
if hasattr(plugin, 'clear'):
plugin.clear()
def background_plugins(self):
- """ Background is called when we are pushed to the background. """
+ ''' Background is called when we are pushed to the background. '''
for plugin in self._plugins:
plugin.goto_background()
def foreground_plugins(self):
- """ Foreground is called when we are return from the background. """
+ ''' Foreground is called when we are return from the background. '''
for plugin in self._plugins:
plugin.return_to_foreground()
def quit_plugins(self):
- """ Quit is called upon program exit. """
+ ''' Quit is called upon program exit. '''
for plugin in self._plugins:
plugin.quit()
def _setup_events(self):
- """ Register the events we listen to. """
+ ''' Register the events we listen to. '''
self.window.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.window.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
self.window.add_events(gtk.gdk.POINTER_MOTION_MASK)
self.window.add_events(gtk.gdk.KEY_PRESS_MASK)
- # self.window.connect('realize', self.do_realize)
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)
@@ -378,7 +388,7 @@ class TurtleArtWindow():
self.window.connect("key-press-event", self._keypress_cb)
def load_media_shapes(self):
- """ Media shapes get positioned onto blocks """
+ ''' Media shapes get positioned onto blocks '''
for name in MEDIA_SHAPES:
if name in self.media_shapes:
continue
@@ -395,7 +405,7 @@ class TurtleArtWindow():
break
def _setup_misc(self):
- """ Misc. sprites for status, overlays, etc. """
+ ''' Misc. sprites for status, overlays, etc. '''
self.load_media_shapes()
for i, name in enumerate(STATUS_SHAPES):
# Temporary hack to use wider shapes
@@ -453,16 +463,16 @@ class TurtleArtWindow():
return self._sharing
def is_project_empty(self):
- """ Check to see if project has any blocks in use """
+ ''' Check to see if project has any blocks in use '''
return len(self.just_blocks()) == 1
def _expose_cb(self, win=None, event=None):
- """ Repaint """
+ ''' Repaint '''
self.do_expose_event(event)
return True
- # Handle the expose-event by drawing
def do_expose_event(self, event=None):
+ ''' Handle the expose-event by drawing '''
# Create the cairo context
cr = self.window.window.cairo_create()
@@ -488,18 +498,20 @@ class TurtleArtWindow():
self.sprite_list.redraw_sprites(cr=cr)
def eraser_button(self):
- """ Eraser_button (hide status block when clearing the screen.) """
+ ''' Eraser_button (hide status block when clearing the screen.) '''
if self.status_spr is not None:
self.status_spr.hide()
self.lc.find_value_blocks() # Are there blocks to update?
self.lc.prim_clear()
- self.display_coordinates()
self.parent.restore_challenge()
+ self.display_coordinates()
def run_button(self, time, running_from_button_push=False):
- """ Run turtle! """
+ ''' Run turtle! '''
if self.running_sugar:
self.activity.recenter()
+ if self.status_spr is not None:
+ self.status_spr.hide()
# Look for a 'start' block
for blk in self.just_blocks():
@@ -528,12 +540,12 @@ class TurtleArtWindow():
return
def stop_button(self):
- """ Stop button """
+ ''' Stop button '''
self.lc.stop_logo()
- self._stop_plugins()
+ # self._stop_plugins()
def set_userdefined(self, blk=None):
- """ Change icon for user-defined blocks after loading Python code. """
+ ''' Change icon for user-defined blocks after loading Python code. '''
if blk is not None:
if blk.name in PYTHON_SKIN:
x, y = self._calc_image_offset('pythonon', blk.spr)
@@ -541,13 +553,13 @@ class TurtleArtWindow():
self._resize_skin(blk)
def set_fullscreen(self):
- """ Enter fullscreen mode """
+ ''' Enter fullscreen mode '''
if self.running_sugar:
self.activity.fullscreen()
self.activity.recenter()
def set_cartesian(self, flag):
- """ Turn on/off Cartesian coordinates """
+ ''' Turn on/off Cartesian coordinates '''
if self.coord_scale == 1:
self.draw_overlay('Cartesian_labeled')
else:
@@ -555,12 +567,12 @@ class TurtleArtWindow():
return
def set_polar(self, flag):
- """ Turn on/off polar coordinates """
+ ''' Turn on/off polar coordinates '''
self.draw_overlay('polar')
return
def set_metric(self, flag):
- """ Turn on/off metric coordinates """
+ ''' Turn on/off metric coordinates '''
self.draw_overlay('metric')
return
@@ -577,7 +589,7 @@ class TurtleArtWindow():
self.canvas.heading = save_heading
def update_overlay_position(self, widget, event):
- """ Reposition the overlays when window size changes """
+ ''' Reposition the overlays when window size changes '''
self.width = event.width
self.height = event.height
for name in OVERLAY_SHAPES:
@@ -604,7 +616,7 @@ class TurtleArtWindow():
self.canvas.move_turtle()
def hideshow_button(self):
- """ Hide/show button """
+ ''' Hide/show button '''
if not self.hide:
for blk in self.just_blocks():
blk.spr.hide()
@@ -623,12 +635,12 @@ class TurtleArtWindow():
self.inval_all()
def inval_all(self):
- """ Force a refresh """
+ ''' Force a refresh '''
if self.interactive_mode:
self.window.queue_draw_area(0, 0, self.width, self.height)
def hideshow_palette(self, state):
- """ Hide or show palette """
+ ''' Hide or show palette '''
if not state:
self.palette = False
if self.running_sugar:
@@ -642,7 +654,7 @@ class TurtleArtWindow():
self.show_palette()
def show_palette(self, n=None):
- """ Show palette. """
+ ''' Show palette. '''
if n is None:
if self.selected_palette is None:
n = 0
@@ -657,7 +669,7 @@ class TurtleArtWindow():
self.palette = True
def hide_palette(self):
- """ Hide the palette. """
+ ''' Hide the palette. '''
self._hide_toolbar_palette()
self.palette_button[self.orientation].hide()
self.palette_button[2].hide()
@@ -668,7 +680,7 @@ class TurtleArtWindow():
self.palette = False
def move_palettes(self, x, y):
- """ Move the palettes. """
+ ''' Move the palettes. '''
for p in self.palettes:
for blk in p:
blk.spr.move((x + blk.spr.save_xy[0], y + blk.spr.save_xy[1]))
@@ -690,7 +702,7 @@ class TurtleArtWindow():
y + gblk.spr.save_xy[1]))
def hideblocks(self):
- """ Callback from 'hide blocks' block """
+ ''' Callback from 'hide blocks' block '''
if not self.interactive_mode:
return
self.hide = False
@@ -699,7 +711,7 @@ class TurtleArtWindow():
self.activity.do_hide_blocks()
def showblocks(self):
- """ Callback from 'show blocks' block """
+ ''' Callback from 'show blocks' block '''
if not self.interactive_mode:
return
self.hide = True
@@ -708,33 +720,16 @@ class TurtleArtWindow():
self.activity.do_show_blocks()
def resize_blocks(self, blocks=None):
- """ Resize blocks or if blocks is None, all of the blocks """
+ ''' Resize blocks or if blocks is None, all of the blocks '''
if blocks is None:
blocks = self.just_blocks()
- # We need to restore collapsed stacks before resizing.
- for blk in blocks:
- if blk.status == 'collapsed':
- bot = find_sandwich_bottom(blk)
- if collapsed(bot):
- dy = bot.values[0]
- restore_stack(find_sandwich_top(blk))
- bot.values[0] = dy
-
# Do the resizing.
for blk in blocks:
blk.rescale(self.block_scale)
for blk in blocks:
self._adjust_dock_positions(blk)
- # Re-collapsed stacks after resizing.
- for blk in blocks:
- if collapsed(blk):
- collapse_stack(find_sandwich_top(blk))
- for blk in blocks:
- if blk.name in ['sandwichtop', 'sandwichtop_no_label']:
- grow_stack_arm(blk)
-
# Resize the skins on some blocks: media content and Python
for blk in blocks:
if blk.name in BLOCKS_WITH_SKIN:
@@ -761,7 +756,7 @@ class TurtleArtWindow():
def show_toolbar_palette(self, n, init_only=False, regenerate=False,
show=True):
- """ Show the toolbar palettes, creating them on init_only """
+ ''' Show the toolbar palettes, creating them on init_only '''
# If we are running the 0.86+ toolbar, the selectors are already
# created, as toolbar buttons. Otherwise, we need to create them.
if (self.activity is None or not self.activity.has_toolbarbox) and \
@@ -821,6 +816,7 @@ class TurtleArtWindow():
blk.spr.hide()
if n == palette_names.index('trash'):
for blk in self.trash_stack:
+ # Deprecated
for gblk in find_group(blk):
if gblk.status != 'collapsed':
gblk.spr.set_layer(TAB_LAYER)
@@ -831,6 +827,39 @@ class TurtleArtWindow():
self.selected_palette = save_selected
self.previous_palette = save_previous
+ def regenerate_palette(self, n):
+ ''' Regenerate palette (used by some plugins) '''
+ if (self.activity is None or not self.activity.has_toolbarbox) and \
+ self.selectors == []:
+ return
+ if self.palette_sprs == []:
+ return
+
+ save_selected = self.selected_palette
+ save_previous = self.previous_palette
+ self.selected_palette = n
+ self.previous_palette = self.selected_palette
+
+ if save_selected == n:
+ self._layout_palette(n, regenerate=True)
+ else:
+ self._layout_palette(n, regenerate=True, show=False)
+
+ for blk in self.palettes[n]:
+ if blk.get_visibility():
+ if hasattr(blk.spr, 'set_layer'):
+ blk.spr.set_layer(PROTO_LAYER)
+ else:
+ debug_output('WARNING: block sprite is None' % (blk.name),
+ self.running_sugar)
+ else:
+ blk.spr.hide()
+
+ if not save_selected == n:
+ self._hide_previous_palette(palette=n)
+ self.selected_palette = save_selected
+ self.previous_palette = save_previous
+
def _display_palette_shift_button(self, n):
''' Palettes too wide (or tall) for the screen get a shift button '''
if self.palette_sprs[n][self.orientation].type == \
@@ -984,7 +1013,7 @@ class TurtleArtWindow():
return
def _hide_toolbar_palette(self):
- """ Hide the toolbar palettes """
+ ''' Hide the toolbar palettes '''
self._hide_previous_palette()
if self.activity is None or not self.activity.has_toolbarbox:
# Hide the selectors
@@ -996,7 +1025,7 @@ class TurtleArtWindow():
palette_names[self.selected_palette] + 'off')
def _hide_previous_palette(self, palette=None):
- """ Hide just the previously viewed toolbar palette """
+ ''' Hide just the previously viewed toolbar palette '''
if palette is None:
palette = self.previous_palette
# Hide previously selected palette
@@ -1023,7 +1052,7 @@ class TurtleArtWindow():
gblk.spr.hide()
def _horizontal_layout(self, x, y, blocks):
- """ Position prototypes in a horizontal palette. """
+ ''' Position prototypes in a horizontal palette. '''
max_w = 0
for blk in blocks:
if not blk.get_visibility():
@@ -1048,7 +1077,7 @@ class TurtleArtWindow():
return x, y, max_w
def _vertical_layout(self, x, y, blocks):
- """ Position prototypes in a vertical palette. """
+ ''' Position prototypes in a vertical palette. '''
row = []
row_w = 0
max_h = 0
@@ -1092,7 +1121,7 @@ class TurtleArtWindow():
return x, y, max_h
def _layout_palette(self, n, regenerate=False, show=True):
- """ Layout prototypes in a palette. """
+ ''' Layout prototypes in a palette. '''
if n is not None:
if self.orientation == HORIZONTAL_PALETTE:
x, y = 20, self.toolbar_offset + 5
@@ -1155,7 +1184,7 @@ class TurtleArtWindow():
svg_str_to_pixbuf(svg.palette(w, h)))
def _buttonpress_cb(self, win, event):
- """ Button press """
+ ''' Button press '''
self.window.grab_focus()
x, y = xy(event)
self.mouse_flag = 1
@@ -1187,7 +1216,24 @@ class TurtleArtWindow():
n -= 1
self.selected_blk.spr.set_label(str(n) + CURSOR)
return True
+ elif self._action_name(self.selected_blk, hat=True):
+ if self.selected_blk.values[0] == _('action'):
+ self._new_stack_block(self.selected_blk.spr.labels[0])
+ self._update_action_names(self.selected_blk.spr.labels[0])
+ elif self._box_name(self.selected_blk, storein=True):
+ if self.selected_blk.values[0] == _('my box'):
+ self._new_box_block(self.selected_blk.spr.labels[0])
+ self._update_box_names(self.selected_blk.spr.labels[0])
+ # Un-highlight any blocks in the stack
+ grp = find_group(self.selected_blk)
+ for blk in grp:
+ if blk.status != 'collapsed':
+ blk.unhighlight()
self._unselect_block()
+ if self.running_sugar and self._sharing and \
+ hasattr(self.activity, 'share_button'):
+ self.activity.share_button.set_tooltip(
+ _('Select blocks to share'))
self.selected_turtle = None
# Always hide the status layer on a click
@@ -1199,13 +1245,19 @@ class TurtleArtWindow():
self.dragging_canvas[1] = x
self.dragging_canvas[2] = y
if spr is None:
- self.dragging_canvas[0] = True
+ if not self.running_blocks and not self.hw in (XO1, XO15, XO175):
+ self.dragging_canvas[0] = True
return True
self.dragging_canvas[0] = False
self.selected_spr = spr
# From the sprite at x, y, look for a corresponding block
blk = self.block_list.spr_to_block(spr)
+ ''' If we were copying and didn't click on a block... '''
+ if self.running_sugar and \
+ (self.activity.copying or self.activity.sharing_blocks):
+ if blk is None or blk.type != 'block':
+ self.activity.restore_cursor()
if blk is not None:
if blk.type == 'block':
self.selected_blk = blk
@@ -1226,12 +1278,24 @@ class TurtleArtWindow():
elif blk.name in MACROS:
self._new_macro(blk.name, x + 20, y + 20)
else:
+ defaults = None
+ name = blk.name
# You can only have one instance of some blocks
if blk.name in ['start', 'hat1', 'hat2']:
if len(self.block_list.get_similar_blocks(
'block', blk.name)) > 0:
self.showlabel('dupstack')
return True
+ # If we autogenerated a stack prototype, we need
+ # to change its name from 'stack_foo' to 'stack'
+ elif blk.name[0:6] == 'stack_':
+ defaults = [blk.name[6:]]
+ name = 'stack'
+ # If we autogenerated a box prototype, we need
+ # to change its name from 'box_foo' to 'box'
+ elif blk.name[0:4] == 'box_':
+ defaults = [blk.name[4:]]
+ name = 'box'
# You cannot mix and match sensor blocks
elif blk.name in ['sound', 'volume', 'pitch']:
if len(self.block_list.get_similar_blocks(
@@ -1257,7 +1321,7 @@ class TurtleArtWindow():
self.showlabel('incompatible')
return True
blk.highlight()
- self._new_block(blk.name, x, y)
+ self._new_block(name, x, y, defaults=defaults)
blk.unhighlight()
return True
@@ -1315,8 +1379,111 @@ class TurtleArtWindow():
self._select_toolbar_button(spr)
return True
+ def _update_action_names(self, name):
+ ''' change the label on action blocks of the same name '''
+ if CURSOR in name:
+ name = name.replace(CURSOR, '')
+ for blk in self.just_blocks():
+ if self._action_name(blk, hat=False):
+ if CURSOR in blk.spr.labels[0]:
+ blk.spr.labels[0] = \
+ blk.spr.labels[0].replace(CURSOR, '')
+ if blk.spr.labels[0] == self._saved_action_name:
+ blk.spr.labels[0] = name
+ blk.values[0] = name
+ blk.spr.set_layer(BLOCK_LAYER)
+ self._update_proto_name(name, 'stack_%s' % (self._saved_action_name),
+ 'stack_%s' % (name), 'basic-style-1arg')
+
+ def _update_box_names(self, name):
+ ''' change the label on box blocks of the same name '''
+ if CURSOR in name:
+ name = name.replace(CURSOR, '')
+ for blk in self.just_blocks():
+ if self._box_name(blk, storein=False):
+ if CURSOR in blk.spr.labels[0]:
+ blk.spr.labels[0] = \
+ blk.spr.labels[0].replace(CURSOR, '')
+ if blk.spr.labels[0] == self._saved_box_name:
+ blk.spr.labels[0] = name
+ blk.values[0] = name
+ blk.spr.set_layer(BLOCK_LAYER)
+ self._update_proto_name(name, 'box_%s' % (self._saved_box_name),
+ 'box_%s' % (name), 'number-style-1strarg')
+
+ def _update_proto_name(self, name, old, new, style, palette='blocks'):
+ ''' Change the name of a proto block '''
+ # The name change has to happen in multiple places:
+ # (1) The proto block itself
+ # (2) The list of block styles
+ # (3) The list of proto blocks on the palette
+ # (4) The list of block names
+ if old == new:
+ debug_output('update_proto_name: %s == %s' % (old, new),
+ self.running_sugar)
+ return
+ found = False
+
+ i = palette_name_to_index(palette)
+ for blk in self.palettes[i]: # self.just_protos():
+ if blk.name == old:
+ blk.name = new
+ blk.spr.labels[0] = name
+ blk.spr.set_layer(PROTO_LAYER)
+ blk.resize()
+ break # Should only be one proto block by this name
+
+ if old in palette_blocks[i]:
+ palette_blocks[i].remove(old)
+ if not new in palette_blocks[i]:
+ palette_blocks[i].append(new)
+
+ if old in block_styles[style]:
+ block_styles[style].remove(old)
+ if not new in block_styles[style]:
+ block_styles[style].append(new)
+
+ if old in block_names:
+ del block_names[old]
+ if not new in block_names:
+ block_names[new] = name
+
+ self.show_toolbar_palette(i, regenerate=True)
+
+ def _action_name(self, blk, hat=False):
+ ''' is this a label for an action block? '''
+ if blk is None:
+ return False
+ if blk.name != 'string': # Ignoring int names
+ return False
+ if blk.connections is None:
+ return False
+ if blk.connections[0] is None:
+ return False
+ if hat and blk.connections[0].name == 'hat':
+ return True
+ if not hat and blk.connections[0].name == 'stack':
+ return True
+ return False
+
+ def _box_name(self, blk, storein=False):
+ ''' is this a label for a storein block? '''
+ if blk is None:
+ return False
+ if blk.name != 'string': # Ignoring int names
+ return False
+ if blk.connections is None:
+ return False
+ if blk.connections[0] is None:
+ return False
+ if storein and blk.connections[0].name == 'storein':
+ return True
+ if not storein and blk.connections[0].name == 'box':
+ return True
+ return False
+
def _select_category(self, spr):
- """ Select a category from the toolbar """
+ ''' Select a category from the toolbar '''
i = self.selectors.index(spr)
spr.set_shape(self.selector_shapes[i][1])
if self.selected_selector is not None:
@@ -1329,21 +1496,22 @@ class TurtleArtWindow():
self.show_palette(i)
def _select_toolbar_button(self, spr):
- """ Select a toolbar button (Used when not running Sugar). """
+ ''' Select a toolbar button (Used when not running Sugar). '''
if not hasattr(spr, 'name'):
return
if spr.name == 'run-fastoff':
self.lc.trace = 0
self.hideblocks()
+ self.display_coordinates(clear=True)
self.run_button(0)
elif spr.name == 'run-slowoff':
self.lc.trace = 1
+ self.showblocks()
self.run_button(3)
- elif spr.name == 'debugoff':
- self.lc.trace = 1
- self.run_button(6)
elif spr.name == 'stopiton':
self.stop_button()
+ self.display_coordinates()
+ self.showblocks()
self.toolbar_shapes['stopiton'].hide()
elif spr.name == 'eraseron':
self.eraser_button()
@@ -1351,53 +1519,72 @@ class TurtleArtWindow():
self.hideshow_button()
def _put_in_trash(self, blk, x=0, y=0):
- """ Put a group of blocks into the trash. """
+ ''' Put a group of blocks into the trash. '''
self.trash_stack.append(blk)
group = find_group(blk)
for gblk in group:
- if gblk.status == 'collapsed':
- # Collapsed stacks are restored for rescaling
- # and then recollapsed after they are moved to the trash.
- bot = find_sandwich_bottom(gblk)
- if collapsed(bot):
- dy = bot.values[0]
- restore_stack(find_sandwich_top(gblk))
- bot.values[0] = dy
gblk.type = 'trash'
gblk.rescale(self.trash_scale)
blk.spr.move((x, y))
for gblk in group:
self._adjust_dock_positions(gblk)
- # Re-collapsing any stacks we had restored for scaling
- for gblk in group:
- if collapsed(gblk):
- collapse_stack(find_sandwich_top(gblk))
-
# And resize any skins.
for gblk in group:
if gblk.name in BLOCKS_WITH_SKIN:
self._resize_skin(gblk)
- # self.show_palette(palette_names.index('trash'))
if self.selected_palette != palette_names.index('trash'):
for gblk in group:
gblk.spr.hide()
+ # If there was a named hat or storein, remove it from the
+ # proto palette, the palette name list, the block name list,
+ # and the style list
+ for gblk in group:
+ if (gblk.name == 'hat' or gblk.name == 'storein') and \
+ gblk.connections is not None and \
+ gblk.connections[1] is not None and \
+ gblk.connections[1].name == 'string':
+ if gblk.name == 'hat':
+ name = 'stack_%s' % gblk.connections[1].values[0]
+ style = 'basic-style-1arg'
+ else:
+ name = 'box_%s' % gblk.connections[1].values[0]
+ style = 'number-style-1strarg'
+ i = palette_name_to_index('blocks')
+ if name in palette_blocks[i]:
+ palette_blocks[i].remove(name)
+ for blk in self.palettes[i]:
+ if blk.name == name:
+ blk.spr.hide()
+ self.palettes[i].remove(blk)
+ self.show_toolbar_palette(i, regenerate=True)
+ if name in block_styles[style]:
+ block_styles[style].remove(name)
+ if name in block_names:
+ del block_names[name]
+
def _restore_all_from_trash(self):
- """ Restore all the blocks in the trash can. """
+ ''' Restore all the blocks in the trash can. '''
for blk in self.block_list.list:
if blk.type == 'trash':
self._restore_from_trash(blk)
def _restore_latest_from_trash(self):
- """ Restore most recent blocks from the trash can. """
+ ''' Restore most recent blocks from the trash can. '''
if len(self.trash_stack) == 0:
return
self._restore_from_trash(self.trash_stack[len(self.trash_stack) - 1])
def _restore_from_trash(self, blk):
group = find_group(blk)
+
+ for gblk in group:
+ if gblk.name == 'sandwichclampcollapsed':
+ restore_clamp(gblk)
+ self.resize_parent_clamps(gblk)
+
for gblk in group:
gblk.rescale(self.block_scale)
gblk.spr.set_layer(BLOCK_LAYER)
@@ -1407,13 +1594,9 @@ class TurtleArtWindow():
else:
gblk.spr.move((x + PALETTE_WIDTH, y))
gblk.type = 'block'
+
for gblk in group:
self._adjust_dock_positions(gblk)
- # If the stack had been collapsed before going into the trash,
- # collapse it again now.
- for gblk in group:
- if collapsed(gblk):
- collapse_stack(find_sandwich_top(gblk))
# And resize any skins.
for gblk in group:
@@ -1423,7 +1606,7 @@ class TurtleArtWindow():
self.trash_stack.remove(blk)
def _empty_trash(self):
- """ Permanently remove all blocks presently in the trash can. """
+ ''' Permanently remove all blocks presently in the trash can. '''
for blk in self.block_list.list:
if blk.type == 'trash':
blk.type = 'deleted'
@@ -1431,7 +1614,7 @@ class TurtleArtWindow():
self.trash_stack = []
def _in_the_trash(self, x, y):
- """ Is x, y over a palette? """
+ ''' Is x, y over a palette? '''
if self.selected_palette is not None and \
self.palette_sprs[self.selected_palette][self.orientation].hit(
(x, y)):
@@ -1439,7 +1622,7 @@ class TurtleArtWindow():
return False
def _block_pressed(self, x, y, blk):
- """ Block pressed """
+ ''' Block pressed '''
if blk is not None:
blk.highlight()
self._disconnect(blk)
@@ -1449,13 +1632,29 @@ class TurtleArtWindow():
for blk in self.drag_group:
if blk.status != 'collapsed':
blk.spr.set_layer(TOP_LAYER)
+ if self.running_sugar and \
+ (self.activity.copying or self.activity.sharing_blocks):
+ for blk in self.drag_group:
+ if blk.status != 'collapsed':
+ blk.highlight()
+ self.block_operation = 'copying'
+ if self.activity.copying:
+ self.activity.send_to_clipboard()
+ else:
+ self.activity.share_blocks()
+ if self.running_sugar and self._sharing and \
+ hasattr(self.activity, 'share_button'):
+ self.activity.share_button.set_tooltip(
+ _('Share selected blocks'))
if len(blk.spr.labels) > 0:
self.saved_string = blk.spr.labels[0]
+ self._saved_action_name = self.saved_string
+ self._saved_box_name = self.saved_string
else:
self.saved_string = ''
def _unselect_block(self):
- """ Unselect block """
+ ''' Unselect block '''
# After unselecting a 'number' block, we need to check its value
if self.selected_blk.name == 'number':
self._number_check()
@@ -1467,7 +1666,7 @@ class TurtleArtWindow():
self.selected_blk = None
def _new_block(self, name, x, y, defaults=None):
- """ Make a new block. """
+ ''' Make a new block. '''
x_pos = x - 20
y_pos = y - 20
if name in content_blocks:
@@ -1546,54 +1745,67 @@ class TurtleArtWindow():
self.used_block_list.append(newblk.spr.labels[0])
def _new_macro(self, name, x, y):
- """ Create a "macro" (predefined stack of blocks). """
+ ''' Create a "macro" (predefined stack of blocks). '''
macro = MACROS[name]
macro[0][2] = x
macro[0][3] = y
top = self.process_data(macro)
self.block_operation = 'new'
- self._check_collapsibles(top)
self.drag_group = find_group(top)
def process_data(self, block_data, offset=0):
- """ Process block_data (from a macro, a file, or the clipboard). """
-
+ ''' Process block_data (from a macro, a file, or the clipboard). '''
+ self._process_block_data = []
+ for blk in block_data:
+ if not self._found_a_turtle(blk):
+ self._process_block_data.append(
+ [blk[0], blk[1], blk[2], blk[3], blk[4]])
+ self._extra_block_data = []
# Create the blocks (or turtle).
blocks = []
- for blk in block_data:
+ for blk in self._process_block_data:
if not self._found_a_turtle(blk):
- blocks.append(self.load_block(blk, offset))
+ newblk = self.load_block(blk, offset)
+ if newblk is not None:
+ blocks.append(newblk)
+ # Some extra blocks may have been added by load_block
+ for blk in self._extra_block_data:
+ self._process_block_data.append(blk)
+ newblk = self.load_block(blk, offset)
+ if newblk is not None:
+ blocks.append(newblk)
# Make the connections.
for i in range(len(blocks)):
cons = []
# Normally, it is simply a matter of copying the connections.
if blocks[i].connections is None:
- if block_data[i][4] is not None:
- for c in block_data[i][4]:
+ if self._process_block_data[i][4] is not None:
+ for c in self._process_block_data[i][4]:
if c is None or c > (len(blocks) - 1):
cons.append(None)
else:
cons.append(blocks[c])
else:
- debug_output("connection error %s" % (str(block_data[i])),
+ debug_output("connection error %s" % (
+ str(self._process_block_data[i])),
self.running_sugar)
cons.append(None)
elif blocks[i].connections == 'check':
# Convert old-style boolean and arithmetic blocks
cons.append(None) # Add an extra connection.
- for c in block_data[i][4]:
+ for c in self._process_block_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 block_styles['boolean-style']:
- if block_data[i][4][0] is not None:
- c = block_data[i][4][0]
- cons[0] = blocks[block_data[c][4][0]]
- c0 = block_data[c][4][0]
- for j, cj in enumerate(block_data[c0][4]):
+ if self._process_block_data[i][4][0] is not None:
+ c = self._process_block_data[i][4][0]
+ cons[0] = blocks[self._process_block_data[c][4][0]]
+ c0 = self._process_block_data[c][4][0]
+ for j, cj in enumerate(self._process_block_data[c0][4]):
if cj == c:
blocks[c0].connections[j] = blocks[i]
if c < i:
@@ -1604,11 +1816,11 @@ class TurtleArtWindow():
debug_output("Warning: dock to the future",
self.running_sugar)
else:
- if block_data[i][4][0] is not None:
- c = block_data[i][4][0]
- cons[0] = blocks[block_data[c][4][0]]
- c0 = block_data[c][4][0]
- for j, cj in enumerate(block_data[c0][4]):
+ if self._process_block_data[i][4][0] is not None:
+ c = self._process_block_data[i][4][0]
+ cons[0] = blocks[self._process_block_data[c][4][0]]
+ c0 = self._process_block_data[c][4][0]
+ for j, cj in enumerate(self._process_block_data[c0][4]):
if cj == c:
blocks[c0].connections[j] = blocks[i]
if c < i:
@@ -1628,15 +1840,27 @@ class TurtleArtWindow():
for blk in blocks:
self._adjust_dock_positions(blk)
- # Look for any stacks that need to be collapsed or sandwiched
+ # Look for any stacks that need to be collapsed
for blk in blocks:
- if collapsed(blk):
- collapse_stack(find_sandwich_top(blk))
- elif blk.name == 'sandwichbottom' and collapsible(blk):
- blk.svg.set_hide(True)
- blk.svg.set_show(False)
- blk.refresh()
- grow_stack_arm(find_sandwich_top(blk))
+ if blk.name == 'sandwichclampcollapsed':
+ collapse_clamp(blk, False)
+
+ # process in reverse order
+ for i in range(len(blocks)):
+ blk = blocks[-i - 1]
+ if blk.name in EXPANDABLE_FLOW:
+ if blk.name in block_styles['clamp-style-1arg'] or\
+ blk.name in block_styles['clamp-style-boolean']:
+ if blk.connections[2] is not None:
+ self._resize_clamp(blk, blk.connections[2])
+ elif blk.name in block_styles['clamp-style']:
+ if blk.connections[1] is not None:
+ self._resize_clamp(blk, blk.connections[1])
+ elif blk.name in block_styles['clamp-style-else']:
+ if blk.connections[2] is not None:
+ self._resize_clamp(blk, blk.connections[2], dockn=2)
+ if blk.connections[3] is not None:
+ self._resize_clamp(blk, blk.connections[3], dockn=3)
# Resize blocks to current scale
self.resize_blocks(blocks)
@@ -1647,7 +1871,7 @@ class TurtleArtWindow():
return None
def _adjust_dock_positions(self, blk):
- """ Adjust the dock x, y positions """
+ ''' Adjust the dock x, y positions '''
if not self.interactive_mode:
return
(sx, sy) = blk.spr.get_xy()
@@ -1704,7 +1928,7 @@ class TurtleArtWindow():
self.turtle_movement_to_share = None
def _mouse_move(self, x, y):
- """ Process mouse movements """
+ ''' Process mouse movements '''
if self.running_sugar and self.dragging_canvas[0]:
dx = self.dragging_canvas[1] - x
@@ -1750,10 +1974,6 @@ class TurtleArtWindow():
elif self.drag_group[0] is not None:
blk = self.drag_group[0]
- # Don't move a bottom blk if the stack is collapsed
- if collapsed(blk):
- return
-
self.selected_spr = blk.spr
dragx, dragy = self.drag_pos
(sx, sy) = blk.spr.get_xy()
@@ -1813,7 +2033,7 @@ class TurtleArtWindow():
self.dy += dy
def _show_popup(self, x, y):
- """ Let's help our users by displaying a little help. """
+ ''' Let's help our users by displaying a little help. '''
spr = self.sprite_list.find_sprite((x, y))
blk = self.block_list.spr_to_block(spr)
if spr and blk is not None:
@@ -1849,7 +2069,7 @@ class TurtleArtWindow():
self.timeout_tag[0] = 0
def _do_show_popup(self, block_name):
- """ Fetch the help text and display it. """
+ ''' Fetch the help text and display it. '''
if self.no_help:
return 0
if block_name in special_names:
@@ -1871,7 +2091,7 @@ class TurtleArtWindow():
return 0
def _buttonrelease_cb(self, win, event):
- """ Button release """
+ ''' Button release '''
x, y = xy(event)
self.mouse_flag = 0
self.mouse_x = x
@@ -1939,7 +2159,6 @@ class TurtleArtWindow():
# Look to see if we can dock the current stack.
self._snap_to_dock()
- self._check_collapsibles(blk)
for gblk in self.drag_group:
if gblk.status != 'collapsed':
gblk.spr.set_layer(BLOCK_LAYER)
@@ -1952,6 +2171,14 @@ class TurtleArtWindow():
abs(self.dx) < MOTION_THRESHOLD and \
abs(self.dy < MOTION_THRESHOLD))):
self._click_block(x, y)
+ elif self.block_operation == 'copying':
+ gobject.timeout_add(500, self._unhighlight_drag_group, blk)
+
+ def _unhighlight_drag_group(self, blk):
+ self.drag_group = find_group(blk)
+ for gblk in self.drag_group:
+ gblk.unhighlight()
+ self.drag_group = None
def remote_turtle(self, name):
''' Is this a remote turtle? '''
@@ -1968,9 +2195,9 @@ class TurtleArtWindow():
if turtle is not None:
turtle.label_block = Block(self.block_list,
self.sprite_list, 'turtle-label', 0, 0,
- 'label', [], 1.0 / self.scale,
+ 'label', [], 2.0 / self.scale,
colors)
- turtle.label_block.spr.set_label_attributes(6.0 / self.scale)
+ turtle.label_block.spr.set_label_attributes(12.0 / self.scale)
if len(name) > 6:
turtle.label_block.spr.set_label(name[0:4] + '…')
else:
@@ -1978,7 +2205,7 @@ class TurtleArtWindow():
turtle.show()
def _move_turtle(self, x, y):
- """ Move the selected turtle to (x, y). """
+ ''' Move the selected turtle to (x, y). '''
self.canvas.xcor = x
self.canvas.ycor = y
self.canvas.move_turtle()
@@ -1992,7 +2219,7 @@ class TurtleArtWindow():
self.canvas.ycor / self.coord_scale)
def _click_block(self, x, y):
- """ Click block: lots of special cases to handle... """
+ ''' Click block: lots of special cases to handle... '''
blk = self.block_list.spr_to_block(self.selected_spr)
if blk is None:
return
@@ -2019,7 +2246,9 @@ class TurtleArtWindow():
elif blk.name == 'identity2' or blk.name == 'hspace':
group = find_group(blk)
if hide_button_hit(blk.spr, x, y):
- dx = blk.reset_x()
+ dx = -20
+ blk.contract_in_x(-dx)
+ # dx = blk.reset_x()
elif show_button_hit(blk.spr, x, y):
dx = 20
blk.expand_in_x(dx)
@@ -2033,7 +2262,9 @@ class TurtleArtWindow():
elif blk.name == 'vspace':
group = find_group(blk)
if hide_button_hit(blk.spr, x, y):
- dy = blk.reset_y()
+ dy = -20
+ blk.contract_in_y(-dy)
+ # dy = blk.reset_y()
elif show_button_hit(blk.spr, x, y):
dy = 20
blk.expand_in_y(dy)
@@ -2043,7 +2274,7 @@ class TurtleArtWindow():
for gblk in group:
if gblk != blk:
gblk.spr.move_relative((0, dy * blk.scale))
- grow_stack_arm(find_sandwich_top(blk))
+ self._resize_parent_clamps(blk)
elif blk.name in expandable_blocks:
# Connection may be lost during expansion, so store it...
@@ -2052,7 +2283,9 @@ class TurtleArtWindow():
dock0 = blk0.connections.index(blk)
if hide_button_hit(blk.spr, x, y):
- dy = blk.reset_y()
+ dy = -20
+ blk.contract_in_y(-dy)
+ # dy = blk.reset_y()
elif show_button_hit(blk.spr, x, y):
dy = 20
blk.expand_in_y(dy)
@@ -2071,7 +2304,7 @@ class TurtleArtWindow():
blk0.connections[dock0] = blk
self._cascade_expandable(blk)
- grow_stack_arm(find_sandwich_top(blk))
+ self._resize_parent_clamps(blk)
elif blk.name in EXPANDABLE_ARGS or blk.name == 'nop':
if show_button_hit(blk.spr, x, y):
@@ -2118,39 +2351,41 @@ class TurtleArtWindow():
blk.connections[n - 1] = argblk
if blk.name in block_styles['number-style-var-arg']:
self._cascade_expandable(blk)
- grow_stack_arm(find_sandwich_top(blk))
+ self._resize_parent_clamps(blk)
elif blk.name in PYTHON_SKIN:
self._import_py()
else:
self._run_stack(blk)
-
- elif blk.name in ['sandwichtop_no_arm_no_label',
- 'sandwichtop_no_arm']:
- restore_stack(blk)
-
- elif blk.name in COLLAPSIBLE or blk.name == 'sandwichtop_no_label':
- if blk.name == 'sandwichtop_no_label':
- if hide_button_hit(blk.spr, x, y):
- collapse_stack(blk)
- else:
- self._run_stack(blk)
- top = find_sandwich_top(blk)
- if collapsed(blk):
- restore_stack(top) # deprecated (bottom block is invisible)
- elif top is not None:
- collapse_stack(top)
+ elif blk.name == 'sandwichclampcollapsed':
+ restore_clamp(blk)
+ if blk.connections[1] is not None:
+ self._resize_clamp(blk, blk.connections[1], 1)
+ self._resize_parent_clamps(blk)
+ elif blk.name == 'sandwichclamp':
+ if hide_button_hit(blk.spr, x, y):
+ collapse_clamp(blk, True)
+ self._resize_parent_clamps(blk)
+ else:
+ self._run_stack(blk)
else:
self._run_stack(blk)
+ def _resize_parent_clamps(self, blk):
+ ''' If we changed size, we need to let any parent clamps know. '''
+ nblk, dockn = self._expandable_flow_above(blk)
+ while nblk is not None:
+ self._resize_clamp(nblk, nblk.connections[dockn], dockn=dockn)
+ nblk, dockn = self._expandable_flow_above(nblk)
+
def _expand_boolean(self, blk, blk2, dy):
- """ Expand a boolean blk if blk2 is too big to fit. """
+ ''' Expand a boolean blk if blk2 is too big to fit. '''
group = find_group(blk2)
for gblk in find_group(blk):
if gblk not in group:
gblk.spr.move_relative((0, -dy * blk.scale))
def _expand_expandable(self, blk, blk2, dy):
- """ Expand an expandable blk if blk2 is too big to fit. """
+ ''' Expand an expandable blk if blk2 is too big to fit. '''
if blk2 is None:
group = [blk]
else:
@@ -2176,7 +2411,7 @@ class TurtleArtWindow():
return False
def _cascade_expandable(self, blk):
- """ If expanding/shrinking a block, cascade. """
+ ''' If expanding/shrinking a block, cascade. '''
while self._number_style(blk.name):
if blk.connections[0] is None:
break
@@ -2204,33 +2439,8 @@ class TurtleArtWindow():
else:
break
- def _check_collapsibles(self, blk):
- """ Check state of collapsible blocks upon change in dock state. """
- group = find_group(blk)
- for gblk in group:
- if gblk.name in COLLAPSIBLE:
- if collapsed(gblk):
- gblk.svg.set_show(True)
- gblk.svg.set_hide(False)
- reset_stack_arm(find_sandwich_top(gblk))
- elif collapsible(gblk):
- gblk.svg.set_hide(True)
- gblk.svg.set_show(False)
- grow_stack_arm(find_sandwich_top(gblk))
- else:
- gblk.svg.set_hide(False)
- gblk.svg.set_show(False)
- # Ouch: When you tear off the sandwich bottom, you
- # no longer have access to the group with the sandwich top
- # so check them all.
- for b in self.just_blocks():
- if b.name in ['sandwichtop', 'sandwichtop_no_label']:
- if find_sandwich_bottom(b) is None:
- reset_stack_arm(b)
- gblk.refresh()
-
def _run_stack(self, blk):
- """ Run a stack of blocks. """
+ ''' Run a stack of blocks. '''
if blk is None:
return
self.lc.find_value_blocks() # Are there blocks to update?
@@ -2238,6 +2448,7 @@ class TurtleArtWindow():
if len(self.block_list.get_similar_blocks('block', 'savesvg')) > 0:
if self.canvas.cr_svg is None:
self.canvas.setup_svg_surface()
+ self.running_blocks = True
self._start_plugins() # Let the plugins know we are running.
top = find_top_block(blk)
self.lc.run_blocks(top, self.just_blocks(), True)
@@ -2248,12 +2459,12 @@ class TurtleArtWindow():
pass
def _snap_to_dock(self):
- """ Snap a block (selected_block) to the dock of another block
- (destination_block).
- """
+ ''' Snap a block (selected_block) to the dock of another block
+ (destination_block). '''
selected_block = self.drag_group[0]
best_destination = None
d = SNAP_THRESHOLD
+ self.inserting_block_mid_stack = False
for selected_block_dockn in range(len(selected_block.docks)):
for destination_block in self.just_blocks():
# Don't link to a block that is hidden
@@ -2264,8 +2475,9 @@ class TurtleArtWindow():
continue
# Check each dock of destination for a possible connection
for destination_dockn in range(len(destination_block.docks)):
- this_xy = dock_dx_dy(destination_block, destination_dockn,
- selected_block, selected_block_dockn)
+ this_xy = self.dock_dx_dy(
+ destination_block, destination_dockn,
+ selected_block, selected_block_dockn)
if magnitude(this_xy) > d:
continue
d = magnitude(this_xy)
@@ -2289,11 +2501,33 @@ class TurtleArtWindow():
(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_destination.connections[best_destination_dockn]
- if blk_in_dock is not None and blk_in_dock != selected_block:
- blk_in_dock.connections[0] = None
- self._put_in_trash(blk_in_dock)
+ if self.inserting_block_mid_stack:
+ # If there was already a block docked there, move it
+ # to the bottom of the drag group.
+ if blk_in_dock is not None and blk_in_dock != selected_block:
+ bot = find_bot_block(self.drag_group[0])
+ if bot is not None:
+ blk_in_dock.connections[0] = None
+ drag_group = find_group(blk_in_dock)
+ blk_in_dock.connections[0] = bot
+ bot.connections[-1] = blk_in_dock
+ dx = bot.spr.get_xy()[0] - \
+ self.drag_group[0].spr.get_xy()[0] + \
+ bot.docks[-1][2] - blk_in_dock.docks[0][2]
+ dy = bot.spr.get_xy()[1] - \
+ self.drag_group[0].spr.get_xy()[1] + \
+ bot.docks[-1][3] - blk_in_dock.docks[0][3]
+ # Move each sprite in the group associated
+ # with the block we are moving.
+ for gblk in drag_group:
+ gblk.spr.move_relative((dx, dy))
+ else:
+ # If there was already a block docked there, move it
+ # to the trash.
+ if blk_in_dock is not None and blk_in_dock != selected_block:
+ blk_in_dock.connections[0] = None
+ self._put_in_trash(blk_in_dock)
# Note the connection in destination dock
best_destination.connections[best_destination_dockn] = \
@@ -2319,6 +2553,23 @@ class TurtleArtWindow():
dy += 45
best_destination.expand_in_y(dy)
self._expand_boolean(best_destination, selected_block, dy)
+ elif best_destination.name in EXPANDABLE_FLOW:
+ if best_destination.name in block_styles['clamp-style-1arg'] or\
+ best_destination.name in block_styles['clamp-style-boolean']:
+ if best_destination_dockn == 2:
+ self._resize_clamp(best_destination, self.drag_group[0])
+ elif best_destination.name in block_styles['clamp-style'] or \
+ best_destination.name in block_styles[
+ 'clamp-style-collapsible']:
+ if best_destination_dockn == 1:
+ self._resize_clamp(best_destination, self.drag_group[0])
+ elif best_destination.name in block_styles['clamp-style-else']:
+ if best_destination_dockn == 2:
+ self._resize_clamp(
+ best_destination, self.drag_group[0], dockn=2)
+ elif best_destination_dockn == 3:
+ self._resize_clamp(
+ best_destination, self.drag_group[0], dockn=3)
elif best_destination.name in expandable_blocks and \
best_destination_dockn == 1:
dy = 0
@@ -2339,20 +2590,30 @@ class TurtleArtWindow():
self._expand_expandable(
best_destination, selected_block, dy)
self._cascade_expandable(best_destination)
- grow_stack_arm(find_sandwich_top(best_destination))
+ # If we are in an expandable flow, expand it...
+ if best_destination is not None:
+ self._resize_parent_clamps(best_destination)
+ '''
+ blk, dockn = self._expandable_flow_above(best_destination)
+ while blk is not None:
+ self._resize_clamp(blk, blk.connections[dockn], dockn=dockn)
+ blk, dockn = self._expandable_flow_above(blk)
+ '''
def _disconnect(self, blk):
- """ Disconnect block from stack above it. """
+ ''' Disconnect block from stack above it. '''
if blk is None:
return
- if blk.connections[0] is None:
+ if blk.connections is None:
return
- if collapsed(blk):
+ if blk.connections[0] is None:
return
+ c = None
blk2 = blk.connections[0]
if blk in blk2.connections:
c = blk2.connections.index(blk)
blk2.connections[c] = None
+ blk3, dockn = self._expandable_flow_above(blk)
if blk2.name in block_styles['boolean-style']:
if c == 2 and blk2.ey > 0:
@@ -2365,12 +2626,79 @@ class TurtleArtWindow():
if dy != 0:
self._expand_expandable(blk2, blk, dy)
self._cascade_expandable(blk2)
- grow_stack_arm(find_sandwich_top(blk2))
-
+ elif c is not None and blk2.name in EXPANDABLE_FLOW:
+ if blk2.name in block_styles['clamp-style-1arg'] or\
+ blk2.name in block_styles['clamp-style-boolean']:
+ if c == 2:
+ self._resize_clamp(blk2, None, c)
+ elif blk2.name in block_styles['clamp-style'] or \
+ blk2.name in block_styles['clamp-style-collapsible']:
+ if c == 1:
+ self._resize_clamp(blk2, None)
+ elif blk2.name in block_styles['clamp-style-else']:
+ if c == 2 or c == 3:
+ self._resize_clamp(blk2, None, dockn=c)
+ while blk3 is not None and blk3.connections[dockn] is not None:
+ self._resize_clamp(blk3, blk3.connections[dockn], dockn=dockn)
+ blk3, dockn = self._expandable_flow_above(blk3)
blk.connections[0] = None
+ def _resize_clamp(self, blk, gblk, dockn=-2):
+ ''' If the content of a clamp changes, resize it '''
+ if dockn < 0:
+ dockn = len(blk.docks) + dockn
+ y1 = blk.docks[-1][3]
+ if blk.name in block_styles['clamp-style-else'] and dockn == 3:
+ blk.reset_y2()
+ else:
+ blk.reset_y()
+ dy = 0
+ # Calculate height of drag group
+ while gblk is not None:
+ delta = int((gblk.docks[-1][3] - gblk.docks[0][3]) / gblk.scale)
+ if delta == 0:
+ dy += 21 # Fixme: don't hardcode size of stop action block
+ else:
+ dy += delta
+ gblk = gblk.connections[-1]
+ # Clamp has room for one "standard" block by default
+ if dy > 0:
+ dy -= 21 # Fixme: don't hardcode
+ if blk.name in block_styles['clamp-style-else'] and dockn == 3:
+ blk.expand_in_y2(dy)
+ else:
+ blk.expand_in_y(dy)
+ y2 = blk.docks[-1][3]
+ gblk = blk.connections[-1]
+ # Move group below clamp up or down
+ if blk.connections[-1] is not None:
+ drag_group = find_group(blk.connections[-1])
+ for gblk in drag_group:
+ gblk.spr.move_relative((0, y2-y1))
+ # We may have to move the else clamp group down too.
+ if blk.name in block_styles['clamp-style-else'] and dockn == 2:
+ if blk.connections[3] is not None:
+ drag_group = find_group(blk.connections[3])
+ for gblk in drag_group:
+ gblk.spr.move_relative((0, y2 - y1))
+
+ def _expandable_flow_above(self, blk):
+ ''' Is there an expandable flow block above this one? '''
+ while blk.connections[0] is not None:
+ if blk.connections[0].name in EXPANDABLE_FLOW:
+ if blk.connections[0].name == 'ifelse':
+ if blk.connections[0].connections[2] == blk:
+ return blk.connections[0], 2
+ elif blk.connections[0].connections[3] == blk:
+ return blk.connections[0], 3
+ else:
+ if blk.connections[0].connections[-2] == blk:
+ return blk.connections[0], -2
+ blk = blk.connections[0]
+ return None, None
+
def _import_from_journal(self, blk):
- """ Import a file from the Sugar Journal """
+ ''' Import a file from the Sugar Journal '''
# TODO: check blk name to set filter
if self.running_sugar:
chooser(self.parent, '', self._update_media_blk)
@@ -2382,7 +2710,7 @@ class TurtleArtWindow():
self._update_media_icon(blk, fname)
def _load_description_block(self, blk):
- """ Look for a corresponding description block """
+ ''' Look for a corresponding description block '''
if blk is None or blk.name != 'journal' or len(blk.values) == 0 or \
blk.connections[0] is None:
return
@@ -2393,12 +2721,12 @@ class TurtleArtWindow():
self._update_media_icon(dblk, None, blk.values[0])
def _update_media_blk(self, dsobject):
- """ Called from the chooser to load a media block """
+ ''' Called from the chooser to load a media block '''
self._update_media_icon(self.selected_blk, dsobject,
dsobject.object_id)
def _update_media_icon(self, blk, name, value=''):
- """ Update the icon on a 'loaded' media block. """
+ ''' Update the icon on a 'loaded' media block. '''
if blk.name == 'journal':
self._load_image_thumb(name, blk)
elif blk.name == 'audio':
@@ -2416,7 +2744,7 @@ class TurtleArtWindow():
blk.spr.set_label(' ')
def _load_image_thumb(self, picture, blk):
- """ Replace icon with a preview image. """
+ ''' Replace icon with a preview image. '''
pixbuf = None
self._block_skin('descriptionon', blk)
@@ -2441,7 +2769,7 @@ class TurtleArtWindow():
self._resize_skin(blk)
def _keypress_cb(self, area, event):
- """ Keyboard """
+ ''' Keyboard '''
keyname = gtk.gdk.keyval_name(event.keyval)
keyunicode = gtk.gdk.keyval_to_unicode(event.keyval)
if event.get_state() & gtk.gdk.MOD1_MASK:
@@ -2475,8 +2803,8 @@ class TurtleArtWindow():
self._process_numeric_input(keyname)
elif self.selected_blk.name == 'string':
self.process_alphanumeric_input(keyname, keyunicode)
- if self.selected_blk is not None:
- self.selected_blk.resize()
+ # if self.selected_blk is not None:
+ self.selected_blk.resize()
elif self.selected_blk.name != 'proto':
self._process_keyboard_commands(keyname, block_flag=True)
@@ -2530,7 +2858,7 @@ class TurtleArtWindow():
self.selected_blk.spr.set_label(newnum + CURSOR)
def process_alphanumeric_input(self, keyname, keyunicode):
- """ Make sure alphanumeric input is properly parsed. """
+ ''' Make sure alphanumeric input is properly parsed. '''
if len(self.selected_blk.spr.labels[0]) > 0:
c = self.selected_blk.spr.labels[0].count(CURSOR)
if c == 0:
@@ -2539,15 +2867,12 @@ class TurtleArtWindow():
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:
- debug_output("[%s]" % self.selected_blk.spr.labels[0],
- self.running_sugar)
- oldleft = self.selected_blk.spr.labels[0]
- oldright = ''
+ elif CURSOR in self.selected_blk.spr.labels[0]:
+ oldleft, oldright = \
+ self.selected_blk.spr.labels[0].split(CURSOR)
+ else: # Where did our cursor go?
+ oldleft = self.selected_blk.spr.labels[0]
+ oldright = ''
else:
oldleft = ''
oldright = ''
@@ -2614,7 +2939,7 @@ class TurtleArtWindow():
self.selected_blk.spr.set_label("%s%s%s" % (newleft, CURSOR, oldright))
def _process_keyboard_commands(self, keyname, block_flag=True):
- """ Use the keyboard to move blocks and turtle """
+ ''' Use the keyboard to move blocks and turtle '''
mov_dict = {'KP_Up': [0, 20], 'j': [0, 20], 'Up': [0, 20],
'KP_Down': [0, -20], 'k': [0, -20], 'Down': [0, -20],
'KP_Left': [-20, 0], 'h': [-20, 0], 'Left': [-20, 0],
@@ -2629,7 +2954,7 @@ class TurtleArtWindow():
return True
if keyname in ['KP_End', 'End']:
- self.run_button(0)
+ self.run_button(self.step_time)
elif self.selected_spr is not None:
if not self.lc.running and block_flag:
blk = self.block_list.spr_to_block(self.selected_spr)
@@ -2659,7 +2984,7 @@ class TurtleArtWindow():
return True
def _jog_turtle(self, dx, dy):
- """ Jog turtle """
+ ''' Jog turtle '''
if dx == -1 and dy == -1:
self.canvas.xcor = 0
self.canvas.ycor = 0
@@ -2672,7 +2997,7 @@ class TurtleArtWindow():
self.selected_turtle = None
def _align_to_grid(self, grid=20):
- """ Align blocks at the top of stacks to a grid """
+ ''' Align blocks at the top of stacks to a grid '''
for blk in self.block_list.list:
if blk.type == 'block':
top = find_top_block(blk)
@@ -2690,11 +3015,9 @@ class TurtleArtWindow():
self._jog_block(top, dx, -dy)
def _jog_block(self, blk, dx, dy):
- """ Jog block """
+ ''' Jog block '''
if blk.type == 'proto':
return
- if collapsed(blk):
- return
if dx == 0 and dy == 0:
return
self._disconnect(blk)
@@ -2715,7 +3038,7 @@ class TurtleArtWindow():
self.drag_group = None
def _number_check(self):
- """ Make sure a 'number' block contains a number. """
+ ''' Make sure a 'number' block contains a number. '''
n = self.selected_blk.spr.labels[0].replace(CURSOR, '')
if n in ['-', '.', '-.', ',', '-,']:
n = 0
@@ -2746,9 +3069,10 @@ class TurtleArtWindow():
s = self.selected_blk.spr.labels[0].replace(CURSOR, '')
self.selected_blk.spr.set_label(s)
self.selected_blk.values[0] = s.replace(RETURN, "\12")
+ self.saved_string = self.selected_blk.values[0]
def load_python_code_from_file(self, fname=None, add_new_block=True):
- """ Load Python code from a file """
+ ''' Load Python code from a file '''
id = None
self.python_code = None
if fname is None:
@@ -2813,7 +3137,7 @@ class TurtleArtWindow():
return id
def load_python_code_from_journal(self, dsobject, blk=None):
- """ Read the Python code from the Journal object """
+ ''' Read the Python code from the Journal object '''
self.python_code = None
try:
debug_output("opening %s " % dsobject.file_path,
@@ -2834,7 +3158,7 @@ class TurtleArtWindow():
blk.values[0] = dsobject.object_id
def _import_py(self):
- """ Import Python code into a block """
+ ''' Import Python code into a block '''
if self.running_sugar:
chooser(self.parent, 'org.laptop.Pippy',
self.load_python_code_from_journal)
@@ -2847,7 +3171,7 @@ class TurtleArtWindow():
self.set_userdefined(self.selected_blk)
def new_project(self):
- """ Start a new project """
+ ''' Start a new project '''
self.lc.stop_logo()
self._loaded_project = ""
# Put current project in the trash.
@@ -2859,11 +3183,11 @@ class TurtleArtWindow():
self.save_file_name = None
def is_new_project(self):
- """ Is this a new project or was a old project loaded from a file? """
+ ''' Is this a new project or was a old project loaded from a file? '''
return self._loaded_project == ""
def project_has_changed(self):
- """ WARNING: order of JSON serialized data may have changed. """
+ ''' WARNING: order of JSON serialized data may have changed. '''
try:
f = open(self._loaded_project, 'r')
saved_project_data = f.read()
@@ -2878,13 +3202,14 @@ class TurtleArtWindow():
return saved_project_data != current_project_data
def load_files(self, ta_file, create_new_project=True):
- """ Load a project from a file """
+ ''' Load a project from a file '''
if create_new_project:
self.new_project()
- self._check_collapsibles(self.process_data(data_from_file(ta_file)))
+ self.process_data(data_from_file(ta_file))
self._loaded_project = ta_file
- def load_file(self, create_new_project=True):
+ def load_file_from_chooser(self, create_new_project=True):
+ ''' Load a project from file chooser '''
_file_name, self.load_save_folder = get_load_name('.ta',
self.load_save_folder)
if _file_name is None:
@@ -2898,7 +3223,7 @@ class TurtleArtWindow():
self.activity.metadata['title'] = os.path.split(_file_name)[1]
def _found_a_turtle(self, blk):
- """ Either [-1, 'turtle', ...] or [-1, ['turtle', key], ...] """
+ ''' Either [-1, 'turtle', ...] or [-1, ['turtle', key], ...] '''
if blk[1] == 'turtle':
self.load_turtle(blk)
return True
@@ -2912,7 +3237,7 @@ class TurtleArtWindow():
return False
def load_turtle(self, blk, key=1):
- """ Restore a turtle from its saved state """
+ ''' Restore a turtle from its saved state '''
tid, name, xcor, ycor, heading, color, shade, pensize = blk
self.canvas.set_turtle(key)
self.canvas.setxy(xcor, ycor, pendown=False)
@@ -2922,7 +3247,7 @@ class TurtleArtWindow():
self.canvas.setpensize(pensize)
def load_block(self, b, offset=0):
- """ Restore individual blocks from saved state """
+ ''' Restore individual blocks from saved state '''
if self.running_sugar:
from sugar.datastore import datastore
@@ -2933,17 +3258,78 @@ class TurtleArtWindow():
btype, value = btype
elif type(btype) == list:
btype, value = btype[0], btype[1]
- if btype in content_blocks or btype in COLLAPSIBLE:
+
+ # Replace deprecated sandwich blocks
+ if btype == 'sandwichtop_no_label':
+ btype = 'sandwichclamp'
+ docks = []
+ for d in b[4]:
+ docks.append(d)
+ docks.append(None)
+ b[4] = docks
+ elif btype == 'sandwichtop_no_arm_no_label':
+ btype = 'sandwichclampcollapsed'
+ docks = []
+ for d in b[4]:
+ docks.append(d)
+ docks.append(None)
+ b[4] = docks
+ # FIXME: blocks after sandwich bottom must be attached to
+ # sandwich top dock[2], currently set to None
+ elif btype in ['sandwichbottom', 'sandwichcollapsed']:
+ btype = 'vspace'
+ # FIXME: blocks after sandwichtop should be in a sandwich clamp
+ elif btype in ['sandwichtop', 'sandwichtop_no_arm']:
+ btype = 'comment'
+
+ # Some blocks can only appear once...
+ if btype in ['start', 'hat1', 'hat2']:
+ if self._check_for_duplicate(btype):
+ name = block_names[btype][0]
+ while self._find_proto_name('stack_%s' % (name), name):
+ name += '_2'
+ i = len(self._process_block_data) + len(self._extra_block_data)
+ self._extra_block_data.append(
+ [i, ['string', name], 0, 0, [b[0], None]])
+ # To do: check for a duplicate name
+ self._new_stack_block(name)
+ btype = 'hat'
+ self._process_block_data[b[0]] = [
+ b[0], b[1], b[2], b[3], [b[4][0], i, b[4][1]]]
+ elif btype == 'hat':
+ if b[4][1] < len(self._process_block_data):
+ i = b[4][1]
+ name = self._process_block_data[i][1][1]
+ else:
+ i = b[4][1] - len(self._process_block_data)
+ name = self._extra_block_data[i][1][1]
+ while self._find_proto_name('stack_%s' % (name), name):
+ name += '_2'
+ if b[4][1] < len(self._process_block_data):
+ dblk = self._process_block_data[i]
+ self._process_block_data[i] = [dblk[0], (dblk[1][0], name),
+ dblk[2], dblk[3], dblk[4]]
+ else:
+ dblk = self._extra_block_data[i]
+ self._extra_block_data[i] = [dblk[0], (dblk[1][0], name),
+ dblk[2], dblk[3], dblk[4]]
+ self._new_stack_block(name)
+ elif btype == 'storein':
+ if b[4][1] < len(self._process_block_data):
+ i = b[4][1]
+ name = self._process_block_data[i][1][1]
+ else:
+ i = b[4][1] - len(self._process_block_data)
+ name = self._extra_block_data[i][1][1]
+ if not self._find_proto_name('box_%s' % (name), name):
+ self._new_box_block(name)
+
+ if btype in content_blocks:
if btype == 'number':
try:
values = [round_int(value)]
except ValueError:
values = [0]
- elif btype in COLLAPSIBLE:
- if value is not None:
- values = [int(value)]
- else:
- values = []
else:
values = [value]
else:
@@ -2961,6 +3347,14 @@ class TurtleArtWindow():
b[3] + self.canvas.cy + offset,
'block', values, self.block_scale)
+ # If it was an unknown block type, we need to match the number
+ # of dock items. TODO: Try to infer the dock type from connections
+ if len(b[4]) > len(blk.docks):
+ debug_output('dock mismatch %d > %d' % (len(b[4]), len(blk.docks)),
+ self.running_sugar)
+ for i in range(len(b[4]) - len(blk.docks)):
+ blk.docks.append(['unavailable', True, 0, 0])
+
# Some blocks get transformed.
if btype in block_styles['basic-style-var-arg'] and value is not None:
# Is there code stored in this userdefined block?
@@ -3049,13 +3443,21 @@ class TurtleArtWindow():
blk.spr.set_label(' ')
blk.resize()
elif btype in EXPANDABLE or btype in expandable_blocks or \
- btype in EXPANDABLE_ARGS or btype == 'nop':
+ btype in EXPANDABLE_FLOW or btype in EXPANDABLE_ARGS or \
+ btype == 'nop':
if btype == 'vspace' or btype in expandable_blocks:
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 in EXPANDABLE_FLOW:
+ if value is not None:
+ if type(value) is int:
+ blk.expand_in_y(value)
+ else: # thenelse blocks
+ blk.expand_in_y(value[0])
+ blk.expand_in_y2(value[1])
elif btype == 'templatelist' or btype == 'list':
for i in range(len(b[4]) - 4):
blk.add_arg()
@@ -3074,6 +3476,7 @@ class TurtleArtWindow():
blk.spr.set_layer(BLOCK_LAYER)
if check_dock:
blk.connections = 'check'
+
if self.running_sugar and len(blk.spr.labels) > 0 and \
blk.name not in ['', ' ', 'number', 'string']:
if len(self.used_block_list) > 0:
@@ -3084,8 +3487,15 @@ class TurtleArtWindow():
self.used_block_list.append(blk.spr.labels[0])
return blk
+ def _check_for_duplicate(self, name):
+ ''' Is there already a block of this name? '''
+ for blk in self.just_blocks():
+ if blk.name == name:
+ return True
+ return False
+
def load_start(self, ta_file=None):
- """ Start a new project with a 'start' brick """
+ ''' Start a new project with a 'start' brick '''
if ta_file is None:
self.process_data([[0, "start", PALETTE_WIDTH + 20,
self.toolbar_offset + PALETTE_HEIGHT + 20,
@@ -3094,7 +3504,7 @@ class TurtleArtWindow():
self.process_data(data_from_file(ta_file))
def save_file(self, _file_name=None):
- """ Start a project to a file """
+ ''' Start a project to a file '''
if self.save_folder is not None:
self.load_save_folder = self.save_folder
if _file_name is None:
@@ -3110,52 +3520,54 @@ class TurtleArtWindow():
self.save_folder = self.load_save_folder
def assemble_data_to_save(self, save_turtle=True, save_project=True):
- """ Pack the project (or stack) into a datastream to be serialized """
- _data = []
- _blks = []
+ ''' Pack the project (or stack) into a datastream to be serialized '''
+ data = []
+ blks = []
if save_project:
- _blks = self.just_blocks()
+ blks = self.just_blocks()
else:
if self.selected_blk is None:
return []
- _blks = find_group(find_top_block(self.selected_blk))
-
- for _i, _blk in enumerate(_blks):
- _blk.id = _i
- for _blk in _blks:
- if _blk.name in content_blocks or _blk.name in COLLAPSIBLE:
- if len(_blk.values) > 0:
- _name = (_blk.name, _blk.values[0])
+ blks = find_group(find_top_block(self.selected_blk))
+
+ for i, blk in enumerate(blks):
+ blk.id = i
+ for blk in blks:
+ if blk.name in content_blocks:
+ if len(blk.values) > 0:
+ name = (blk.name, blk.values[0])
else:
- _name = (_blk.name)
- elif _blk.name in block_styles['basic-style-var-arg'] and \
- len(_blk.values) > 0:
- _name = (_blk.name, _blk.values[0])
- elif _blk.name in EXPANDABLE or _blk.name in expandable_blocks or\
- _blk.name in EXPANDABLE_ARGS:
- _ex, _ey = _blk.get_expand_x_y()
- if _ex > 0:
- _name = (_blk.name, _ex)
- elif _ey > 0:
- _name = (_blk.name, _ey)
+ name = (blk.name)
+ elif blk.name in block_styles['basic-style-var-arg'] and \
+ len(blk.values) > 0:
+ name = (blk.name, blk.values[0])
+ elif blk.name in EXPANDABLE or blk.name in expandable_blocks or \
+ blk.name in EXPANDABLE_ARGS or blk.name in EXPANDABLE_FLOW:
+ ex, ey, ey2 = blk.get_expand_x_y()
+ if blk.name in block_styles['clamp-style-else']:
+ name = (blk.name, (ey, ey2))
+ elif ex > 0:
+ name = (blk.name, ex)
+ elif ey > 0:
+ name = (blk.name, ey)
else:
- _name = (_blk.name, 0)
- elif _blk.name == 'start': # save block_size in start block
- _name = (_blk.name, self.block_scale)
+ name = (blk.name, 0)
+ elif blk.name == 'start': # save block_size in start block
+ name = (blk.name, self.block_scale)
else:
- _name = (_blk.name)
- if hasattr(_blk, 'connections') and _blk.connections is not None:
- connections = [get_id(_cblk) for _cblk in _blk.connections]
+ name = (blk.name)
+ if hasattr(blk, 'connections') and blk.connections is not None:
+ connections = [get_id(cblk) for cblk in blk.connections]
else:
connections = None
- (_sx, _sy) = _blk.spr.get_xy()
+ (sx, sy) = blk.spr.get_xy()
# Add a slight offset for copy/paste
if not save_project:
- _sx += 20
- _sy += 20
- _data.append((_blk.id, _name, _sx - self.canvas.cx,
- _sy - self.canvas.cy, connections))
+ sx += 20
+ sy += 20
+ data.append((blk.id, name, sx - self.canvas.cx,
+ sy - self.canvas.cy, connections))
if save_turtle:
for turtle in iter(self.turtles.dict):
# Don't save remote turtles
@@ -3163,27 +3575,37 @@ class TurtleArtWindow():
# Save default turtle as 'Yertle'
if turtle == self.nick:
turtle = DEFAULT_TURTLE
- _data.append((-1, ['turtle', turtle],
+ data.append((-1, ['turtle', turtle],
self.canvas.xcor, self.canvas.ycor,
self.canvas.heading, self.canvas.color,
self.canvas.shade, self.canvas.pensize))
- return _data
+ return data
- def display_coordinates(self):
- """ Display the coordinates of the current turtle on the toolbar """
- x = round_int(float(self.canvas.xcor) / self.coord_scale)
- y = round_int(float(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()
- elif self.interactive_mode:
- self.parent.set_title("%s — %s: %d %s: %d %s: %d" % \
- (_("Turtle Art"), _("xcor"), x, _("ycor"), y, _("heading"), h))
+ def display_coordinates(self, clear=False):
+ ''' Display the coordinates of the current turtle on the toolbar '''
+ if clear:
+ if self.running_sugar:
+ self.activity.coordinates_label.set_text('')
+ self.activity.coordinates_label.show()
+ elif self.interactive_mode:
+ self.parent.set_title('')
+ else:
+ x = round_int(float(self.canvas.xcor) / self.coord_scale)
+ y = round_int(float(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()
+ elif self.interactive_mode:
+ self.parent.set_title(
+ "%s — %s: %d %s: %d %s: %d" % (
+ _("Turtle Art"), _("xcor"), x, _("ycor"), y,
+ _("heading"), h))
def showlabel(self, shp, label=''):
- """ Display a message on a status block """
+ ''' Display a message on a status block '''
if not self.interactive_mode:
debug_output(label, self.running_sugar)
return
@@ -3214,7 +3636,7 @@ class TurtleArtWindow():
self.status_spr.move((0, self.height - 100))
def calc_position(self, template):
- """ Relative placement of portfolio objects (deprecated) """
+ ''' Relative placement of portfolio objects (deprecated) '''
w, h, x, y, dx, dy = TEMPLATES[template]
x *= self.canvas.width
y *= self.canvas.height
@@ -3225,7 +3647,7 @@ class TurtleArtWindow():
return(w, h, x, y, dx, dy)
def save_for_upload(self, _file_name):
- """ Grab the current canvas and save it for upload """
+ ''' Grab the current canvas and save it for upload '''
if _file_name[-3:] == '.ta':
_file_name = _file_name[0: -3]
data_to_file(self.assemble_data_to_save(), _file_name + '.ta')
@@ -3234,8 +3656,8 @@ class TurtleArtWindow():
image_file = _file_name + '.png'
return ta_file, image_file
- def save_as_image(self, name="", svg=False, pixbuf=None):
- """ Grab the current canvas and save it. """
+ def save_as_image(self, name="", svg=False):
+ ''' Grab the current canvas and save it. '''
if svg:
suffix = '.svg'
else:
@@ -3297,21 +3719,29 @@ class TurtleArtWindow():
os.remove(file_path)
else:
if svg:
- output = subprocess.check_output(
+ subprocess.check_output(
['mv', os.path.join(datapath, 'output.svg'),
os.path.join(datapath, filename)])
self.saved_pictures.append((file_path, svg))
def just_blocks(self):
- """ Filter out 'proto', 'trash', and 'deleted' blocks """
+ ''' Filter out 'proto', 'trash', and 'deleted' blocks '''
just_blocks_list = []
- for _blk in self.block_list.list:
- if _blk.type == 'block':
- just_blocks_list.append(_blk)
+ for blk in self.block_list.list:
+ if blk.type == 'block':
+ just_blocks_list.append(blk)
return just_blocks_list
+ def just_protos(self):
+ ''' Filter out 'block', 'trash', and 'deleted' blocks '''
+ just_protos_list = []
+ for blk in self.block_list.list:
+ if blk.type == 'proto':
+ just_protos_list.append(blk)
+ return just_protos_list
+
def _width_and_height(self, blk):
- """ What are the width and height of a stack? """
+ ''' What are the width and height of a stack? '''
minx = 10000
miny = 10000
maxx = -10000
@@ -3332,7 +3762,7 @@ class TurtleArtWindow():
# Utilities related to putting a image 'skin' on a block
def _calc_image_offset(self, name, spr, iw=0, ih=0):
- """ Calculate the postion for placing an image onto a sprite. """
+ ''' Calculate the postion for placing an image onto a sprite. '''
_l, _t = spr.label_left_top()
if name == '':
return _l, _t
@@ -3344,7 +3774,7 @@ class TurtleArtWindow():
return int(_l + (_w - iw) / 2), int(_t + (_h - ih) / 2)
def _calc_w_h(self, name, spr):
- """ Calculate new image size """
+ ''' Calculate new image size '''
target_w = spr.label_safe_width()
target_h = spr.label_safe_height()
if name == '':
@@ -3361,18 +3791,18 @@ class TurtleArtWindow():
return int(new_w), int(new_h)
def _proto_skin(self, name, n, i):
- """ Utility for calculating proto skin images """
+ ''' Utility for calculating proto skin images '''
x, y = self._calc_image_offset(name, self.palettes[n][i].spr)
self.palettes[n][i].spr.set_image(self.media_shapes[name], 1, x, y)
def _block_skin(self, name, blk):
- """ Some blocks get a skin """
+ ''' Some blocks get a skin '''
x, y = self._calc_image_offset(name, blk.spr)
blk.set_image(self.media_shapes[name], x, y)
self._resize_skin(blk)
def _resize_skin(self, blk):
- """ Resize the 'skin' when block scale changes. """
+ ''' Resize the 'skin' when block scale changes. '''
if blk.name in PYTHON_SKIN:
w, h = self._calc_w_h('pythonoff', blk.spr)
x, y = self._calc_image_offset('pythonoff', blk.spr, w, h)
@@ -3390,35 +3820,139 @@ class TurtleArtWindow():
x, y = self._calc_image_offset('', blk.spr, w, h)
blk.scale_image(x, y, w, h)
+ def _find_proto_name(self, name, label, palette='blocks'):
+ ''' Look for a protoblock with this name '''
+ i = palette_name_to_index(palette)
+ for blk in self.palettes[i]:
+ if blk.name == name and blk.spr.labels[0] == label:
+ 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 _new_stack_block(self, name):
+ ''' Add a stack block to the 'blocks' palette '''
+ if CURSOR in name:
+ name = name.replace(CURSOR, '')
+ if name == _('action'):
+ return
+ # Choose a palette for the new block.
+ palette = make_palette('blocks')
+
+ # Create a new block prototype.
+ primitive_dictionary['stack'] = self._prim_stack
+ palette.add_block('stack_%s' % (name),
+ style='basic-style-1arg',
+ label=name,
+ string_or_number=True,
+ prim_name='stack',
+ logo_command='action',
+ default=name,
+ help_string=_('invokes named action stack'))
+ self.lc.def_prim('stack', 1, primitive_dictionary['stack'], True)
+
+ # Regenerate the palette, which will now include the new block.
+ self.show_toolbar_palette(palette_name_to_index('blocks'),
+ regenerate=True)
+
+ def _new_box_block(self, name):
+ ''' Add a box block to the 'blocks' palette '''
+ if CURSOR in name:
+ name = name.replace(CURSOR, '')
+ if name == _('box'):
+ return
+ # Choose a palette for the new block.
+ palette = make_palette('blocks')
+
+ # Create a new block prototype.
+ primitive_dictionary['box'] = self._prim_box
+ palette.add_block('box_%s' % (name),
+ style='number-style-1strarg',
+ label=name,
+ string_or_number=True,
+ prim_name='box',
+ default=name,
+ logo_command='box',
+ help_string=_('named variable (numeric value)'))
+ self.lc.def_prim('box', 1,
+ lambda self, x: primitive_dictionary['box'](x))
+
+ # Regenerate the palette, which will now include the new block.
+ self.show_toolbar_palette(palette_name_to_index('blocks'),
+ regenerate=True)
+
+ def _prim_stack(self, x):
+ ''' Process a named stack '''
+ if type(convert(x, float, False)) == float:
+ if int(float(x)) == x:
+ x = int(x)
+ if 'stack3' + str(x) not in self.lc.stacks or \
+ self.lc.stacks['stack3' + str(x)] is None:
+ raise logoerror("#nostack")
+ self.lc.icall(self.lc.evline,
+ self.lc.stacks['stack3' + str(x)][:])
+ yield True
+ self.lc.procstop = False
+ self.lc.ireturn()
+ yield True
+
+ def _prim_box(self, x):
+ ''' Retrieve value from named box '''
+ if type(convert(x, float, False)) == float:
+ if int(float(x)) == x:
+ x = int(x)
+ try:
+ return self.lc.boxes['box3' + str(x)]
+ except KeyError:
+ raise logoerror("#emptybox")
+
+ def dock_dx_dy(self, block1, dock1n, block2, dock2n):
+ ''' Find the distance between the dock points of two blocks. '''
+ # Cannot dock a block to itself
+ if block1 == block2:
+ return NO_DOCK
+ dock1 = block1.docks[dock1n]
+ dock2 = block2.docks[dock2n]
+ # Dock types include flow, number, string, unavailable
+ # Dock directions: Flow: True -> in; False -> out
+ # Dock directions: Number: True -> out; False -> in
+ # Each dock point as an associated relative x, y position on its block
+ d1type, d1dir, d1x, d1y = dock1[0:4]
+ d2type, d2dir, d2x, d2y = dock2[0:4]
+ # Cannot connect an innie to an innie or an outie to an outie
+ if d1dir == d2dir:
+ return NO_DOCK
+ # Flow blocks can be inserted into the middle of a stack
+ if d2type is 'flow' and dock2n is 0:
+ if block1.connections is not None and \
+ dock1n == len(block1.connections) - 1 and \
+ block1.connections[dock1n] is not None:
+ self.inserting_block_mid_stack = True
+ elif block1.connections is not None and \
+ block1.name in EXPANDABLE_FLOW and \
+ dock1n == 2 and \
+ block1.connections[dock1n] is not None:
+ self.inserting_block_mid_stack = True
+ # Only number blocks can be docked when the dock is not empty
+ elif 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 NO_DOCK
+ if block2.connections is not None and \
+ dock2n < len(block2.connections) and \
+ block2.connections[dock2n] is not None:
+ return NO_DOCK
+ # Only some dock types are interchangeable
+ if d1type != d2type:
+ # Some blocks will take strings or numbers
+ if block1.name in string_or_number_args:
+ if d2type == 'number' or d2type == 'string':
+ pass
+ # Some blocks will take content blocks
+ elif block1.name in CONTENT_ARGS:
+ if d2type in content_blocks:
+ pass
+ else:
+ return NO_DOCK
+ (b1x, b1y) = block1.spr.get_xy()
+ (b2x, b2y) = block2.spr.get_xy()
+ return ((b1x + d1x) - (b2x + d2x), (b1y + d1y) - (b2y + d2y))