Code

r11472@tres: ted | 2006-04-22 20:32:45 -0700
[inkscape.git] / src / extension / extension.cpp
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")) {
95                 Parameter * param;
96                 param = Parameter::make(child_repr, this);
97                 if (param != NULL)
98                     parameters = g_slist_append(parameters, param);
99             } /* param */
100             if (!strcmp(chname, "dependency")) {
101                 _deps.push_back(new Dependency(child_repr));
102             } /* param */
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;
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)
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;
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)
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;
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)
204     return _state;
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)
214     return get_state() == STATE_LOADED;
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)
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;
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)
283         error_file << _("Extension \"") << name << _("\" failed to load because ");
284         error_file << reason.raw();
285         error_file << std::endl;
286         return;
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)
296     return repr;
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)
306     return id;
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)
316     return name;
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)
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;
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)
354     return get_state() == STATE_DEACTIVATED;
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)
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));
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 Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
417     Parameter * param;
419     param = param_shared(name, parameters);
420     return param->get_string(doc, node);
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 Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
437     Parameter * param;
439     param = param_shared(name, parameters);
440     return param->get_bool(doc, node);
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 Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
457     Parameter * param;
459     param = param_shared(name, parameters);
460     return param->get_int(doc, node);
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 Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
477     Parameter * param;
478     param = param_shared(name, parameters);
479     return param->get_float(doc, node);
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, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
497     Parameter * param;
498     param = param_shared(name, parameters);
499     return param->set_bool(value, doc, node);
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, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
517     Parameter * param;
518     param = param_shared(name, parameters);
519     return param->set_int(value, doc, node);
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, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
537     Parameter * param;
538     param = param_shared(name, parameters);
539     return param->set_float(value, doc, node);
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, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
557     Parameter * param;
558     param = param_shared(name, parameters);
559     return param->set_string(value, doc, node);
562 /** \brief A function to open the error log file. */
563 void
564 Extension::error_file_open (void)
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)
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
593         
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);
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 (void)
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();
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)
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;
663 /* Extension editor dialog stuff */
665 Gtk::VBox *
666 Extension::get_info_widget(void)
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;
686 void
687 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
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;
704 Gtk::VBox *
705 Extension::get_help_widget(void)
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;
724 Gtk::VBox *
725 Extension::get_params_widget(void)
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;
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 :