Code

r16217@tres: ted | 2007-08-10 10:36:27 -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") || !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;
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 */
134     
135     // delete parameters: 
136     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
137         Parameter * param = reinterpret_cast<Parameter *>(list->data);
138         delete param;
139     }
140     g_slist_free(parameters);
141     
142     
143     for (unsigned int i = 0 ; i < _deps.size(); i++) {
144         delete _deps[i];
145     }
146     _deps.clear();
148     return;
151 /**
152     \return   none
153     \brief    A function to set whether the extension should be loaded
154               or unloaded
155     \param    in_state  Which state should the extension be in?
157     It checks to see if this is a state change or not.  If we're changing
158     states it will call the appropriate function in the implementation,
159     load or unload.  Currently, there is no error checking in this
160     function.  There should be.
161 */
162 void
163 Extension::set_state (state_t in_state)
165     if (_state == STATE_DEACTIVATED) return;
166     if (in_state != _state) {
167         /** \todo Need some more error checking here! */
168         switch (in_state) {
169             case STATE_LOADED:
170                 if (imp->load(this))
171                     _state = STATE_LOADED;
173                 if (timer != NULL) {
174                     delete timer;
175                 }
176                 timer = new ExpirationTimer(this);
178                 break;
179             case STATE_UNLOADED:
180                 // std::cout << "Unloading: " << name << std::endl;
181                 imp->unload(this);
182                 _state = STATE_UNLOADED;
184                 if (timer != NULL) {
185                     delete timer;
186                     timer = NULL;
187                 }
188                 break;
189             case STATE_DEACTIVATED:
190                 _state = STATE_DEACTIVATED;
192                 if (timer != NULL) {
193                     delete timer;
194                     timer = NULL;
195                 }
196                 break;
197             default:
198                 break;
199         }
200     }
202     return;
205 /**
206     \return   The state the extension is in
207     \brief    A getter for the state variable.
208 */
209 Extension::state_t
210 Extension::get_state (void)
212     return _state;
215 /**
216     \return  Whether the extension is loaded or not
217     \brief   A quick function to test the state of the extension
218 */
219 bool
220 Extension::loaded (void)
222     return get_state() == STATE_LOADED;
225 /**
226     \return  A boolean saying whether the extension passed the checks
227     \brief   A function to check the validity of the extension
229     This function chekcs to make sure that there is an id, a name, a
230     repr and an implemenation for this extension.  Then it checks all
231     of the dependencies to see if they pass.  Finally, it asks the
232     implmentation to do a check of itself.
234     On each check, if there is a failure, it will print a message to the
235     error log for that failure.  It is important to note that the function
236     keeps executing if it finds an error, to try and get as many of them
237     into the error log as possible.  This should help people debug
238     installations, and figure out what they need to get for the full
239     functionality of Inkscape to be available.
240 */
241 bool
242 Extension::check (void)
244     bool retval = true;
246     // static int i = 0;
247     // std::cout << "Checking module[" << i++ << "]: " << name << std::endl;
249     const char * inx_failure = _("  This is caused by an improper .inx file for this extension."
250                                  "  An improper .inx file could have been caused by a faulty installation of Inkscape.");
251     if (id == NULL) {
252         printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure);
253         retval = false;
254     }
255     if (name == NULL) {
256         printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure);
257         retval = false;
258     }
259     if (repr == NULL) {
260         printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure);
261         retval = false;
262     }
263     if (imp == NULL) {
264         printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure);
265         retval = false;
266     }
268     for (unsigned int i = 0 ; i < _deps.size(); i++) {
269         if (_deps[i]->check() == FALSE) {
270             // std::cout << "Failed: " << *(_deps[i]) << std::endl;
271             printFailure(Glib::ustring(_("a dependency was not met.")));
272             error_file << *_deps[i] << std::endl;
273             retval = false;
274         }
275     }
277     if (retval)
278         return imp->check(this);
279     return retval;
282 /** \brief A quick function to print out a standard start of extension
283            errors in the log.
284     \param reason  A string explaining why this failed
286     Real simple, just put everything into \c error_file.
287 */
288 void
289 Extension::printFailure (Glib::ustring reason)
291         error_file << _("Extension \"") << name << _("\" failed to load because ");
292         error_file << reason.raw();
293         error_file << std::endl;
294         return;
297 /**
298     \return  The XML tree that is used to define the extension
299     \brief   A getter for the internal Repr, does not add a reference.
300 */
301 Inkscape::XML::Node *
302 Extension::get_repr (void)
304     return repr;
307 /**
308     \return  The textual id of this extension
309     \brief   Get the ID of this extension - not a copy don't delete!
310 */
311 gchar *
312 Extension::get_id (void)
314     return id;
317 /**
318     \return  The textual name of this extension
319     \brief   Get the name of this extension - not a copy don't delete!
320 */
321 gchar *
322 Extension::get_name (void)
324     return name;
327 /**
328     \return  None
329     \brief   This function diactivates the extension (which makes it
330              unusable, but not deleted)
332     This function is used to removed an extension from functioning, but
333     not delete it completely.  It sets the state to \c STATE_DEACTIVATED to
334     mark to the world that it has been deactivated.  It also removes
335     the current implementation and replaces it with a standard one.  This
336     makes it so that we don't have to continually check if there is an
337     implementation, but we are gauranteed to have a benign one.
339     \warning It is important to note that there is no 'activate' function.
340     Running this function is irreversable.
341 */
342 void
343 Extension::deactivate (void)
345     set_state(STATE_DEACTIVATED);
347     /* Removing the old implementation, and making this use the default. */
348     /* This should save some memory */
349     delete imp;
350     imp = new Implementation::Implementation();
352     return;
355 /**
356     \return  Whether the extension has been deactivated
357     \brief   Find out the status of the extension
358 */
359 bool
360 Extension::deactivated (void)
362     return get_state() == STATE_DEACTIVATED;
365 /**
366     \return    Parameter structure with a name of 'name'
367     \brief     This function looks through the linked list for a parameter
368                structure with the name of the passed in name
369     \param     name   The name to search for
370     \param     list   The list to look for
372     This is an inline function that is used by all the get_param and
373     set_param functions to find a param_t in the linked list with
374     the passed in name.  It is done as an inline so that it will be
375     optimized into a 'jump' by the compiler.
377     This function can throw a 'param_not_exist' exception if the
378     name is not found.
380     The first thing that this function checks is if the list is NULL.
381     It could be NULL because there are no parameters for this extension
382     or because all of them have been checked (I'll spoil the ending and
383     tell you that this function is called recursively).  If the list
384     is NULL then the 'param_not_exist' exception is thrown.
386     Otherwise, the function looks at the current param_t that the element
387     list points to.  If the name of that param_t matches the passed in
388     name then that param_t is returned.  Otherwise, this function is
389     called again with g_slist_next as a parameter.
390 */
391 Parameter *
392 param_shared (const gchar * name, GSList * list)
394     Parameter * output;
396     if (name == NULL) {
397         throw Extension::param_not_exist();
398     }
399     if (list == NULL) {
400         throw Extension::param_not_exist();
401     }
403     output = static_cast<Parameter *>(list->data);
404     if (!strcmp(output->name(), name)) {
405         return output;
406     }
408     return param_shared(name, g_slist_next(list));
411 /**
412     \return   A constant pointer to the string held by the parameters.
413     \brief    Gets a parameter identified by name with the string placed
414               in value.  It isn't duplicated into the value string.
415     \param    name    The name of the parameter to get
416     \param    doc    The document to look in for document specific parameters
417     \param    node   The node to look in for a specific parameter
419     Look up in the parameters list, then execute the function on that
420     found parameter.
421 */
422 const gchar *
423 Extension::get_param_string (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
425     Parameter * param;
427     param = param_shared(name, parameters);
428     return param->get_string(doc, node);
431 const gchar *
432 Extension::get_param_enum (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
434     Parameter* param = param_shared(name, parameters);
435     return param->get_enum(doc, node);
438 /**
439     \return   The value of the parameter identified by the name
440     \brief    Gets a parameter identified by name with the bool 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
444     \param    node   The node to look in for a specific parameter
446     Look up in the parameters list, then execute the function on that
447     found parameter.
448 */
449 bool
450 Extension::get_param_bool (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
452     Parameter * param;
454     param = param_shared(name, parameters);
455     return param->get_bool(doc, node);
458 /**
459     \return   The integer value for the parameter specified
460     \brief    Gets a parameter identified by name with the integer placed
461               in value.
462     \param    name    The name of the parameter to get
463     \param    doc    The document to look in for document specific parameters
464     \param    node   The node to look in for a specific parameter
466     Look up in the parameters list, then execute the function on that
467     found parameter.
468 */
469 int
470 Extension::get_param_int (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
472     Parameter * param;
474     param = param_shared(name, parameters);
475     return param->get_int(doc, node);
478 /**
479     \return   The float value for the parameter specified
480     \brief    Gets a parameter identified by name with the float placed
481               in value.
482     \param    name    The name of the parameter to get
483     \param    doc    The document to look in for document specific parameters
484     \param    node   The node to look in for a specific parameter
486     Look up in the parameters list, then execute the function on that
487     found parameter.
488 */
489 float
490 Extension::get_param_float (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
492     Parameter * param;
493     param = param_shared(name, parameters);
494     return param->get_float(doc, node);
497 /**
498     \return   The string value for the parameter specified
499     \brief    Gets a parameter identified by name with the float placed
500               in value.
501     \param    name    The name of the parameter to get
502     \param    doc    The document to look in for document specific parameters
503     \param    node   The node to look in for a specific parameter
505     Look up in the parameters list, then execute the function on that
506     found parameter.
507 */
508 SPColor*
509 Extension::get_param_color (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
511     Parameter* param = param_shared(name, parameters);
512     return param->get_color(doc, node);
515 /**
516     \return   The passed in value
517     \brief    Sets a parameter identified by name with the boolean
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
522     \param    node   The node to look in for a specific parameter
524     Look up in the parameters list, then execute the function on that
525     found parameter.
526 */
527 bool
528 Extension::set_param_bool (const gchar * name, bool value, SPDocument * doc, Inkscape::XML::Node * node)
530     Parameter * param;
531     param = param_shared(name, parameters);
532     return param->set_bool(value, doc, node);
535 /**
536     \return   The passed in value
537     \brief    Sets a parameter identified by name with the integer
538               in the parameter value.
539     \param    name    The name of the parameter to set
540     \param    value   The value to set the parameter to
541     \param    doc    The document to look in for document specific parameters
542     \param    node   The node to look in for a specific parameter
544     Look up in the parameters list, then execute the function on that
545     found parameter.
546 */
547 int
548 Extension::set_param_int (const gchar * name, int value, SPDocument * doc, Inkscape::XML::Node * node)
550     Parameter * param;
551     param = param_shared(name, parameters);
552     return param->set_int(value, doc, node);
555 /**
556     \return   The passed in value
557     \brief    Sets a parameter identified by name with the integer
558               in the parameter value.
559     \param    name    The name of the parameter to set
560     \param    value   The value to set the parameter to
561     \param    doc    The document to look in for document specific parameters
562     \param    node   The node to look in for a specific parameter
564     Look up in the parameters list, then execute the function on that
565     found parameter.
566 */
567 float
568 Extension::set_param_float (const gchar * name, float value, SPDocument * doc, Inkscape::XML::Node * node)
570     Parameter * param;
571     param = param_shared(name, parameters);
572     return param->set_float(value, doc, node);
575 /**
576     \return   The passed in value
577     \brief    Sets a parameter identified by name with the string
578               in the parameter value.
579     \param    name    The name of the parameter to set
580     \param    value   The value to set the parameter to
581     \param    doc    The document to look in for document specific parameters
582     \param    node   The node to look in for a specific parameter
584     Look up in the parameters list, then execute the function on that
585     found parameter.
586 */
587 const gchar *
588 Extension::set_param_string (const gchar * name, const gchar * value, SPDocument * doc, Inkscape::XML::Node * node)
590     Parameter * param;
591     param = param_shared(name, parameters);
592     return param->set_string(value, doc, node);
595 /**
596     \return   The passed in value
597     \brief    Sets a parameter identified by name with the string
598               in the parameter value.
599     \param    name    The name of the parameter to set
600     \param    value   The value to set the parameter to
601     \param    doc    The document to look in for document specific parameters
602     \param    node   The node to look in for a specific parameter
604     Look up in the parameters list, then execute the function on that
605     found parameter.
606 */
607 SPColor*
608 Extension::set_param_color (const gchar * name, SPColor* color, SPDocument * doc, Inkscape::XML::Node * node)
610     Parameter* param = param_shared(name, parameters);
611     return param->set_color(color, doc, node);
614 /** \brief A function to open the error log file. */
615 void
616 Extension::error_file_open (void)
618     gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
619     gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
620     error_file.open(filename);
621     if (!error_file.is_open()) {
622         g_warning(_("Could not create extension error log file '%s'"),
623                   filename);
624     }
625     g_free(filename);
626     g_free(ext_error_file);
627 };
629 /** \brief A function to close the error log file. */
630 void
631 Extension::error_file_close (void)
633     error_file.close();
634 };
636 /** \brief  A widget to represent the inside of an AutoGUI widget */
637 class AutoGUI : public Gtk::VBox {
638     Gtk::Tooltips _tooltips;
639 public:
640     /** \brief  Create an AutoGUI object */
641     AutoGUI (void) : Gtk::VBox() {};
642     /** \brief  Adds a widget with a tool tip into the autogui
643         \param  widg  Widget to add
644         \param  tooltip   Tooltip for the widget
645         
646         If there is no widget, nothing happens.  Otherwise it is just
647         added into the VBox.  If there is a tooltip (non-NULL) then it
648         is placed on the widget.
649     */
650     void addWidget (Gtk::Widget * widg, gchar const * tooltip) {
651         if (widg == NULL) return;
652         this->pack_start(*widg, true, true, 2);
653         if (tooltip != NULL) {
654             _tooltips.set_tip(*widg, Glib::ustring(tooltip));
655         }
656         return;
657     };
658 };
660 /** \brief  A function to automatically generate a GUI using the parameters
661     \return Generated widget
663     This function just goes through each parameter, and calls it's 'get_widget'
664     function to get each widget.  Then, each of those is placed into
665     a Gtk::VBox, which is then returned to the calling function.
667     If there are no parameters, this function just returns NULL.
668 */
669 Gtk::Widget *
670 Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
672     if (g_slist_length(parameters) == 0) return NULL;
674     AutoGUI * agui = Gtk::manage(new AutoGUI());
676     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
677         Parameter * param = reinterpret_cast<Parameter *>(list->data);
678         Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
679         gchar const * tip = param->get_tooltip();
680         agui->addWidget(widg, tip);
681     }
683     agui->show();
684     return agui;
685 };
687 /**
688     \brief  A function to get the parameters in a string form
689     \return An array with all the parameters in it.
691 */
692 void
693 Extension::paramListString (std::list <std::string> &retlist)
695     //std::list <std::string> retarray;
697     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
698         Parameter * param = reinterpret_cast<Parameter *>(list->data);
700         std::string param_string;
701         param_string += "--";
702         param_string += param->name();
703         param_string += "=";
704         Glib::ustring * out = param->string();
705         param_string += *out;
706         delete out;
708         retlist.insert(retlist.end(), param_string);
709     }
710     //g_message("paramstring=%s", param_string->c_str());
712     return;
715 /* Extension editor dialog stuff */
717 Gtk::VBox *
718 Extension::get_info_widget(void)
720     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
722     Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
723     retval->pack_start(*info, true, true, 5);
725     Gtk::Table * table = Gtk::manage(new Gtk::Table());
726     info->add(*table);
728     int row = 0;
729     add_val(_("Name:"), _(name), table, &row);
730     add_val(_("ID:"), id, table, &row);
731     add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
734     retval->show_all();
735     return retval;
738 void
739 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
741     Gtk::Label * label;
742     Gtk::Label * value;
744     (*row)++; 
745     label = Gtk::manage(new Gtk::Label(labelstr));
746     value = Gtk::manage(new Gtk::Label(valuestr));
747     table->attach(*label, 0, 1, (*row) - 1, *row);
748     table->attach(*value, 1, 2, (*row) - 1, *row);
750     label->show();
751     value->show();
753     return;
756 Gtk::VBox *
757 Extension::get_help_widget(void)
759     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
761     if (_help == NULL) {
762         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."));
763         retval->pack_start(*content, true, true, 5);
764         content->set_line_wrap(true);
765         content->show();
766     } else {
770     }
772     retval->show();
773     return retval;
776 Gtk::VBox *
777 Extension::get_params_widget(void)
779     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
780     Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
781     retval->pack_start(*content, true, true, 5);
782     content->show();
783     retval->show();
784     return retval;
787 }  /* namespace Extension */
788 }  /* namespace Inkscape */
791 /*
792   Local Variables:
793   mode:c++
794   c-file-style:"stroustrup"
795   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
796   indent-tabs-mode:nil
797   fill-column:99
798   End:
799 */
800 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :