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
408 \param node The node to look in for a specific parameter
410 Look up in the parameters list, then execute the function on that
411 found parameter.
412 */
413 const gchar *
414 Extension::get_param_string (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
415 {
416 Parameter * param;
418 param = param_shared(name, parameters);
419 return param->get_string(doc, node);
420 }
422 /**
423 \return The value of the parameter identified by the name
424 \brief Gets a parameter identified by name with the bool placed
425 in value.
426 \param name The name of the parameter to get
427 \param doc The document to look in for document specific parameters
428 \param node The node to look in for a specific parameter
430 Look up in the parameters list, then execute the function on that
431 found parameter.
432 */
433 bool
434 Extension::get_param_bool (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
435 {
436 Parameter * param;
438 param = param_shared(name, parameters);
439 return param->get_bool(doc, node);
440 }
442 /**
443 \return The integer value for the parameter specified
444 \brief Gets a parameter identified by name with the integer placed
445 in value.
446 \param name The name of the parameter to get
447 \param doc The document to look in for document specific parameters
448 \param node The node to look in for a specific parameter
450 Look up in the parameters list, then execute the function on that
451 found parameter.
452 */
453 int
454 Extension::get_param_int (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
455 {
456 Parameter * param;
458 param = param_shared(name, parameters);
459 return param->get_int(doc, node);
460 }
462 /**
463 \return The float value for the parameter specified
464 \brief Gets a parameter identified by name with the float placed
465 in value.
466 \param name The name of the parameter to get
467 \param doc The document to look in for document specific parameters
468 \param node The node to look in for a specific parameter
470 Look up in the parameters list, then execute the function on that
471 found parameter.
472 */
473 float
474 Extension::get_param_float (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
475 {
476 Parameter * param;
477 param = param_shared(name, parameters);
478 return param->get_float(doc, node);
479 }
481 /**
482 \return The passed in value
483 \brief Sets a parameter identified by name with the boolean
484 in the parameter value.
485 \param name The name of the parameter to set
486 \param value The value to set the parameter to
487 \param doc The document to look in for document specific parameters
488 \param node The node to look in for a specific parameter
490 Look up in the parameters list, then execute the function on that
491 found parameter.
492 */
493 bool
494 Extension::set_param_bool (const gchar * name, bool value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
495 {
496 Parameter * param;
497 param = param_shared(name, parameters);
498 return param->set_bool(value, doc, node);
499 }
501 /**
502 \return The passed in value
503 \brief Sets a parameter identified by name with the integer
504 in the parameter value.
505 \param name The name of the parameter to set
506 \param value The value to set the parameter to
507 \param doc The document to look in for document specific parameters
508 \param node The node to look in for a specific parameter
510 Look up in the parameters list, then execute the function on that
511 found parameter.
512 */
513 int
514 Extension::set_param_int (const gchar * name, int value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
515 {
516 Parameter * param;
517 param = param_shared(name, parameters);
518 return param->set_int(value, doc, node);
519 }
521 /**
522 \return The passed in value
523 \brief Sets a parameter identified by name with the integer
524 in the parameter value.
525 \param name The name of the parameter to set
526 \param value The value to set the parameter to
527 \param doc The document to look in for document specific parameters
528 \param node The node to look in for a specific parameter
530 Look up in the parameters list, then execute the function on that
531 found parameter.
532 */
533 float
534 Extension::set_param_float (const gchar * name, float value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
535 {
536 Parameter * param;
537 param = param_shared(name, parameters);
538 return param->set_float(value, doc, node);
539 }
541 /**
542 \return The passed in value
543 \brief Sets a parameter identified by name with the string
544 in the parameter value.
545 \param name The name of the parameter to set
546 \param value The value to set the parameter to
547 \param doc The document to look in for document specific parameters
548 \param node The node to look in for a specific parameter
550 Look up in the parameters list, then execute the function on that
551 found parameter.
552 */
553 const gchar *
554 Extension::set_param_string (const gchar * name, const gchar * value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
555 {
556 Parameter * param;
557 param = param_shared(name, parameters);
558 return param->set_string(value, doc, node);
559 }
561 /** \brief A function to open the error log file. */
562 void
563 Extension::error_file_open (void)
564 {
565 gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
566 gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
567 error_file.open(filename);
568 if (!error_file.is_open()) {
569 g_warning(_("Could not create extension error log file '%s'"),
570 filename);
571 }
572 g_free(filename);
573 g_free(ext_error_file);
574 };
576 /** \brief A function to close the error log file. */
577 void
578 Extension::error_file_close (void)
579 {
580 error_file.close();
581 };
583 /** \brief A function to automatically generate a GUI using the parameters
584 \return Generated widget
586 This function just goes through each parameter, and calls it's 'get_widget'
587 function to get each widget. Then, each of those is placed into
588 a Gtk::VBox, which is then returned to the calling function.
590 If there are no parameters, this function just returns NULL.
591 */
592 Gtk::Widget *
593 Extension::autogui (void)
594 {
595 if (g_slist_length(parameters) == 0) return NULL;
597 Gtk::VBox * vbox = new Gtk::VBox();
598 vbox = new Gtk::VBox();
600 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
601 Parameter * param = reinterpret_cast<Parameter *>(list->data);
602 Gtk::Widget * widg = param->get_widget();
603 if (widg != NULL)
604 vbox->pack_start(*widg, true, true);
605 }
607 vbox->show();
608 return vbox;
609 };
611 /**
612 \brief A function to get the parameters in a string form
613 \return A string with all the parameters as command line arguements
615 I don't really like this function, but it works for now.
617 \todo Do this better.
618 */
619 Glib::ustring *
620 Extension::paramString (void)
621 {
622 Glib::ustring * param_string = new Glib::ustring("");
624 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
625 Parameter * param = reinterpret_cast<Parameter *>(list->data);
627 *param_string += " --";
628 *param_string += param->name();
629 *param_string += "=";
630 Glib::ustring * paramstr = param->string();
631 *param_string += *paramstr;
632 delete paramstr;
633 }
635 return param_string;
636 }
638 /* Extension editor dialog stuff */
640 Gtk::VBox *
641 Extension::get_info_widget(void)
642 {
643 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
645 Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
646 retval->pack_start(*info, true, true, 5);
648 Gtk::Table * table = Gtk::manage(new Gtk::Table());
649 info->add(*table);
651 int row = 0;
652 add_val(_("Name:"), _(name), table, &row);
653 add_val(_("ID:"), id, table, &row);
654 add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
657 retval->show_all();
658 return retval;
659 }
661 void
662 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
663 {
664 Gtk::Label * label;
665 Gtk::Label * value;
667 (*row)++;
668 label = Gtk::manage(new Gtk::Label(labelstr));
669 value = Gtk::manage(new Gtk::Label(valuestr));
670 table->attach(*label, 0, 1, (*row) - 1, *row);
671 table->attach(*value, 1, 2, (*row) - 1, *row);
673 label->show();
674 value->show();
676 return;
677 }
679 Gtk::VBox *
680 Extension::get_help_widget(void)
681 {
682 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
684 if (_help == NULL) {
685 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."));
686 retval->pack_start(*content, true, true, 5);
687 content->set_line_wrap(true);
688 content->show();
689 } else {
693 }
695 retval->show();
696 return retval;
697 }
699 Gtk::VBox *
700 Extension::get_params_widget(void)
701 {
702 Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
703 Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
704 retval->pack_start(*content, true, true, 5);
705 content->show();
706 retval->show();
707 return retval;
708 }
710 } /* namespace Extension */
711 } /* namespace Inkscape */
714 /*
715 Local Variables:
716 mode:c++
717 c-file-style:"stroustrup"
718 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
719 indent-tabs-mode:nil
720 fill-column:99
721 End:
722 */
723 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :