Code

switch from invokeBbox to getBounds (need to fix problems with empty
[inkscape.git] / src / extension / implementation / script.cpp
index bd9b085e250e9a7dc6542a9d36861a07aebeb81d..a244e9abbbb30c32c55ebb68740077dffae28145 100644 (file)
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
+/*
+TODO:
+FIXME:
+  After Inkscape makes a formal requirement for a GTK version above 2.11.4, please
+  replace all the instances of ink_ext_XXXXXX in this file that represent
+  svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions
+  that call inkscape to manipulate the file.
+  
+  "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
+  
+  references:
+  http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp
+  http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes
+  http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp
+  
+  --Aaron Spike
+*/
 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
 
 #ifdef HAVE_CONFIG_H
@@ -20,8 +37,7 @@
 #include <unistd.h>
 
 #include <errno.h>
-#include <gtkmm/textview.h>
-#include <gtkmm/scrolledwindow.h>
+#include <gtkmm.h>
 
 #include "ui/view/view.h"
 #include "desktop-handles.h"
 #include "prefs-utils.h"
 #include "../system.h"
 #include "extension/effect.h"
+#include "extension/output.h"
 #include "extension/db.h"
 #include "script.h"
+#include "dialogs/dialog-events.h"
 
 #include "util/glib-list-iterators.h"
 
+
+
 #ifdef WIN32
 #include <windows.h>
+#include <sys/stat.h>
+#include "registrytool.h"
 #endif
 
+
+
 /** This is the command buffer that gets allocated from the stack */
 #define BUFSIZE (255)
 
+
+
 /* Namespaces */
 namespace Inkscape {
 namespace Extension {
 namespace Implementation {
 
-/* Real functions */
+
+
+//Interpreter lookup table
+struct interpreter_t {
+        gchar * identity;
+        gchar * prefstring;
+        gchar * defaultval;
+};
+
+
+static interpreter_t interpreterTab[] = {
+        {"perl",   "perl-interpreter",   "perl"   },
+        {"python", "python-interpreter", "python" },
+        {"ruby",   "ruby-interpreter",   "ruby"   },
+        {"shell",  "shell-interpreter",  "sh"     },
+        { NULL,    NULL,                  NULL    }
+};
+
+
+
+/**
+ * Look up an interpreter name, and translate to something that
+ * is executable
+ */
+static Glib::ustring
+resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
+{
+
+    Glib::ustring interpName = interpNameArg;
+
+    interpreter_t *interp;
+    bool foundInterp = false;
+    for (interp =  interpreterTab ; interp->identity ; interp++ ){
+        if (interpName == interp->identity) {
+            foundInterp = true;
+            break;
+        }
+    }
+
+    // Do we have a supported interpreter type?
+    if (!foundInterp)
+        return "";
+    interpName = interp->defaultval;
+
+    // 1.  Check preferences
+    gchar *prefInterp = (gchar *)prefs_get_string_attribute(
+                                "extensions", interp->prefstring);
+
+    if (prefInterp) {
+        interpName = prefInterp;
+        return interpName;
+    }
+
+#ifdef _WIN32
+
+    // 2.  Windows.  Try looking relative to inkscape.exe
+    RegistryTool rt;
+    Glib::ustring fullPath;
+    Glib::ustring path;
+    Glib::ustring exeName;
+    if (rt.getExeInfo(fullPath, path, exeName)) {
+        Glib::ustring interpPath = path;
+        interpPath.append("\\");
+        interpPath.append(interpName);
+        interpPath.append("\\");
+        interpPath.append(interpName);
+        interpPath.append(".exe");
+        struct stat finfo;
+        if (stat(interpPath .c_str(), &finfo) ==0) {
+            g_message("Found local interpreter, '%s',  Size: %d",
+                      interpPath .c_str(),
+                      (int)finfo.st_size);
+            return interpPath;
+        }                       
+    }
+
+    // 3. Try searching the path
+    char szExePath[MAX_PATH];
+    char szCurrentDir[MAX_PATH];
+    GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
+    unsigned int ret = (unsigned int)FindExecutable(
+                  interpName.c_str(), szCurrentDir, szExePath);
+    if (ret > 32) {
+        interpName = szExePath;
+        return interpName;
+    }
+
+#endif // win32
+
+
+    return interpName;
+}
+
+
+
+
+
+
 /**
     \return    A script object
     \brief     This function creates a script object and sets up the
@@ -59,12 +182,20 @@ namespace Implementation {
    of memory in the unloaded state.
 */
 Script::Script() :
-    Implementation(),
-    command(NULL),
-    helper_extension(NULL)
+    Implementation()
 {
 }
 
+
+/**
+ *   brief     Destructor
+ */
+Script::~Script()
+{
+}
+
+
+
 /**
     \return    A string with the complete string with the relative directory expanded
     \brief     This function takes in a Repr that contains a reldir entry
@@ -80,29 +211,45 @@ Script::Script() :
     string.  This means that the caller of this function can always
     free what they are given (and should do it too!).
 */
-gchar *
+Glib::ustring
 Script::solve_reldir(Inkscape::XML::Node *reprin) {
-    gchar const *reldir = reprin->attribute("reldir");
 
-    if (reldir == NULL) {
-        return g_strdup(sp_repr_children(reprin)->content());
+    gchar const *s = reprin->attribute("reldir");
+
+    if (!s) {
+        Glib::ustring str = sp_repr_children(reprin)->content();
+        return str;
     }
 
-    if (!strcmp(reldir, "extensions")) {
-        for(unsigned int i=0; i<Inkscape::Extension::Extension::search_path.size(); i++) {
-            gchar * filename = g_build_filename(Inkscape::Extension::Extension::search_path[i], sp_repr_children(reprin)->content(), NULL);
-            if ( Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) ) {
+    Glib::ustring reldir = s;
+
+    if (reldir == "extensions") {
+
+        for (unsigned int i=0;
+            i < Inkscape::Extension::Extension::search_path.size();
+            i++) {
+
+            gchar * fname = g_build_filename(
+               Inkscape::Extension::Extension::search_path[i],
+               sp_repr_children(reprin)->content(),
+               NULL);
+            Glib::ustring filename = fname;
+            g_free(fname);
+
+            if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
                 return filename;
-            }
-            g_free(filename);
+
         }
     } else {
-        return g_strdup(sp_repr_children(reprin)->content());
+        Glib::ustring str = sp_repr_children(reprin)->content();
+        return str;
     }
 
-    return NULL;
+    return "";
 }
 
+
+
 /**
     \return   Whether the command given exists, including in the path
     \brief    This function is used to find out if something exists for
@@ -120,65 +267,64 @@ Script::solve_reldir(Inkscape::XML::Node *reprin) {
     then a FALSE is returned, the command could not be found.
 */
 bool
-Script::check_existance(gchar const *command)
+Script::check_existance(const Glib::ustring &command)
 {
-    if (*command == '\0') {
-        /* We check the simple case first. */
-        return FALSE;
+
+    // Check the simple case first
+    if (command.size() == 0) {
+        return false;
     }
 
-    if (g_utf8_strchr(command, -1, G_DIR_SEPARATOR) != NULL) {
-        /* Don't search when it contains a slash. */
-        if (Inkscape::IO::file_test(command, G_FILE_TEST_EXISTS))
-            return TRUE;
+    //Don't search when it contains a slash. */
+    if (command.find(G_DIR_SEPARATOR) != command.npos) {
+        if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
+            return true;
         else
-            return FALSE;
+            return false;
     }
 
 
-    gchar *path = g_strdup(g_getenv("PATH"));
-    if (path == NULL) {
-        /* There is no `PATH' in the environment.
+    Glib::ustring path; 
+    gchar *s = (gchar *) g_getenv("PATH");
+    if (s)
+        path = s;
+    else
+       /* There is no `PATH' in the environment.
            The default search path is the current directory */
-        path = g_strdup(G_SEARCHPATH_SEPARATOR_S);
-    }
-    gchar *orig_path = path;
+        path = G_SEARCHPATH_SEPARATOR_S;
 
-    for (; path != NULL;) {
-        gchar *const local_path = path;
-        path = g_utf8_strchr(path, -1, G_SEARCHPATH_SEPARATOR);
-        if (path == NULL) {
-            break;
-        }
-        /* Not sure whether this is UTF8 happy, but it would seem
-           like it considering that I'm searching (and finding)
-           the ':' character */
-        if (path != local_path && path != NULL) {
-            path[0] = '\0';
-            path++;
-        } else {
-            path = NULL;
-        }
+    unsigned int pos  = 0;
+    unsigned int pos2 = 0;
+    while ( pos < path.size() ) {
+
+        Glib::ustring localPath;
 
-        gchar *final_name;
-        if (local_path == '\0') {
-            final_name = g_strdup(command);
+        pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
+        if (pos2 == path.npos) {
+            localPath = path.substr(pos);
+            pos = path.size();
         } else {
-            final_name = g_build_filename(local_path, command, NULL);
+            localPath = path.substr(pos, pos2-pos);
+            pos = pos2+1;
         }
+        
+        //printf("### %s\n", localPath.c_str());
+        Glib::ustring candidatePath = 
+                      Glib::build_filename(localPath, command);
 
-        if (Inkscape::IO::file_test(final_name, G_FILE_TEST_EXISTS)) {
-            g_free(final_name);
-            g_free(orig_path);
-            return TRUE;
-        }
+        if (Inkscape::IO::file_test(candidatePath .c_str(),
+                      G_FILE_TEST_EXISTS))
+            return true;
 
-        g_free(final_name);
     }
 
-    return FALSE;
+    return false;
 }
 
+
+
+
+
 /**
     \return   none
     \brief    This function 'loads' an extention, basically it determines
@@ -200,15 +346,14 @@ Script::check_existance(gchar const *command)
 bool
 Script::load(Inkscape::Extension::Extension *module)
 {
-    if (module->loaded()) {
+    if (module->loaded())
         return TRUE;
-    }
 
-    helper_extension = NULL;
+    helper_extension = "";
 
     /* This should probably check to find the executable... */
     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
-    gchar *command_text = NULL;
+    Glib::ustring command_text;
     while (child_repr != NULL) {
         if (!strcmp(child_repr->name(), "script")) {
             child_repr = sp_repr_children(child_repr);
@@ -216,46 +361,18 @@ Script::load(Inkscape::Extension::Extension *module)
                 if (!strcmp(child_repr->name(), "command")) {
                     command_text = solve_reldir(child_repr);
 
-                    const gchar * interpretstr = child_repr->attribute("interpreter");
+                    const gchar *interpretstr = child_repr->attribute("interpreter");
                     if (interpretstr != NULL) {
-                        struct interpreter_t {
-                            gchar * identity;
-                            gchar * prefstring;
-                            gchar * defaultval;
-                        };
-                        const interpreter_t interpreterlst[] = {
-                            {"perl", "perl-interpreter", "perl"},
-                            {"python", "python-interpreter", "python"},
-                            {"ruby", "ruby-interpreter", "ruby"},
-                            {"shell", "shell-interpreter", "sh"}
-                        }; /* Change count below if you change structure */
-                        for (unsigned int i = 0; i < 4; i++) {
-                            if (!strcmp(interpretstr, interpreterlst[i].identity)) {
-                                const gchar * insertText = interpreterlst[i].defaultval;
-                                if (prefs_get_string_attribute("extensions", interpreterlst[i].prefstring) != NULL)
-                                    insertText = prefs_get_string_attribute("extensions", interpreterlst[i].prefstring);
-#ifdef _WIN32
-                                else {
-                                    char szExePath[MAX_PATH];
-                                    char szCurrentDir[MAX_PATH];
-                                    GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
-                                    if (reinterpret_cast<unsigned>(FindExecutable(command_text, szCurrentDir, szExePath)) > 32)
-                                        insertText = szExePath;
-                                }
-#endif
-
-                                gchar * temp = command_text;
-                                command_text = g_strconcat(insertText, " ", temp, NULL);
-                                g_free(temp);
-
-                                break;
-                            }
-                        }
+                        Glib::ustring interpString =
+                            resolveInterpreterExecutable(interpretstr);
+                        interpString .append(" \"");
+                        interpString .append(command_text);
+                        interpString .append("\"");                        
+                        command_text = interpString;
                     }
                 }
-                if (!strcmp(child_repr->name(), "helper_extension")) {
-                    helper_extension = g_strdup(sp_repr_children(child_repr)->content());
-                }
+                if (!strcmp(child_repr->name(), "helper_extension"))
+                    helper_extension = sp_repr_children(child_repr)->content();
                 child_repr = sp_repr_next(child_repr);
             }
 
@@ -264,15 +381,13 @@ Script::load(Inkscape::Extension::Extension *module)
         child_repr = sp_repr_next(child_repr);
     }
 
-    g_return_val_if_fail(command_text != NULL, FALSE);
+    g_return_val_if_fail(command_text.size() > 0, FALSE);
 
-    if (command != NULL)
-        g_free(command);
     command = command_text;
-
-    return TRUE;
+    return true;
 }
 
+
 /**
     \return   None.
     \brief    Unload this puppy!
@@ -284,18 +399,13 @@ Script::load(Inkscape::Extension::Extension *module)
 void
 Script::unload(Inkscape::Extension::Extension *module)
 {
-    if (command != NULL) {
-        g_free(command);
-        command = NULL;
-    }
-    if (helper_extension != NULL) {
-        g_free(helper_extension);
-        helper_extension = NULL;
-    }
-
-    return;
+    command          = "";
+    helper_extension = "";
 }
 
+
+
+
 /**
     \return   Whether the check passed or not
     \brief    Check every dependency that was given to make sure we should keep this extension
@@ -311,13 +421,10 @@ Script::check(Inkscape::Extension::Extension *module)
             child_repr = sp_repr_children(child_repr);
             while (child_repr != NULL) {
                 if (!strcmp(child_repr->name(), "check")) {
-                    gchar *command_text = solve_reldir(child_repr);
-                    if (command_text != NULL) {
+                    Glib::ustring command_text = solve_reldir(child_repr);
+                    if (command_text.size() > 0) {
                         /* I've got the command */
-                        bool existance;
-
-                        existance = check_existance(command_text);
-                        g_free(command_text);
+                        bool existance = check_existance(command_text);
                         if (!existance)
                             return FALSE;
                     }
@@ -338,9 +445,11 @@ Script::check(Inkscape::Extension::Extension *module)
         child_repr = sp_repr_next(child_repr);
     }
 
-    return TRUE;
+    return true;
 }
 
+
+
 /**
     \return   A dialog for preferences
     \brief    A stub funtion right now
@@ -350,12 +459,15 @@ Script::check(Inkscape::Extension::Extension *module)
     This function should really do something, right now it doesn't.
 */
 Gtk::Widget *
-Script::prefs_input(Inkscape::Extension::Input *module, gchar const *filename)
+Script::prefs_input(Inkscape::Extension::Input *module,
+                    const gchar *filename)
 {
     /*return module->autogui(); */
     return NULL;
 }
 
+
+
 /**
     \return   A dialog for preferences
     \brief    A stub funtion right now
@@ -366,10 +478,11 @@ Script::prefs_input(Inkscape::Extension::Input *module, gchar const *filename)
 Gtk::Widget *
 Script::prefs_output(Inkscape::Extension::Output *module)
 {
-    /*return module->autogui();*/
-    return NULL;
+    return module->autogui(NULL, NULL); 
 }
 
+
+
 /**
     \return   A dialog for preferences
     \brief    A stub funtion right now
@@ -378,19 +491,25 @@ Script::prefs_output(Inkscape::Extension::Output *module)
     This function should really do something, right now it doesn't.
 */
 Gtk::Widget *
-Script::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *view)
+Script::prefs_effect(Inkscape::Extension::Effect *module,
+                     Inkscape::UI::View::View *view)
 {
+
     SPDocument * current_document = view->doc();
 
     using Inkscape::Util::GSListConstIterator;
-    GSListConstIterator<SPItem *> selected = sp_desktop_selection((SPDesktop *)view)->itemList();
+    GSListConstIterator<SPItem *> selected =
+           sp_desktop_selection((SPDesktop *)view)->itemList();
     Inkscape::XML::Node * first_select = NULL;
     if (selected != NULL) 
-        first_select = SP_OBJECT_REPR(*selected);
+           first_select = SP_OBJECT_REPR(*selected);
 
     return module->autogui(current_document, first_select);
 }
 
+
+
+
 /**
     \return  A new document that has been opened
     \brief   This function uses a filename that is put in, and calls
@@ -413,14 +532,17 @@ Script::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::Vi
     That document is then returned from this function.
 */
 SPDocument *
-Script::open(Inkscape::Extension::Input *module, gchar const *filename)
+Script::open(Inkscape::Extension::Input *module,
+             const gchar *filenameArg)
 {
-    int data_read = 0;
-    gint tempfd;
-    gchar *tempfilename_out;
+
+    Glib::ustring filename = filenameArg;
+
+    gchar *tmpname;
 
     // FIXME: process the GError instead of passing NULL
-    if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) {
+    gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
+    if (tempfd == -1) {
         /* Error, couldn't create temporary filename */
         if (errno == EINVAL) {
             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
@@ -436,36 +558,45 @@ Script::open(Inkscape::Extension::Input *module, gchar const *filename)
         }
     }
 
+    Glib::ustring tempfilename_out = tmpname;
+    g_free(tmpname);
+
     gsize bytesRead = 0;
     gsize bytesWritten = 0;
     GError *error = NULL;
-    gchar *local_filename = g_filename_from_utf8( filename,
-                                                  -1,  &bytesRead,  &bytesWritten, &error);
+    Glib::ustring local_filename =
+            g_filename_from_utf8( filename.c_str(), -1,
+                                  &bytesRead,  &bytesWritten, &error);
 
-    data_read = execute(command, local_filename, tempfilename_out);
-    g_free(local_filename);
+    int data_read = execute(command, local_filename, tempfilename_out);
 
     SPDocument *mydoc = NULL;
     if (data_read > 10) {
-        if (helper_extension == NULL) {
-            mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out);
+        if (helper_extension.size()==0) {
+            mydoc = Inkscape::Extension::open(
+                Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
+                                            tempfilename_out.c_str());
         } else {
-            mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(helper_extension), tempfilename_out);
+            mydoc = Inkscape::Extension::open(
+                Inkscape::Extension::db.get(helper_extension.c_str()),
+                                            tempfilename_out.c_str());
         }
     }
 
     if (mydoc != NULL)
-        sp_document_set_uri(mydoc, (const gchar *)filename);
+        sp_document_set_uri(mydoc, (const gchar *)filename.c_str());
 
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd);
     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
-    unlink(tempfilename_out);
-    g_free(tempfilename_out);
+    unlink(tempfilename_out.c_str());
+
 
     return mydoc;
 }
 
+
+
 /**
     \return   none
     \brief    This function uses an extention to save a document.  It first
@@ -491,12 +622,17 @@ Script::open(Inkscape::Extension::Input *module, gchar const *filename)
     delete the temporary file.
 */
 void
-Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename)
+Script::save(Inkscape::Extension::Output *module,
+             SPDocument *doc,
+             const gchar *filenameArg)
 {
-    gint tempfd;
-    gchar *tempfilename_in;
+
+    Glib::ustring filename = filenameArg;
+
+    gchar *tmpname;
     // FIXME: process the GError instead of passing NULL
-    if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) {
+    gint tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
+    if (tempfd == -1) {
         /* Error, couldn't create temporary filename */
         if (errno == EINVAL) {
             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
@@ -512,29 +648,41 @@ Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *
         }
     }
 
-    if (helper_extension == NULL) {
-        Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), doc, tempfilename_in, FALSE, FALSE, FALSE);
+    Glib::ustring tempfilename_in = tmpname;
+    g_free(tmpname);
+
+    if (helper_extension.size() == 0) {
+        Inkscape::Extension::save(
+                   Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
+                   doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
     } else {
-        Inkscape::Extension::save(Inkscape::Extension::db.get(helper_extension), doc, tempfilename_in, FALSE, FALSE, FALSE);
+        Inkscape::Extension::save(
+                   Inkscape::Extension::db.get(helper_extension.c_str()),
+                   doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
     }
 
     gsize bytesRead = 0;
     gsize bytesWritten = 0;
     GError *error = NULL;
-    gchar *local_filename = g_filename_from_utf8( filename,
-                                                  -1,  &bytesRead,  &bytesWritten, &error);
+    Glib::ustring local_filename =
+            g_filename_from_utf8( filename.c_str(), -1,
+                                 &bytesRead,  &bytesWritten, &error);
+
+    Glib::ustring local_command = command;
+    Glib::ustring paramString   = *module->paramString();
+    local_command.append(paramString);
 
-    execute(command, tempfilename_in, local_filename);
+    execute(local_command, tempfilename_in, local_filename);
 
-    g_free(local_filename);
 
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd);
     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
-    unlink(tempfilename_in);
-    g_free(tempfilename_in);
+    unlink(tempfilename_in.c_str());
 }
 
+
+
 /**
     \return    none
     \brief     This function uses an extention as a effect on a document.
@@ -544,7 +692,7 @@ Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *
     This function is a little bit trickier than the previous two.  It
     needs two temporary files to get it's work done.  Both of these
     files have random names created for them using the g_file_open_temp function
-    with the sp_ext_ prefix in the temporary directory.  Like the other
+    with the ink_ext_ prefix in the temporary directory.  Like the other
     functions, the temporary files are deleted at the end.
 
     To save/load the two temporary documents (both are SVG) the internal
@@ -566,13 +714,23 @@ Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *
 void
 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
 {
-    int data_read = 0;
-    SPDocument * mydoc = NULL;
-    gint tempfd_in;
-    gchar *tempfilename_in;
+    if (module->no_doc) { 
+        // this is a no-doc extension, e.g. a Help menu command; 
+        // just run the command without any files, ignoring errors
+        Glib::ustring local_command(command);
+        Glib::ustring paramString = *module->paramString();
+        local_command.append(paramString);
+
+        Glib::ustring empty;
+        execute(local_command, empty, empty);
 
+        return;
+    }
+
+    gchar *tmpname;
     // FIXME: process the GError instead of passing NULL
-    if ((tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) {
+    gint tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
+    if (tempfd_in == -1) {
         /* Error, couldn't create temporary filename */
         if (errno == EINVAL) {
             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
@@ -588,10 +746,13 @@ Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *do
         }
     }
 
-    gint tempfd_out;
-    gchar *tempfilename_out;
+    Glib::ustring tempfilename_in = tmpname;
+    g_free(tmpname);
+
+
     // FIXME: process the GError instead of passing NULL
-    if ((tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) {
+    gint tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tmpname, NULL);
+    if (tempfd_out == -1) {
         /* Error, couldn't create temporary filename */
         if (errno == EINVAL) {
             /* The  last  six characters of template were not XXXXXX.  Now template is unchanged. */
@@ -607,8 +768,12 @@ Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *do
         }
     }
 
-    Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
-                              doc->doc(), tempfilename_in, FALSE, FALSE, FALSE);
+    Glib::ustring tempfilename_out= tmpname;
+    g_free(tmpname);
+
+    Inkscape::Extension::save(
+              Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
+              doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
 
     Glib::ustring local_command(command);
 
@@ -616,8 +781,8 @@ Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *do
      * of classes. */
     SPDesktop *desktop = (SPDesktop *) doc;
     if (desktop != NULL) {
-        using Inkscape::Util::GSListConstIterator;
-        GSListConstIterator<SPItem *> selected = sp_desktop_selection(desktop)->itemList();
+        Inkscape::Util::GSListConstIterator<SPItem *> selected =
+             sp_desktop_selection(desktop)->itemList();
         while ( selected != NULL ) {
             local_command += " --id=";
             local_command += SP_OBJECT_ID(*selected);
@@ -625,36 +790,41 @@ Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *do
         }
     }
 
-    Glib::ustring * paramString = module->paramString();
-    local_command += *paramString;
-    delete paramString;
+    Glib::ustring paramString = *module->paramString();
+    local_command.append(paramString);
+
 
     // std::cout << local_command << std::endl;
 
-    data_read = execute(local_command.c_str(), tempfilename_in, tempfilename_out);
+    int data_read = execute(local_command, tempfilename_in, tempfilename_out);
 
+    SPDocument * mydoc = NULL;
     if (data_read > 10)
-        mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out);
+        mydoc = Inkscape::Extension::open(
+              Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
+              tempfilename_out.c_str());
 
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd_in);
     close(tempfd_out);
+
     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
-    unlink(tempfilename_in);
-    g_free(tempfilename_in);
-    unlink(tempfilename_out);
-    g_free(tempfilename_out);
+    unlink(tempfilename_in.c_str());
+    unlink(tempfilename_out.c_str());
+
 
     /* Do something with mydoc.... */
-    if (mydoc != NULL) {
+    if (mydoc) {
         doc->doc()->emitReconstructionStart();
         copy_doc(doc->doc()->rroot, mydoc->rroot);
         doc->doc()->emitReconstructionFinish();
         mydoc->release();
+        sp_namedview_update_layers_from_document(desktop);
     }
 }
 
 
+
 /**
     \brief  A function to take all the svg elements from one document
             and put them in another.
@@ -679,8 +849,6 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
             child = child->next()) {
         if (!strcmp("sodipodi:namedview", child->name()))
             continue;
-        if (!strcmp("svg:defs", child->name()))
-            continue;
         delete_list.push_back(child);
     }
     for (unsigned int i = 0; i < delete_list.size(); i++)
@@ -691,8 +859,6 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
             child = child->next()) {
         if (!strcmp("sodipodi:namedview", child->name()))
             continue;
-        if (!strcmp("svg:defs", child->name()))
-            continue;
         oldroot->appendChild(child->duplicate());
     }
 
@@ -700,13 +866,17 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
     /** \todo  Restore correct selection */
 }
 
+
+
 /* Helper class used by Script::execute */
 class pipe_t {
 public:
     /* These functions set errno if they return false.
        I'm not sure whether that's a good idea or not, but it should be reasonably
        straightforward to change it if needed. */
-    bool open(char *command, char const *errorFile, int mode);
+    bool open(const Glib::ustring &command,
+              const Glib::ustring &errorFile,
+              int mode);
     bool close();
 
     /* These return the number of bytes read/written. */
@@ -730,8 +900,10 @@ private:
 #endif
 };
 
+
+
+
 /**
-    \return   none
     \brief    This is the core of the extension file as it actually does
               the execution of the extension.
     \param    in_command  The command to be executed
@@ -760,27 +932,39 @@ private:
     are closed, and we return to what we were doing.
 */
 int
-Script::execute (const gchar * in_command, const gchar * filein, const gchar * fileout)
+Script::execute (const Glib::ustring &in_command,
+                 const Glib::ustring &filein,
+                 const Glib::ustring &fileout)
 {
-    g_return_val_if_fail(in_command != NULL, 0);
+    g_return_val_if_fail(in_command.size() > 0, 0);
     // printf("Executing: %s\n", in_command);
 
-    gchar * errorFile;
+    gchar *tmpname;
     gint errorFileNum;
-    errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &errorFile, NULL);
+    errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &tmpname, NULL);
     if (errorFileNum != 0) {
         close(errorFileNum);
     } else {
-        g_free(errorFile);
-        errorFile = NULL;
+        g_free(tmpname);
+    }
+
+    Glib::ustring errorFile = tmpname;
+    g_free(tmpname);
+
+    Glib::ustring localCommand = in_command;
+
+    if (!(filein.empty())) {
+        localCommand .append(" \"");
+        localCommand .append(filein);
+        localCommand .append("\"");
     }
 
-    char *command = g_strdup_printf("%s \"%s\"", in_command, filein);
     // std::cout << "Command to run: " << command << std::endl;
 
     pipe_t pipe;
-    bool open_success = pipe.open(command, errorFile, pipe_t::mode_read);
-    g_free(command);
+    bool open_success = pipe.open((char *)localCommand.c_str(),
+                                  errorFile.c_str(),
+                                  pipe_t::mode_read);
 
     /* Run script */
     if (!open_success) {
@@ -795,8 +979,17 @@ Script::execute (const gchar * in_command, const gchar * filein, const gchar * f
         return 0;
     }
 
-    Inkscape::IO::dump_fopen_call(fileout, "J");
-    FILE *pfile = Inkscape::IO::fopen_utf8name(fileout, "w");
+    if (fileout.empty()) { // no output file to create; just close everything and return 0
+        if (errorFile.size()>0) {
+            unlink(errorFile.c_str());
+        }
+        pipe.close();
+        return 0;
+    }
+
+    /* Copy pipe output to fileout (temporary file) */
+    Inkscape::IO::dump_fopen_call(fileout.c_str(), "J");
+    FILE *pfile = Inkscape::IO::fopen_utf8name(fileout.c_str(), "w");
 
     if (pfile == NULL) {
         /* Error - could not open file */
@@ -808,7 +1001,6 @@ Script::execute (const gchar * in_command, const gchar * filein, const gchar * f
         return 0;
     }
 
-    /* Copy pipe output to a temporary file */
     int amount_read = 0;
     char buf[BUFSIZE];
     int num_read;
@@ -834,7 +1026,7 @@ Script::execute (const gchar * in_command, const gchar * filein, const gchar * f
         } else if (errno == ECHILD) {
             perror("Extension::Script:  Could not obtain child status for pclose\n");
         } else {
-            if (errorFile != NULL) {
+            if (!errorFile.empty()) {
                 checkStderr(errorFile, Gtk::MESSAGE_ERROR,
                     _("Inkscape has received an error from the script that it called.  "
                       "The text returned with the error is included below.  "
@@ -847,32 +1039,37 @@ Script::execute (const gchar * in_command, const gchar * filein, const gchar * f
          * to count on what was read being good */
         amount_read = 0;
     } else {
-        if (errorFile != NULL) {
+        if (errorFile.size()>0) {
             checkStderr(errorFile, Gtk::MESSAGE_INFO,
                 _("Inkscape has received additional data from the script executed.  "
                   "The script did not return an error, but this may indicate the results will not be as expected."));
         }
     }
 
-    if (errorFile != NULL) {
-        unlink(errorFile);
-        g_free(errorFile);
+    if (errorFile.size()>0) {
+        unlink(errorFile.c_str());
     }
 
     return amount_read;
 }
 
+
+
+
 /**  \brief  This function checks the stderr file, and if it has data,
              shows it in a warning dialog to the user
      \param  filename  Filename of the stderr file
 */
 void
-Script::checkStderr (gchar * filename, Gtk::MessageType type, gchar * message)
+Script::checkStderr (const Glib::ustring &filename,
+                     Gtk::MessageType type,
+                     const Glib::ustring &message)
 {
+
     // magic win32 crlf->lf conversion means the file length is not the same as
     // the text length, but luckily gtk will accept crlf in textviews so we can
     // just use binary mode
-    std::ifstream stderrf (filename, std::ios_base::in | std::ios_base::binary);
+    std::ifstream stderrf (filename.c_str(), std::ios_base::in | std::ios_base::binary);
     if (!stderrf.is_open()) return;
 
     stderrf.seekg(0, std::ios::end);
@@ -882,6 +1079,8 @@ Script::checkStderr (gchar * filename, Gtk::MessageType type, gchar * message)
 
     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
     warning.set_resizable(true);
+    GtkWidget *dlg = GTK_WIDGET(warning.gobj());
+    sp_transientize(dlg);
 
     Gtk::VBox * vbox = warning.get_vbox();
 
@@ -910,59 +1109,78 @@ Script::checkStderr (gchar * filename, Gtk::MessageType type, gchar * message)
     return;
 }
 
+
+
+
 #ifdef WIN32
 
-bool pipe_t::open(char *command, char const *errorFile, int mode_p) {
+
+bool pipe_t::open(const Glib::ustring &command,
+                  const Glib::ustring &errorFile,
+                  int mode_p) {
     HANDLE pipe_write;
 
-    // Create pipe
-    {
-        SECURITY_ATTRIBUTES secattrs;
-        ZeroMemory(&secattrs, sizeof(secattrs));
-        secattrs.nLength = sizeof(secattrs);
-        secattrs.lpSecurityDescriptor = 0;
-        secattrs.bInheritHandle = TRUE;
-        HANDLE t_pipe_read = 0;
-        if ( !CreatePipe(&t_pipe_read, &pipe_write, &secattrs, 0) ) {
-            errno = translate_error(GetLastError());
-            return false;
-        }
-        // This duplicate handle makes the read pipe uninheritable
-        if ( !DuplicateHandle(GetCurrentProcess(), t_pipe_read, GetCurrentProcess(), &hpipe, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) ) {
-            int en = translate_error(GetLastError());
-            CloseHandle(t_pipe_read);
-            CloseHandle(pipe_write);
-            errno = en;
-            return false;
-        }
+    //###############  Create pipe
+    SECURITY_ATTRIBUTES secattrs;
+    ZeroMemory(&secattrs, sizeof(secattrs));
+    secattrs.nLength = sizeof(secattrs);
+    secattrs.lpSecurityDescriptor = 0;
+    secattrs.bInheritHandle = TRUE;
+    HANDLE t_pipe_read = 0;
+    if ( !CreatePipe(&t_pipe_read, &pipe_write, &secattrs, 0) ) {
+        errno = translate_error(GetLastError());
+        return false;
+    }
+    // This duplicate handle makes the read pipe uninheritable
+    BOOL ret = DuplicateHandle(GetCurrentProcess(),
+                               t_pipe_read,
+                               GetCurrentProcess(),
+                               &hpipe, 0, FALSE,
+                               DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+    if (!ret) {
+        int en = translate_error(GetLastError());
+        CloseHandle(t_pipe_read);
+        CloseHandle(pipe_write);
+        errno = en;
+        return false;
     }
-    // Open stderr file
-    HANDLE hStdErrFile = CreateFile(errorFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+
+    //############### Open stderr file
+    HANDLE hStdErrFile = CreateFile(errorFile.c_str(),
+                      GENERIC_WRITE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, CREATE_ALWAYS, 0, NULL);
     HANDLE hInheritableStdErr;
-    DuplicateHandle(GetCurrentProcess(), hStdErrFile, GetCurrentProcess(), &hInheritableStdErr, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-
-    // Create process
-    {
-        PROCESS_INFORMATION procinfo;
-        STARTUPINFO startupinfo;
-        ZeroMemory(&procinfo, sizeof(procinfo));
-        ZeroMemory(&startupinfo, sizeof(startupinfo));
-        startupinfo.cb = sizeof(startupinfo);
-        //startupinfo.lpReserved = 0;
-        //startupinfo.lpDesktop = 0;
-        //startupinfo.lpTitle = 0;
-        startupinfo.dwFlags = STARTF_USESTDHANDLES;
-        startupinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
-        startupinfo.hStdOutput = pipe_write;
-        startupinfo.hStdError = hInheritableStdErr;
-
-        if ( !CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &startupinfo, &procinfo) ) {
-            errno = translate_error(GetLastError());
-            return false;
-        }
-        CloseHandle(procinfo.hThread);
-        CloseHandle(procinfo.hProcess);
+    DuplicateHandle(GetCurrentProcess(),
+                    hStdErrFile,
+                    GetCurrentProcess(),
+                    &hInheritableStdErr,
+                    0, 
+                    TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+
+    //############### Create process
+    PROCESS_INFORMATION procinfo;
+    STARTUPINFO startupinfo;
+    ZeroMemory(&procinfo, sizeof(procinfo));
+    ZeroMemory(&startupinfo, sizeof(startupinfo));
+    startupinfo.cb = sizeof(startupinfo);
+    //startupinfo.lpReserved = 0;
+    //startupinfo.lpDesktop = 0;
+    //startupinfo.lpTitle = 0;
+    startupinfo.dwFlags = STARTF_USESTDHANDLES;
+    startupinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+    startupinfo.hStdOutput = pipe_write;
+    startupinfo.hStdError = hInheritableStdErr;
+
+    if ( !CreateProcess(NULL, (CHAR *)command.c_str(),
+                        NULL, NULL, TRUE,
+                        0, NULL, NULL,
+                        &startupinfo, &procinfo) ) {
+        errno = translate_error(GetLastError());
+        return false;
     }
+    CloseHandle(procinfo.hThread);
+    CloseHandle(procinfo.hProcess);
 
     // Close our copy of the write handle
     CloseHandle(hInheritableStdErr);
@@ -971,11 +1189,12 @@ bool pipe_t::open(char *command, char const *errorFile, int mode_p) {
     return true;
 }
 
+
+
 bool pipe_t::close() {
     BOOL retval = CloseHandle(hpipe);
-    if ( !retval ) {
+    if ( !retval )
         errno = translate_error(GetLastError());
-    }
     return retval != FALSE;
 }
 
@@ -1003,50 +1222,62 @@ int pipe_t::translate_error(DWORD err) {
     }
 }
 
-#else // Win32
 
-bool pipe_t::open(char *command, char const *errorFile, int mode_p) {
-    char popen_mode[4] = {0,0,0,0};
-    char *popen_mode_cur = popen_mode;
+#else // not Win32
 
-    if ( (mode_p & mode_read) != 0 ) {
-        *popen_mode_cur++ = 'r';
-    }
 
-    if ( (mode_p & mode_write) != 0 ) {
-        *popen_mode_cur++ = 'w';
+bool pipe_t::open(const Glib::ustring &command,
+                  const Glib::ustring &errorFile,
+                  int mode_p) {
+
+    Glib::ustring popen_mode;
+
+    if ( (mode_p & mode_read) != 0 )
+        popen_mode.append("r");
+
+    if ( (mode_p & mode_write) != 0 )
+        popen_mode.append("w");
+
+    // Get the commandline to be run
+    Glib::ustring pipeStr = command;
+    if (errorFile.size()>0) {
+        pipeStr .append(" 2> ");
+        pipeStr .append(errorFile);
     }
 
-    /* Get the commandline to be run */
-    if (errorFile != NULL) {
-        char * temp;
-        temp = g_strdup_printf("%s 2> %s", command, errorFile);
-        ppipe = popen(temp, popen_mode);
-        g_free(temp);
-    } else
-        ppipe = popen(command, popen_mode);
+    ppipe = popen(pipeStr.c_str(), popen_mode.c_str());
 
     return ppipe != NULL;
 }
 
+
 bool pipe_t::close() {
     return fclose(ppipe) == 0;
 }
 
+
 size_t pipe_t::read(void *buffer, size_t size) {
     return fread(buffer, 1, size, ppipe);
 }
 
+
 size_t pipe_t::write(void const *buffer, size_t size) {
     return fwrite(buffer, 1, size, ppipe);
 }
 
+
+
+
 #endif // (Non-)Win32
 
 
-}  /* Inkscape  */
-}  /* module  */
-}  /* Implementation  */
+
+
+}  // namespace Implementation
+}  // namespace Extension
+}  // namespace Inkscape
+
+
 
 
 /*