1 #define __SP_MODULE_C__
2 /** \file
3 *
4 * Inkscape::Extension::Extension:
5 * the ability to have features that are more modular so that they
6 * can be added and removed easily. This is the basis for defining
7 * those actions.
8 */
10 /*
11 * Authors:
12 * Ted Gould <ted@gould.cx>
13 *
14 * Copyright (C) 2002-2005 Authors
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
24 #include <glibmm/i18n.h>
25 #include <gtkmm/box.h>
26 #include <gtkmm/label.h>
27 #include <gtkmm/frame.h>
28 #include <gtkmm/table.h>
29 #include <gtkmm/tooltips.h>
31 #include "inkscape.h"
32 #include "extension/implementation/implementation.h"
34 #include "db.h"
35 #include "dependency.h"
36 #include "timer.h"
37 #include "param/parameter.h"
39 namespace Inkscape {
40 namespace Extension {
42 /* Inkscape::Extension::Extension */
44 std::vector<const gchar *> Extension::search_path;
45 std::ofstream Extension::error_file;
47 Parameter * param_shared (const gchar * name, GSList * list);
49 /**
50 \return none
51 \brief Constructs an Extension from a Inkscape::XML::Node
52 \param in_repr The repr that should be used to build it
54 This function is the basis of building an extension for Inkscape. It
55 currently extracts the fields from the Repr that are used in the
56 extension. The Repr will likely include other children that are
57 not related to the module directly. If the Repr does not include
58 a name and an ID the module will be left in an errored state.
59 */
60 Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : _help(NULL)
61 {
62 repr = in_repr;
63 Inkscape::GC::anchor(in_repr);
65 id = NULL;
66 name = NULL;
67 _state = STATE_UNLOADED;
68 parameters = NULL;
70 if (in_imp == NULL) {
71 imp = new Implementation::Implementation();
72 } else {
73 imp = in_imp;
74 }
76 // printf("Extension Constructor: ");
77 if (repr != NULL) {
78 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
79 /* TODO: Handle what happens if we don't have these two */
80 while (child_repr != NULL) {
81 char const * chname = child_repr->name();
82 if (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
83 chname += strlen(INKSCAPE_EXTENSION_NS);
84 }
85 if (chname[0] == '_') /* Allow _ for translation of tags */
86 chname++;
87 if (!strcmp(chname, "id")) {
88 gchar const *val = sp_repr_children(child_repr)->content();
89 id = g_strdup (val);
90 } /* id */
91 if (!strcmp(chname, "name")) {
92 name = g_strdup (sp_repr_children(child_repr)->content());
93 } /* name */
94 if (!strcmp(chname, "help")) {
95 _help = g_strdup (sp_repr_children(child_repr)->content());
96 } /* name */
97 if (!strcmp(chname, "param") || !strcmp(chname, "_param")) {
98 Parameter * param;
99 param = Parameter::make(child_repr, this);
100 if (param != NULL)
101 parameters = g_slist_append(parameters, param);
102 } /* param || _param */
103 if (!strcmp(chname, "dependency")) {
104 _deps.push_back(new Dependency(child_repr));
105 } /* dependency */
106 child_repr = sp_repr_next(child_repr);
107 }
109 db.register_ext (this);
110 }
111 // printf("%s\n", name);
112 timer = NULL;
114 return;
115 }
117 /**
118 \return none
119 \brief Destroys the Extension
121 This function frees all of the strings that could be attached
122 to the extension and also unreferences the repr. This is better
123 than freeing it because it may (I wouldn't know why) be referenced
124 in another place.
125 */
126 Extension::~Extension (void)
127 {
128 // printf("Extension Destructor: %s\n", name);
129 set_state(STATE_UNLOADED);
130 db.unregister_ext(this);
131 Inkscape::GC::release(repr);
132 g_free(id);
133 g_free(name);
134 delete timer;
135 timer = NULL;
136 /** \todo Need to do parameters here */
138 // delete parameters:
139 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
140 Parameter * param = reinterpret_cast<Parameter *>(list->data);
141 delete param;
142 }
143 g_slist_free(parameters);
146 for (unsigned int i = 0 ; i < _deps.size(); i++) {
147 delete _deps[i];
148 }
149 _deps.clear();
151 return;
152 }
154 /**
155 \return none
156 \brief A function to set whether the extension should be loaded
157 or unloaded
158 \param in_state Which state should the extension be in?
160 It checks to see if this is a state change or not. If we're changing
161 states it will call the appropriate function in the implementation,
162 load or unload. Currently, there is no error checking in this
163 function. There should be.
164 */
165 void
166 Extension::set_state (state_t in_state)
167 {
168 if (_state == STATE_DEACTIVATED) return;
169 if (in_state != _state) {
170 /** \todo Need some more error checking here! */
171 switch (in_state) {
172 case STATE_LOADED:
173 if (imp->load(this))
174 _state = STATE_LOADED;
176 if (timer != NULL) {
177 delete timer;
178 }
179 timer = new ExpirationTimer(this);
181 break;
182 case STATE_UNLOADED:
183 // std::cout << "Unloading: " << name << std::endl;
184 imp->unload(this);
185 _state = STATE_UNLOADED;
187 if (timer != NULL) {
188 delete timer;
189 timer = NULL;
190 }
191 break;
192 case STATE_DEACTIVATED:
193 _state = STATE_DEACTIVATED;
195 if (timer != NULL) {
196 delete timer;
197 timer = NULL;
198 }
199 break;
200 default:
201 break;
202 }
203 }
205 return;
206 }
208 /**
209 \return The state the extension is in
210 \brief A getter for the state variable.
211 */
212 Extension::state_t
213 Extension::get_state (void)
214 {
215 return _state;
216 }
218 /**
219 \return Whether the extension is loaded or not
220 \brief A quick function to test the state of the extension
221 */
222 bool
223 Extension::loaded (void)
224 {
225 return get_state() == STATE_LOADED;
226 }
228 /**
229 \return A boolean saying whether the extension passed the checks
230 \brief A function to check the validity of the extension
232 This function chekcs to make sure that there is an id, a name, a
233 repr and an implemenation for this extension. Then it checks all
234 of the dependencies to see if they pass. Finally, it asks the
235 implmentation to do a check of itself.
237 On each check, if there is a failure, it will print a message to the
238 error log for that failure. It is important to note that the function
239 keeps executing if it finds an error, to try and get as many of them
240 into the error log as possible. This should help people debug
241 installations, and figure out what they need to get for the full
242 functionality of Inkscape to be available.
243 */
244 bool
245 Extension::check (void)
246 {
247 bool retval = true;
249 // static int i = 0;
250 // std::cout << "Checking module[" << i++ << "]: " << name << std::endl;
252 const char * inx_failure = _(" This is caused by an improper .inx file for this extension."
253 " An improper .inx file could have been caused by a faulty installation of Inkscape.");
254 if (id == NULL) {
255 printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure);
256 retval = false;
257 }
258 if (name == NULL) {
259 printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure);
260 retval = false;
261 }
262 if (repr == NULL) {
263 printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure);
264 retval = false;
265 }
266 if (imp == NULL) {
267 printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure);
268 retval = false;
269 }
271 for (unsigned int i = 0 ; i < _deps.size(); i++) {
272 if (_deps[i]->check() == FALSE) {
273 // std::cout << "Failed: " << *(_deps[i]) << std::endl;
274 printFailure(Glib::ustring(_("a dependency was not met.")));
275 error_file << *_deps[i] << std::endl;
276 retval = false;
277 }
278 }
280 if (retval)
281 return imp->check(this);
282 return retval;
283 }
285 /** \brief A quick function to print out a standard start of extension
286 errors in the log.
287 \param reason A string explaining why this failed
289 Real simple, just put everything into \c error_file.
290 */
291 void
292 Extension::printFailure (Glib::ustring reason)
293 {
294 error_file << _("Extension \"") << name << _("\" failed to load because ");
295 error_file << reason.raw();
296 error_file << std::endl;
297 return;
298 }
300 /**
301 \return The XML tree that is used to define the extension
302 \brief A getter for the internal Repr, does not add a reference.
303 */
304 Inkscape::XML::Node *
305 Extension::get_repr (void)
306 {
307 return repr;
308 }
310 /**
311 \return The textual id of this extension
312 \brief Get the ID of this extension - not a copy don't delete!
313 */
314 gchar *
315 Extension::get_id (void)
316 {
317 return id;
318 }
320 /**
321 \return The textual name of this extension
322 \brief Get the name of this extension - not a copy don't delete!
323 */
324 gchar *
325 Extension::get_name (void)
326 {
327 return name;
328 }
330 /**
331 \return None
332 \brief This function diactivates the extension (which makes it
333 unusable, but not deleted)
335 This function is used to removed an extension from functioning, but
336 not delete it completely. It sets the state to \c STATE_DEACTIVATED to
337 mark to the world that it has been deactivated. It also removes
338 the current implementation and replaces it with a standard one. This
339 makes it so that we don't have to continually check if there is an
340 implementation, but we are gauranteed to have a benign one.
342 \warning It is important to note that there is no 'activate' function.
343 Running this function is irreversable.
344 */
345 void
346 Extension::deactivate (void)
347 {
348 set_state(STATE_DEACTIVATED);
350 /* Removing the old implementation, and making this use the default. */
351 /* This should save some memory */
352 delete imp;
353 imp = new Implementation::Implementation();
355 return;
356 }
358 /**
359 \return Whether the extension has been deactivated
360 \brief Find out the status of the extension
361 */
362 bool
363 Extension::deactivated (void)
364 {
365 return get_state() == STATE_DEACTIVATED;
366 }
368 /**
369 \return Parameter structure with a name of 'name'
370 \brief This function looks through the linked list for a parameter
371 structure with the name of the passed in name
372 \param name The name to search for
373 \param list The list to look for
375 This is an inline function that is used by all the get_param and
376 set_param functions to find a param_t in the linked list with
377 the passed in name. It is done as an inline so that it will be
378 optimized into a 'jump' by the compiler.
380 This function can throw a 'param_not_exist' exception if the
381 name is not found.
383 The first thing that this function checks is if the list is NULL.
384 It could be NULL because there are no parameters for this extension
385 or because all of them have been checked (I'll spoil the ending and
386 tell you that this function is called recursively). If the list
387 is NULL then the 'param_not_exist' exception is thrown.
389 Otherwise, the function looks at the current param_t that the element
390 list points to. If the name of that param_t matches the passed in
391 name then that param_t is returned. Otherwise, this function is
392 called again with g_slist_next as a parameter.
393 */
394 Parameter *
395 param_shared (const gchar * name, GSList * list)
396 {
397 Parameter * output;
399 if (name == NULL) {
400 throw Extension::param_not_exist();
401 }
402 if (list == NULL) {
403 throw Extension::param_not_exist();
404 }
406 output = static_cast<Parameter *>(list->data);
407 if (!strcmp(output->name(), name)) {
408 return output;
409 }
411 return param_shared(name, g_slist_next(list));
412 }
414 /**
415 \return A constant pointer to the string held by the parameters.
416 \brief Gets a parameter identified by name with the string placed
417 in value. It isn't duplicated into the value string.
418 \param name The name of the parameter to get
419 \param doc The document to look in for document specific parameters
420 \param node The node to look in for a specific parameter
422 Look up in the parameters list, then execute the function on that
423 found parameter.
424 */
425 const gchar *
426 Extension::get_param_string (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
427 {
428 Parameter * param;
430 param = param_shared(name, parameters);
431 return param->get_string(doc, node);
432 }
434 const gchar *
435 Extension::get_param_enum (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
436 {
437 Parameter* param = param_shared(name, parameters);
438 return param->get_enum(doc, node);
439 }
441 /**
442 \return The value of the parameter identified by the name
443 \brief Gets a parameter identified by name with the bool placed
444 in value.
445 \param name The name of the parameter to get
446 \param doc The document to look in for document specific parameters
447 \param node The node to look in for a specific parameter
449 Look up in the parameters list, then execute the function on that
450 found parameter.
451 */
452 bool
453 Extension::get_param_bool (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
454 {
455 Parameter * param;
457 param = param_shared(name, parameters);
458 return param->get_bool(doc, node);
459 }
461 /**
462 \return The integer value for the parameter specified
463 \brief Gets a parameter identified by name with the integer placed
464 in value.
465 \param name The name of the parameter to get
466 \param doc The document to look in for document specific parameters
467 \param node The node to look in for a specific parameter
469 Look up in the parameters list, then execute the function on that
470 found parameter.
471 */
472 int
473 Extension::get_param_int (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
474 {
475 Parameter * param;
477 param = param_shared(name, parameters);
478 return param->get_int(doc, node);
479 }
481 /**
482 \return The float value for the parameter specified
483 \brief Gets a parameter identified by name with the float placed
484 in value.
485 \param name The name of the parameter to get
486 \param doc The document to look in for document specific parameters
487 \param node The node to look in for a specific parameter
489 Look up in the parameters list, then execute the function on that
490 found parameter.
491 */
492 float
493 Extension::get_param_float (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
494 {
495 Parameter * param;
496 param = param_shared(name, parameters);
497 return param->get_float(doc, node);
498 }
500 /**
501 \return The string value for the parameter specified
502 \brief Gets a parameter identified by name with the float placed
503 in value.
504 \param name The name of the parameter to get
505 \param doc The document to look in for document specific parameters
506 \param node The node to look in for a specific parameter
508 Look up in the parameters list, then execute the function on that
509 found parameter.
510 */
511 guint32
512 Extension::get_param_color (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
513 {
514 Parameter* param = param_shared(name, parameters);
515 return param->get_color(doc, node);
516 }
518 /**
519 \return The passed in value
520 \brief Sets a parameter identified by name with the boolean
521 in the parameter value.
522 \param name The name of the parameter to set
523 \param value The value to set the parameter to
524 \param doc The document to look in for document specific parameters
525 \param node The node to look in for a specific parameter
527 Look up in the parameters list, then execute the function on that
528 found parameter.
529 */
530 bool
531 Extension::set_param_bool (const gchar * name, bool value, SPDocument * doc, Inkscape::XML::Node * node)
532 {
533 Parameter * param;
534 param = param_shared(name, parameters);
535 return param->set_bool(value, doc, node);
536 }
538 /**
539 \return The passed in value
540 \brief Sets a parameter identified by name with the integer
541 in the parameter value.
542 \param name The name of the parameter to set
543 \param value The value to set the parameter to
544 \param doc The document to look in for document specific parameters
545 \param node The node to look in for a specific parameter
547 Look up in the parameters list, then execute the function on that
548 found parameter.
549 */
550 int
551 Extension::set_param_int (const gchar * name, int value, SPDocument * doc, Inkscape::XML::Node * node)
552 {
553 Parameter * param;
554 param = param_shared(name, parameters);
555 return param->set_int(value, doc, node);
556 }
558 /**
559 \return The passed in value
560 \brief Sets a parameter identified by name with the integer
561 in the parameter value.
562 \param name The name of the parameter to set
563 \param value The value to set the parameter to
564 \param doc The document to look in for document specific parameters
565 \param node The node to look in for a specific parameter
567 Look up in the parameters list, then execute the function on that
568 found parameter.
569 */
570 float
571 Extension::set_param_float (const gchar * name, float value, SPDocument * doc, Inkscape::XML::Node * node)
572 {
573 Parameter * param;
574 param = param_shared(name, parameters);
575 return param->set_float(value, doc, node);
576 }
578 /**
579 \return The passed in value
580 \brief Sets a parameter identified by name with the string
581 in the parameter value.
582 \param name The name of the parameter to set
583 \param value The value to set the parameter to
584 \param doc The document to look in for document specific parameters
585 \param node The node to look in for a specific parameter
587 Look up in the parameters list, then execute the function on that
588 found parameter.
589 */
590 const gchar *
591 Extension::set_param_string (const gchar * name, const gchar * value, SPDocument * doc, Inkscape::XML::Node * node)
592 {
593 Parameter * param;
594 param = param_shared(name, parameters);
595 return param->set_string(value, doc, node);
596 }
598 /**
599 \return The passed in value
600 \brief Sets a parameter identified by name with the string
601 in the parameter value.
602 \param name The name of the parameter to set
603 \param value The value to set the parameter to
604 \param doc The document to look in for document specific parameters
605 \param node The node to look in for a specific parameter
607 Look up in the parameters list, then execute the function on that
608 found parameter.
609 */
610 guint32
611 Extension::set_param_color (const gchar * name, guint32 color, SPDocument * doc, Inkscape::XML::Node * node)
612 {
613 Parameter* param = param_shared(name, parameters);
614 return param->set_color(color, doc, node);
615 }
617 /** \brief A function to open the error log file. */
618 void
619 Extension::error_file_open (void)
620 {
621 gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
622 gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
623 error_file.open(filename);
624 if (!error_file.is_open()) {
625 g_warning(_("Could not create extension error log file '%s'"),
626 filename);
627 }
628 g_free(filename);
629 g_free(ext_error_file);
630 };
632 /** \brief A function to close the error log file. */
633 void
634 Extension::error_file_close (void)
635 {
636 error_file.close();
637 };
639 /** \brief A widget to represent the inside of an AutoGUI widget */
640 class AutoGUI : public Gtk::VBox {
641 Gtk::Tooltips _tooltips;
642 public:
643 /** \brief Create an AutoGUI object */
644 AutoGUI (void) : Gtk::VBox() {};
645 /** \brief Adds a widget with a tool tip into the autogui
646 \param widg Widget to add
647 \param tooltip Tooltip for the widget
649 If there is no widget, nothing happens. Otherwise it is just
650 added into the VBox. If there is a tooltip (non-NULL) then it
651 is placed on the widget.
652 */
653 void addWidget (Gtk::Widget * widg, gchar const * tooltip) {
654 if (widg == NULL) return;
655 this->pack_start(*widg, true, true, 2);
656 if (tooltip != NULL) {
657 _tooltips.set_tip(*widg, Glib::ustring(tooltip));
658 }
659 return;
660 };
661 };
663 /** \brief A function to automatically generate a GUI using the parameters
664 \return Generated widget
666 This function just goes through each parameter, and calls it's 'get_widget'
667 function to get each widget. Then, each of those is placed into
668 a Gtk::VBox, which is then returned to the calling function.
670 If there are no visible parameters, this function just returns NULL.
671 If all parameters are gui_visible = false NULL is returned as well.
672 */
673 Gtk::Widget *
674 Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
675 {
676 if (param_visible_count() == 0) return NULL;
678 AutoGUI * agui = Gtk::manage(new AutoGUI());
680 //go through the list of parameters to see if there are any non-hidden ones
681 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
682 Parameter * param = reinterpret_cast<Parameter *>(list->data);
683 if (param->get_gui_hidden()) continue; //Ignore hidden parameters
684 Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
685 gchar const * tip = param->get_tooltip();
686 agui->addWidget(widg, tip);
687 }
689 agui->show();
690 return agui;
691 };
693 /**
694 \brief A function to get the parameters in a string form
695 \return An array with all the parameters in it.
697 */
698 void
699 Extension::paramListString (std::list <std::string> &retlist)
700 {
701 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
702 Parameter * param = reinterpret_cast<Parameter *>(list->data);
703 param->string(retlist);
704 }
706 return;
707 }
709 /* Extension editor dialog stuff */
711 Gtk::VBox *
712 Extension::get_info_widget(void)
713 {
714 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
716 Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
717 retval->pack_start(*info, true, true, 5);
719 Gtk::Table * table = Gtk::manage(new Gtk::Table());
720 info->add(*table);
722 int row = 0;
723 add_val(_("Name:"), _(name), table, &row);
724 add_val(_("ID:"), id, table, &row);
725 add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
728 retval->show_all();
729 return retval;
730 }
732 void
733 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
734 {
735 Gtk::Label * label;
736 Gtk::Label * value;
738 (*row)++;
739 label = Gtk::manage(new Gtk::Label(labelstr));
740 value = Gtk::manage(new Gtk::Label(valuestr));
741 table->attach(*label, 0, 1, (*row) - 1, *row);
742 table->attach(*value, 1, 2, (*row) - 1, *row);
744 label->show();
745 value->show();
747 return;
748 }
750 Gtk::VBox *
751 Extension::get_help_widget(void)
752 {
753 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
755 if (_help == NULL) {
756 Gtk::Label * content = Gtk::manage(new Gtk::Label(_("Currently there is no help available for this Extension. Please look on the Inkscape website or ask on the mailing lists if you have questions regarding this extension.")));
757 retval->pack_start(*content, true, true, 5);
758 content->set_line_wrap(true);
759 content->show();
760 } else {
764 }
766 retval->show();
767 return retval;
768 }
770 Gtk::VBox *
771 Extension::get_params_widget(void)
772 {
773 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
774 Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
775 retval->pack_start(*content, true, true, 5);
776 content->show();
777 retval->show();
778 return retval;
779 }
781 unsigned int Extension::param_visible_count ( )
782 {
783 unsigned int _visible_count = 0;
784 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
785 Parameter * param = reinterpret_cast<Parameter *>(list->data);
786 if (!param->get_gui_hidden()) _visible_count++;
787 }
788 return _visible_count;
789 }
791 } /* namespace Extension */
792 } /* namespace Inkscape */
795 /*
796 Local Variables:
797 mode:c++
798 c-file-style:"stroustrup"
799 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
800 indent-tabs-mode:nil
801 fill-column:99
802 End:
803 */
804 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :