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