Code

fixed bug: fixed two of the 'quark > 0' console errors.
[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 /**
432     \return   The value of the parameter identified by the name
433     \brief    Gets a parameter identified by name with the bool placed
434               in value.
435     \param    name    The name of the parameter to get
436     \param    doc    The document to look in for document specific parameters
437     \param    node   The node to look in for a specific parameter
439     Look up in the parameters list, then execute the function on that
440     found parameter.
441 */
442 bool
443 Extension::get_param_bool (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
445     Parameter * param;
447     param = param_shared(name, parameters);
448     return param->get_bool(doc, node);
451 /**
452     \return   The integer value for the parameter specified
453     \brief    Gets a parameter identified by name with the integer placed
454               in value.
455     \param    name    The name of the parameter to get
456     \param    doc    The document to look in for document specific parameters
457     \param    node   The node to look in for a specific parameter
459     Look up in the parameters list, then execute the function on that
460     found parameter.
461 */
462 int
463 Extension::get_param_int (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
465     Parameter * param;
467     param = param_shared(name, parameters);
468     return param->get_int(doc, node);
471 /**
472     \return   The float value for the parameter specified
473     \brief    Gets a parameter identified by name with the float placed
474               in value.
475     \param    name    The name of the parameter to get
476     \param    doc    The document to look in for document specific parameters
477     \param    node   The node to look in for a specific parameter
479     Look up in the parameters list, then execute the function on that
480     found parameter.
481 */
482 float
483 Extension::get_param_float (const gchar * name, const SPDocument * doc, const Inkscape::XML::Node * node)
485     Parameter * param;
486     param = param_shared(name, parameters);
487     return param->get_float(doc, node);
490 /**
491     \return   The passed in value
492     \brief    Sets a parameter identified by name with the boolean
493               in the parameter value.
494     \param    name    The name of the parameter to set
495     \param    value   The value to set the parameter to
496     \param    doc    The document to look in for document specific parameters
497     \param    node   The node to look in for a specific parameter
499     Look up in the parameters list, then execute the function on that
500     found parameter.
501 */
502 bool
503 Extension::set_param_bool (const gchar * name, bool value, SPDocument * doc, Inkscape::XML::Node * node)
505     Parameter * param;
506     param = param_shared(name, parameters);
507     return param->set_bool(value, doc, node);
510 /**
511     \return   The passed in value
512     \brief    Sets a parameter identified by name with the integer
513               in the parameter value.
514     \param    name    The name of the parameter to set
515     \param    value   The value to set the parameter to
516     \param    doc    The document to look in for document specific parameters
517     \param    node   The node to look in for a specific parameter
519     Look up in the parameters list, then execute the function on that
520     found parameter.
521 */
522 int
523 Extension::set_param_int (const gchar * name, int value, SPDocument * doc, Inkscape::XML::Node * node)
525     Parameter * param;
526     param = param_shared(name, parameters);
527     return param->set_int(value, doc, node);
530 /**
531     \return   The passed in value
532     \brief    Sets a parameter identified by name with the integer
533               in the parameter value.
534     \param    name    The name of the parameter to set
535     \param    value   The value to set the parameter to
536     \param    doc    The document to look in for document specific parameters
537     \param    node   The node to look in for a specific parameter
539     Look up in the parameters list, then execute the function on that
540     found parameter.
541 */
542 float
543 Extension::set_param_float (const gchar * name, float value, SPDocument * doc, Inkscape::XML::Node * node)
545     Parameter * param;
546     param = param_shared(name, parameters);
547     return param->set_float(value, doc, node);
550 /**
551     \return   The passed in value
552     \brief    Sets a parameter identified by name with the string
553               in the parameter value.
554     \param    name    The name of the parameter to set
555     \param    value   The value to set the parameter to
556     \param    doc    The document to look in for document specific parameters
557     \param    node   The node to look in for a specific parameter
559     Look up in the parameters list, then execute the function on that
560     found parameter.
561 */
562 const gchar *
563 Extension::set_param_string (const gchar * name, const gchar * value, SPDocument * doc, Inkscape::XML::Node * node)
565     Parameter * param;
566     param = param_shared(name, parameters);
567     return param->set_string(value, doc, node);
570 /** \brief A function to open the error log file. */
571 void
572 Extension::error_file_open (void)
574     gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
575     gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
576     error_file.open(filename);
577     if (!error_file.is_open()) {
578         g_warning(_("Could not create extension error log file '%s'"),
579                   filename);
580     }
581     g_free(filename);
582     g_free(ext_error_file);
583 };
585 /** \brief A function to close the error log file. */
586 void
587 Extension::error_file_close (void)
589     error_file.close();
590 };
592 /** \brief  A widget to represent the inside of an AutoGUI widget */
593 class AutoGUI : public Gtk::VBox {
594     Gtk::Tooltips _tooltips;
595 public:
596     /** \brief  Create an AutoGUI object */
597     AutoGUI (void) : Gtk::VBox() {};
598     /** \brief  Adds a widget with a tool tip into the autogui
599         \param  widg  Widget to add
600         \param  tooltip   Tooltip for the widget
601         
602         If there is no widget, nothing happens.  Otherwise it is just
603         added into the VBox.  If there is a tooltip (non-NULL) then it
604         is placed on the widget.
605     */
606     void addWidget (Gtk::Widget * widg, gchar const * tooltip) {
607         if (widg == NULL) return;
608         this->pack_start(*widg, true, true, 2);
609         if (tooltip != NULL) {
610             _tooltips.set_tip(*widg, Glib::ustring(tooltip));
611         }
612         return;
613     };
614 };
616 /** \brief  A function to automatically generate a GUI using the parameters
617     \return Generated widget
619     This function just goes through each parameter, and calls it's 'get_widget'
620     function to get each widget.  Then, each of those is placed into
621     a Gtk::VBox, which is then returned to the calling function.
623     If there are no parameters, this function just returns NULL.
624 */
625 Gtk::Widget *
626 Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node)
628     if (g_slist_length(parameters) == 0) return NULL;
630     AutoGUI * agui = Gtk::manage(new AutoGUI());
632     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
633         Parameter * param = reinterpret_cast<Parameter *>(list->data);
634         Gtk::Widget * widg = param->get_widget(doc, node);
635         gchar const * tip = param->get_tooltip();
636         agui->addWidget(widg, tip);
637     }
639     agui->show();
640     return agui;
641 };
643 /**
644     \brief  A function to get the parameters in a string form
645     \return A string with all the parameters as command line arguements
647     I don't really like this function, but it works for now.
649     \todo  Do this better.
650 */
651 Glib::ustring *
652 Extension::paramString (void)
654     Glib::ustring * param_string = new Glib::ustring("");
656     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
657         Parameter * param = reinterpret_cast<Parameter *>(list->data);
659         *param_string += " --";
660         *param_string += param->name();
661         *param_string += "=";
662         Glib::ustring * paramstr = param->string();
663         *param_string += *paramstr;
664         delete paramstr;
665     }
667     return param_string;
670 /* Extension editor dialog stuff */
672 Gtk::VBox *
673 Extension::get_info_widget(void)
675     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
677     Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
678     retval->pack_start(*info, true, true, 5);
680     Gtk::Table * table = Gtk::manage(new Gtk::Table());
681     info->add(*table);
683     int row = 0;
684     add_val(_("Name:"), _(name), table, &row);
685     add_val(_("ID:"), id, table, &row);
686     add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
689     retval->show_all();
690     return retval;
693 void
694 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
696     Gtk::Label * label;
697     Gtk::Label * value;
699     (*row)++; 
700     label = Gtk::manage(new Gtk::Label(labelstr));
701     value = Gtk::manage(new Gtk::Label(valuestr));
702     table->attach(*label, 0, 1, (*row) - 1, *row);
703     table->attach(*value, 1, 2, (*row) - 1, *row);
705     label->show();
706     value->show();
708     return;
711 Gtk::VBox *
712 Extension::get_help_widget(void)
714     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
716     if (_help == NULL) {
717         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."));
718         retval->pack_start(*content, true, true, 5);
719         content->set_line_wrap(true);
720         content->show();
721     } else {
725     }
727     retval->show();
728     return retval;
731 Gtk::VBox *
732 Extension::get_params_widget(void)
734     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
735     Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
736     retval->pack_start(*content, true, true, 5);
737     content->show();
738     retval->show();
739     return retval;
742 }  /* namespace Extension */
743 }  /* namespace Inkscape */
746 /*
747   Local Variables:
748   mode:c++
749   c-file-style:"stroustrup"
750   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
751   indent-tabs-mode:nil
752   fill-column:99
753   End:
754 */
755 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :