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 "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 (chname[0] == '_') /* Allow _ for translation of tags */
83 chname++;
84 if (!strcmp(chname, "id")) {
85 gchar const *val = sp_repr_children(child_repr)->content();
86 id = g_strdup (val);
87 } /* id */
88 if (!strcmp(chname, "name")) {
89 name = g_strdup (sp_repr_children(child_repr)->content());
90 } /* name */
91 if (!strcmp(chname, "help")) {
92 _help = g_strdup (sp_repr_children(child_repr)->content());
93 } /* name */
94 if (!strcmp(chname, "param") || !strcmp(chname, "_param")) {
95 Parameter * param;
96 param = Parameter::make(child_repr, this);
97 if (param != NULL)
98 parameters = g_slist_append(parameters, param);
99 } /* param || _param */
100 if (!strcmp(chname, "dependency")) {
101 _deps.push_back(new Dependency(child_repr));
102 } /* dependency */
103 child_repr = sp_repr_next(child_repr);
104 }
106 db.register_ext (this);
107 }
108 // printf("%s\n", name);
109 timer = NULL;
111 return;
112 }
114 /**
115 \return none
116 \brief Destroys the Extension
118 This function frees all of the strings that could be attached
119 to the extension and also unreferences the repr. This is better
120 than freeing it because it may (I wouldn't know why) be referenced
121 in another place.
122 */
123 Extension::~Extension (void)
124 {
125 // printf("Extension Destructor: %s\n", name);
126 set_state(STATE_UNLOADED);
127 db.unregister_ext(this);
128 Inkscape::GC::release(repr);
129 g_free(id);
130 g_free(name);
131 delete timer;
132 timer = NULL;
133 /** \todo Need to do parameters here */
135 for (unsigned int i = 0 ; i < _deps.size(); i++) {
136 delete _deps[i];
137 }
138 _deps.clear();
140 return;
141 }
143 /**
144 \return none
145 \brief A function to set whether the extension should be loaded
146 or unloaded
147 \param in_state Which state should the extension be in?
149 It checks to see if this is a state change or not. If we're changing
150 states it will call the appropriate function in the implementation,
151 load or unload. Currently, there is no error checking in this
152 function. There should be.
153 */
154 void
155 Extension::set_state (state_t in_state)
156 {
157 if (_state == STATE_DEACTIVATED) return;
158 if (in_state != _state) {
159 /** \todo Need some more error checking here! */
160 switch (in_state) {
161 case STATE_LOADED:
162 if (imp->load(this))
163 _state = STATE_LOADED;
165 if (timer != NULL) {
166 delete timer;
167 }
168 timer = new ExpirationTimer(this);
170 break;
171 case STATE_UNLOADED:
172 // std::cout << "Unloading: " << name << std::endl;
173 imp->unload(this);
174 _state = STATE_UNLOADED;
176 if (timer != NULL) {
177 delete timer;
178 timer = NULL;
179 }
180 break;
181 case STATE_DEACTIVATED:
182 _state = STATE_DEACTIVATED;
184 if (timer != NULL) {
185 delete timer;
186 timer = NULL;
187 }
188 break;
189 default:
190 break;
191 }
192 }
194 return;
195 }
197 /**
198 \return The state the extension is in
199 \brief A getter for the state variable.
200 */
201 Extension::state_t
202 Extension::get_state (void)
203 {
204 return _state;
205 }
207 /**
208 \return Whether the extension is loaded or not
209 \brief A quick function to test the state of the extension
210 */
211 bool
212 Extension::loaded (void)
213 {
214 return get_state() == STATE_LOADED;
215 }
217 /**
218 \return A boolean saying whether the extension passed the checks
219 \brief A function to check the validity of the extension
221 This function chekcs to make sure that there is an id, a name, a
222 repr and an implemenation for this extension. Then it checks all
223 of the dependencies to see if they pass. Finally, it asks the
224 implmentation to do a check of itself.
226 On each check, if there is a failure, it will print a message to the
227 error log for that failure. It is important to note that the function
228 keeps executing if it finds an error, to try and get as many of them
229 into the error log as possible. This should help people debug
230 installations, and figure out what they need to get for the full
231 functionality of Inkscape to be available.
232 */
233 bool
234 Extension::check (void)
235 {
236 bool retval = true;
238 // static int i = 0;
239 // std::cout << "Checking module[" << i++ << "]: " << name << std::endl;
241 const char * inx_failure = _(" This is caused by an improper .inx file for this extension."
242 " An improper .inx file could have been caused by a faulty installation of Inkscape.");
243 if (id == NULL) {
244 printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure);
245 retval = false;
246 }
247 if (name == NULL) {
248 printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure);
249 retval = false;
250 }
251 if (repr == NULL) {
252 printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure);
253 retval = false;
254 }
255 if (imp == NULL) {
256 printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure);
257 retval = false;
258 }
260 for (unsigned int i = 0 ; i < _deps.size(); i++) {
261 if (_deps[i]->check() == FALSE) {
262 // std::cout << "Failed: " << *(_deps[i]) << std::endl;
263 printFailure(Glib::ustring(_("a dependency was not met.")));
264 error_file << *_deps[i] << std::endl;
265 retval = false;
266 }
267 }
269 if (retval)
270 return imp->check(this);
271 return retval;
272 }
274 /** \brief A quick function to print out a standard start of extension
275 errors in the log.
276 \param reason A string explaining why this failed
278 Real simple, just put everything into \c error_file.
279 */
280 void
281 Extension::printFailure (Glib::ustring reason)
282 {
283 error_file << _("Extension \"") << name << _("\" failed to load because ");
284 error_file << reason.raw();
285 error_file << std::endl;
286 return;
287 }
289 /**
290 \return The XML tree that is used to define the extension
291 \brief A getter for the internal Repr, does not add a reference.
292 */
293 Inkscape::XML::Node *
294 Extension::get_repr (void)
295 {
296 return repr;
297 }
299 /**
300 \return The textual id of this extension
301 \brief Get the ID of this extension - not a copy don't delete!
302 */
303 gchar *
304 Extension::get_id (void)
305 {
306 return id;
307 }
309 /**
310 \return The textual name of this extension
311 \brief Get the name of this extension - not a copy don't delete!
312 */
313 gchar *
314 Extension::get_name (void)
315 {
316 return name;
317 }
319 /**
320 \return None
321 \brief This function diactivates the extension (which makes it
322 unusable, but not deleted)
324 This function is used to removed an extension from functioning, but
325 not delete it completely. It sets the state to \c STATE_DEACTIVATED to
326 mark to the world that it has been deactivated. It also removes
327 the current implementation and replaces it with a standard one. This
328 makes it so that we don't have to continually check if there is an
329 implementation, but we are gauranteed to have a benign one.
331 \warning It is important to note that there is no 'activate' function.
332 Running this function is irreversable.
333 */
334 void
335 Extension::deactivate (void)
336 {
337 set_state(STATE_DEACTIVATED);
339 /* Removing the old implementation, and making this use the default. */
340 /* This should save some memory */
341 delete imp;
342 imp = new Implementation::Implementation();
344 return;
345 }
347 /**
348 \return Whether the extension has been deactivated
349 \brief Find out the status of the extension
350 */
351 bool
352 Extension::deactivated (void)
353 {
354 return get_state() == STATE_DEACTIVATED;
355 }
357 /**
358 \return Parameter structure with a name of 'name'
359 \brief This function looks through the linked list for a parameter
360 structure with the name of the passed in name
361 \param name The name to search for
362 \param list The list to look for
364 This is an inline function that is used by all the get_param and
365 set_param functions to find a param_t in the linked list with
366 the passed in name. It is done as an inline so that it will be
367 optimized into a 'jump' by the compiler.
369 This function can throw a 'param_not_exist' exception if the
370 name is not found.
372 The first thing that this function checks is if the list is NULL.
373 It could be NULL because there are no parameters for this extension
374 or because all of them have been checked (I'll spoil the ending and
375 tell you that this function is called recursively). If the list
376 is NULL then the 'param_not_exist' exception is thrown.
378 Otherwise, the function looks at the current param_t that the element
379 list points to. If the name of that param_t matches the passed in
380 name then that param_t is returned. Otherwise, this function is
381 called again with g_slist_next as a parameter.
382 */
383 Parameter *
384 param_shared (const gchar * name, GSList * list)
385 {
386 Parameter * output;
388 if (name == NULL) {
389 throw Extension::param_not_exist();
390 }
391 if (list == NULL) {
392 throw Extension::param_not_exist();
393 }
395 output = static_cast<Parameter *>(list->data);
396 if (!strcmp(output->name(), name)) {
397 return output;
398 }
400 return param_shared(name, g_slist_next(list));
401 }
403 /**
404 \return A constant pointer to the string held by the parameters.
405 \brief Gets a parameter identified by name with the string placed
406 in value. It isn't duplicated into the value string.
407 \param name The name of the parameter to get
408 \param doc The document to look in for document specific parameters
409 \param node The node to look in for a specific parameter
411 Look up in the parameters list, then execute the function on that
412 found parameter.
413 */
414 const gchar *
415 Extension::get_param_string (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
416 {
417 Parameter * param;
419 param = param_shared(name, parameters);
420 return param->get_string(doc, node);
421 }
423 /**
424 \return The value of the parameter identified by the name
425 \brief Gets a parameter identified by name with the bool placed
426 in value.
427 \param name The name of the parameter to get
428 \param doc The document to look in for document specific parameters
429 \param node The node to look in for a specific parameter
431 Look up in the parameters list, then execute the function on that
432 found parameter.
433 */
434 bool
435 Extension::get_param_bool (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
436 {
437 Parameter * param;
439 param = param_shared(name, parameters);
440 return param->get_bool(doc, node);
441 }
443 /**
444 \return The integer value for the parameter specified
445 \brief Gets a parameter identified by name with the integer placed
446 in value.
447 \param name The name of the parameter to get
448 \param doc The document to look in for document specific parameters
449 \param node The node to look in for a specific parameter
451 Look up in the parameters list, then execute the function on that
452 found parameter.
453 */
454 int
455 Extension::get_param_int (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
456 {
457 Parameter * param;
459 param = param_shared(name, parameters);
460 return param->get_int(doc, node);
461 }
463 /**
464 \return The float value for the parameter specified
465 \brief Gets a parameter identified by name with the float placed
466 in value.
467 \param name The name of the parameter to get
468 \param doc The document to look in for document specific parameters
469 \param node The node to look in for a specific parameter
471 Look up in the parameters list, then execute the function on that
472 found parameter.
473 */
474 float
475 Extension::get_param_float (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
476 {
477 Parameter * param;
478 param = param_shared(name, parameters);
479 return param->get_float(doc, node);
480 }
482 /**
483 \return The passed in value
484 \brief Sets a parameter identified by name with the boolean
485 in the parameter value.
486 \param name The name of the parameter to set
487 \param value The value to set the parameter to
488 \param doc The document to look in for document specific parameters
489 \param node The node to look in for a specific parameter
491 Look up in the parameters list, then execute the function on that
492 found parameter.
493 */
494 bool
495 Extension::set_param_bool (const gchar * name, bool value, SPDocument * doc, Inkscape::XML::Node * node)
496 {
497 Parameter * param;
498 param = param_shared(name, parameters);
499 return param->set_bool(value, doc, node);
500 }
502 /**
503 \return The passed in value
504 \brief Sets a parameter identified by name with the integer
505 in the parameter value.
506 \param name The name of the parameter to set
507 \param value The value to set the parameter to
508 \param doc The document to look in for document specific parameters
509 \param node The node to look in for a specific parameter
511 Look up in the parameters list, then execute the function on that
512 found parameter.
513 */
514 int
515 Extension::set_param_int (const gchar * name, int value, SPDocument * doc, Inkscape::XML::Node * node)
516 {
517 Parameter * param;
518 param = param_shared(name, parameters);
519 return param->set_int(value, doc, node);
520 }
522 /**
523 \return The passed in value
524 \brief Sets a parameter identified by name with the integer
525 in the parameter value.
526 \param name The name of the parameter to set
527 \param value The value to set the parameter to
528 \param doc The document to look in for document specific parameters
529 \param node The node to look in for a specific parameter
531 Look up in the parameters list, then execute the function on that
532 found parameter.
533 */
534 float
535 Extension::set_param_float (const gchar * name, float value, SPDocument * doc, Inkscape::XML::Node * node)
536 {
537 Parameter * param;
538 param = param_shared(name, parameters);
539 return param->set_float(value, doc, node);
540 }
542 /**
543 \return The passed in value
544 \brief Sets a parameter identified by name with the string
545 in the parameter value.
546 \param name The name of the parameter to set
547 \param value The value to set the parameter to
548 \param doc The document to look in for document specific parameters
549 \param node The node to look in for a specific parameter
551 Look up in the parameters list, then execute the function on that
552 found parameter.
553 */
554 const gchar *
555 Extension::set_param_string (const gchar * name, const gchar * value, SPDocument * doc, Inkscape::XML::Node * node)
556 {
557 Parameter * param;
558 param = param_shared(name, parameters);
559 return param->set_string(value, doc, node);
560 }
562 /** \brief A function to open the error log file. */
563 void
564 Extension::error_file_open (void)
565 {
566 gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
567 gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
568 error_file.open(filename);
569 if (!error_file.is_open()) {
570 g_warning(_("Could not create extension error log file '%s'"),
571 filename);
572 }
573 g_free(filename);
574 g_free(ext_error_file);
575 };
577 /** \brief A function to close the error log file. */
578 void
579 Extension::error_file_close (void)
580 {
581 error_file.close();
582 };
584 /** \brief A widget to represent the inside of an AutoGUI widget */
585 class AutoGUI : public Gtk::VBox {
586 Gtk::Tooltips _tooltips;
587 public:
588 /** \brief Create an AutoGUI object */
589 AutoGUI (void) : Gtk::VBox() {};
590 /** \brief Adds a widget with a tool tip into the autogui
591 \param widg Widget to add
592 \param tooltip Tooltip for the widget
594 If there is no widget, nothing happens. Otherwise it is just
595 added into the VBox. If there is a tooltip (non-NULL) then it
596 is placed on the widget.
597 */
598 void addWidget (Gtk::Widget * widg, gchar const * tooltip) {
599 if (widg == NULL) return;
600 this->pack_start(*widg, true, true, 2);
601 if (tooltip != NULL) {
602 _tooltips.set_tip(*widg, Glib::ustring(tooltip));
603 // printf("Setting tooltip: %s\n", tooltip);
604 }
605 return;
606 };
607 };
609 /** \brief A function to automatically generate a GUI using the parameters
610 \return Generated widget
612 This function just goes through each parameter, and calls it's 'get_widget'
613 function to get each widget. Then, each of those is placed into
614 a Gtk::VBox, which is then returned to the calling function.
616 If there are no parameters, this function just returns NULL.
617 */
618 Gtk::Widget *
619 Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node)
620 {
621 if (g_slist_length(parameters) == 0) return NULL;
623 AutoGUI * agui = Gtk::manage(new AutoGUI());
625 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
626 Parameter * param = reinterpret_cast<Parameter *>(list->data);
627 Gtk::Widget * widg = param->get_widget(doc, node);
628 gchar const * tip = param->get_tooltip();
629 agui->addWidget(widg, tip);
630 }
632 agui->show();
633 return agui;
634 };
636 /**
637 \brief A function to get the parameters in a string form
638 \return A string with all the parameters as command line arguements
640 I don't really like this function, but it works for now.
642 \todo Do this better.
643 */
644 Glib::ustring *
645 Extension::paramString (void)
646 {
647 Glib::ustring * param_string = new Glib::ustring("");
649 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
650 Parameter * param = reinterpret_cast<Parameter *>(list->data);
652 *param_string += " --";
653 *param_string += param->name();
654 *param_string += "=";
655 Glib::ustring * paramstr = param->string();
656 *param_string += *paramstr;
657 delete paramstr;
658 }
660 return param_string;
661 }
663 /* Extension editor dialog stuff */
665 Gtk::VBox *
666 Extension::get_info_widget(void)
667 {
668 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
670 Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
671 retval->pack_start(*info, true, true, 5);
673 Gtk::Table * table = Gtk::manage(new Gtk::Table());
674 info->add(*table);
676 int row = 0;
677 add_val(_("Name:"), _(name), table, &row);
678 add_val(_("ID:"), id, table, &row);
679 add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
682 retval->show_all();
683 return retval;
684 }
686 void
687 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
688 {
689 Gtk::Label * label;
690 Gtk::Label * value;
692 (*row)++;
693 label = Gtk::manage(new Gtk::Label(labelstr));
694 value = Gtk::manage(new Gtk::Label(valuestr));
695 table->attach(*label, 0, 1, (*row) - 1, *row);
696 table->attach(*value, 1, 2, (*row) - 1, *row);
698 label->show();
699 value->show();
701 return;
702 }
704 Gtk::VBox *
705 Extension::get_help_widget(void)
706 {
707 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
709 if (_help == NULL) {
710 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."));
711 retval->pack_start(*content, true, true, 5);
712 content->set_line_wrap(true);
713 content->show();
714 } else {
718 }
720 retval->show();
721 return retval;
722 }
724 Gtk::VBox *
725 Extension::get_params_widget(void)
726 {
727 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
728 Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
729 retval->pack_start(*content, true, true, 5);
730 content->show();
731 retval->show();
732 return retval;
733 }
735 } /* namespace Extension */
736 } /* namespace Inkscape */
739 /*
740 Local Variables:
741 mode:c++
742 c-file-style:"stroustrup"
743 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
744 indent-tabs-mode:nil
745 fill-column:99
746 End:
747 */
748 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :