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 }
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)
467 {
468 return module->autogui(NULL, NULL);
469 }
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)
482 {
483 return module->autogui(NULL, NULL);
484 }
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)
499 {
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);
512 }
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)
541 {
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)
615 {
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;
651 }
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)
685 {
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;
772 }
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)
793 {
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 */
835 }
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)
845 {
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;
872 }
874 bool
875 Script::cancelProcessing (void) {
876 _canceled = true;
877 _main_loop->quit();
878 Glib::spawn_close_pid(_pid);
880 return true;
881 }
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)
916 {
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();
993 }
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 :