Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TurtleArt/taplugin.py117
-rw-r--r--TurtleArtActivity.py115
-rwxr-xr-xturtleblocks.py79
3 files changed, 203 insertions, 108 deletions
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)