Code

allow effects to modify namedview and document dimensions
[inkscape.git] / src / extension / implementation / script.cpp
1 /** \file
2  * Code for handling extensions (i.e.\ scripts).
3  */
4 /*
5  * Authors:
6  *   Bryce Harrington <bryce@osdl.org>
7  *   Ted Gould <ted@gould.cx>
8  *
9  * Copyright (C) 2002-2005,2007 Authors
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 /*
15 TODO:
16 FIXME:
17   After Inkscape makes a formal requirement for a GTK version above 2.11.4, please
18   replace all the instances of ink_ext_XXXXXX in this file that represent
19   svg files with ink_ext_XXXXXX.svg . Doing so will prevent errors in extensions
20   that call inkscape to manipulate the file.
21   
22   "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
23   
24   references:
25   http://www.gtk.org/api/2.6/glib/glib-File-Utilities.html#g-mkstemp
26   http://ftp.gnome.org/pub/gnome/sources/glib/2.11/glib-2.11.4.changes
27   http://developer.gnome.org/doc/API/2.0/glib/glib-File-Utilities.html#g-mkstemp
28   
29   --Aaron Spike
30 */
31 #define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
37 #include <unistd.h>
39 #include <errno.h>
40 #include <gtkmm.h>
42 #include "ui/view/view.h"
43 #include "desktop-handles.h"
44 #include "selection.h"
45 #include "sp-namedview.h"
46 #include "io/sys.h"
47 #include "prefs-utils.h"
48 #include "../system.h"
49 #include "extension/effect.h"
50 #include "extension/output.h"
51 #include "extension/input.h"
52 #include "extension/db.h"
53 #include "script.h"
54 #include "dialogs/dialog-events.h"
56 #include "util/glib-list-iterators.h"
60 #ifdef WIN32
61 #include <windows.h>
62 #include <sys/stat.h>
63 #include "registrytool.h"
64 #endif
68 /** This is the command buffer that gets allocated from the stack */
69 #define BUFSIZE (255)
73 /* Namespaces */
74 namespace Inkscape {
75 namespace Extension {
76 namespace Implementation {
78 void pump_events (void) {
79     while( Gtk::Main::events_pending() )
80         Gtk::Main::iteration();
81     return;
82 }
84 //Interpreter lookup table
85 struct interpreter_t {
86         gchar const *identity;
87         gchar const *prefstring;
88         gchar const *defaultval;
89 };
92 /** \brief  A table of what interpreters to call for a given language
94     This table is used to keep track of all the programs to execute a
95     given script.  It also tracks the preference to use to overwrite
96     the given interpreter to a custom one per user.
97 */
98 static interpreter_t const interpreterTab[] = {
99         {"perl",   "perl-interpreter",   "perl"   },
100         {"python", "python-interpreter", "python" },
101         {"ruby",   "ruby-interpreter",   "ruby"   },
102         {"shell",  "shell-interpreter",  "sh"     },
103         { NULL,    NULL,                  NULL    }
104 };
108 /**
109  * Look up an interpreter name, and translate to something that
110  * is executable
111  */
112 static Glib::ustring
113 resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
116     Glib::ustring interpName = interpNameArg;
118     interpreter_t const *interp;
119     bool foundInterp = false;
120     for (interp =  interpreterTab ; interp->identity ; interp++ ){
121         if (interpName == interp->identity) {
122             foundInterp = true;
123             break;
124         }
125     }
127     // Do we have a supported interpreter type?
128     if (!foundInterp)
129         return "";
130     interpName = interp->defaultval;
132     // 1.  Check preferences
133     gchar const *prefInterp = prefs_get_string_attribute("extensions", interp->prefstring);
135     if (prefInterp) {
136         interpName = prefInterp;
137         return interpName;
138     }
140 #ifdef _WIN32
142     // 2.  Windows.  Try looking relative to inkscape.exe
143     RegistryTool rt;
144     Glib::ustring fullPath;
145     Glib::ustring path;
146     Glib::ustring exeName;
147     if (rt.getExeInfo(fullPath, path, exeName)) {
148         Glib::ustring interpPath = path;
149         interpPath.append("\\");
150         interpPath.append(interpName);
151         interpPath.append("\\");
152         interpPath.append(interpName);
153         interpPath.append(".exe");
154         struct stat finfo;
155         if (stat(interpPath .c_str(), &finfo) ==0) {
156             g_message("Found local interpreter, '%s',  Size: %d",
157                       interpPath .c_str(),
158                       (int)finfo.st_size);
159             return interpPath;
160         }                       
161     }
163     // 3. Try searching the path
164     char szExePath[MAX_PATH];
165     char szCurrentDir[MAX_PATH];
166     GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
167     unsigned int ret = (unsigned int)FindExecutable(
168                   interpName.c_str(), szCurrentDir, szExePath);
169     if (ret > 32) {
170         interpName = szExePath;
171         return interpName;
172     }
174 #endif // win32
177     return interpName;
182 /** \brief     This function creates a script object and sets up the
183                variables.
184     \return    A script object
186    This function just sets the command to NULL.  It should get built
187    officially in the load function.  This allows for less allocation
188    of memory in the unloaded state.
189 */
190 Script::Script() :
191     Implementation()
196 /**
197  *   brief     Destructor
198  */
199 Script::~Script()
205 /**
206     \return    A string with the complete string with the relative directory expanded
207     \brief     This function takes in a Repr that contains a reldir entry
208                and returns that data with the relative directory expanded.
209                Mostly it is here so that relative directories all get used
210                the same way.
211     \param     reprin   The Inkscape::XML::Node with the reldir in it.
213     Basically this function looks at an attribute of the Repr, and makes
214     a decision based on that.  Currently, it is only working with the
215     'extensions' relative directory, but there will be more of them.
216     One thing to notice is that this function always returns an allocated
217     string.  This means that the caller of this function can always
218     free what they are given (and should do it too!).
219 */
220 Glib::ustring
221 Script::solve_reldir(Inkscape::XML::Node *reprin) {
223     gchar const *s = reprin->attribute("reldir");
225     if (!s) {
226         Glib::ustring str = sp_repr_children(reprin)->content();
227         return str;
228     }
230     Glib::ustring reldir = s;
232     if (reldir == "extensions") {
234         for (unsigned int i=0;
235             i < Inkscape::Extension::Extension::search_path.size();
236             i++) {
238             gchar * fname = g_build_filename(
239                Inkscape::Extension::Extension::search_path[i],
240                sp_repr_children(reprin)->content(),
241                NULL);
242             Glib::ustring filename = fname;
243             g_free(fname);
245             if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
246                 return filename;
248         }
249     } else {
250         Glib::ustring str = sp_repr_children(reprin)->content();
251         return str;
252     }
254     return "";
259 /**
260     \return   Whether the command given exists, including in the path
261     \brief    This function is used to find out if something exists for
262               the check command.  It can look in the path if required.
263     \param    command   The command or file that should be looked for
265     The first thing that this function does is check to see if the
266     incoming file name has a directory delimiter in it.  This would
267     mean that it wants to control the directories, and should be
268     used directly.
270     If not, the path is used.  Each entry in the path is stepped through,
271     attached to the string, and then tested.  If the file is found
272     then a TRUE is returned.  If we get all the way through the path
273     then a FALSE is returned, the command could not be found.
274 */
275 bool
276 Script::check_existance(const Glib::ustring &command)
279     // Check the simple case first
280     if (command.size() == 0) {
281         return false;
282     }
284     //Don't search when it contains a slash. */
285     if (command.find(G_DIR_SEPARATOR) != command.npos) {
286         if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
287             return true;
288         else
289             return false;
290     }
293     Glib::ustring path; 
294     gchar *s = (gchar *) g_getenv("PATH");
295     if (s)
296         path = s;
297     else
298        /* There is no `PATH' in the environment.
299            The default search path is the current directory */
300         path = G_SEARCHPATH_SEPARATOR_S;
302     std::string::size_type pos  = 0;
303     std::string::size_type pos2 = 0;
304     while ( pos < path.size() ) {
306         Glib::ustring localPath;
308         pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
309         if (pos2 == path.npos) {
310             localPath = path.substr(pos);
311             pos = path.size();
312         } else {
313             localPath = path.substr(pos, pos2-pos);
314             pos = pos2+1;
315         }
316         
317         //printf("### %s\n", localPath.c_str());
318         Glib::ustring candidatePath = 
319                       Glib::build_filename(localPath, command);
321         if (Inkscape::IO::file_test(candidatePath .c_str(),
322                       G_FILE_TEST_EXISTS)) {
323             return true;
324         }
326     }
328     return false;
335 /**
336     \return   none
337     \brief    This function 'loads' an extention, basically it determines
338               the full command for the extention and stores that.
339     \param    module  The extention to be loaded.
341     The most difficult part about this function is finding the actual
342     command through all of the Reprs.  Basically it is hidden down a
343     couple of layers, and so the code has to move down too.  When
344     the command is actually found, it has its relative directory
345     solved.
347     At that point all of the loops are exited, and there is an
348     if statement to make sure they didn't exit because of not finding
349     the command.  If that's the case, the extention doesn't get loaded
350     and should error out at a higher level.
351 */
353 bool
354 Script::load(Inkscape::Extension::Extension *module)
356     if (module->loaded())
357         return TRUE;
359     helper_extension = "";
361     /* This should probably check to find the executable... */
362     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
363     while (child_repr != NULL) {
364         if (!strcmp(child_repr->name(), "script")) {
365             child_repr = sp_repr_children(child_repr);
366             while (child_repr != NULL) {
367                 if (!strcmp(child_repr->name(), "command")) {
368                     const gchar *interpretstr = child_repr->attribute("interpreter");
369                     if (interpretstr != NULL) {
370                         Glib::ustring interpString =
371                             resolveInterpreterExecutable(interpretstr);
372                         command.insert(command.end(), interpretstr);
373                     }
375                     command.insert(command.end(), solve_reldir(child_repr));
376                 }
377                 if (!strcmp(child_repr->name(), "helper_extension")) {
378                     helper_extension = sp_repr_children(child_repr)->content();
379                 }
380                 child_repr = sp_repr_next(child_repr);
381             }
383             break;
384         }
385         child_repr = sp_repr_next(child_repr);
386     }
388     //g_return_val_if_fail(command.length() > 0, FALSE);
390     return true;
394 /**
395     \return   None.
396     \brief    Unload this puppy!
397     \param    module  Extension to be unloaded.
399     This function just sets the module to unloaded.  It free's the
400     command if it has been allocated.
401 */
402 void
403 Script::unload(Inkscape::Extension::Extension *module)
405     command.clear();
406     helper_extension = "";
412 /**
413     \return   Whether the check passed or not
414     \brief    Check every dependency that was given to make sure we should keep this extension
415     \param    module  The Extension in question
417 */
418 bool
419 Script::check(Inkscape::Extension::Extension *module)
421     Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
422     while (child_repr != NULL) {
423         if (!strcmp(child_repr->name(), "script")) {
424             child_repr = sp_repr_children(child_repr);
425             while (child_repr != NULL) {
426                 if (!strcmp(child_repr->name(), "check")) {
427                     Glib::ustring command_text = solve_reldir(child_repr);
428                     if (command_text.size() > 0) {
429                         /* I've got the command */
430                         bool existance = check_existance(command_text);
431                         if (!existance)
432                             return FALSE;
433                     }
434                 }
436                 if (!strcmp(child_repr->name(), "helper_extension")) {
437                     gchar const *helper = sp_repr_children(child_repr)->content();
438                     if (Inkscape::Extension::db.get(helper) == NULL) {
439                         return FALSE;
440                     }
441                 }
443                 child_repr = sp_repr_next(child_repr);
444             }
446             break;
447         }
448         child_repr = sp_repr_next(child_repr);
449     }
451     return true;
456 /**
457     \return   A dialog for preferences
458     \brief    A stub funtion right now
459     \param    module    Module who's preferences need getting
460     \param    filename  Hey, the file you're getting might be important
462     This function should really do something, right now it doesn't.
463 */
464 Gtk::Widget *
465 Script::prefs_input(Inkscape::Extension::Input *module,
466                     const gchar *filename)
468     return module->autogui(NULL, NULL);
473 /**
474     \return   A dialog for preferences
475     \brief    A stub funtion right now
476     \param    module    Module whose preferences need getting
478     This function should really do something, right now it doesn't.
479 */
480 Gtk::Widget *
481 Script::prefs_output(Inkscape::Extension::Output *module)
483     return module->autogui(NULL, NULL); 
488 /**
489     \return   A dialog for preferences
490     \brief    A stub funtion right now
491     \param    module    Module who's preferences need getting
493     This function should really do something, right now it doesn't.
494 */
495 Gtk::Widget *
496 Script::prefs_effect(Inkscape::Extension::Effect *module,
497                      Inkscape::UI::View::View *view,
498                      sigc::signal<void> * changeSignal)
500     SPDocument * current_document = view->doc();
502     using Inkscape::Util::GSListConstIterator;
503     GSListConstIterator<SPItem *> selected =
504            sp_desktop_selection((SPDesktop *)view)->itemList();
505     Inkscape::XML::Node * first_select = NULL;
506     if (selected != NULL) {
507         const SPItem * item = *selected;
508         first_select = SP_OBJECT_REPR(item);
509     }
511     return module->autogui(current_document, first_select, changeSignal);
517 /**
518     \return  A new document that has been opened
519     \brief   This function uses a filename that is put in, and calls
520              the extension's command to create an SVG file which is
521              returned.
522     \param   module   Extension to use.
523     \param   filename File to open.
525     First things first, this function needs a temporary file name.  To
526     create on of those the function g_file_open_tmp is used with
527     the header of ink_ext_.
529     The extension is then executed using the 'execute' function
530     with the filname coming in, and the temporary filename.  After
531     That executing, the SVG should be in the temporary file.
533     Finally, the temporary file is opened using the SVG input module and
534     a document is returned.  That document has its filename set to
535     the incoming filename (so that it's not the temporary filename).
536     That document is then returned from this function.
537 */
538 SPDocument *
539 Script::open(Inkscape::Extension::Input *module,
540              const gchar *filenameArg)
542     std::list<std::string> params;
543     module->paramListString(params);
545     std::string tempfilename_out;
546     int tempfd_out = 0;
547     try {
548         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
549     } catch (...) {
550         /// \todo Popup dialog here
551         return NULL;
552     }
554     std::string lfilename = Glib::filename_from_utf8(filenameArg);
556     file_listener fileout;
557     int data_read = execute(command, params, lfilename, fileout);
558     fileout.toFile(tempfilename_out);
560     SPDocument * mydoc = NULL;
561     if (data_read > 10) {
562         if (helper_extension.size()==0) {
563             mydoc = Inkscape::Extension::open(
564                   Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
565                   tempfilename_out.c_str());
566         } else {
567             mydoc = Inkscape::Extension::open(
568                   Inkscape::Extension::db.get(helper_extension.c_str()),
569                   tempfilename_out.c_str());
570         }
571     } // data_read
573     if (mydoc != NULL) {
574         sp_document_set_uri(mydoc, filenameArg);
575     }
577     // make sure we don't leak file descriptors from g_file_open_tmp
578     close(tempfd_out);
580     unlink(tempfilename_out.c_str());
582     return mydoc;
583 } // open
587 /**
588     \return   none
589     \brief    This function uses an extention to save a document.  It first
590               creates an SVG file of the document, and then runs it through
591               the script.
592     \param    module    Extention to be used
593     \param    doc       Document to be saved
594     \param    filename  The name to save the final file as
596     Well, at some point people need to save - it is really what makes
597     the entire application useful.  And, it is possible that someone
598     would want to use an extetion for this, so we need a function to
599     do that eh?
601     First things first, the document is saved to a temporary file that
602     is an SVG file.  To get the temporary filename g_file_open_tmp is used with
603     ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
604     end of the function.
606     After we have the SVG file, then extention_execute is called with
607     the temporary file name and the final output filename.  This should
608     put the output of the script into the final output file.  We then
609     delete the temporary file.
610 */
611 void
612 Script::save(Inkscape::Extension::Output *module,
613              SPDocument *doc,
614              const gchar *filenameArg)
616     std::list<std::string> params;
617     module->paramListString(params);
619     std::string tempfilename_in;
620     int tempfd_in = 0;
621     try {
622         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
623     } catch (...) {
624         /// \todo Popup dialog here
625         return;
626     }
628     if (helper_extension.size() == 0) {
629         Inkscape::Extension::save(
630                    Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
631                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
632     } else {
633         Inkscape::Extension::save(
634                    Inkscape::Extension::db.get(helper_extension.c_str()),
635                    doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
636     }
639     file_listener fileout;
640     execute(command, params, tempfilename_in, fileout);
642     std::string lfilename = Glib::filename_from_utf8(filenameArg);
643     fileout.toFile(lfilename);
645     // make sure we don't leak file descriptors from g_file_open_tmp
646     close(tempfd_in);
647     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
648     unlink(tempfilename_in.c_str());
650     return;
655 /**
656     \return    none
657     \brief     This function uses an extention as a effect on a document.
658     \param     module   Extention to effect with.
659     \param     doc      Document to run through the effect.
661     This function is a little bit trickier than the previous two.  It
662     needs two temporary files to get it's work done.  Both of these
663     files have random names created for them using the g_file_open_temp function
664     with the ink_ext_ prefix in the temporary directory.  Like the other
665     functions, the temporary files are deleted at the end.
667     To save/load the two temporary documents (both are SVG) the internal
668     modules for SVG load and save are used.  They are both used through
669     the module system function by passing their keys into the functions.
671     The command itself is built a little bit differently than in other
672     functions because the effect support selections.  So on the command
673     line a list of all the ids that are selected is included.  Currently,
674     this only works for a single selected object, but there will be more.
675     The command string is filled with the data, and then after the execution
676     it is freed.
678     The execute function is used at the core of this function
679     to execute the Script on the two SVG documents (actually only one
680     exists at the time, the other is created by that script).  At that
681     point both should be full, and the second one is loaded.
682 */
683 void
684 Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
686     std::list<std::string> params;
687     module->paramListString(params);
689     if (module->no_doc) { 
690         // this is a no-doc extension, e.g. a Help menu command; 
691         // just run the command without any files, ignoring errors
693         Glib::ustring empty;
694         file_listener outfile;
695         execute(command, params, empty, outfile);
697         return;
698     }
700     std::string tempfilename_in;
701     int tempfd_in = 0;
702     try {
703         tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
704     } catch (...) {
705         /// \todo Popup dialog here
706         return;
707     }
709     std::string tempfilename_out;
710     int tempfd_out = 0;
711     try {
712         tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
713     } catch (...) {
714         /// \todo Popup dialog here
715         return;
716     }
718     SPDesktop *desktop = (SPDesktop *) doc;
719     sp_namedview_document_from_window(desktop);
721     Inkscape::Extension::save(
722               Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
723               doc->doc(), tempfilename_in.c_str(), FALSE, FALSE, FALSE);
725     pump_events();
727     if (desktop != NULL) {
728         Inkscape::Util::GSListConstIterator<SPItem *> selected =
729              sp_desktop_selection(desktop)->itemList();
730         while ( selected != NULL ) {
731             Glib::ustring selected_id;
732             selected_id += "--id=";
733             selected_id += SP_OBJECT_ID(*selected);
734             params.insert(params.begin(), selected_id);
735             ++selected;
736         }
737     }
739     file_listener fileout;
740     int data_read = execute(command, params, tempfilename_in, fileout);
741     fileout.toFile(tempfilename_out);
743     pump_events();
745     SPDocument * mydoc = NULL;
746     if (data_read > 10) {
747         mydoc = Inkscape::Extension::open(
748               Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
749               tempfilename_out.c_str());
750     } // data_read
752     pump_events();
754     // make sure we don't leak file descriptors from g_file_open_tmp
755     close(tempfd_in);
756     close(tempfd_out);
758     // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
759     unlink(tempfilename_in.c_str());
760     unlink(tempfilename_out.c_str());
762     /* Do something with mydoc.... */
763     if (mydoc) {
764         doc->doc()->emitReconstructionStart();
765         copy_doc(doc->doc()->rroot, mydoc->rroot);
766         doc->doc()->emitReconstructionFinish();
767         mydoc->release();
768         sp_namedview_update_layers_from_document(desktop);
769     }
771     return;
776 /**
777     \brief  A function to take all the svg elements from one document
778             and put them in another.
779     \param  oldroot  The root node of the document to be replaced
780     \param  newroot  The root node of the document to replace it with
782     This function first deletes all of the data in the old document.  It
783     does this by creating a list of what needs to be deleted, and then
784     goes through the list.  This two pass approach removes issues with
785     the list being change while parsing through it.  Lots of nasty bugs.
787     Then, it goes through the new document, duplicating all of the
788     elements and putting them into the old document.  The copy
789     is then complete.
790 */
791 void
792 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
794     std::vector<Inkscape::XML::Node *> delete_list;
795     Inkscape::XML::Node * oldroot_namedview = NULL;
797     for (Inkscape::XML::Node * child = oldroot->firstChild();
798             child != NULL;
799             child = child->next()) {
800         if (!strcmp("sodipodi:namedview", child->name())) {
801             oldroot_namedview = child;
802             for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
803                     oldroot_namedview_child != NULL;
804                     oldroot_namedview_child = oldroot_namedview_child->next()) {
805                 delete_list.push_back(oldroot_namedview_child);
806             }
807         } else {
808             delete_list.push_back(child);
809         }
810     }
811     for (unsigned int i = 0; i < delete_list.size(); i++)
812         sp_repr_unparent(delete_list[i]);
814     for (Inkscape::XML::Node * child = newroot->firstChild();
815             child != NULL;
816             child = child->next()) {
817         if (!strcmp("sodipodi:namedview", child->name())) {
818             if (oldroot_namedview != NULL) {
819                 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
820                         newroot_namedview_child != NULL;
821                         newroot_namedview_child = newroot_namedview_child->next()) {
822                     oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document()));
823                 }
824             }
825         } else {
826             oldroot->appendChild(child->duplicate(newroot->document()));
827         }
828     }
830     oldroot->setAttribute("width", newroot->attribute("width"));
831     oldroot->setAttribute("height", newroot->attribute("height"));
833     /** \todo  Restore correct layer */
834     /** \todo  Restore correct selection */
837 /**  \brief  This function checks the stderr file, and if it has data,
838              shows it in a warning dialog to the user
839      \param  filename  Filename of the stderr file
840 */
841 void
842 Script::checkStderr (const Glib::ustring &data,
843                            Gtk::MessageType type,
844                      const Glib::ustring &message)
846     Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
847     warning.set_resizable(true);
848     GtkWidget *dlg = GTK_WIDGET(warning.gobj());
849     sp_transientize(dlg);
851     Gtk::VBox * vbox = warning.get_vbox();
853     /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
854     Gtk::TextView * textview = new Gtk::TextView();
855     textview->set_editable(false);
856     textview->set_wrap_mode(Gtk::WRAP_WORD);
857     textview->show();
859     textview->get_buffer()->set_text(data.c_str());
861     Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
862     scrollwindow->add(*textview);
863     scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
864     scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
865     scrollwindow->show();
867     vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
869     warning.run();
871     return;
874 bool
875 Script::cancelProcessing (void) {
876     _canceled = true;
877     _main_loop->quit();
878     Glib::spawn_close_pid(_pid);
880     return true;
884 /** \brief    This is the core of the extension file as it actually does
885               the execution of the extension.
886     \param    in_command  The command to be executed
887     \param    filein      Filename coming in
888     \param    fileout     Filename of the out file
889     \return   Number of bytes that were read into the output file.
891     The first thing that this function does is build the command to be
892     executed.  This consists of the first string (in_command) and then
893     the filename for input (filein).  This file is put on the command
894     line.
896     The next thing is that this function does is open a pipe to the
897     command and get the file handle in the ppipe variable.  It then
898     opens the output file with the output file handle.  Both of these
899     operations are checked extensively for errors.
901     After both are opened, then the data is copied from the output
902     of the pipe into the file out using fread and fwrite.  These two
903     functions are used because of their primitive nature they make
904     no assumptions about the data.  A buffer is used in the transfer,
905     but the output of fread is stored so the exact number of bytes
906     is handled gracefully.
908     At the very end (after the data has been copied) both of the files
909     are closed, and we return to what we were doing.
910 */
911 int
912 Script::execute (const std::list<std::string> &in_command,
913                  const std::list<std::string> &in_params,
914                  const Glib::ustring &filein,
915                  file_listener &fileout)
917     g_return_val_if_fail(in_command.size() > 0, 0);
918     // printf("Executing\n");
920     std::vector <std::string> argv;
922     for (std::list<std::string>::const_iterator i = in_command.begin();
923             i != in_command.end(); i++) {
924         argv.push_back(*i);
925     }
927     if (!(filein.empty())) {
928         argv.push_back(filein);
929     }
931     for (std::list<std::string>::const_iterator i = in_params.begin();
932             i != in_params.end(); i++) {
933         argv.push_back(*i);
934     }
936 /*
937     for (std::vector<std::string>::const_iterator i = argv.begin();
938             i != argv.end(); i++) {
939         std::cout << *i << std::endl;
940     }
941 */
943     int stdout_pipe, stderr_pipe;
945     try {
946         Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
947                                      argv,  // arg v
948                                      Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
949                                      sigc::slot<void>(),
950                                      &_pid,          // Pid
951                                      NULL,           // STDIN
952                                      &stdout_pipe,   // STDOUT
953                                      &stderr_pipe);  // STDERR
954     } catch (Glib::SpawnError e) {
955         printf("Can't Spawn!!! %d\n", e.code());
956         return 0;
957     }
959     _main_loop = Glib::MainLoop::create(false);
961     file_listener fileerr;
962     fileout.init(stdout_pipe, _main_loop);
963     fileerr.init(stderr_pipe, _main_loop);
965     _canceled = false;
966     _main_loop->run();
968     // Ensure all the data is out of the pipe
969     while (!fileout.isDead())
970         fileout.read(Glib::IO_IN);
971     while (!fileerr.isDead())
972         fileerr.read(Glib::IO_IN);
974     if (_canceled) {
975         // std::cout << "Script Canceled" << std::endl;
976         return 0;
977     }
979     Glib::ustring stderr_data = fileerr.string();
980     if (stderr_data.length() != 0) {
981         checkStderr(stderr_data, Gtk::MESSAGE_INFO,
982                                  _("Inkscape has received additional data from the script executed.  "
983                                    "The script did not return an error, but this may indicate the results will not be as expected."));
984     }
986     Glib::ustring stdout_data = fileout.string();
987     if (stdout_data.length() == 0) {
988         return 0;
989     }
991     // std::cout << "Finishing Execution." << std::endl;
992     return stdout_data.length();
998 }  // namespace Implementation
999 }  // namespace Extension
1000 }  // namespace Inkscape
1002 /*
1003   Local Variables:
1004   mode:c++
1005   c-file-style:"stroustrup"
1006   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1007   indent-tabs-mode:nil
1008   fill-column:99
1009   End:
1010 */
1011 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :