diff options
Diffstat (limited to 'TurtleArt/tawindow.py')
-rw-r--r-- | TurtleArt/tawindow.py | 240 |
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: |