From 3eb32aa6c17543f59d4d90d7194f8278a4954bef Mon Sep 17 00:00:00 2001 From: Daniel Francis Date: Tue, 03 Dec 2013 18:17:42 +0000 Subject: Add load-plugin support to GNOME version --- diff --git a/TurtleArt/taplugin.py b/TurtleArt/taplugin.py new file mode 100644 index 0000000..4cf2563 --- /dev/null +++ b/TurtleArt/taplugin.py @@ -0,0 +1,117 @@ +#Copyright (c) 2013 Walter Bender +#Copyright (c) 2013 Daniel Francis + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import ConfigParser +from gettext import gettext as _ +import os +import shutil +import subprocess +from TurtleArt.tapalette import (palette_names, help_strings) + + +def cancel_plugin_install(self, tmp_dir): + ''' If we cancel, just cleanup ''' + shutil.rmtree(tmp_dir) + + +def complete_plugin_install(self, tmp_dir, tmp_path, plugin_path, + plugin_name, file_info): + ''' We complete the installation directly or from ConfirmationAlert ''' + status = subprocess.call(['cp', '-r', tmp_path, plugin_path + '/']) + if status == 0: + # Save the plugin.info file in the plugin directory + subprocess.call(['cp', os.path.join(tmp_dir, 'plugin.info'), + os.path.join(plugin_path, plugin_name) + '/']) + if self.has_toolbarbox: + palette_name_list = [] + if file_info.has_option('Plugin', 'palette'): + palette_name_list = file_info.get( + 'Plugin', 'palette').split(',') + create_palette = [] + for palette_name in palette_name_list: + if not palette_name.strip() in palette_names: + create_palette.append(True) + else: + create_palette.append(False) + self.tw.init_plugin(plugin_name) + self.tw.turtleart_plugins[-1].setup() + self.tw.load_media_shapes() + for i, palette_name in enumerate(palette_name_list): + if create_palette[i]: + j = len(self.palette_buttons) + self.palette_buttons.append( + self._radio_button_factory( + palette_name.strip() + 'off', + self._palette_toolbar, + self.do_palette_buttons_cb, + j - 1, + help_strings[palette_name.strip()], + self.palette_buttons[0])) + self._overflow_buttons.append( + self._add_button( + palette_name.strip() + 'off', + None, + self.do_palette_buttons_cb, + None, + arg=j - 1)) + self._overflow_box.pack_start( + self._overflow_buttons[j - 1]) + self.tw.palettes.insert(j - 1, []) + self.tw.palette_sprs.insert(j - 1, [None, None]) + else: + # We need to change the index associated with the + # Trash Palette Button. + j = len(palette_names) + pidx = palette_names.index(palette_name.strip()) + self.palette_buttons[pidx].connect( + 'clicked', self.do_palette_buttons_cb, j - 1) + self._overflow_buttons[pidx].connect( + 'clicked', self.do_palette_buttons_cb, j - 1) + self._setup_palette_toolbar() + else: + self.tw.showlabel('status', label=_('Please restart Turtle Art \ +in order to use the plugin.')) + else: + self.tw.showlabel('status', label=_('Plugin could not be installed.')) + status = subprocess.call(['rm', '-r', tmp_path]) + shutil.rmtree(tmp_dir) + + +def load_a_plugin(self, tmp_dir): + ''' Load a plugin from the Journal and initialize it ''' + plugin_path = os.path.join(tmp_dir, 'plugin.info') + file_info = ConfigParser.ConfigParser() + if len(file_info.read(plugin_path)) == 0: + self.tw.showlabel('status', + label=_('Plugin could not be installed.')) + elif not file_info.has_option('Plugin', 'name'): + self.tw.showlabel( + 'status', label=_('Plugin could not be installed.')) + else: + plugin_name = file_info.get('Plugin', 'name') + tmp_path = os.path.join(tmp_dir, plugin_name) + plugin_path = os.path.join(self.bundle_path, 'plugins') + if os.path.exists(os.path.join(plugin_path, plugin_name)): + self._reload_plugin_alert(tmp_dir, tmp_path, plugin_path, + plugin_name, file_info) + else: + complete_plugin_install(self, tmp_dir, tmp_path, plugin_path, + plugin_name, file_info) diff --git a/TurtleArtActivity.py b/TurtleArtActivity.py index f15650e..f3cb17f 100644 --- a/TurtleArtActivity.py +++ b/TurtleArtActivity.py @@ -63,6 +63,8 @@ except ImportError: from gettext import gettext as _ +from TurtleArt.taplugin import (load_a_plugin, cancel_plugin_install, + complete_plugin_install) from TurtleArt.tapalette import (palette_names, help_strings, help_palettes, help_windows, default_values) from TurtleArt.taconstants import (BLOCK_SCALE, XO1, XO15, XO175, XO4, @@ -95,6 +97,8 @@ class TurtleArtActivity(activity.Activity): self.tw = None self.init_complete = False + self.bundle_path = activity.get_bundle_path() + self.error_list = [] self.palette_buttons = [] @@ -1337,109 +1341,6 @@ class TurtleArtActivity(activity.Activity): self.metadata['error_list'] = data_to_string(errors) _logger.debug('Wrote to file: %s' % (file_path)) - def _load_a_plugin(self, tmp_dir): - ''' Load a plugin from the Journal and initialize it ''' - plugin_path = os.path.join(tmp_dir, 'plugin.info') - _logger.debug(plugin_path) - file_info = ConfigParser.ConfigParser() - if len(file_info.read(plugin_path)) == 0: - _logger.debug('Required file plugin.info could not be found.') - self.tw.showlabel('status', - label=_('Plugin could not be installed.')) - elif not file_info.has_option('Plugin', 'name'): - _logger.debug('Required open name not found in \ -Plugin section of plugin.info file.') - self.tw.showlabel( - 'status', label=_('Plugin could not be installed.')) - else: - plugin_name = file_info.get('Plugin', 'name') - _logger.debug('Plugin name: %s' % (plugin_name)) - tmp_path = os.path.join(tmp_dir, plugin_name) - plugin_path = os.path.join(activity.get_bundle_path(), 'plugins') - if os.path.exists(os.path.join(plugin_path, plugin_name)): - self._reload_plugin_alert(tmp_dir, tmp_path, plugin_path, - plugin_name, file_info) - else: - self._complete_plugin_install(tmp_dir, tmp_path, plugin_path, - plugin_name, file_info) - - def _complete_plugin_install(self, tmp_dir, tmp_path, plugin_path, - plugin_name, file_info): - ''' We complete the installation directly or from ConfirmationAlert ''' - status = subprocess.call(['cp', '-r', tmp_path, plugin_path + '/']) - if status == 0: - # Save the plugin.info file in the plugin directory - subprocess.call(['cp', os.path.join(tmp_dir, 'plugin.info'), - os.path.join(plugin_path, plugin_name) + '/']) - _logger.debug('Plugin installed successfully.') - if self.has_toolbarbox: - palette_name_list = [] - if file_info.has_option('Plugin', 'palette'): - palette_name_list = file_info.get( - 'Plugin', 'palette').split(',') - create_palette = [] - for palette_name in palette_name_list: - if not palette_name.strip() in palette_names: - create_palette.append(True) - else: - create_palette.append(False) - _logger.debug('Initializing plugin...') - self.tw.init_plugin(plugin_name) - self.tw.turtleart_plugins[-1].setup() - self.tw.load_media_shapes() - for i, palette_name in enumerate(palette_name_list): - if create_palette[i]: - _logger.debug('Creating plugin palette %s (%d)' % - (palette_name.strip(), i)) - j = len(self.palette_buttons) - self.palette_buttons.append( - self._radio_button_factory( - palette_name.strip() + 'off', - self._palette_toolbar, - self.do_palette_buttons_cb, - j - 1, - help_strings[palette_name.strip()], - self.palette_buttons[0])) - self._overflow_buttons.append( - self._add_button( - palette_name.strip() + 'off', - None, - self.do_palette_buttons_cb, - None, - arg=j - 1)) - self._overflow_box.pack_start( - self._overflow_buttons[j - 1]) - self.tw.palettes.insert(j - 1, []) - self.tw.palette_sprs.insert(j - 1, [None, None]) - else: - _logger.debug('Palette already exists... \ -skipping insert') - # We need to change the index associated with the - # Trash Palette Button. - j = len(palette_names) - pidx = palette_names.index(palette_name.strip()) - self.palette_buttons[pidx].connect( - 'clicked', self.do_palette_buttons_cb, j - 1) - self._overflow_buttons[pidx].connect( - 'clicked', self.do_palette_buttons_cb, j - 1) - _logger.debug('reinitializing palette toolbar') - self._setup_palette_toolbar() - else: - self.tw.showlabel('status', - label=_('Please restart Turtle Art \ -in order to use the plugin.')) - else: - self.tw.showlabel( - 'status', label=_('Plugin could not be installed.')) - status = subprocess.call(['rm', '-r', tmp_path]) - if status != 0: - _logger.debug('Problems cleaning up tmp_path.') - shutil.rmtree(tmp_dir) - - def _cancel_plugin_install(self, tmp_dir): - ''' If we cancel, just cleanup ''' - shutil.rmtree(tmp_dir) - def _reload_plugin_alert(self, tmp_dir, tmp_path, plugin_path, plugin_name, file_info): ''' We warn the user if the plugin was previously loaded ''' @@ -1453,12 +1354,12 @@ in order to use the plugin.')) if response_id is gtk.RESPONSE_OK: _logger.debug('continue to install') self.remove_alert(alert) - self._complete_plugin_install(tmp_dir, tmp_path, plugin_path, - plugin_name, file_info) + complete_plugin_install(self, tmp_dir, tmp_path, plugin_path, + plugin_name, file_info) elif response_id is gtk.RESPONSE_CANCEL: _logger.debug('cancel install') self.remove_alert(alert) - self._cancel_plugin_install(tmp_dir) + cancel_plugin_install(self, tmp_dir) alert.connect('response', _reload_plugin_alert_response_cb, self, tmp_dir, tmp_path, plugin_path, plugin_name, file_info) @@ -1501,7 +1402,7 @@ in order to use the plugin.')) gobject.idle_add(self._project_loader, turtle_code) else: _logger.debug('load a plugin from %s' % (tmp_dir)) - self._load_a_plugin(tmp_dir) + load_a_plugin(self, tmp_dir) self.restore_cursor() except: _logger.debug('Could not extract files from %s.' % diff --git a/turtleblocks.py b/turtleblocks.py index 7e3aba4..a87cd0b 100755 --- a/turtleblocks.py +++ b/turtleblocks.py @@ -36,6 +36,9 @@ import cStringIO import errno import ConfigParser import gconf +import tarfile +import tempfile +import subprocess try: # Try to use XDG Base Directory standard for config files. @@ -53,12 +56,15 @@ from gettext import gettext as _ from TurtleArt.taconstants import (OVERLAY_LAYER, DEFAULT_TURTLE_COLORS, TAB_LAYER, SUFFIX) -from TurtleArt.tautils import (data_from_string, get_save_name) +from TurtleArt.tautils import (data_from_string, get_load_name, + get_path, get_save_name) from TurtleArt.tapalette import default_values from TurtleArt.tawindow import TurtleArtWindow from TurtleArt.taexportlogo import save_logo from TurtleArt.taexportpython import save_python from TurtleArt.taprimitive import PyExportError +from TurtleArt.taplugin import (load_a_plugin, cancel_plugin_install, + complete_plugin_install) from util.menubuilder import MenuBuilder @@ -89,6 +95,7 @@ class TurtleMain(): self.summary = file_activity_info.get('Activity', 'summary') self.website = file_activity_info.get('Activity', 'website') self.icon_name = file_activity_info.get('Activity', 'icon') + self.bundle_path = self._abspath path = os.path.abspath('./locale/') gettext.bindtextdomain(bundle_id, path) gettext.textdomain(bundle_id) @@ -107,6 +114,7 @@ class TurtleMain(): self._gnome_plugins = [] self._selected_sample = None self._sample_window = None + self.has_toolbarbox = False if self._output_png: # Outputing to file, so no need for a canvas @@ -347,6 +355,18 @@ return %s(self)" % (p, P, P) self.vbox.set_size_request(rect[2], rect[3]) self.menu_height = self.menu_bar.size_request()[1] + def restore_cursor(self): + ''' No longer copying or sharing, so restore standard cursor. ''' + self.tw.copying_blocks = False + self.tw.sharing_blocks = False + self.tw.saving_blocks = False + self.tw.deleting_blocks = False + if hasattr(self, 'get_window'): + if hasattr(self.get_window(), 'get_cursor'): + self.get_window().set_cursor(self._old_cursor) + else: + self.get_window().set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)) + def _setup_gtk(self): ''' Set up a scrolled window in which to run Turtle Blocks. ''' win = gtk.Window(gtk.WINDOW_TOPLEVEL) @@ -404,6 +424,8 @@ return %s(self)" % (p, P, P) self._create_store) MenuBuilder.make_menu_item(menu, _('Open'), self._do_open_cb) MenuBuilder.make_menu_item(menu, _('Load project'), self._do_load_cb) + MenuBuilder.make_menu_item(menu, _('Load plugin'), + self._do_load_plugin_cb) MenuBuilder.make_menu_item(menu, _('Save'), self._do_save_cb) MenuBuilder.make_menu_item(menu, _('Save as'), self._do_save_as_cb) MenuBuilder.make_menu_item(menu, _('Save as image'), @@ -518,6 +540,27 @@ Would you like to save before quitting?')) dlg.destroy() return resp + def _reload_plugin_alert(self, tmp_dir, tmp_path, plugin_path, plugin_name, + file_info): + print "Already installed" + title = _('Plugin %s already installed') % plugin_name + msg = _('Do you want to reinstall %s?') % plugin_name + dlg = gtk.MessageDialog(parent=None, type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_YES_NO, + message_format=title) + dlg.format_secondary_text(msg) + dlg.set_title(title) + dlg.set_property('skip-taskbar-hint', False) + + resp = dlg.run() + dlg.destroy() + + if resp is gtk.RESPONSE_OK: + complete_plugin_install(tmp_dir, tmp_path, plugin_path, + plugin_name, file_info) + elif resp is gtk.RESPONSE_CANCEL: + cancel_plugin_install(tmp_dir) + def _do_new_cb(self, widget): ''' Callback for new project. ''' self.tw.new_project() @@ -531,6 +574,40 @@ Would you like to save before quitting?')) ''' Callback for load project (add to current project). ''' self.tw.load_file_from_chooser(False) + def _do_load_plugin_cb(self, widget): + file_path, loaddir = get_load_name('.tar.gz', self.tw.load_save_folder) + if file_path is None: + return + try: + # Copy to tmp file since some systems had trouble + # with gunzip directly from datastore + datapath = get_path(None, 'instance') + if not os.path.exists(datapath): + os.makedirs(datapath) + tmpfile = os.path.join(datapath, 'tmpfile.tar.gz') + subprocess.call(['cp', file_path, tmpfile]) + status = subprocess.call(['gunzip', tmpfile]) + if status == 0: + tar_fd = tarfile.open(tmpfile[:-3], 'r') + else: + tar_fd = tarfile.open(tmpfile, 'r') + except: + tar_fd = tarfile.open(file_path, 'r') + + tmp_dir = tempfile.mkdtemp() + + try: + tar_fd.extractall(tmp_dir) + load_a_plugin(self, tmp_dir) + self.restore_cursor() + except: + self.restore_cursor() + finally: + tar_fd.close() + # Remove tmpfile.tar + subprocess.call(['rm', + os.path.join(datapath, 'tmpfile.tar')]) + def _do_save_cb(self, widget): ''' Callback for save project. ''' self.tw.save_file(self._ta_file) -- cgit v0.9.1