Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TurtleArt/tawindow.py
diff options
context:
space:
mode:
Diffstat (limited to 'TurtleArt/tawindow.py')
-rw-r--r--TurtleArt/tawindow.py240
1 files changed, 187 insertions, 53 deletions
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index 20eeb34..b7523e5 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#Copyright (c) 2007, Playful Invention Company
-#Copyright (c) 2008-11, Walter Bender
+#Copyright (c) 2008-12, Walter Bender
#Copyright (c) 2009-11 Raúl Gutiérrez Segalés
#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
@@ -36,6 +36,7 @@ except ImportError:
GST_AVAILABLE = False
import os
+import subprocess
from math import atan2, pi
DEGTOR = 2 * pi / 360
@@ -43,7 +44,7 @@ DEGTOR = 2 * pi / 360
import locale
from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
- MEDIA_SHAPES, STATUS_SHAPES, OVERLAY_SHAPES, STRING_OR_NUMBER_ARGS, \
+ MEDIA_SHAPES, STATUS_SHAPES, OVERLAY_SHAPES, \
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, \
@@ -53,7 +54,7 @@ from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
CONSTANTS, EXPAND_SKIN, PROTO_LAYER
from tapalette import palette_names, palette_blocks, expandable_blocks, \
block_names, content_blocks, default_values, special_names, block_styles, \
- help_strings, hidden_proto_blocks
+ help_strings, hidden_proto_blocks, string_or_number_args
from talogo import LogoCode
from tacanvas import TurtleGraphics
from tablock import Blocks, Block
@@ -61,7 +62,7 @@ from taturtle import Turtles, Turtle
from tautils import magnitude, get_load_name, get_save_name, data_from_file, \
data_to_file, round_int, get_id, get_pixbuf_from_journal, \
movie_media_type, audio_media_type, image_media_type, save_picture, \
- save_svg, calc_image_size, get_path, reset_stack_arm, grow_stack_arm, \
+ 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, \
arithmetic_check, xy, find_block_to_run, find_top_block, journal_check, \
@@ -76,6 +77,7 @@ if GST_AVAILABLE:
MOTION_THRESHOLD = 6
SNAP_THRESHOLD = 200
+
class TurtleArtWindow():
""" TurtleArt Window class abstraction """
timeout_tag = [0]
@@ -199,10 +201,9 @@ class TurtleArtWindow():
self.drag_group = None
self.drag_turtle = 'move', 0, 0
self.drag_pos = 0, 0
+ self.dragging_canvas = [False, 0, 0]
self.turtle_movement_to_share = None
self.paste_offset = 20 # Don't paste on top of where you copied.
- self.saving_svg = False
- self.svg_string = ''
self.block_list = Blocks(font_scale_factor=self.scale,
decimal_point=self.decimal_point)
@@ -289,29 +290,25 @@ class TurtleArtWindow():
""" Try importing plugin files from the plugin dir. """
for plugin_dir in self._get_plugins_from_plugins_dir(
self._get_plugin_home()):
- 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)
- plugins = {}
- try:
- exec f in globals(), plugins
- self._plugins.append(plugins.values()[0](self))
- debug_output('successfully importing %s' % (plugin_class),
- self.running_sugar)
- except ImportError, e:
- debug_output('failed to import %s: %s' % (plugin_class, str(e)),
- self.running_sugar)
- '''
+ self.init_plugin(plugin_dir)
+
+ def init_plugin(self, 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)
+ plugins = {}
+ try:
exec f in globals(), plugins
self._plugins.append(plugins.values()[0](self))
- debug_output('successfully importing %s' % (plugin_class))
- '''
-
- # Add the icon dir for each plugin to the icon_theme search path
- for plugin_dir in self._get_plugins_from_plugins_dir(
- self._get_plugin_home()):
+ debug_output('Successfully importing %s' % (plugin_class),
+ self.running_sugar)
+ # Add the icon dir to the icon_theme search path
self._add_plugin_icon_dir(os.path.join(self._get_plugin_home(),
plugin_dir))
+ except ImportError, e:
+ debug_output('Failed to import %s: %s' % (plugin_class, str(e)),
+ self.running_sugar)
def _add_plugin_icon_dir(self, dirname):
''' If there is an icon subdir, add it to the search path. '''
@@ -347,7 +344,7 @@ class TurtleArtWindow():
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()
@@ -380,10 +377,11 @@ class TurtleArtWindow():
self.window.connect("motion-notify-event", self._move_cb)
self.window.connect("key-press-event", self._keypress_cb)
- def _setup_misc(self):
- """ Misc. sprites for status, overlays, etc. """
- # media blocks get positioned into other blocks
+ def load_media_shapes(self):
+ """ Media shapes get positioned onto blocks """
for name in MEDIA_SHAPES:
+ if name in self.media_shapes:
+ continue
if name[0:7] == 'journal' and not self.running_sugar:
filename = 'file' + name[7:]
else:
@@ -396,9 +394,17 @@ class TurtleArtWindow():
os.path.join(self.path, path, filename + '.svg')))
break
+ def _setup_misc(self):
+ """ Misc. sprites for status, overlays, etc. """
+ self.load_media_shapes()
for i, name in enumerate(STATUS_SHAPES):
- self.status_shapes[name] = svg_str_to_pixbuf(svg_from_file(
- os.path.join(self.path, 'images', name + '.svg')))
+ # Temporary hack to use wider shapes
+ if name in ['print', 'help', 'status'] and self.width > 1024:
+ self.status_shapes[name] = svg_str_to_pixbuf(svg_from_file(
+ os.path.join(self.path, 'images', name + '1200.svg')))
+ else:
+ self.status_shapes[name] = svg_str_to_pixbuf(svg_from_file(
+ os.path.join(self.path, 'images', name + '.svg')))
self.status_spr = Sprite(self.sprite_list, 0, self.height - 200,
self.status_shapes['status'])
self.status_spr.hide()
@@ -414,7 +420,8 @@ class TurtleArtWindow():
self.overlay_shapes[name].type = 'overlay'
if not self.running_sugar:
- offset = self.width - 55 * len(TOOLBAR_SHAPES)
+ # offset = 2 * self.width - 55 * len(TOOLBAR_SHAPES)
+ offset = 55 * (2 + len(palette_blocks))
for i, name in enumerate(TOOLBAR_SHAPES):
self.toolbar_shapes[name] = Sprite(
self.sprite_list, i * 55 + offset, 0,
@@ -459,6 +466,10 @@ class TurtleArtWindow():
# Create the cairo context
cr = self.window.window.cairo_create()
+ # TODO: set global scale
+ # find_sprite needs rescaled coordinates
+ # sw needs new bounds set
+ # cr.scale(self.activity.global_x_scale, self.activity.global_y_scale)
if event is None:
cr.rectangle(self.rect.x, self.rect.y,
@@ -484,7 +495,7 @@ class TurtleArtWindow():
self.lc.prim_clear()
self.display_coordinates()
- def run_button(self, time):
+ def run_button(self, time, running_from_button_push=False):
""" Run turtle! """
if self.running_sugar:
self.activity.recenter()
@@ -495,6 +506,10 @@ class TurtleArtWindow():
self.step_time = time
debug_output("running stack starting from %s" % (blk.name),
self.running_sugar)
+ if running_from_button_push:
+ self.selected_blk = None
+ else:
+ self.selected_blk = blk
self._run_stack(blk)
return
@@ -504,6 +519,10 @@ class TurtleArtWindow():
self.step_time = time
debug_output("running stack starting from %s" % (blk.name),
self.running_sugar)
+ if running_from_button_push:
+ self.selected_blk = None
+ else:
+ self.selected_blk = blk
self._run_stack(blk)
return
@@ -588,6 +607,8 @@ class TurtleArtWindow():
if not self.hide:
for blk in self.just_blocks():
blk.spr.hide()
+ for spr in self.triangle_sprs:
+ spr.hide()
self.hide_palette()
self.hide = True
else:
@@ -629,6 +650,7 @@ class TurtleArtWindow():
self.show_toolbar_palette(n)
self.palette_button[self.orientation].set_layer(TAB_LAYER)
self.palette_button[2].set_layer(TAB_LAYER)
+ self._display_palette_shift_button(n)
if self.activity is None or not self.activity.has_toolbarbox:
self.toolbar_spr.set_layer(CATEGORY_LAYER)
self.palette = True
@@ -638,6 +660,8 @@ class TurtleArtWindow():
self._hide_toolbar_palette()
self.palette_button[self.orientation].hide()
self.palette_button[2].hide()
+ self.palette_button[3].hide()
+ self.palette_button[4].hide()
if self.activity is None or not self.activity.has_toolbarbox:
self.toolbar_spr.hide()
self.palette = False
@@ -715,6 +739,25 @@ class TurtleArtWindow():
if blk.name in BLOCKS_WITH_SKIN:
self._resize_skin(blk)
+ def _shift_toolbar_palette(self, n):
+ ''' Shift blocks on specified palette '''
+ x, y = self.palette_sprs[n][self.orientation].get_xy()
+ w, h = self.palette_sprs[n][self.orientation].get_dimensions()
+ bx, by = self.palettes[n][0].spr.get_xy()
+ if self.orientation == 0:
+ dx = w - self.width
+ dy = 0
+ if bx - x > 0:
+ dx *= -1
+ else:
+ dx = 0
+ dy = h - self.height + ICON_SIZE
+ if by - y > 0:
+ dy *= -1
+ for blk in self.palettes[n]:
+ if blk.get_visibility():
+ blk.spr.move_relative((dx, dy))
+
def show_toolbar_palette(self, n, init_only=False, regenerate=False,
show=True):
""" Show the toolbar palettes, creating them on init_only """
@@ -746,7 +789,8 @@ class TurtleArtWindow():
# Make sure all of the selectors are visible. (We don't need to do
# this for 0.86+ toolbars since the selectors are toolbar buttons.)
- if show and (self.activity is None or not self.activity.has_toolbarbox):
+ if show and \
+ (self.activity is None or not self.activity.has_toolbarbox):
self.selected_selector = self.selectors[n]
self.selectors[n].set_shape(self.selector_shapes[n][1])
for i in range(len(palette_blocks)):
@@ -754,7 +798,9 @@ class TurtleArtWindow():
# Show the palette with the current orientation.
if self.palette_sprs[n][self.orientation] is not None:
- self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+ self.palette_sprs[n][self.orientation].set_layer(
+ CATEGORY_LAYER)
+ self._display_palette_shift_button(n)
# Create 'proto' blocks for each palette entry
self._create_proto_blocks(n)
@@ -784,6 +830,20 @@ class TurtleArtWindow():
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 == \
+ 'category-shift-horizontal':
+ self.palette_button[3].set_layer(CATEGORY_LAYER)
+ self.palette_button[4].hide()
+ elif self.palette_sprs[n][self.orientation].type == \
+ 'category-shift-vertical':
+ self.palette_button[3].hide()
+ self.palette_button[4].set_layer(CATEGORY_LAYER)
+ else:
+ self.palette_button[3].hide()
+ self.palette_button[4].hide()
+
def _create_the_selectors(self):
''' Create the palette selector buttons: only when running
old-style Sugar toolbars or from GNOME '''
@@ -824,7 +884,7 @@ class TurtleArtWindow():
# Create the toolbar background for the selectors
self.toolbar_offset = ICON_SIZE
self.toolbar_spr = Sprite(self.sprite_list, 0, 0,
- svg_str_to_pixbuf(svg.toolbar(self.width, ICON_SIZE)))
+ svg_str_to_pixbuf(svg.toolbar(2 * self.width, ICON_SIZE)))
self.toolbar_spr.type = 'toolbar'
self.toolbar_spr.set_layer(CATEGORY_LAYER)
@@ -860,11 +920,32 @@ class TurtleArtWindow():
self.palette_button[2].type = 'palette'
self.palette_button[2].set_layer(TAB_LAYER)
+ # Create the palette shift buttons
+ dims = self.palette_button[0].get_dimensions()
+ self.palette_button.append(Sprite(self.sprite_list, 0,
+ self.toolbar_offset + dims[1], svg_str_to_pixbuf(svg_from_file(
+ "%s/images/palettehshift.svg" % (self.path)))))
+ self.palette_button.append(Sprite(self.sprite_list, dims[0],
+ self.toolbar_offset, svg_str_to_pixbuf(svg_from_file(
+ "%s/images/palettevshift.svg" % (self.path)))))
+ self.palette_button[3].name = _('shift')
+ self.palette_button[4].name = _('shift')
+ self.palette_button[3].type = 'palette'
+ self.palette_button[4].type = 'palette'
+ self.palette_button[3].hide()
+ self.palette_button[4].hide()
+
def _create_proto_blocks(self, n):
''' Create the protoblocks that will populate a palette. '''
# Reload the palette, but reuse the existing blocks
# If a block doesn't exist, add it
+ if not n < len(self.palettes):
+ debug_output(
+ '_create_proto_blocks: palette index %d is out of range' % (n),
+ self.running_sugar)
+ return
+
for blk in self.palettes[n]:
blk.spr.hide()
old_blocks = self.palettes[n][:]
@@ -900,7 +981,7 @@ class TurtleArtWindow():
elif name in PYTHON_SKIN:
self._proto_skin('pythonsmall', n, -1)
return
-
+
def _hide_toolbar_palette(self):
""" Hide the toolbar palettes """
self._hide_previous_palette()
@@ -919,6 +1000,11 @@ class TurtleArtWindow():
palette = self.previous_palette
# Hide previously selected palette
if palette is not None:
+ if not palette < len(self.palettes):
+ debug_output(
+ '_hide_previous_palette: index %d is out of range' % \
+ (palette), self.running_sugar)
+ return
for proto in self.palettes[palette]:
proto.spr.hide()
if self.palette_sprs[palette][self.orientation] is not None:
@@ -1031,11 +1117,14 @@ class TurtleArtWindow():
self.palette_button[2].move((PALETTE_WIDTH - 20,
self.toolbar_offset))
if show:
- self.palette_button[2].save_xy = self.palette_button[2].get_xy()
+ self.palette_button[2].save_xy = \
+ self.palette_button[2].get_xy()
if self.running_sugar and not self.hw in [XO1]:
self.palette_button[2].move_relative(
(self.activity.hadj_value, self.activity.vadj_value))
- self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+ self.palette_sprs[n][self.orientation].set_layer(
+ CATEGORY_LAYER)
+ self._display_palette_shift_button(n)
def _make_palette_spr(self, n, x, y, w, h, regenerate=False):
''' Make the background for the palette. '''
@@ -1051,7 +1140,14 @@ class TurtleArtWindow():
if self.running_sugar and not self.hw in [XO1]:
self.palette_sprs[n][self.orientation].move_relative(
(self.activity.hadj_value, self.activity.vadj_value))
- self.palette_sprs[n][self.orientation].type = 'category'
+ if self.orientation == 0 and w > self.width:
+ self.palette_sprs[n][self.orientation].type = \
+ 'category-shift-horizontal'
+ elif self.orientation == 1 and h > self.height - ICON_SIZE:
+ self.palette_sprs[n][self.orientation].type = \
+ 'category-shift-vertical'
+ else:
+ self.palette_sprs[n][self.orientation].type = 'category'
if n == palette_names.index('trash'):
svg = SVG()
self.palette_sprs[n][self.orientation].set_shape(
@@ -1075,7 +1171,8 @@ class TurtleArtWindow():
# Unselect things that may have been selected earlier
if self.selected_blk is not None:
- if self.selected_blk.name == 'number' and spr in self.triangle_sprs:
+ if self.selected_blk.name == 'number' and \
+ spr in self.triangle_sprs:
# increment or decrement a number block
nf = float(self.selected_blk.spr.labels[0].replace(CURSOR, ''))
ni = int(nf)
@@ -1087,7 +1184,7 @@ class TurtleArtWindow():
n += 1
else:
n -= 1
- self.selected_blk.spr.set_label(str(n) + CURSOR)
+ self.selected_blk.spr.set_label(str(n) + CURSOR)
return True
self._unselect_block()
self.selected_turtle = None
@@ -1098,8 +1195,12 @@ class TurtleArtWindow():
self.dx = 0
self.dy = 0
+ self.dragging_canvas[1] = x
+ self.dragging_canvas[2] = y
if spr is None:
+ 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
@@ -1174,7 +1275,8 @@ class TurtleArtWindow():
if hasattr(spr, 'type'):
if spr.type == 'selector':
self._select_category(spr)
- elif spr.type == 'category':
+ elif spr.type in ['category', 'category-shift-horizontal',
+ 'category-shift-vertical']:
if hide_button_hit(spr, x, y):
self.hideshow_palette(False)
elif spr.type == 'palette':
@@ -1198,6 +1300,8 @@ class TurtleArtWindow():
self.activity.palette_buttons[i].set_icon(
palette_names[i] + 'on')
self.show_palette(i)
+ elif spr.name == _('shift'):
+ self._shift_toolbar_palette(self.selected_palette)
else:
self.orientation = 1 - self.orientation
self.palette_button[self.orientation].set_layer(TAB_LAYER)
@@ -1229,6 +1333,7 @@ class TurtleArtWindow():
return
if spr.name == 'run-fastoff':
self.lc.trace = 0
+ self.hideblocks()
self.run_button(0)
elif spr.name == 'run-slowoff':
self.lc.trace = 1
@@ -1430,7 +1535,6 @@ class TurtleArtWindow():
newblk.connections[i + 1] = argblk
self.drag_group = find_group(newblk)
self.block_operation = 'new'
- debug_output(newblk.name, True)
if len(newblk.spr.labels) > 0 and newblk.spr.labels[0] is not None \
and newblk.name not in ['', 'number', 'string']:
if len(self.used_block_list) > 0:
@@ -1600,6 +1704,15 @@ class TurtleArtWindow():
def _mouse_move(self, x, y):
""" Process mouse movements """
+
+ if self.running_sugar and self.dragging_canvas[0]:
+ dx = self.dragging_canvas[1] - x
+ dy = self.dragging_canvas[2] - y
+ self.dragging_canvas[1] = x
+ self.dragging_canvas[2] = y
+ self.activity.adjust_sw(dx, dy)
+ return True
+
self.block_operation = 'move'
# First, check to see if we are dragging or rotating a turtle.
@@ -1768,6 +1881,13 @@ class TurtleArtWindow():
return True
def button_release(self, x, y):
+ if self.running_sugar and self.dragging_canvas[0]:
+ self.dragging_canvas[0] = False
+ self.dragging_canvas[1] = x
+ self.dragging_canvas[2] = y
+ self.activity.adjust_palette()
+ return True
+
# We may have been moving the turtle
if self.selected_turtle is not None:
(tx, ty) = self.selected_turtle.get_xy()
@@ -2112,6 +2232,10 @@ class TurtleArtWindow():
if blk is None:
return
self.lc.find_value_blocks() # Are there blocks to update?
+ # Is there a savesvg block?
+ if len(self.block_list.get_similar_blocks('block', 'savesvg')) > 0:
+ if self.canvas.cr_svg is None:
+ self.canvas.setup_svg_surface()
self._start_plugins() # Let the plugins know we are running.
top = find_top_block(blk)
self.lc.run_blocks(top, self.just_blocks(), True)
@@ -2184,7 +2308,8 @@ class TurtleArtWindow():
if best_destination_dockn == 2 and \
(selected_block.name in block_styles['boolean-style'] or \
selected_block.name in block_styles['compare-style'] or \
- selected_block.name in block_styles['compare-porch-style']):
+ selected_block.name in block_styles['compare-porch-style']
+ ):
dy = selected_block.ey - best_destination.ey
if selected_block.name in block_styles['boolean-style']:
# Even without expanding, boolean blocks are
@@ -2607,7 +2732,6 @@ class TurtleArtWindow():
else:
n = 0
self.selected_blk.spr.set_label(str(n))
- debug_output(str(n), True)
try:
self.selected_blk.values[0] = \
float(str(n).replace(self.decimal_point, '.'))
@@ -3072,7 +3196,10 @@ class TurtleArtWindow():
label = ''
self.status_spr.set_shape(self.status_shapes[shp])
self.status_spr.set_label_attributes(12.0, rescale=False)
- self.status_spr.set_label(str(label))
+ if shp == 'status':
+ self.status_spr.set_label('"%s"' % (str(label)))
+ else:
+ self.status_spr.set_label(str(label))
self.status_spr.set_layer(STATUS_LAYER)
if shp == 'info':
self.status_spr.move((PALETTE_WIDTH, self.height - 400))
@@ -3136,10 +3263,9 @@ class TurtleArtWindow():
file_path = os.path.join(datapath, filename)
if svg:
- if self.svg_string == '':
+ if self.canvas.cr_svg is None:
return
- save_svg(self.svg_string, file_path)
- self.svg_string = ''
+ self.canvas.svg_reset()
else:
save_picture(self.canvas, file_path)
@@ -3156,14 +3282,22 @@ class TurtleArtWindow():
dsobject.metadata['icon-color'] = profile.get_color().to_string()
if svg:
dsobject.metadata['mime_type'] = 'image/svg+xml'
+ dsobject.set_file_path(os.path.join(datapath, 'output.svg'))
else:
dsobject.metadata['mime_type'] = 'image/png'
- dsobject.set_file_path(file_path)
+ dsobject.set_file_path(file_path)
datastore.write(dsobject)
dsobject.destroy()
self.saved_pictures.append((dsobject.object_id, svg))
- os.remove(file_path)
+ if svg:
+ os.remove(os.path.join(datapath, 'output.svg'))
+ else:
+ os.remove(file_path)
else:
+ if svg:
+ 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):
@@ -3275,7 +3409,7 @@ def dock_dx_dy(block1, dock1n, block2, dock2n):
block2.connections[dock2n] is not None:
return (100, 100)
if _d1type != _d2type:
- if block1.name in STRING_OR_NUMBER_ARGS:
+ if block1.name in string_or_number_args:
if _d2type == 'number' or _d2type == 'string':
pass
elif block1.name in CONTENT_ARGS: