16ab66dc0f9e96aacb9c7214a4822e8aa1256b93
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.
22 "** (inkscape:5848): WARNING **: Format autodetect failed. The file is being opened as SVG."
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
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)
114 {
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;
178 }
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()
192 {
193 }
196 /**
197 * brief Destructor
198 */
199 Script::~Script()
200 {
201 }
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 "";
255 }
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)
277 {
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 }
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;
329 }
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)
355 {
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;
391 }
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)
404 {
405 command.clear();
406 helper_extension = "";
407 }
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)
420 {
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;
452 }
454 class ScriptDocCache : public ImplementationDocumentCache {
455 friend class Script;
456 protected:
457 std::string _filename;
458 int _tempfd;
459 public:
460 ScriptDocCache (Inkscape::UI::View::View * view);
461 ~ScriptDocCache ( );
462 };
464 ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
465 ImplementationDocumentCache(view),
466 _filename(""),
467 _tempfd(0)
468 {
469 try {
470 _tempfd = Glib::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
471 } catch (...) {
472 /// \todo Popup dialog here
473 return;
474 }
476 SPDesktop *desktop = (SPDesktop *) view;
477 sp_namedview_document_from_window(desktop);
479 Inkscape::Extension::save(
480 Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
481 view->doc(), _filename.c_str(), FALSE, FALSE, FALSE);
483 return;
484 }
486 ScriptDocCache::~ScriptDocCache ( )
487 {
488 close(_tempfd);
489 unlink(_filename.c_str());
490 }
492 ImplementationDocumentCache *
493 Script::newDocCache (Inkscape::Extension::Extension * ext, Inkscape::UI::View::View * view) {
494 return new ScriptDocCache(view);
495 }
498 /**
499 \return A dialog for preferences
500 \brief A stub funtion right now
501 \param module Module who's preferences need getting
502 \param filename Hey, the file you're getting might be important
504 This function should really do something, right now it doesn't.
505 */
506 Gtk::Widget *
507 Script::prefs_input(Inkscape::Extension::Input *module,
508 const gchar *filename)
509 {
510 return module->autogui(NULL, NULL);
511 }
515 /**
516 \return A dialog for preferences
517 \brief A stub funtion right now
518 \param module Module whose preferences need getting
520 This function should really do something, right now it doesn't.
521 */
522 Gtk::Widget *
523 Script::prefs_output(Inkscape::Extension::Output *module)
524 {
525 return module->autogui(NULL, NULL);
526 }
530 /**
531 \return A dialog for preferences
532 \brief A stub funtion right now
533 \param module Module who's preferences need getting
535 This function should really do something, right now it doesn't.
536 */
537 Gtk::Widget *
538 Script::prefs_effect(Inkscape::Extension::Effect *module,
539 Inkscape::UI::View::View *view,
540 sigc::signal<void> * changeSignal,
541 ImplementationDocumentCache * docCache)
542 {
543 SPDocument * current_document = view->doc();
545 using Inkscape::Util::GSListConstIterator;
546 GSListConstIterator<SPItem *> selected =
547 sp_desktop_selection((SPDesktop *)view)->itemList();
548 Inkscape::XML::Node * first_select = NULL;
549 if (selected != NULL) {
550 const SPItem * item = *selected;
551 first_select = SP_OBJECT_REPR(item);
552 }
554 return module->autogui(current_document, first_select, changeSignal);
555 }
560 /**
561 \return A new document that has been opened
562 \brief This function uses a filename that is put in, and calls
563 the extension's command to create an SVG file which is
564 returned.
565 \param module Extension to use.
566 \param filename File to open.
568 First things first, this function needs a temporary file name. To
569 create on of those the function g_file_open_tmp is used with
570 the header of ink_ext_.
572 The extension is then executed using the 'execute' function
573 with the filname coming in, and the temporary filename. After
574 That executing, the SVG should be in the temporary file.
576 Finally, the temporary file is opened using the SVG input module and
577 a document is returned. That document has its filename set to
578 the incoming filename (so that it's not the temporary filename).
579 That document is then returned from this function.
580 */
581 SPDocument *
582 Script::open(Inkscape::Extension::Input *module,
583 const gchar *filenameArg)
584 {
585 std::list<std::string> params;
586 module->paramListString(params);
588 std::string tempfilename_out;
589 int tempfd_out = 0;
590 try {
591 tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX");
592 } catch (...) {
593 /// \todo Popup dialog here
594 return NULL;
595 }
597 std::string lfilename = Glib::filename_from_utf8(filenameArg);
599 file_listener fileout;
600 int data_read = execute(command, params, lfilename, fileout);
601 fileout.toFile(tempfilename_out);
603 SPDocument * mydoc = NULL;
604 if (data_read > 10) {
605 if (helper_extension.size()==0) {
606 mydoc = Inkscape::Extension::open(
607 Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
608 tempfilename_out.c_str());
609 } else {
610 mydoc = Inkscape::Extension::open(
611 Inkscape::Extension::db.get(helper_extension.c_str()),
612 tempfilename_out.c_str());
613 }
614 } // data_read
616 if (mydoc != NULL) {
617 sp_document_set_uri(mydoc, filenameArg);
618 }
620 // make sure we don't leak file descriptors from g_file_open_tmp
621 close(tempfd_out);
623 unlink(tempfilename_out.c_str());
625 return mydoc;
626 } // open
630 /**
631 \return none
632 \brief This function uses an extention to save a document. It first
633 creates an SVG file of the document, and then runs it through
634 the script.
635 \param module Extention to be used
636 \param doc Document to be saved
637 \param filename The name to save the final file as
639 Well, at some point people need to save - it is really what makes
640 the entire application useful. And, it is possible that someone
641 would want to use an extetion for this, so we need a function to
642 do that eh?
644 First things first, the document is saved to a temporary file that
645 is an SVG file. To get the temporary filename g_file_open_tmp is used with
646 ink_ext_ as a prefix. Don't worry, this file gets deleted at the
647 end of the function.
649 After we have the SVG file, then extention_execute is called with
650 the temporary file name and the final output filename. This should
651 put the output of the script into the final output file. We then
652 delete the temporary file.
653 */
654 void
655 Script::save(Inkscape::Extension::Output *module,
656 SPDocument *doc,
657 const gchar *filenameArg)
658 {
659 std::list<std::string> params;
660 module->paramListString(params);
662 std::string tempfilename_in;
663 int tempfd_in = 0;
664 try {
665 tempfd_in = Glib::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX");
666 } catch (...) {
667 /// \todo Popup dialog here
668 return;
669 }
671 if (helper_extension.size() == 0) {
672 Inkscape::Extension::save(
673 Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
674 doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
675 } else {
676 Inkscape::Extension::save(
677 Inkscape::Extension::db.get(helper_extension.c_str()),
678 doc, tempfilename_in.c_str(), FALSE, FALSE, FALSE);
679 }
682 file_listener fileout;
683 execute(command, params, tempfilename_in, fileout);
685 std::string lfilename = Glib::filename_from_utf8(filenameArg);
686 fileout.toFile(lfilename);
688 // make sure we don't leak file descriptors from g_file_open_tmp
689 close(tempfd_in);
690 // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
691 unlink(tempfilename_in.c_str());
693 return;
694 }
698 /**
699 \return none
700 \brief This function uses an extention as a effect on a document.
701 \param module Extention to effect with.
702 \param doc Document to run through the effect.
704 This function is a little bit trickier than the previous two. It
705 needs two temporary files to get it's work done. Both of these
706 files have random names created for them using the g_file_open_temp function
707 with the ink_ext_ prefix in the temporary directory. Like the other
708 functions, the temporary files are deleted at the end.
710 To save/load the two temporary documents (both are SVG) the internal
711 modules for SVG load and save are used. They are both used through
712 the module system function by passing their keys into the functions.
714 The command itself is built a little bit differently than in other
715 functions because the effect support selections. So on the command
716 line a list of all the ids that are selected is included. Currently,
717 this only works for a single selected object, but there will be more.
718 The command string is filled with the data, and then after the execution
719 it is freed.
721 The execute function is used at the core of this function
722 to execute the Script on the two SVG documents (actually only one
723 exists at the time, the other is created by that script). At that
724 point both should be full, and the second one is loaded.
725 */
726 void
727 Script::effect(Inkscape::Extension::Effect *module,
728 Inkscape::UI::View::View *doc,
729 ImplementationDocumentCache * docCache)
730 {
731 if (docCache == NULL) {
732 docCache = newDocCache(module, doc);
733 }
734 ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
735 if (dc == NULL) {
736 printf("TOO BAD TO LIVE!!!");
737 exit(1);
738 }
740 std::list<std::string> params;
741 module->paramListString(params);
743 if (module->no_doc) {
744 // this is a no-doc extension, e.g. a Help menu command;
745 // just run the command without any files, ignoring errors
747 Glib::ustring empty;
748 file_listener outfile;
749 execute(command, params, empty, outfile);
751 return;
752 }
754 std::string tempfilename_out;
755 int tempfd_out = 0;
756 try {
757 tempfd_out = Glib::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
758 } catch (...) {
759 /// \todo Popup dialog here
760 return;
761 }
763 SPDesktop *desktop = (SPDesktop *) doc;
764 sp_namedview_document_from_window(desktop);
766 if (desktop != NULL) {
767 Inkscape::Util::GSListConstIterator<SPItem *> selected =
768 sp_desktop_selection(desktop)->itemList();
769 while ( selected != NULL ) {
770 Glib::ustring selected_id;
771 selected_id += "--id=";
772 selected_id += SP_OBJECT_ID(*selected);
773 params.insert(params.begin(), selected_id);
774 ++selected;
775 }
776 }
778 file_listener fileout;
779 int data_read = execute(command, params, dc->_filename, fileout);
780 fileout.toFile(tempfilename_out);
782 pump_events();
784 SPDocument * mydoc = NULL;
785 if (data_read > 10) {
786 mydoc = Inkscape::Extension::open(
787 Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
788 tempfilename_out.c_str());
789 } // data_read
791 pump_events();
793 // make sure we don't leak file descriptors from g_file_open_tmp
794 close(tempfd_out);
796 // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
797 unlink(tempfilename_out.c_str());
799 /* Do something with mydoc.... */
800 if (mydoc) {
801 doc->doc()->emitReconstructionStart();
802 copy_doc(doc->doc()->rroot, mydoc->rroot);
803 doc->doc()->emitReconstructionFinish();
804 mydoc->release();
805 sp_namedview_update_layers_from_document(desktop);
806 }
808 return;
809 }
813 /**
814 \brief A function to take all the svg elements from one document
815 and put them in another.
816 \param oldroot The root node of the document to be replaced
817 \param newroot The root node of the document to replace it with
819 This function first deletes all of the data in the old document. It
820 does this by creating a list of what needs to be deleted, and then
821 goes through the list. This two pass approach removes issues with
822 the list being change while parsing through it. Lots of nasty bugs.
824 Then, it goes through the new document, duplicating all of the
825 elements and putting them into the old document. The copy
826 is then complete.
827 */
828 void
829 Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
830 {
831 std::vector<Inkscape::XML::Node *> delete_list;
832 Inkscape::XML::Node * oldroot_namedview = NULL;
834 for (Inkscape::XML::Node * child = oldroot->firstChild();
835 child != NULL;
836 child = child->next()) {
837 if (!strcmp("sodipodi:namedview", child->name())) {
838 oldroot_namedview = child;
839 for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
840 oldroot_namedview_child != NULL;
841 oldroot_namedview_child = oldroot_namedview_child->next()) {
842 delete_list.push_back(oldroot_namedview_child);
843 }
844 } else {
845 delete_list.push_back(child);
846 }
847 }
848 for (unsigned int i = 0; i < delete_list.size(); i++)
849 sp_repr_unparent(delete_list[i]);
851 for (Inkscape::XML::Node * child = newroot->firstChild();
852 child != NULL;
853 child = child->next()) {
854 if (!strcmp("sodipodi:namedview", child->name())) {
855 if (oldroot_namedview != NULL) {
856 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
857 newroot_namedview_child != NULL;
858 newroot_namedview_child = newroot_namedview_child->next()) {
859 oldroot_namedview->appendChild(newroot_namedview_child->duplicate(child->document()));
860 }
861 }
862 } else {
863 oldroot->appendChild(child->duplicate(newroot->document()));
864 }
865 }
867 oldroot->setAttribute("width", newroot->attribute("width"));
868 oldroot->setAttribute("height", newroot->attribute("height"));
870 /** \todo Restore correct layer */
871 /** \todo Restore correct selection */
872 }
874 /** \brief This function checks the stderr file, and if it has data,
875 shows it in a warning dialog to the user
876 \param filename Filename of the stderr file
877 */
878 void
879 Script::checkStderr (const Glib::ustring &data,
880 Gtk::MessageType type,
881 const Glib::ustring &message)
882 {
883 Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
884 warning.set_resizable(true);
885 GtkWidget *dlg = GTK_WIDGET(warning.gobj());
886 sp_transientize(dlg);
888 Gtk::VBox * vbox = warning.get_vbox();
890 /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
891 Gtk::TextView * textview = new Gtk::TextView();
892 textview->set_editable(false);
893 textview->set_wrap_mode(Gtk::WRAP_WORD);
894 textview->show();
896 textview->get_buffer()->set_text(data.c_str());
898 Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
899 scrollwindow->add(*textview);
900 scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
901 scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
902 scrollwindow->show();
904 vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
906 warning.run();
908 return;
909 }
911 bool
912 Script::cancelProcessing (void) {
913 _canceled = true;
914 _main_loop->quit();
915 Glib::spawn_close_pid(_pid);
917 return true;
918 }
921 /** \brief This is the core of the extension file as it actually does
922 the execution of the extension.
923 \param in_command The command to be executed
924 \param filein Filename coming in
925 \param fileout Filename of the out file
926 \return Number of bytes that were read into the output file.
928 The first thing that this function does is build the command to be
929 executed. This consists of the first string (in_command) and then
930 the filename for input (filein). This file is put on the command
931 line.
933 The next thing is that this function does is open a pipe to the
934 command and get the file handle in the ppipe variable. It then
935 opens the output file with the output file handle. Both of these
936 operations are checked extensively for errors.
938 After both are opened, then the data is copied from the output
939 of the pipe into the file out using fread and fwrite. These two
940 functions are used because of their primitive nature they make
941 no assumptions about the data. A buffer is used in the transfer,
942 but the output of fread is stored so the exact number of bytes
943 is handled gracefully.
945 At the very end (after the data has been copied) both of the files
946 are closed, and we return to what we were doing.
947 */
948 int
949 Script::execute (const std::list<std::string> &in_command,
950 const std::list<std::string> &in_params,
951 const Glib::ustring &filein,
952 file_listener &fileout)
953 {
954 g_return_val_if_fail(in_command.size() > 0, 0);
955 // printf("Executing\n");
957 std::vector <std::string> argv;
959 for (std::list<std::string>::const_iterator i = in_command.begin();
960 i != in_command.end(); i++) {
961 argv.push_back(*i);
962 }
964 if (!(filein.empty())) {
965 argv.push_back(filein);
966 }
968 for (std::list<std::string>::const_iterator i = in_params.begin();
969 i != in_params.end(); i++) {
970 argv.push_back(*i);
971 }
973 /*
974 for (std::vector<std::string>::const_iterator i = argv.begin();
975 i != argv.end(); i++) {
976 std::cout << *i << std::endl;
977 }
978 */
980 int stdout_pipe, stderr_pipe;
982 try {
983 Glib::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
984 argv, // arg v
985 Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
986 sigc::slot<void>(),
987 &_pid, // Pid
988 NULL, // STDIN
989 &stdout_pipe, // STDOUT
990 &stderr_pipe); // STDERR
991 } catch (Glib::SpawnError e) {
992 printf("Can't Spawn!!! %d\n", e.code());
993 return 0;
994 }
996 _main_loop = Glib::MainLoop::create(false);
998 file_listener fileerr;
999 fileout.init(stdout_pipe, _main_loop);
1000 fileerr.init(stderr_pipe, _main_loop);
1002 _canceled = false;
1003 _main_loop->run();
1005 // Ensure all the data is out of the pipe
1006 while (!fileout.isDead())
1007 fileout.read(Glib::IO_IN);
1008 while (!fileerr.isDead())
1009 fileerr.read(Glib::IO_IN);
1011 if (_canceled) {
1012 // std::cout << "Script Canceled" << std::endl;
1013 return 0;
1014 }
1016 Glib::ustring stderr_data = fileerr.string();
1017 if (stderr_data.length() != 0) {
1018 checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1019 _("Inkscape has received additional data from the script executed. "
1020 "The script did not return an error, but this may indicate the results will not be as expected."));
1021 }
1023 Glib::ustring stdout_data = fileout.string();
1024 if (stdout_data.length() == 0) {
1025 return 0;
1026 }
1028 // std::cout << "Finishing Execution." << std::endl;
1029 return stdout_data.length();
1030 }
1035 } // namespace Implementation
1036 } // namespace Extension
1037 } // namespace Inkscape
1039 /*
1040 Local Variables:
1041 mode:c++
1042 c-file-style:"stroustrup"
1043 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1044 indent-tabs-mode:nil
1045 fill-column:99
1046 End:
1047 */
1048 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :