Code

Sorry, I got off on a branch and ended up with a bunch of things. I'm just going...
[inkscape.git] / src / extension / implementation / script.cpp
index 9892e897fc1e935e7e006390dae7a84d066645ed..9475b479623ed6e98865d48cd5baf03c8bf5a6e2 100644 (file)
@@ -6,7 +6,7 @@
  *   Bryce Harrington <bryce@osdl.org>
  *   Ted Gould <ted@gould.cx>
  *
- * Copyright (C) 2002-2005 Authors
+ * Copyright (C) 2002-2005,2007 Authors
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
 #include "ui/view/view.h"
 #include "desktop-handles.h"
+#include "desktop.h"
 #include "selection.h"
 #include "sp-namedview.h"
 #include "io/sys.h"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "../system.h"
 #include "extension/effect.h"
 #include "extension/output.h"
+#include "extension/input.h"
 #include "extension/db.h"
 #include "script.h"
+#include "dialogs/dialog-events.h"
+#include "application/application.h"
 
 #include "util/glib-list-iterators.h"
 
@@ -56,19 +60,32 @@ namespace Inkscape {
 namespace Extension {
 namespace Implementation {
 
+/** \brief  Make GTK+ events continue to come through a little bit
+       
+       This just keeps coming the events through so that we'll make the GUI
+       update and look pretty.
+*/
+void
+Script::pump_events (void) {
+    while( Gtk::Main::events_pending() )
+        Gtk::Main::iteration();
+    return;
+}
 
 
-//Interpreter lookup table
-struct interpreter_t {
-        gchar * identity;
-        gchar * prefstring;
-        gchar * defaultval;
-};
+/** \brief  A table of what interpreters to call for a given language
 
-
-static interpreter_t interpreterTab[] = {
+    This table is used to keep track of all the programs to execute a
+    given script.  It also tracks the preference to use to overwrite
+    the given interpreter to a custom one per user.
+*/
+Script::interpreter_t const Script::interpreterTab[] = {
         {"perl",   "perl-interpreter",   "perl"   },
+#ifdef WIN32
+        {"python", "python-interpreter", "pythonw" },
+#else
         {"python", "python-interpreter", "python" },
+#endif
         {"ruby",   "ruby-interpreter",   "ruby"   },
         {"shell",  "shell-interpreter",  "sh"     },
         { NULL,    NULL,                  NULL    }
@@ -76,17 +93,18 @@ static interpreter_t interpreterTab[] = {
 
 
 
-/**
- * Look up an interpreter name, and translate to something that
- * is executable
- */
-static Glib::ustring
-resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
+/** \brief Look up an interpreter name, and translate to something that
+           is executable
+    \param interpNameArg  The name of the interpreter that we're looking
+                             for, should be an entry in interpreterTab
+*/
+Glib::ustring
+Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
 {
 
     Glib::ustring interpName = interpNameArg;
 
-    interpreter_t *interp;
+    interpreter_t const *interp;
     bool foundInterp = false;
     for (interp =  interpreterTab ; interp->identity ; interp++ ){
         if (interpName == interp->identity) {
@@ -101,15 +119,15 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     interpName = interp->defaultval;
 
     // 1.  Check preferences
-    gchar *prefInterp = (gchar *)prefs_get_string_attribute(
-                                "extensions", interp->prefstring);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    Glib::ustring prefInterp = prefs->getString("extensions", interp->prefstring);
 
-    if (prefInterp) {
+    if (!prefInterp.empty()) {
         interpName = prefInterp;
         return interpName;
     }
 
-#ifdef _WIN32
+#ifdef WIN32
 
     // 2.  Windows.  Try looking relative to inkscape.exe
     RegistryTool rt;
@@ -119,7 +137,7 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     if (rt.getExeInfo(fullPath, path, exeName)) {
         Glib::ustring interpPath = path;
         interpPath.append("\\");
-        interpPath.append(interpName);
+        interpPath.append(interpNameArg);
         interpPath.append("\\");
         interpPath.append(interpName);
         interpPath.append(".exe");
@@ -129,7 +147,7 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
                       interpPath .c_str(),
                       (int)finfo.st_size);
             return interpPath;
-        }                       
+        }
     }
 
     // 3. Try searching the path
@@ -149,15 +167,9 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     return interpName;
 }
 
-
-
-
-
-
-/**
-    \return    A script object
-    \brief     This function creates a script object and sets up the
+/** \brief     This function creates a script object and sets up the
                variables.
+    \return    A script object
 
    This function just sets the command to NULL.  It should get built
    officially in the load function.  This allows for less allocation
@@ -168,7 +180,6 @@ Script::Script() :
 {
 }
 
-
 /**
  *   brief     Destructor
  */
@@ -266,7 +277,7 @@ Script::check_existance(const Glib::ustring &command)
     }
 
 
-    Glib::ustring path; 
+    Glib::ustring path;
     gchar *s = (gchar *) g_getenv("PATH");
     if (s)
         path = s;
@@ -275,8 +286,8 @@ Script::check_existance(const Glib::ustring &command)
            The default search path is the current directory */
         path = G_SEARCHPATH_SEPARATOR_S;
 
-    unsigned int pos  = 0;
-    unsigned int pos2 = 0;
+    std::string::size_type pos  = 0;
+    std::string::size_type pos2 = 0;
     while ( pos < path.size() ) {
 
         Glib::ustring localPath;
@@ -289,14 +300,15 @@ Script::check_existance(const Glib::ustring &command)
             localPath = path.substr(pos, pos2-pos);
             pos = pos2+1;
         }
-        
+
         //printf("### %s\n", localPath.c_str());
-        Glib::ustring candidatePath = 
+        Glib::ustring candidatePath =
                       Glib::build_filename(localPath, command);
 
         if (Inkscape::IO::file_test(candidatePath .c_str(),
-                      G_FILE_TEST_EXISTS))
+                      G_FILE_TEST_EXISTS)) {
             return true;
+        }
 
     }
 
@@ -329,31 +341,33 @@ bool
 Script::load(Inkscape::Extension::Extension *module)
 {
     if (module->loaded())
-        return TRUE;
+        return true;
 
     helper_extension = "";
 
     /* This should probably check to find the executable... */
     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
-    Glib::ustring command_text;
     while (child_repr != NULL) {
-        if (!strcmp(child_repr->name(), "script")) {
+        if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
             child_repr = sp_repr_children(child_repr);
             while (child_repr != NULL) {
-                if (!strcmp(child_repr->name(), "command")) {
-                    command_text = solve_reldir(child_repr);
-
+                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
                     const gchar *interpretstr = child_repr->attribute("interpreter");
                     if (interpretstr != NULL) {
                         Glib::ustring interpString =
                             resolveInterpreterExecutable(interpretstr);
-                        interpString .append(" ");
-                        interpString .append(command_text);
-                        command_text = interpString;
+                        //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
+                        command.insert(command.end(), interpretstr);
                     }
+                    Glib::ustring tmp = "\"";
+                    tmp += solve_reldir(child_repr);
+                    tmp += "\"";
+
+                    command.insert(command.end(), tmp);
                 }
-                if (!strcmp(child_repr->name(), "helper_extension"))
+                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
                     helper_extension = sp_repr_children(child_repr)->content();
+                }
                 child_repr = sp_repr_next(child_repr);
             }
 
@@ -362,9 +376,8 @@ Script::load(Inkscape::Extension::Extension *module)
         child_repr = sp_repr_next(child_repr);
     }
 
-    g_return_val_if_fail(command_text.size() > 0, FALSE);
+    //g_return_val_if_fail(command.length() > 0, false);
 
-    command = command_text;
     return true;
 }
 
@@ -378,9 +391,9 @@ Script::load(Inkscape::Extension::Extension *module)
     command if it has been allocated.
 */
 void
-Script::unload(Inkscape::Extension::Extension *module)
+Script::unload(Inkscape::Extension::Extension */*module*/)
 {
-    command          = "";
+    command.clear();
     helper_extension = "";
 }
 
@@ -396,25 +409,27 @@ Script::unload(Inkscape::Extension::Extension *module)
 bool
 Script::check(Inkscape::Extension::Extension *module)
 {
+       int script_count = 0;
     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
     while (child_repr != NULL) {
-        if (!strcmp(child_repr->name(), "script")) {
+        if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
+                       script_count++;
             child_repr = sp_repr_children(child_repr);
             while (child_repr != NULL) {
-                if (!strcmp(child_repr->name(), "check")) {
+                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
                     Glib::ustring command_text = solve_reldir(child_repr);
                     if (command_text.size() > 0) {
                         /* I've got the command */
                         bool existance = check_existance(command_text);
                         if (!existance)
-                            return FALSE;
+                            return false;
                     }
                 }
 
-                if (!strcmp(child_repr->name(), "helper_extension")) {
+                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
                     gchar const *helper = sp_repr_children(child_repr)->content();
                     if (Inkscape::Extension::db.get(helper) == NULL) {
-                        return FALSE;
+                        return false;
                     }
                 }
 
@@ -426,40 +441,70 @@ Script::check(Inkscape::Extension::Extension *module)
         child_repr = sp_repr_next(child_repr);
     }
 
+       if (script_count == 0) {
+               return false;
+       }
+
     return true;
 }
 
+class ScriptDocCache : public ImplementationDocumentCache {
+    friend class Script;
+protected:
+    std::string _filename;
+    int _tempfd;
+public:
+    ScriptDocCache (Inkscape::UI::View::View * view);
+    ~ScriptDocCache ( );
+};
+
+ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
+    ImplementationDocumentCache(view),
+    _filename(""),
+    _tempfd(0)
+{
+    try {
+        _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
+    } catch (...) {
+        /// \todo Popup dialog here
+        return;
+    }
 
+    SPDesktop *desktop = (SPDesktop *) view;
+    sp_namedview_document_from_window(desktop);
 
-/**
-    \return   A dialog for preferences
-    \brief    A stub funtion right now
-    \param    module    Module who's preferences need getting
-    \param    filename  Hey, the file you're getting might be important
+    Inkscape::Extension::save(
+              Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
+              view->doc(), _filename.c_str(), false, false, false);
 
-    This function should really do something, right now it doesn't.
-*/
-Gtk::Widget *
-Script::prefs_input(Inkscape::Extension::Input *module,
-                    const gchar *filename)
+    return;
+}
+
+ScriptDocCache::~ScriptDocCache ( )
 {
-    /*return module->autogui(); */
-    return NULL;
+    close(_tempfd);
+    unlink(_filename.c_str());
 }
 
+ImplementationDocumentCache *
+Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
+    return new ScriptDocCache(view);
+}
 
 
 /**
     \return   A dialog for preferences
     \brief    A stub funtion right now
-    \param    module    Module whose preferences need getting
+    \param    module    Module who's preferences need getting
+    \param    filename  Hey, the file you're getting might be important
 
     This function should really do something, right now it doesn't.
 */
 Gtk::Widget *
-Script::prefs_output(Inkscape::Extension::Output *module)
+Script::prefs_input(Inkscape::Extension::Input *module,
+                    const gchar */*filename*/)
 {
-    return module->autogui(NULL, NULL); 
+    return module->autogui(NULL, NULL);
 }
 
 
@@ -467,30 +512,16 @@ Script::prefs_output(Inkscape::Extension::Output *module)
 /**
     \return   A dialog for preferences
     \brief    A stub funtion right now
-    \param    module    Module who's preferences need getting
+    \param    module    Module whose preferences need getting
 
     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_output(Inkscape::Extension::Output *module)
 {
-
-    SPDocument * current_document = view->doc();
-
-    using Inkscape::Util::GSListConstIterator;
-    GSListConstIterator<SPItem *> selected =
-           sp_desktop_selection((SPDesktop *)view)->itemList();
-    Inkscape::XML::Node * first_select = NULL;
-    if (selected != NULL) 
-           first_select = SP_OBJECT_REPR(*selected);
-
-    return module->autogui(current_document, first_select);
+    return module->autogui(NULL, NULL);
 }
 
-
-
-
 /**
     \return  A new document that has been opened
     \brief   This function uses a filename that is put in, and calls
@@ -516,66 +547,48 @@ SPDocument *
 Script::open(Inkscape::Extension::Input *module,
              const gchar *filenameArg)
 {
-
-    Glib::ustring filename = filenameArg;
-
-    gchar *tmpname;
-
-    // FIXME: process the GError instead of passing NULL
-    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. */
-            perror("Extension::Script:  template for filenames is misconfigured.\n");
-            exit(-1);
-        } else if (errno == EEXIST) {
-            /* Now the  contents of template are undefined. */
-            perror("Extension::Script:  Could not create a unique temporary filename\n");
-            return NULL;
-        } else {
-            perror("Extension::Script:  Unknown error creating temporary filename\n");
-            exit(-1);
-        }
+    std::list<std::string> params;
+    module->paramListString(params);
+
+    std::string tempfilename_out;
+    int tempfd_out = 0;
+    try {
+        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
+    } catch (...) {
+        /// \todo Popup dialog here
+        return NULL;
     }
 
-    Glib::ustring tempfilename_out = tmpname;
-    g_free(tmpname);
+    std::string lfilename = Glib::filename_from_utf8(filenameArg);
 
-    gsize bytesRead = 0;
-    gsize bytesWritten = 0;
-    GError *error = NULL;
-    Glib::ustring local_filename =
-            g_filename_from_utf8( filename.c_str(), -1,
-                                  &bytesRead,  &bytesWritten, &error);
+    file_listener fileout;
+    int data_read = execute(command, params, lfilename, fileout);
+    fileout.toFile(tempfilename_out);
 
-    int data_read = execute(command, local_filename, tempfilename_out);
-
-
-    SPDocument *mydoc = NULL;
+    SPDocument * mydoc = NULL;
     if (data_read > 10) {
         if (helper_extension.size()==0) {
             mydoc = Inkscape::Extension::open(
-                Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
-                                            tempfilename_out.c_str());
+                  Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
+                  tempfilename_out.c_str());
         } else {
             mydoc = Inkscape::Extension::open(
-                Inkscape::Extension::db.get(helper_extension.c_str()),
-                                            tempfilename_out.c_str());
+                  Inkscape::Extension::db.get(helper_extension.c_str()),
+                  tempfilename_out.c_str());
         }
-    }
+    } // data_read
 
-    if (mydoc != NULL)
-        sp_document_set_uri(mydoc, (const gchar *)filename.c_str());
+    if (mydoc != NULL) {
+        sp_document_set_uri(mydoc, filenameArg);
+    }
 
     // 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.c_str());
+    close(tempfd_out);
 
+    unlink(tempfilename_out.c_str());
 
     return mydoc;
-}
+} // open
 
 
 
@@ -608,59 +621,41 @@ Script::save(Inkscape::Extension::Output *module,
              SPDocument *doc,
              const gchar *filenameArg)
 {
-
-    Glib::ustring filename = filenameArg;
-
-    gchar *tmpname;
-    // FIXME: process the GError instead of passing NULL
-    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. */
-            perror("Extension::Script:  template for filenames is misconfigured.\n");
-            exit(-1);
-        } else if (errno == EEXIST) {
-            /* Now the  contents of template are undefined. */
-            perror("Extension::Script:  Could not create a unique temporary filename\n");
-            return;
-        } else {
-            perror("Extension::Script:  Unknown error creating temporary filename\n");
-            exit(-1);
-        }
+    std::list<std::string> params;
+    module->paramListString(params);
+
+    std::string tempfilename_in;
+    int tempfd_in = 0;
+    try {
+        tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
+    } catch (...) {
+        /// \todo Popup dialog here
+        return;
     }
 
-    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);
+                   doc, tempfilename_in.c_str(), false, false, false);
     } else {
         Inkscape::Extension::save(
                    Inkscape::Extension::db.get(helper_extension.c_str()),
-                   doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
+                   doc, tempfilename_in.c_str(), false, false, false);
     }
 
-    gsize bytesRead = 0;
-    gsize bytesWritten = 0;
-    GError *error = NULL;
-    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(local_command, tempfilename_in, local_filename);
+    file_listener fileout;
+    execute(command, params, tempfilename_in, fileout);
 
+    std::string lfilename = Glib::filename_from_utf8(filenameArg);
+    fileout.toFile(lfilename);
 
     // make sure we don't leak file descriptors from g_file_open_tmp
-    close(tempfd);
+    close(tempfd_in);
     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
     unlink(tempfilename_in.c_str());
+
+    return;
 }
 
 
@@ -674,7 +669,7 @@ Script::save(Inkscape::Extension::Output *module,
     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
@@ -694,102 +689,93 @@ Script::save(Inkscape::Extension::Output *module,
     point both should be full, and the second one is loaded.
 */
 void
-Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
+Script::effect(Inkscape::Extension::Effect *module,
+               Inkscape::UI::View::View *doc,
+               ImplementationDocumentCache * docCache)
 {
-    SPDocument * mydoc = NULL;
-
-    gchar *tmpname;
-    // FIXME: process the GError instead of passing NULL
-    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. */
-            perror("Extension::Script:  template for filenames is misconfigured.\n");
-            exit(-1);
-        } else if (errno == EEXIST) {
-            /* Now the  contents of template are undefined. */
-            perror("Extension::Script:  Could not create a unique temporary filename\n");
-            return;
-        } else {
-            perror("Extension::Script:  Unknown error creating temporary filename\n");
-            exit(-1);
-        }
+    if (docCache == NULL) {
+        docCache = newDocCache(module, doc);
     }
-
-    Glib::ustring tempfilename_in = tmpname;
-    g_free(tmpname);
-
-
-    // FIXME: process the GError instead of passing NULL
-    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. */
-            perror("Extension::Script:  template for filenames is misconfigured.\n");
-            exit(-1);
-        } else if (errno == EEXIST) {
-            /* Now the  contents of template are undefined. */
-            perror("Extension::Script:  Could not create a unique temporary filename\n");
-            return;
-        } else {
-            perror("Extension::Script:  Unknown error creating temporary filename\n");
-            exit(-1);
-        }
+    ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
+    if (dc == NULL) {
+        printf("TOO BAD TO LIVE!!!");
+        exit(1);
     }
 
-    Glib::ustring tempfilename_out= tmpname;
-    g_free(tmpname);
+    SPDesktop *desktop = (SPDesktop *)doc;
+    sp_namedview_document_from_window(desktop);
 
-    Inkscape::Extension::save(
-              Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
-              doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
+    gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
 
-    Glib::ustring local_command(command);
+    std::list<std::string> params;
+    module->paramListString(params);
+
+    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 empty;
+        file_listener outfile;
+        execute(command, params, empty, outfile);
+
+        return;
+    }
+
+    std::string tempfilename_out;
+    int tempfd_out = 0;
+    try {
+        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
+    } catch (...) {
+        /// \todo Popup dialog here
+        return;
+    }
 
-    /* fixme: Should be some sort of checking here.  Don't know how to do this with structs instead
-     * of classes. */
-    SPDesktop *desktop = (SPDesktop *) doc;
     if (desktop != NULL) {
         Inkscape::Util::GSListConstIterator<SPItem *> selected =
              sp_desktop_selection(desktop)->itemList();
         while ( selected != NULL ) {
-            local_command += " --id=";
-            local_command += SP_OBJECT_ID(*selected);
+            Glib::ustring selected_id;
+            selected_id += "--id=";
+            selected_id += SP_OBJECT_ID(*selected);
+            params.insert(params.begin(), selected_id);
             ++selected;
         }
     }
 
-    Glib::ustring paramString = *module->paramString();
-    local_command.append(paramString);
-
-
-    // std::cout << local_command << std::endl;
+    file_listener fileout;
+    int data_read = execute(command, params, dc->_filename, fileout);
+    fileout.toFile(tempfilename_out);
 
-    int data_read = execute(local_command, tempfilename_in, tempfilename_out);
+    pump_events();
 
-    if (data_read > 10)
+    SPDocument * mydoc = NULL;
+    if (data_read > 10) {
         mydoc = Inkscape::Extension::open(
               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
               tempfilename_out.c_str());
+    } // data_read
+
+    pump_events();
 
     // 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.c_str());
     unlink(tempfilename_out.c_str());
 
-
     /* Do something with mydoc.... */
     if (mydoc) {
         doc->doc()->emitReconstructionStart();
         copy_doc(doc->doc()->rroot, mydoc->rroot);
         doc->doc()->emitReconstructionFinish();
         mydoc->release();
+        sp_namedview_update_layers_from_document(desktop);
+
+        sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
     }
+    g_free(orig_output_extension);
+
+    return;
 }
 
 
@@ -813,14 +799,21 @@ void
 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
 {
     std::vector<Inkscape::XML::Node *> delete_list;
+    Inkscape::XML::Node * oldroot_namedview = NULL;
+
     for (Inkscape::XML::Node * child = oldroot->firstChild();
             child != NULL;
             child = child->next()) {
-        if (!strcmp("sodipodi:namedview", child->name()))
-            continue;
-        if (!strcmp("svg:defs", child->name()))
-            continue;
-        delete_list.push_back(child);
+        if (!strcmp("sodipodi:namedview", child->name())) {
+            oldroot_namedview = child;
+            for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
+                    oldroot_namedview_child != NULL;
+                    oldroot_namedview_child = oldroot_namedview_child->next()) {
+                delete_list.push_back(oldroot_namedview_child);
+            }
+        } else {
+            delete_list.push_back(child);
+        }
     }
     for (unsigned int i = 0; i < delete_list.size(); i++)
         sp_repr_unparent(delete_list[i]);
@@ -828,57 +821,74 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
     for (Inkscape::XML::Node * child = newroot->firstChild();
             child != NULL;
             child = child->next()) {
-        if (!strcmp("sodipodi:namedview", child->name()))
-            continue;
-        if (!strcmp("svg:defs", child->name()))
-            continue;
-        oldroot->appendChild(child->duplicate());
+        if (!strcmp("sodipodi:namedview", child->name())) {
+            if (oldroot_namedview != NULL) {
+                for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
+                        newroot_namedview_child != NULL;
+                        newroot_namedview_child = newroot_namedview_child->next()) {
+                    oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
+                }
+            }
+        } else {
+            oldroot->appendChild(child->duplicate(oldroot->document()));
+        }
     }
 
+    oldroot->setAttribute("width", newroot->attribute("width"));
+    oldroot->setAttribute("height", newroot->attribute("height"));
+
     /** \todo  Restore correct layer */
     /** \todo  Restore correct selection */
 }
 
+/**  \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 (const Glib::ustring &data,
+                           Gtk::MessageType type,
+                     const Glib::ustring &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();
 
-/* 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(const Glib::ustring &command,
-              const Glib::ustring &errorFile,
-              int mode);
-    bool close();
-
-    /* These return the number of bytes read/written. */
-    size_t read(void *buffer, size_t size);
-    size_t write(void const *buffer, size_t size);
-
-    enum {
-        mode_read  = 1 << 0,
-        mode_write = 1 << 1,
-    };
-
-private:
-#ifdef WIN32
-    /* This is used to translate win32 errors into errno errors.
-       It only recognizes a few win32 errors for the moment though. */
-    static int translate_error(DWORD err);
+    /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
+    Gtk::TextView * textview = new Gtk::TextView();
+    textview->set_editable(false);
+    textview->set_wrap_mode(Gtk::WRAP_WORD);
+    textview->show();
 
-    HANDLE hpipe;
-#else
-    FILE *ppipe;
-#endif
-};
+    textview->get_buffer()->set_text(data.c_str());
+
+    Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
+    scrollwindow->add(*textview);
+    scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+    scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
+    scrollwindow->show();
 
+    vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
 
+    warning.run();
 
+    return;
+}
 
-/**
-    \return   none
-    \brief    This is the core of the extension file as it actually does
+bool
+Script::cancelProcessing (void) {
+    _canceled = true;
+    _main_loop->quit();
+    Glib::spawn_close_pid(_pid);
+
+    return true;
+}
+
+
+/** \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
     \param    filein      Filename coming in
@@ -906,341 +916,143 @@ private:
     are closed, and we return to what we were doing.
 */
 int
-Script::execute (const Glib::ustring &in_command,
+Script::execute (const std::list<std::string> &in_command,
+                 const std::list<std::string> &in_params,
                  const Glib::ustring &filein,
-                 const Glib::ustring &fileout)
+                 file_listener &fileout)
 {
     g_return_val_if_fail(in_command.size() > 0, 0);
-    // printf("Executing: %s\n", in_command);
-
-    gchar *tmpname;
-    gint errorFileNum;
-    errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &tmpname, NULL);
-    if (errorFileNum != 0) {
-        close(errorFileNum);
-    } else {
-        g_free(tmpname);
-    }
-
-    Glib::ustring errorFile = tmpname;
-    g_free(tmpname);
-
-    Glib::ustring localCommand = in_command;
-    localCommand .append(" \"");
-    localCommand .append(filein);
-    localCommand .append("\"");
+    // printf("Executing\n");
 
-    // std::cout << "Command to run: " << command << std::endl;
+    std::vector <std::string> argv;
 
-    pipe_t pipe;
-    bool open_success = pipe.open((char *)localCommand.c_str(),
-                                  errorFile.c_str(),
-                                  pipe_t::mode_read);
-
-    /* Run script */
-    if (!open_success) {
-        /* Error - could not open pipe - check errno */
-        if (errno == EINVAL) {
-            perror("Extension::Script:  Invalid mode argument in popen\n");
-        } else if (errno == ECHILD) {
-            perror("Extension::Script:  Cannot obtain child extension status in popen\n");
-        } else {
-            perror("Extension::Script:  Unknown error for popen\n");
-        }
-        return 0;
-    }
-
-    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 */
-        if (errno == EINVAL) {
-            perror("Extension::Script:  The mode provided to fopen was invalid\n");
-        } else {
-            perror("Extension::Script:  Unknown error attempting to open temporary file\n");
-        }
-        return 0;
-    }
-
-    /* Copy pipe output to a temporary file */
-    int amount_read = 0;
-    char buf[BUFSIZE];
-    int num_read;
-    while ((num_read = pipe.read(buf, BUFSIZE)) != 0) {
-        amount_read += num_read;
-        fwrite(buf, 1, num_read, pfile);
+/*
+    for (std::list<std::string>::const_iterator i = in_command.begin();
+            i != in_command.end(); i++) {
+        argv.push_back(*i);
     }
+*/
+    // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
+    // we tokenize so that spwan does not need to quote over all params
+    for (std::list<std::string>::const_iterator i = in_command.begin();
+            i != in_command.end(); i++) {
+        std::string param_str = *i;
+        do {
+            //g_message("param: %s", param_str.c_str());
+            size_t first_space = param_str.find_first_of(' ');
+            size_t first_quote = param_str.find_first_of('"');
+            //std::cout << "first space " << first_space << std::endl;
+            //std::cout << "first quote " << first_quote << std::endl;
+
+            if((first_quote != std::string::npos) && (first_quote == 0)) {
+                size_t next_quote = param_str.find_first_of('"', first_quote + 1);
+                //std::cout << "next quote " << next_quote << std::endl;
+
+                if(next_quote != std::string::npos) {
+                    //std::cout << "now split " << next_quote << std::endl;
+                    //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
+                    //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
+                    std::string part_str = param_str.substr(1, next_quote - 1);
+                    if(part_str.size() > 0)
+                        argv.push_back(part_str);
+                    param_str = param_str.substr(next_quote + 1);
 
-    /* Close file */
-    if (fclose(pfile) == EOF) {
-        if (errno == EBADF) {
-            perror("Extension::Script:  The filedescriptor for the temporary file is invalid\n");
-            return 0;
-        } else {
-            perror("Extension::Script:  Unknown error closing temporary file\n");
-        }
-    }
+                }
+                else {
+                    if(param_str.size() > 0)
+                        argv.push_back(param_str);
+                    param_str = "";
+                }
 
-    /* Close pipe */
-    if (!pipe.close()) {
-        if (errno == EINVAL) {
-            perror("Extension::Script:  Invalid mode set for pclose\n");
-        } else if (errno == ECHILD) {
-            perror("Extension::Script:  Could not obtain child status for pclose\n");
-        } else {
-            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.  "
-                      "Inkscape will continue working, but the action you requested has been cancelled."));
-            } else {
-                perror("Extension::Script:  Unknown error for pclose\n");
             }
-        }
-        /* Could be a lie, but if there is an error, we don't want
-         * to count on what was read being good */
-        amount_read = 0;
-    } else {
-        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."));
-        }
+            else if(first_space != std::string::npos) {
+                //std::cout << "now split " << first_space << std::endl;
+                //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
+                //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
+                std::string part_str = param_str.substr(0, first_space);
+                if(part_str.size() > 0)
+                    argv.push_back(part_str);
+                param_str = param_str.substr(first_space + 1);
+            }
+            else {
+                if(param_str.size() > 0)
+                    argv.push_back(param_str);
+                param_str = "";
+            }
+        } while(param_str.size() > 0);
     }
 
-    if (errorFile.size()>0) {
-        unlink(errorFile.c_str());
+    for (std::list<std::string>::const_iterator i = in_params.begin();
+            i != in_params.end(); i++) {
+       //g_message("Script parameter: %s",(*i)g.c_str());
+        argv.push_back(*i);        
     }
 
-    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 (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.c_str(), std::ios_base::in | std::ios_base::binary);
-    if (!stderrf.is_open()) return;
-
-    stderrf.seekg(0, std::ios::end);
-    int length = stderrf.tellg();
-    if (0 == length) return;
-    stderrf.seekg(0, std::ios::beg);
-
-    Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
-    warning.set_resizable(true);
-
-    Gtk::VBox * vbox = warning.get_vbox();
-
-    /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
-    Gtk::TextView * textview = new Gtk::TextView();
-    textview->set_editable(false);
-    textview->set_wrap_mode(Gtk::WRAP_WORD);
-    textview->show();
-
-    char * buffer = new char [length];
-    stderrf.read(buffer, length);
-    textview->get_buffer()->set_text(buffer, buffer + length);
-    delete buffer;
-    stderrf.close();
-
-    Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
-    scrollwindow->add(*textview);
-    scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
-    scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
-    scrollwindow->show();
-
-    vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
-
-    warning.run();
-
-    return;
-}
-
-
-
-
-#ifdef WIN32
-
-
-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
-    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;
+    if (!(filein.empty())) {
+               argv.push_back(filein);
     }
 
-    //############### 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, (CHAR *)command.c_str(),
-                        NULL, NULL, TRUE,
-                        0, NULL, NULL,
-                        &startupinfo, &procinfo) ) {
-        errno = translate_error(GetLastError());
-        return false;
+    int stdout_pipe, stderr_pipe;
+
+    try {
+        Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
+                                     argv,  // arg v
+                                     Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
+                                     sigc::slot<void>(),
+                                     &_pid,          // Pid
+                                     NULL,           // STDIN
+                                     &stdout_pipe,   // STDOUT
+                                     &stderr_pipe);  // STDERR
+    } catch (Glib::SpawnError e) {
+        printf("Can't Spawn!!! spawn returns: %d\n", e.code());
+        return 0;
     }
-    CloseHandle(procinfo.hThread);
-    CloseHandle(procinfo.hProcess);
 
-    // Close our copy of the write handle
-    CloseHandle(hInheritableStdErr);
-    CloseHandle(pipe_write);
-
-    return true;
-}
+    _main_loop = Glib::MainLoop::create(false);
 
+    file_listener fileerr;
+    fileout.init(stdout_pipe, _main_loop);
+    fileerr.init(stderr_pipe, _main_loop);
 
+    _canceled = false;
+    _main_loop->run();
 
-bool pipe_t::close() {
-    BOOL retval = CloseHandle(hpipe);
-    if ( !retval )
-        errno = translate_error(GetLastError());
-    return retval != FALSE;
-}
-
-size_t pipe_t::read(void *buffer, size_t size) {
-    DWORD bytes_read = 0;
-    ReadFile(hpipe, buffer, size, &bytes_read, 0);
-    return bytes_read;
-}
+    // Ensure all the data is out of the pipe
+    while (!fileout.isDead())
+        fileout.read(Glib::IO_IN);
+    while (!fileerr.isDead())
+        fileerr.read(Glib::IO_IN);
 
-size_t pipe_t::write(void const *buffer, size_t size) {
-    DWORD bytes_written = 0;
-    WriteFile(hpipe, buffer, size, &bytes_written, 0);
-    return bytes_written;
-}
-
-int pipe_t::translate_error(DWORD err) {
-    switch (err) {
-        case ERROR_FILE_NOT_FOUND:
-            return ENOENT;
-        case ERROR_INVALID_HANDLE:
-        case ERROR_INVALID_PARAMETER:
-            return EINVAL;
-        default:
-            return 0;
+    if (_canceled) {
+        // std::cout << "Script Canceled" << std::endl;
+        return 0;
     }
-}
 
-
-#else // not Win32
-
-
-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);
+    Glib::ustring stderr_data = fileerr.string();
+    if (stderr_data.length() != 0 &&
+        Inkscape::NSApplication::Application::getUseGui()
+       ) {
+        checkStderr(stderr_data, 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."));
     }
 
-    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);
-}
-
+    Glib::ustring stdout_data = fileout.string();
+    if (stdout_data.length() == 0) {
+        return 0;
+    }
 
-size_t pipe_t::write(void const *buffer, size_t size) {
-    return fwrite(buffer, 1, size, ppipe);
+    // std::cout << "Finishing Execution." << std::endl;
+    return stdout_data.length();
 }
 
 
 
 
-#endif // (Non-)Win32
-
-
-
-
 }  // namespace Implementation
 }  // namespace Extension
 }  // namespace Inkscape
 
-
-
-
 /*
   Local Variables:
   mode:c++