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>
30 #include "inkscape.h"
31 #include "extension/implementation/implementation.h"
33 #include "db.h"
34 #include "dependency.h"
35 #include "timer.h"
36 #include "parameter.h"
38 namespace Inkscape {
39 namespace Extension {
41 /* Inkscape::Extension::Extension */
43 std::vector<const gchar *> Extension::search_path;
44 std::ofstream Extension::error_file;
46 Parameter * param_shared (const gchar * name, GSList * list);
48 /**
49 \return none
50 \brief Constructs an Extension from a Inkscape::XML::Node
51 \param in_repr The repr that should be used to build it
53 This function is the basis of building an extension for Inkscape. It
54 currently extracts the fields from the Repr that are used in the
55 extension. The Repr will likely include other children that are
56 not related to the module directly. If the Repr does not include
57 a name and an ID the module will be left in an errored state.
58 */
59 Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : _help(NULL)
60 {
61 repr = in_repr;
62 Inkscape::GC::anchor(in_repr);
64 id = NULL;
65 name = NULL;
66 _state = STATE_UNLOADED;
67 parameters = NULL;
69 if (in_imp == NULL) {
70 imp = new Implementation::Implementation();
71 } else {
72 imp = in_imp;
73 }
75 // printf("Extension Constructor: ");
76 if (repr != NULL) {
77 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
78 /* TODO: Handle what happens if we don't have these two */
79 while (child_repr != NULL) {
80 char const * chname = child_repr->name();
81 if (chname[0] == '_') /* Allow _ for translation of tags */
82 chname++;
83 if (!strcmp(chname, "id")) {
84 gchar const *val = sp_repr_children(child_repr)->content();
85 id = g_strdup (val);
86 } /* id */
87 if (!strcmp(chname, "name")) {
88 name = g_strdup (sp_repr_children(child_repr)->content());
89 } /* name */
90 if (!strcmp(chname, "help")) {
91 _help = g_strdup (sp_repr_children(child_repr)->content());
92 } /* name */
93 if (!strcmp(chname, "param")) {
94 Parameter * param;
95 param = Parameter::make(child_repr, this);
96 if (param != NULL)
97 parameters = g_slist_append(parameters, param);
98 } /* param */
99 if (!strcmp(chname, "dependency")) {
100 _deps.push_back(new Dependency(child_repr));
101 } /* param */
102 child_repr = sp_repr_next(child_repr);
103 }
105 db.register_ext (this);
106 }
107 // printf("%s\n", name);
108 timer = NULL;
110 return;
111 }
113 /**
114 \return none
115 \brief Destroys the Extension
117 This function frees all of the strings that could be attached
118 to the extension and also unreferences the repr. This is better
119 than freeing it because it may (I wouldn't know why) be referenced
120 in another place.
121 */
122 Extension::~Extension (void)
123 {
124 // printf("Extension Destructor: %s\n", name);
125 set_state(STATE_UNLOADED);
126 db.unregister_ext(this);
127 Inkscape::GC::release(repr);
128 g_free(id);
129 g_free(name);
130 delete timer;
131 timer = NULL;
132 /** \todo Need to do parameters here */
134 for (unsigned int i = 0 ; i < _deps.size(); i++) {
135 delete _deps[i];
136 }
137 _deps.clear();
139 return;
140 }
142 /**
143 \return none
144 \brief A function to set whether the extension should be loaded
145 or unloaded
146 \param in_state Which state should the extension be in?
148 It checks to see if this is a state change or not. If we're changing
149 states it will call the appropriate function in the implementation,
150 load or unload. Currently, there is no error checking in this
151 function. There should be.
152 */
153 void
154 Extension::set_state (state_t in_state)
155 {
156 if (_state == STATE_DEACTIVATED) return;
157 if (in_state != _state) {
158 /** \todo Need some more error checking here! */
159 switch (in_state) {
160 case STATE_LOADED:
161 if (imp->load(this))
162 _state = STATE_LOADED;
164 if (timer != NULL) {
165 delete timer;
166 }
167 timer = new ExpirationTimer(this);
169 break;
170 case STATE_UNLOADED:
171 // std::cout << "Unloading: " << name << std::endl;
172 imp->unload(this);
173 _state = STATE_UNLOADED;
175 if (timer != NULL) {
176 delete timer;
177 timer = NULL;
178 }
179 break;
180 case STATE_DEACTIVATED:
181 _state = STATE_DEACTIVATED;
183 if (timer != NULL) {
184 delete timer;
185 timer = NULL;
186 }
187 break;
188 default:
189 break;
190 }
191 }
193 return;
194 }
196 /**
197 \return The state the extension is in
198 \brief A getter for the state variable.
199 */
200 Extension::state_t
201 Extension::get_state (void)
202 {
203 return _state;
204 }
206 /**
207 \return Whether the extension is loaded or not
208 \brief A quick function to test the state of the extension
209 */
210 bool
211 Extension::loaded (void)
212 {
213 return get_state() == STATE_LOADED;
214 }
216 /**
217 \return A boolean saying whether the extension passed the checks
218 \brief A function to check the validity of the extension
220 This function chekcs to make sure that there is an id, a name, a
221 repr and an implemenation for this extension. Then it checks all
222 of the dependencies to see if they pass. Finally, it asks the
223 implmentation to do a check of itself.
225 On each check, if there is a failure, it will print a message to the
226 error log for that failure. It is important to note that the function
227 keeps executing if it finds an error, to try and get as many of them
228 into the error log as possible. This should help people debug
229 installations, and figure out what they need to get for the full
230 functionality of Inkscape to be available.
231 */
232 bool
233 Extension::check (void)
234 {
235 bool retval = true;
237 // static int i = 0;
238 // std::cout << "Checking module[" << i++ << "]: " << name << std::endl;
240 const char * inx_failure = _(" This is caused by an improper .inx file for this extension."
241 " An improper .inx file could have been caused by a faulty installation of Inkscape.");
242 if (id == NULL) {
243 printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure);
244 retval = false;
245 }
246 if (name == NULL) {
247 printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure);
248 retval = false;
249 }
250 if (repr == NULL) {
251 printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure);
252 retval = false;
253 }
254 if (imp == NULL) {
255 printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure);
256 retval = false;
257 }
259 for (unsigned int i = 0 ; i < _deps.size(); i++) {
260 if (_deps[i]->check() == FALSE) {
261 // std::cout << "Failed: " << *(_deps[i]) << std::endl;
262 printFailure(Glib::ustring(_("a dependency was not met.")));
263 error_file << *_deps[i] << std::endl;
264 retval = false;
265 }
266 }
268 if (retval)
269 return imp->check(this);
270 return retval;
271 }
273 /** \brief A quick function to print out a standard start of extension
274 errors in the log.
275 \param reason A string explaining why this failed
277 Real simple, just put everything into \c error_file.
278 */
279 void
280 Extension::printFailure (Glib::ustring reason)
281 {
282 error_file << _("Extension \"") << name << _("\" failed to load because ");
283 error_file << reason.raw();
284 error_file << std::endl;
285 return;
286 }
288 /**
289 \return The XML tree that is used to define the extension
290 \brief A getter for the internal Repr, does not add a reference.
291 */
292 Inkscape::XML::Node *
293 Extension::get_repr (void)
294 {
295 return repr;
296 }
298 /**
299 \return The textual id of this extension
300 \brief Get the ID of this extension - not a copy don't delete!
301 */
302 gchar *
303 Extension::get_id (void)
304 {
305 return id;
306 }
308 /**
309 \return The textual name of this extension
310 \brief Get the name of this extension - not a copy don't delete!
311 */
312 gchar *
313 Extension::get_name (void)
314 {
315 return name;
316 }
318 /**
319 \return None
320 \brief This function diactivates the extension (which makes it
321 unusable, but not deleted)
323 This function is used to removed an extension from functioning, but
324 not delete it completely. It sets the state to \c STATE_DEACTIVATED to
325 mark to the world that it has been deactivated. It also removes
326 the current implementation and replaces it with a standard one. This
327 makes it so that we don't have to continually check if there is an
328 implementation, but we are gauranteed to have a benign one.
330 \warning It is important to note that there is no 'activate' function.
331 Running this function is irreversable.
332 */
333 void
334 Extension::deactivate (void)
335 {
336 set_state(STATE_DEACTIVATED);
338 /* Removing the old implementation, and making this use the default. */
339 /* This should save some memory */
340 delete imp;
341 imp = new Implementation::Implementation();
343 return;
344 }
346 /**
347 \return Whether the extension has been deactivated
348 \brief Find out the status of the extension
349 */
350 bool
351 Extension::deactivated (void)
352 {
353 return get_state() == STATE_DEACTIVATED;
354 }
356 /**
357 \return Parameter structure with a name of 'name'
358 \brief This function looks through the linked list for a parameter
359 structure with the name of the passed in name
360 \param name The name to search for
361 \param list The list to look for
363 This is an inline function that is used by all the get_param and
364 set_param functions to find a param_t in the linked list with
365 the passed in name. It is done as an inline so that it will be
366 optimized into a 'jump' by the compiler.
368 This function can throw a 'param_not_exist' exception if the
369 name is not found.
371 The first thing that this function checks is if the list is NULL.
372 It could be NULL because there are no parameters for this extension
373 or because all of them have been checked (I'll spoil the ending and
374 tell you that this function is called recursively). If the list
375 is NULL then the 'param_not_exist' exception is thrown.
377 Otherwise, the function looks at the current param_t that the element
378 list points to. If the name of that param_t matches the passed in
379 name then that param_t is returned. Otherwise, this function is
380 called again with g_slist_next as a parameter.
381 */
382 Parameter *
383 param_shared (const gchar * name, GSList * list)
384 {
385 Parameter * output;
387 if (name == NULL) {
388 throw Extension::param_not_exist();
389 }
390 if (list == NULL) {
391 throw Extension::param_not_exist();
392 }
394 output = static_cast<Parameter *>(list->data);
395 if (!strcmp(output->name(), name)) {
396 return output;
397 }
399 return param_shared(name, g_slist_next(list));
400 }
402 /**
403 \return A constant pointer to the string held by the parameters.
404 \brief Gets a parameter identified by name with the string placed
405 in value. It isn't duplicated into the value string.
406 \param name The name of the parameter to get
407 \param doc The document to look in for document specific parameters
409 Look up in the parameters list, then execute the function on that
410 found parameter.
411 */
412 const gchar *
413 Extension::get_param_string (const gchar * name, const Inkscape::XML::Document * doc)
414 {
415 Parameter * param;
417 param = param_shared(name, parameters);
418 return param->get_string(doc);
419 }
421 /**
422 \return The value of the parameter identified by the name
423 \brief Gets a parameter identified by name with the bool placed
424 in value.
425 \param name The name of the parameter to get
426 \param doc The document to look in for document specific parameters
428 Look up in the parameters list, then execute the function on that
429 found parameter.
430 */
431 bool
432 Extension::get_param_bool (const gchar * name, const Inkscape::XML::Document * doc)
433 {
434 Parameter * param;
436 param = param_shared(name, parameters);
437 return param->get_bool(doc);
438 }
440 /**
441 \return The integer value for the parameter specified
442 \brief Gets a parameter identified by name with the integer placed
443 in value.
444 \param name The name of the parameter to get
445 \param doc The document to look in for document specific parameters
447 Look up in the parameters list, then execute the function on that
448 found parameter.
449 */
450 int
451 Extension::get_param_int (const gchar * name, const Inkscape::XML::Document * doc)
452 {
453 Parameter * param;
455 param = param_shared(name, parameters);
456 return param->get_int(doc);
457 }
459 /**
460 \return The float value for the parameter specified
461 \brief Gets a parameter identified by name with the float placed
462 in value.
463 \param name The name of the parameter to get
464 \param doc The document to look in for document specific parameters
466 Look up in the parameters list, then execute the function on that
467 found parameter.
468 */
469 float
470 Extension::get_param_float (const gchar * name, const Inkscape::XML::Document * doc)
471 {
472 Parameter * param;
473 param = param_shared(name, parameters);
474 return param->get_float(doc);
475 }
477 /**
478 \return The passed in value
479 \brief Sets a parameter identified by name with the boolean
480 in the parameter value.
481 \param name The name of the parameter to set
482 \param value The value to set the parameter to
483 \param doc The document to look in for document specific parameters
485 Look up in the parameters list, then execute the function on that
486 found parameter.
487 */
488 bool
489 Extension::set_param_bool (const gchar * name, bool value, Inkscape::XML::Document * doc)
490 {
491 Parameter * param;
492 param = param_shared(name, parameters);
493 return param->set_bool(value, doc);
494 }
496 /**
497 \return The passed in value
498 \brief Sets a parameter identified by name with the integer
499 in the parameter value.
500 \param name The name of the parameter to set
501 \param value The value to set the parameter to
502 \param doc The document to look in for document specific parameters
504 Look up in the parameters list, then execute the function on that
505 found parameter.
506 */
507 int
508 Extension::set_param_int (const gchar * name, int value, Inkscape::XML::Document * doc)
509 {
510 Parameter * param;
511 param = param_shared(name, parameters);
512 return param->set_int(value, doc);
513 }
515 /**
516 \return The passed in value
517 \brief Sets a parameter identified by name with the integer
518 in the parameter value.
519 \param name The name of the parameter to set
520 \param value The value to set the parameter to
521 \param doc The document to look in for document specific parameters
523 Look up in the parameters list, then execute the function on that
524 found parameter.
525 */
526 float
527 Extension::set_param_float (const gchar * name, float value, Inkscape::XML::Document * doc)
528 {
529 Parameter * param;
530 param = param_shared(name, parameters);
531 return param->set_float(value, doc);
532 }
534 /**
535 \return The passed in value
536 \brief Sets a parameter identified by name with the string
537 in the parameter value.
538 \param name The name of the parameter to set
539 \param value The value to set the parameter to
540 \param doc The document to look in for document specific parameters
542 Look up in the parameters list, then execute the function on that
543 found parameter.
544 */
545 const gchar *
546 Extension::set_param_string (const gchar * name, const gchar * value, Inkscape::XML::Document * doc)
547 {
548 Parameter * param;
549 param = param_shared(name, parameters);
550 return param->set_string(value, doc);
551 }
553 /** \brief A function to open the error log file. */
554 void
555 Extension::error_file_open (void)
556 {
557 gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
558 gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
559 error_file.open(filename);
560 if (!error_file.is_open()) {
561 g_warning(_("Could not create extension error log file '%s'"),
562 filename);
563 }
564 g_free(filename);
565 g_free(ext_error_file);
566 };
568 /** \brief A function to close the error log file. */
569 void
570 Extension::error_file_close (void)
571 {
572 error_file.close();
573 };
575 /** \brief A function to automatically generate a GUI using the parameters
576 \return Generated widget
578 This function just goes through each parameter, and calls it's 'get_widget'
579 function to get each widget. Then, each of those is placed into
580 a Gtk::VBox, which is then returned to the calling function.
582 If there are no parameters, this function just returns NULL.
583 */
584 Gtk::Widget *
585 Extension::autogui (void)
586 {
587 if (g_slist_length(parameters) == 0) return NULL;
589 Gtk::VBox * vbox = new Gtk::VBox();
590 vbox = new Gtk::VBox();
592 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
593 Parameter * param = reinterpret_cast<Parameter *>(list->data);
594 Gtk::Widget * widg = param->get_widget();
595 if (widg != NULL)
596 vbox->pack_start(*widg, true, true);
597 }
599 vbox->show();
600 return vbox;
601 };
603 /**
604 \brief A function to get the parameters in a string form
605 \return A string with all the parameters as command line arguements
607 I don't really like this function, but it works for now.
609 \todo Do this better.
610 */
611 Glib::ustring *
612 Extension::paramString (void)
613 {
614 Glib::ustring * param_string = new Glib::ustring("");
616 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
617 Parameter * param = reinterpret_cast<Parameter *>(list->data);
619 *param_string += " --";
620 *param_string += param->name();
621 *param_string += "=";
622 Glib::ustring * paramstr = param->string();
623 *param_string += *paramstr;
624 delete paramstr;
625 }
627 return param_string;
628 }
630 /* Extension editor dialog stuff */
632 Gtk::VBox *
633 Extension::get_info_widget(void)
634 {
635 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
637 Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
638 retval->pack_start(*info, true, true, 5);
640 Gtk::Table * table = Gtk::manage(new Gtk::Table());
641 info->add(*table);
643 int row = 0;
644 add_val(_("Name:"), _(name), table, &row);
645 add_val(_("ID:"), id, table, &row);
646 add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
649 retval->show_all();
650 return retval;
651 }
653 void
654 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
655 {
656 Gtk::Label * label;
657 Gtk::Label * value;
659 (*row)++;
660 label = Gtk::manage(new Gtk::Label(labelstr));
661 value = Gtk::manage(new Gtk::Label(valuestr));
662 table->attach(*label, 0, 1, (*row) - 1, *row);
663 table->attach(*value, 1, 2, (*row) - 1, *row);
665 label->show();
666 value->show();
668 return;
669 }
671 Gtk::VBox *
672 Extension::get_help_widget(void)
673 {
674 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
676 if (_help == NULL) {
677 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."));
678 retval->pack_start(*content, true, true, 5);
679 content->set_line_wrap(true);
680 content->show();
681 } else {
685 }
687 retval->show();
688 return retval;
689 }
691 Gtk::VBox *
692 Extension::get_params_widget(void)
693 {
694 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
695 Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
696 retval->pack_start(*content, true, true, 5);
697 content->show();
698 retval->show();
699 return retval;
700 }
702 } /* namespace Extension */
703 } /* namespace Inkscape */
706 /*
707 Local Variables:
708 mode:c++
709 c-file-style:"stroustrup"
710 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
711 indent-tabs-mode:nil
712 fill-column:99
713 End:
714 */
715 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :