Code

Compilation fixes for Windows 64-bit by Fridrich
[inkscape.git] / src / extension / implementation / script.cpp
index 09dc9eb30dd4f99a00294da59cef9fcdb69fb361..7a558b6fcbe29fd57a91929e06d305c6dc3526c8 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
@@ -45,7 +28,7 @@ FIXME:
 #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"
@@ -53,6 +36,9 @@ FIXME:
 #include "extension/db.h"
 #include "script.h"
 #include "dialogs/dialog-events.h"
+#include "application/application.h"
+#include "xml/node.h"
+#include "xml/attribute-record.h"
 
 #include "util/glib-list-iterators.h"
 
@@ -76,19 +62,18 @@ namespace Inkscape {
 namespace Extension {
 namespace Implementation {
 
-void pump_events (void) {
+/** \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 const *identity;
-        gchar const *prefstring;
-        gchar const *defaultval;
-};
-
 
 /** \brief  A table of what interpreters to call for a given language
 
@@ -96,9 +81,13 @@ struct interpreter_t {
     given script.  It also tracks the preference to use to overwrite
     the given interpreter to a custom one per user.
 */
-static interpreter_t const interpreterTab[] = {
+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    }
@@ -106,12 +95,13 @@ static interpreter_t const 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;
@@ -131,14 +121,15 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     interpName = interp->defaultval;
 
     // 1.  Check preferences
-    gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(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;
@@ -148,7 +139,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");
@@ -165,7 +156,7 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     char szExePath[MAX_PATH];
     char szCurrentDir[MAX_PATH];
     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
-    unsigned int ret = (unsigned int)FindExecutable(
+    HINSTANCE ret = (unsigned int)FindExecutable(
                   interpName.c_str(), szCurrentDir, szExePath);
     if (ret > 32) {
         interpName = szExePath;
@@ -178,8 +169,6 @@ resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
     return interpName;
 }
 
-
-
 /** \brief     This function creates a script object and sets up the
                variables.
     \return    A script object
@@ -193,7 +182,6 @@ Script::Script() :
 {
 }
 
-
 /**
  *   brief     Destructor
  */
@@ -355,27 +343,31 @@ 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());
     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")) {
+                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
                     const gchar *interpretstr = child_repr->attribute("interpreter");
                     if (interpretstr != NULL) {
                         Glib::ustring interpString =
                             resolveInterpreterExecutable(interpretstr);
+                        //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(), solve_reldir(child_repr));
+                    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);
@@ -386,7 +378,7 @@ Script::load(Inkscape::Extension::Extension *module)
         child_repr = sp_repr_next(child_repr);
     }
 
-    //g_return_val_if_fail(command.length() > 0, FALSE);
+    //g_return_val_if_fail(command.length() > 0, false);
 
     return true;
 }
@@ -419,25 +411,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;
                     }
                 }
 
@@ -449,26 +443,30 @@ 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;
+    friend class Script;
 protected:
-       std::string _filename;
+    std::string _filename;
     int _tempfd;
 public:
-       ScriptDocCache (Inkscape::UI::View::View * view);
-       ~ScriptDocCache ( );
+    ScriptDocCache (Inkscape::UI::View::View * view);
+    ~ScriptDocCache ( );
 };
 
 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
-       ImplementationDocumentCache(view),
-       _filename(""),
-       _tempfd(0)
+    ImplementationDocumentCache(view),
+    _filename(""),
+    _tempfd(0)
 {
     try {
-        _tempfd = Glib::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
+        _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
     } catch (...) {
         /// \todo Popup dialog here
         return;
@@ -479,9 +477,9 @@ ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
 
     Inkscape::Extension::save(
               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
-              view->doc(), _filename.c_str(), FALSE, FALSE, FALSE);
+              view->doc(), _filename.c_str(), false, false, false, Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
 
-       return;
+    return;
 }
 
 ScriptDocCache::~ScriptDocCache ( )
@@ -526,38 +524,6 @@ Script::prefs_output(Inkscape::Extension::Output *module)
     return module->autogui(NULL, NULL);
 }
 
-
-
-/**
-    \return   A dialog for preferences
-    \brief    A stub funtion right now
-    \param    module    Module who's 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,
-                      sigc::signal<void> * changeSignal,
-                      ImplementationDocumentCache * /*docCache*/ )
-{
-    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) {
-        const SPItem * item = *selected;
-        first_select = SP_OBJECT_REPR(item);
-    }
-
-    return module->autogui(current_document, first_select, changeSignal);
-}
-
-
-
-
 /**
     \return  A new document that has been opened
     \brief   This function uses a filename that is put in, and calls
@@ -589,7 +555,7 @@ Script::open(Inkscape::Extension::Input *module,
     std::string tempfilename_out;
     int tempfd_out = 0;
     try {
-        tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
+        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
     } catch (...) {
         /// \todo Popup dialog here
         return NULL;
@@ -615,7 +581,9 @@ Script::open(Inkscape::Extension::Input *module,
     } // data_read
 
     if (mydoc != NULL) {
-        sp_document_set_uri(mydoc, filenameArg);
+        g_free(mydoc->base);
+        mydoc->base = NULL;
+        sp_document_change_uri_and_hrefs(mydoc, filenameArg);
     }
 
     // make sure we don't leak file descriptors from g_file_open_tmp
@@ -636,6 +604,7 @@ Script::open(Inkscape::Extension::Input *module,
     \param    module    Extention to be used
     \param    doc       Document to be saved
     \param    filename  The name to save the final file as
+    \return   false in case of any failure writing the file, otherwise true
 
     Well, at some point people need to save - it is really what makes
     the entire application useful.  And, it is possible that someone
@@ -663,20 +632,22 @@ Script::save(Inkscape::Extension::Output *module,
     std::string tempfilename_in;
     int tempfd_in = 0;
     try {
-        tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
+        tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
     } catch (...) {
         /// \todo Popup dialog here
-        return;
+        throw Inkscape::Extension::Output::save_failed();
     }
 
     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,
+                   Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
     } 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,
+                   Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
     }
 
 
@@ -684,13 +655,17 @@ Script::save(Inkscape::Extension::Output *module,
     execute(command, params, tempfilename_in, fileout);
 
     std::string lfilename = Glib::filename_from_utf8(filenameArg);
-    fileout.toFile(lfilename);
+    bool success = fileout.toFile(lfilename);
 
     // make sure we don't leak file descriptors from g_file_open_tmp
     close(tempfd_in);
     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
     unlink(tempfilename_in.c_str());
 
+    if(success == false) {
+        throw Inkscape::Extension::Output::save_failed();
+    }
+
     return;
 }
 
@@ -727,22 +702,20 @@ Script::save(Inkscape::Extension::Output *module,
 void
 Script::effect(Inkscape::Extension::Effect *module,
                Inkscape::UI::View::View *doc,
-                          ImplementationDocumentCache * docCache)
+               ImplementationDocumentCache * docCache)
 {
-       if (docCache == NULL) {
-               docCache = newDocCache(module, doc);
-       }
-       ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
-       if (dc == NULL) {
-               printf("TOO BAD TO LIVE!!!");
-               exit(1);
-       }
+    if (docCache == NULL) {
+        docCache = newDocCache(module, doc);
+    }
+    ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
+    if (dc == NULL) {
+        printf("TOO BAD TO LIVE!!!");
+        exit(1);
+    }
 
     SPDesktop *desktop = (SPDesktop *)doc;
     sp_namedview_document_from_window(desktop);
 
-       gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
-
     std::list<std::string> params;
     module->paramListString(params);
 
@@ -760,7 +733,7 @@ Script::effect(Inkscape::Extension::Effect *module,
     std::string tempfilename_out;
     int tempfd_out = 0;
     try {
-        tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
+        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
     } catch (...) {
         /// \todo Popup dialog here
         return;
@@ -806,10 +779,7 @@ Script::effect(Inkscape::Extension::Effect *module,
         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;
 }
@@ -862,16 +832,34 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
                 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(child->document()));
+                    oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
                 }
             }
         } else {
-            oldroot->appendChild(child->duplicate(newroot->document()));
+            oldroot->appendChild(child->duplicate(oldroot->document()));
         }
     }
 
-    oldroot->setAttribute("width", newroot->attribute("width"));
-    oldroot->setAttribute("height", newroot->attribute("height"));
+    {
+        using Inkscape::Util::List;
+        using Inkscape::XML::AttributeRecord;
+        std::vector<gchar const *> attribs;
+
+        // Make a list of all attributes of the old root node.
+        for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
+            attribs.push_back(g_quark_to_string(iter->key));
+        }
+
+        // Delete the attributes of the old root nodes.
+        for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++)
+            oldroot->setAttribute(*it, NULL);
+
+        // Set the new attributes.
+        for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
+            gchar const *name = g_quark_to_string(iter->key);
+            oldroot->setAttribute(name, newroot->attribute(name));
+        }
+    }
 
     /** \todo  Restore correct layer */
     /** \todo  Restore correct selection */
@@ -973,28 +961,33 @@ Script::execute (const std::list<std::string> &in_command,
     for (std::list<std::string>::const_iterator i = in_command.begin();
             i != in_command.end(); i++) {
         std::string param_str = *i;
-        //std::cout << "params " << param_str << std::endl;
         do {
-            //std::cout << "param " << param_str << std::endl;
+            //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);
+                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) << 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);
+                    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);
 
                 }
+                else {
+                    if(param_str.size() > 0)
+                        argv.push_back(param_str);
+                    param_str = "";
+                }
+
             }
             else if(first_space != std::string::npos) {
                 //std::cout << "now split " << first_space << std::endl;
@@ -1013,36 +1006,20 @@ Script::execute (const std::list<std::string> &in_command,
         } while(param_str.size() > 0);
     }
 
-    if (!(filein.empty())) {
-//        if(argv.size() == 1) {
-            argv.push_back(filein);
-/*        }
-        else {
-            std::string parameter_str = argv.back();
-            argv.pop_back();
-            //TODO: quote
-            parameter_str += " " + filein;
-            argv.push_back(parameter_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);
     }
 
-/*
-    for (std::vector<std::string>::const_iterator i = argv.begin();
-            i != argv.end(); i++) {
-        std::cout << "_" << *i << "_" << std::endl;
+    if (!(filein.empty())) {
+               argv.push_back(filein);
     }
-*/
 
     int stdout_pipe, stderr_pipe;
 
     try {
-        Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
+        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>(),
@@ -1076,7 +1053,9 @@ Script::execute (const std::list<std::string> &in_command,
     }
 
     Glib::ustring stderr_data = fileerr.string();
-    if (stderr_data.length() != 0) {
+    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."));