Code

r11140@tres: ted | 2006-03-27 21:37:00 -0800
[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>
30 #include "inkscape.h"
31 #include "extension/implementation/implementation.h"
33 #include "db.h"
34 #include "dependency.h"
35 #include "timer.h"
36 #include "parameter.h"
38 namespace Inkscape {
39 namespace Extension {
41 /* Inkscape::Extension::Extension */
43 std::vector<const gchar *> Extension::search_path;
44 std::ofstream Extension::error_file;
46 Parameter * param_shared (const gchar * name, GSList * list);
48 /**
49     \return  none
50     \brief   Constructs an Extension from a Inkscape::XML::Node
51     \param   in_repr    The repr that should be used to build it
53     This function is the basis of building an extension for Inkscape.  It
54     currently extracts the fields from the Repr that are used in the
55     extension.  The Repr will likely include other children that are
56     not related to the module directly.  If the Repr does not include
57     a name and an ID the module will be left in an errored state.
58 */
59 Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : _help(NULL)
60 {
61     repr = in_repr;
62     Inkscape::GC::anchor(in_repr);
64     id = NULL;
65     name = NULL;
66     _state = STATE_UNLOADED;
67     parameters = NULL;
69     if (in_imp == NULL) {
70         imp = new Implementation::Implementation();
71     } else {
72         imp = in_imp;
73     }
75     // printf("Extension Constructor: ");
76     if (repr != NULL) {
77         Inkscape::XML::Node *child_repr = sp_repr_children(repr);
78         /* TODO: Handle what happens if we don't have these two */
79         while (child_repr != NULL) {
80             char const * chname = child_repr->name();
81             if (chname[0] == '_') /* Allow _ for translation of tags */
82                 chname++;
83             if (!strcmp(chname, "id")) {
84                 gchar const *val = sp_repr_children(child_repr)->content();
85                 id = g_strdup (val);
86             } /* id */
87             if (!strcmp(chname, "name")) {
88                 name = g_strdup (sp_repr_children(child_repr)->content());
89             } /* name */
90             if (!strcmp(chname, "help")) {
91                 _help = g_strdup (sp_repr_children(child_repr)->content());
92             } /* name */
93             if (!strcmp(chname, "param")) {
94                 Parameter * param;
95                 param = Parameter::make(child_repr, this);
96                 if (param != NULL)
97                     parameters = g_slist_append(parameters, param);
98             } /* param */
99             if (!strcmp(chname, "dependency")) {
100                 _deps.push_back(new Dependency(child_repr));
101             } /* param */
102             child_repr = sp_repr_next(child_repr);
103         }
105         db.register_ext (this);
106     }
107     // printf("%s\n", name);
108     timer = NULL;
110     return;
113 /**
114     \return   none
115     \brief    Destroys the Extension
117     This function frees all of the strings that could be attached
118     to the extension and also unreferences the repr.  This is better
119     than freeing it because it may (I wouldn't know why) be referenced
120     in another place.
121 */
122 Extension::~Extension (void)
124 //    printf("Extension Destructor: %s\n", name);
125     set_state(STATE_UNLOADED);
126     db.unregister_ext(this);
127     Inkscape::GC::release(repr);
128     g_free(id);
129     g_free(name);
130     delete timer;
131     timer = NULL;
132     /** \todo Need to do parameters here */
134     for (unsigned int i = 0 ; i < _deps.size(); i++) {
135         delete _deps[i];
136     }
137     _deps.clear();
139     return;
142 /**
143     \return   none
144     \brief    A function to set whether the extension should be loaded
145               or unloaded
146     \param    in_state  Which state should the extension be in?
148     It checks to see if this is a state change or not.  If we're changing
149     states it will call the appropriate function in the implementation,
150     load or unload.  Currently, there is no error checking in this
151     function.  There should be.
152 */
153 void
154 Extension::set_state (state_t in_state)
156     if (_state == STATE_DEACTIVATED) return;
157     if (in_state != _state) {
158         /** \todo Need some more error checking here! */
159         switch (in_state) {
160             case STATE_LOADED:
161                 if (imp->load(this))
162                     _state = STATE_LOADED;
164                 if (timer != NULL) {
165                     delete timer;
166                 }
167                 timer = new ExpirationTimer(this);
169                 break;
170             case STATE_UNLOADED:
171                 // std::cout << "Unloading: " << name << std::endl;
172                 imp->unload(this);
173                 _state = STATE_UNLOADED;
175                 if (timer != NULL) {
176                     delete timer;
177                     timer = NULL;
178                 }
179                 break;
180             case STATE_DEACTIVATED:
181                 _state = STATE_DEACTIVATED;
183                 if (timer != NULL) {
184                     delete timer;
185                     timer = NULL;
186                 }
187                 break;
188             default:
189                 break;
190         }
191     }
193     return;
196 /**
197     \return   The state the extension is in
198     \brief    A getter for the state variable.
199 */
200 Extension::state_t
201 Extension::get_state (void)
203     return _state;
206 /**
207     \return  Whether the extension is loaded or not
208     \brief   A quick function to test the state of the extension
209 */
210 bool
211 Extension::loaded (void)
213     return get_state() == STATE_LOADED;
216 /**
217     \return  A boolean saying whether the extension passed the checks
218     \brief   A function to check the validity of the extension
220     This function chekcs to make sure that there is an id, a name, a
221     repr and an implemenation for this extension.  Then it checks all
222     of the dependencies to see if they pass.  Finally, it asks the
223     implmentation to do a check of itself.
225     On each check, if there is a failure, it will print a message to the
226     error log for that failure.  It is important to note that the function
227     keeps executing if it finds an error, to try and get as many of them
228     into the error log as possible.  This should help people debug
229     installations, and figure out what they need to get for the full
230     functionality of Inkscape to be available.
231 */
232 bool
233 Extension::check (void)
235     bool retval = true;
237     // static int i = 0;
238     // std::cout << "Checking module[" << i++ << "]: " << name << std::endl;
240     const char * inx_failure = _("  This is caused by an improper .inx file for this extension."
241                                  "  An improper .inx file could have been caused by a faulty installation of Inkscape.");
242     if (id == NULL) {
243         printFailure(Glib::ustring(_("an ID was not defined for it.")) + inx_failure);
244         retval = false;
245     }
246     if (name == NULL) {
247         printFailure(Glib::ustring(_("there was no name defined for it.")) + inx_failure);
248         retval = false;
249     }
250     if (repr == NULL) {
251         printFailure(Glib::ustring(_("the XML description of it got lost.")) + inx_failure);
252         retval = false;
253     }
254     if (imp == NULL) {
255         printFailure(Glib::ustring(_("no implementation was defined for the extension.")) + inx_failure);
256         retval = false;
257     }
259     for (unsigned int i = 0 ; i < _deps.size(); i++) {
260         if (_deps[i]->check() == FALSE) {
261             // std::cout << "Failed: " << *(_deps[i]) << std::endl;
262             printFailure(Glib::ustring(_("a dependency was not met.")));
263             error_file << *_deps[i] << std::endl;
264             retval = false;
265         }
266     }
268     if (retval)
269         return imp->check(this);
270     return retval;
273 /** \brief A quick function to print out a standard start of extension
274            errors in the log.
275     \param reason  A string explaining why this failed
277     Real simple, just put everything into \c error_file.
278 */
279 void
280 Extension::printFailure (Glib::ustring reason)
282         error_file << _("Extension \"") << name << _("\" failed to load because ");
283         error_file << reason.raw();
284         error_file << std::endl;
285         return;
288 /**
289     \return  The XML tree that is used to define the extension
290     \brief   A getter for the internal Repr, does not add a reference.
291 */
292 Inkscape::XML::Node *
293 Extension::get_repr (void)
295     return repr;
298 /**
299     \return  The textual id of this extension
300     \brief   Get the ID of this extension - not a copy don't delete!
301 */
302 gchar *
303 Extension::get_id (void)
305     return id;
308 /**
309     \return  The textual name of this extension
310     \brief   Get the name of this extension - not a copy don't delete!
311 */
312 gchar *
313 Extension::get_name (void)
315     return name;
318 /**
319     \return  None
320     \brief   This function diactivates the extension (which makes it
321              unusable, but not deleted)
323     This function is used to removed an extension from functioning, but
324     not delete it completely.  It sets the state to \c STATE_DEACTIVATED to
325     mark to the world that it has been deactivated.  It also removes
326     the current implementation and replaces it with a standard one.  This
327     makes it so that we don't have to continually check if there is an
328     implementation, but we are gauranteed to have a benign one.
330     \warning It is important to note that there is no 'activate' function.
331     Running this function is irreversable.
332 */
333 void
334 Extension::deactivate (void)
336     set_state(STATE_DEACTIVATED);
338     /* Removing the old implementation, and making this use the default. */
339     /* This should save some memory */
340     delete imp;
341     imp = new Implementation::Implementation();
343     return;
346 /**
347     \return  Whether the extension has been deactivated
348     \brief   Find out the status of the extension
349 */
350 bool
351 Extension::deactivated (void)
353     return get_state() == STATE_DEACTIVATED;
356 /**
357     \return    Parameter structure with a name of 'name'
358     \brief     This function looks through the linked list for a parameter
359                structure with the name of the passed in name
360     \param     name   The name to search for
361     \param     list   The list to look for
363     This is an inline function that is used by all the get_param and
364     set_param functions to find a param_t in the linked list with
365     the passed in name.  It is done as an inline so that it will be
366     optimized into a 'jump' by the compiler.
368     This function can throw a 'param_not_exist' exception if the
369     name is not found.
371     The first thing that this function checks is if the list is NULL.
372     It could be NULL because there are no parameters for this extension
373     or because all of them have been checked (I'll spoil the ending and
374     tell you that this function is called recursively).  If the list
375     is NULL then the 'param_not_exist' exception is thrown.
377     Otherwise, the function looks at the current param_t that the element
378     list points to.  If the name of that param_t matches the passed in
379     name then that param_t is returned.  Otherwise, this function is
380     called again with g_slist_next as a parameter.
381 */
382 Parameter *
383 param_shared (const gchar * name, GSList * list)
385     Parameter * output;
387     if (name == NULL) {
388         throw Extension::param_not_exist();
389     }
390     if (list == NULL) {
391         throw Extension::param_not_exist();
392     }
394     output = static_cast<Parameter *>(list->data);
395     if (!strcmp(output->name(), name)) {
396         return output;
397     }
399     return param_shared(name, g_slist_next(list));
402 /**
403     \return   A constant pointer to the string held by the parameters.
404     \brief    Gets a parameter identified by name with the string placed
405               in value.  It isn't duplicated into the value string.
406     \param    name    The name of the parameter to get
407     \param    doc    The document to look in for document specific parameters
408     \param    node   The node to look in for a specific parameter
410     Look up in the parameters list, then execute the function on that
411     found parameter.
412 */
413 const gchar *
414 Extension::get_param_string (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
416     Parameter * param;
418     param = param_shared(name, parameters);
419     return param->get_string(doc, node);
422 /**
423     \return   The value of the parameter identified by the name
424     \brief    Gets a parameter identified by name with the bool placed
425               in value.
426     \param    name    The name of the parameter to get
427     \param    doc    The document to look in for document specific parameters
428     \param    node   The node to look in for a specific parameter
430     Look up in the parameters list, then execute the function on that
431     found parameter.
432 */
433 bool
434 Extension::get_param_bool (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
436     Parameter * param;
438     param = param_shared(name, parameters);
439     return param->get_bool(doc, node);
442 /**
443     \return   The integer value for the parameter specified
444     \brief    Gets a parameter identified by name with the integer placed
445               in value.
446     \param    name    The name of the parameter to get
447     \param    doc    The document to look in for document specific parameters
448     \param    node   The node to look in for a specific parameter
450     Look up in the parameters list, then execute the function on that
451     found parameter.
452 */
453 int
454 Extension::get_param_int (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
456     Parameter * param;
458     param = param_shared(name, parameters);
459     return param->get_int(doc, node);
462 /**
463     \return   The float value for the parameter specified
464     \brief    Gets a parameter identified by name with the float placed
465               in value.
466     \param    name    The name of the parameter to get
467     \param    doc    The document to look in for document specific parameters
468     \param    node   The node to look in for a specific parameter
470     Look up in the parameters list, then execute the function on that
471     found parameter.
472 */
473 float
474 Extension::get_param_float (const gchar * name, const Inkscape::XML::Document * doc, const Inkscape::XML::Node * node)
476     Parameter * param;
477     param = param_shared(name, parameters);
478     return param->get_float(doc, node);
481 /**
482     \return   The passed in value
483     \brief    Sets a parameter identified by name with the boolean
484               in the parameter value.
485     \param    name    The name of the parameter to set
486     \param    value   The value to set the parameter to
487     \param    doc    The document to look in for document specific parameters
488     \param    node   The node to look in for a specific parameter
490     Look up in the parameters list, then execute the function on that
491     found parameter.
492 */
493 bool
494 Extension::set_param_bool (const gchar * name, bool value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
496     Parameter * param;
497     param = param_shared(name, parameters);
498     return param->set_bool(value, doc, node);
501 /**
502     \return   The passed in value
503     \brief    Sets a parameter identified by name with the integer
504               in the parameter value.
505     \param    name    The name of the parameter to set
506     \param    value   The value to set the parameter to
507     \param    doc    The document to look in for document specific parameters
508     \param    node   The node to look in for a specific parameter
510     Look up in the parameters list, then execute the function on that
511     found parameter.
512 */
513 int
514 Extension::set_param_int (const gchar * name, int value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
516     Parameter * param;
517     param = param_shared(name, parameters);
518     return param->set_int(value, doc, node);
521 /**
522     \return   The passed in value
523     \brief    Sets a parameter identified by name with the integer
524               in the parameter value.
525     \param    name    The name of the parameter to set
526     \param    value   The value to set the parameter to
527     \param    doc    The document to look in for document specific parameters
528     \param    node   The node to look in for a specific parameter
530     Look up in the parameters list, then execute the function on that
531     found parameter.
532 */
533 float
534 Extension::set_param_float (const gchar * name, float value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
536     Parameter * param;
537     param = param_shared(name, parameters);
538     return param->set_float(value, doc, node);
541 /**
542     \return   The passed in value
543     \brief    Sets a parameter identified by name with the string
544               in the parameter value.
545     \param    name    The name of the parameter to set
546     \param    value   The value to set the parameter to
547     \param    doc    The document to look in for document specific parameters
548     \param    node   The node to look in for a specific parameter
550     Look up in the parameters list, then execute the function on that
551     found parameter.
552 */
553 const gchar *
554 Extension::set_param_string (const gchar * name, const gchar * value, Inkscape::XML::Document * doc, Inkscape::XML::Node * node)
556     Parameter * param;
557     param = param_shared(name, parameters);
558     return param->set_string(value, doc, node);
561 /** \brief A function to open the error log file. */
562 void
563 Extension::error_file_open (void)
565     gchar * ext_error_file = profile_path(EXTENSION_ERROR_LOG_FILENAME);
566     gchar * filename = g_filename_from_utf8( ext_error_file, -1, NULL, NULL, NULL );
567     error_file.open(filename);
568     if (!error_file.is_open()) {
569         g_warning(_("Could not create extension error log file '%s'"),
570                   filename);
571     }
572     g_free(filename);
573     g_free(ext_error_file);
574 };
576 /** \brief A function to close the error log file. */
577 void
578 Extension::error_file_close (void)
580     error_file.close();
581 };
583 /** \brief  A function to automatically generate a GUI using the parameters
584     \return Generated widget
586     This function just goes through each parameter, and calls it's 'get_widget'
587     function to get each widget.  Then, each of those is placed into
588     a Gtk::VBox, which is then returned to the calling function.
590     If there are no parameters, this function just returns NULL.
591 */
592 Gtk::Widget *
593 Extension::autogui (void)
595     if (g_slist_length(parameters) == 0) return NULL;
597     Gtk::VBox * vbox = new Gtk::VBox();
598     vbox = new Gtk::VBox();
600     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
601         Parameter * param = reinterpret_cast<Parameter *>(list->data);
602         Gtk::Widget * widg = param->get_widget();
603         if (widg != NULL)
604             vbox->pack_start(*widg, true, true);
605     }
607     vbox->show();
608     return vbox;
609 };
611 /**
612     \brief  A function to get the parameters in a string form
613     \return A string with all the parameters as command line arguements
615     I don't really like this function, but it works for now.
617     \todo  Do this better.
618 */
619 Glib::ustring *
620 Extension::paramString (void)
622     Glib::ustring * param_string = new Glib::ustring("");
624     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
625         Parameter * param = reinterpret_cast<Parameter *>(list->data);
627         *param_string += " --";
628         *param_string += param->name();
629         *param_string += "=";
630         Glib::ustring * paramstr = param->string();
631         *param_string += *paramstr;
632         delete paramstr;
633     }
635     return param_string;
638 /* Extension editor dialog stuff */
640 Gtk::VBox *
641 Extension::get_info_widget(void)
643     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
645     Gtk::Frame * info = Gtk::manage(new Gtk::Frame("General Extension Information"));
646     retval->pack_start(*info, true, true, 5);
648     Gtk::Table * table = Gtk::manage(new Gtk::Table());
649     info->add(*table);
651     int row = 0;
652     add_val(_("Name:"), _(name), table, &row);
653     add_val(_("ID:"), id, table, &row);
654     add_val(_("State:"), _state == STATE_LOADED ? _("Loaded") : _state == STATE_UNLOADED ? _("Unloaded") : _("Deactivated"), table, &row);
657     retval->show_all();
658     return retval;
661 void
662 Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Table * table, int * row)
664     Gtk::Label * label;
665     Gtk::Label * value;
667     (*row)++; 
668     label = Gtk::manage(new Gtk::Label(labelstr));
669     value = Gtk::manage(new Gtk::Label(valuestr));
670     table->attach(*label, 0, 1, (*row) - 1, *row);
671     table->attach(*value, 1, 2, (*row) - 1, *row);
673     label->show();
674     value->show();
676     return;
679 Gtk::VBox *
680 Extension::get_help_widget(void)
682     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
684     if (_help == NULL) {
685         Gtk::Label * content = Gtk::manage(new Gtk::Label("Currently there is no help available for this Extension.  Please look on the Inkscape website or ask on the mailing lists if you have questions regarding this extension."));
686         retval->pack_start(*content, true, true, 5);
687         content->set_line_wrap(true);
688         content->show();
689     } else {
693     }
695     retval->show();
696     return retval;
699 Gtk::VBox *
700 Extension::get_params_widget(void)
702     Gtk::VBox * retval = Gtk::manage(new Gtk::VBox());
703     Gtk::Widget * content = Gtk::manage(new Gtk::Label("Params"));
704     retval->pack_start(*content, true, true, 5);
705     content->show();
706     retval->show();
707     return retval;
710 }  /* namespace Extension */
711 }  /* namespace Inkscape */
714 /*
715   Local Variables:
716   mode:c++
717   c-file-style:"stroustrup"
718   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
719   indent-tabs-mode:nil
720   fill-column:99
721   End:
722 */
723 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :