Code

Revert recent refactoring changes by johnce because they break the build, which canno...
[inkscape.git] / src / extension / system.cpp
1 /*
2  * This is file is kind of the junk file.  Basically everything that
3  * didn't fit in one of the other well defined areas, well, it's now
4  * here.  Which is good in someways, but this file really needs some
5  * definition.  Hopefully that will come ASAP.
6  *
7  * Authors:
8  *   Ted Gould <ted@gould.cx>
9  *   Johan Engelen <johan@shouraizou.nl>
10  *
11  * Copyright (C) 2006-2007 Johan Engelen
12  * Copyright (C) 2002-2004 Ted Gould
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <interface.h>
23 #include "extension.h"
24 #include "db.h"
25 #include "input.h"
26 #include "output.h"
27 #include "effect.h"
28 #include "patheffect.h"
29 #include "print.h"
30 #include "implementation/script.h"
31 #include "implementation/xslt.h"
32 #include "xml/rebase-hrefs.h"
33 /* #include "implementation/plugin.h" */
35 namespace Inkscape {
36 namespace Extension {
38 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
39 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
40 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
42 /**
43  * \return   A new document created from the filename passed in
44  * \brief    This is a generic function to use the open function of
45  *           a module (including Autodetect)
46  * \param    key       Identifier of which module to use
47  * \param    filename  The file that should be opened
48  *
49  * First things first, are we looking at an autodetection?  Well if that's the case then the module
50  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
51  * function is called, with the parameter being a gpointer array.  It contains both the filename
52  * (to find its extension) and where to write the module when it is found.
53  *
54  * If there is no autodetection, then the module database is queried with the key given.
55  *
56  * If everything is cool at this point, the module is loaded, and there is possibility for
57  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
58  * After it is finished the function continues.
59  *
60  * Lastly, the open function is called in the module itself.
61  */
62 SPDocument *
63 open(Extension *key, gchar const *filename)
64 {
65     Input *imod = NULL;
66     if (key == NULL) {
67         gpointer parray[2];
68         parray[0] = (gpointer)filename;
69         parray[1] = (gpointer)&imod;
70         db.foreach(open_internal, (gpointer)&parray);
71     } else {
72         imod = dynamic_cast<Input *>(key);
73     }
75     bool last_chance_svg = false;
76     if (key == NULL && imod == NULL) {
77         last_chance_svg = true;
78         imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
79     }
81     if (imod == NULL) {
82         throw Input::no_extension_found();
83     }
85     imod->set_state(Extension::STATE_LOADED);
87     if (!imod->loaded()) {
88         throw Input::open_failed();
89     }
91     if (!imod->prefs(filename))
92         return NULL;
94     SPDocument *doc = imod->open(filename);
95     if (!doc) {
96         throw Input::open_failed();
97     }
99     if (last_chance_svg) {
100         /* We can't call sp_ui_error_dialog because we may be
101            running from the console, in which case calling sp_ui
102            routines will cause a segfault.  See bug 1000350 - bryce */
103         // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
104         g_warning(_("Format autodetect failed. The file is being opened as SVG."));
105     }
107     /* This kinda overkill as most of these are already set, but I want
108        to make sure for this release -- TJG */
109     doc->setModifiedSinceSave(false);
111     sp_document_set_uri(doc, filename);
113     return doc;
116 /**
117  * \return   none
118  * \brief    This is the function that searches each module to see
119  *           if it matches the filename for autodetection.
120  * \param    in_plug  The module to be tested
121  * \param    in_data  An array of pointers containing the filename, and
122  *                    the place to put a successfully found module.
123  *
124  * Basically this function only looks at input modules as it is part of the open function.  If the
125  * module is an input module, it then starts to take it apart, and the data that is passed in.
126  * Because the data being passed in is in such a weird format, there are a few casts to make it
127  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
128  * compiler.
129  *
130  * First thing that is checked is if the filename is shorter than the extension itself.  There is
131  * no way for a match in that case.  If it's long enough then there is a string compare of the end
132  * of the filename (for the length of the extension), and the extension itself.  If this passes
133  * then the pointer passed in is set to the current module.
134  */
135 static void
136 open_internal(Extension *in_plug, gpointer in_data)
138     if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
139         gpointer *parray = (gpointer *)in_data;
140         gchar const *filename = (gchar const *)parray[0];
141         Input **pimod = (Input **)parray[1];
143         // skip all the rest if we already found a function to open it
144         // since they're ordered by preference now.
145         if (!*pimod) {
146             gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
148             gchar *filenamelower = g_utf8_strdown(filename, -1);
149             gchar *extensionlower = g_utf8_strdown(ext, -1);
151             if (g_str_has_suffix(filenamelower, extensionlower)) {
152                 *pimod = dynamic_cast<Input *>(in_plug);
153             }
155             g_free(filenamelower);
156             g_free(extensionlower);
157         }
158     }
160     return;
163 /**
164  * \return   None
165  * \brief    This is a generic function to use the save function of
166  *           a module (including Autodetect)
167  * \param    key       Identifier of which module to use
168  * \param    doc       The document to be saved
169  * \param    filename  The file that the document should be saved to
170  * \param    official  (optional) whether to set :output_module and :modified in the
171  *                     document; is true for normal save, false for temporary saves
172  *
173  * First things first, are we looking at an autodetection?  Well if that's the case then the module
174  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
175  * function is called, with the parameter being a gpointer array.  It contains both the filename
176  * (to find its extension) and where to write the module when it is found.
177  *
178  * If there is no autodetection the module database is queried with the key given.
179  *
180  * If everything is cool at this point, the module is loaded, and there is possibility for
181  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
182  * After it is finished the function continues.
183  *
184  * Lastly, the save function is called in the module itself.
185  */
186 void
187 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
189     Output *omod;
190     if (key == NULL) {
191         gpointer parray[2];
192         parray[0] = (gpointer)filename;
193         parray[1] = (gpointer)&omod;
194         omod = NULL;
195         db.foreach(save_internal, (gpointer)&parray);
197         /* This is a nasty hack, but it is required to ensure that
198            autodetect will always save with the Inkscape extensions
199            if they are available. */
200         if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
201             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
202         }
203         /* If autodetect fails, save as Inkscape SVG */
204         if (omod == NULL) {
205             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
206         }
207     } else {
208         omod = dynamic_cast<Output *>(key);
209     }
211     if (!dynamic_cast<Output *>(omod)) {
212         g_warning("Unable to find output module to handle file: %s\n", filename);
213         throw Output::no_extension_found();
214         return;
215     }
217     omod->set_state(Extension::STATE_LOADED);
218     if (!omod->loaded()) {
219         throw Output::save_failed();
220     }
222     if (!omod->prefs()) {
223         throw Output::save_cancelled();
224     }
226     gchar *fileName = NULL;
227     if (setextension) {
228         gchar *lowerfile = g_utf8_strdown(filename, -1);
229         gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
231         if (!g_str_has_suffix(lowerfile, lowerext)) {
232             fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
233         }
235         g_free(lowerfile);
236         g_free(lowerext);
237     }
239     if (fileName == NULL) {
240         fileName = g_strdup(filename);
241     }
243     if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
244         g_free(fileName);
245         throw Output::no_overwrite();
246     }
248     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
251     // remember attributes in case this is an unofficial save and/or overwrite fails
252     gchar *saved_uri = g_strdup(doc->uri);
253     bool saved_modified = false;
254     gchar *saved_output_extension = NULL;
255     gchar *saved_dataloss = NULL;
256     saved_modified = doc->isModifiedSinceSave();
257     saved_output_extension = g_strdup(repr->attribute("inkscape:output_extension"));
258     saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
259     if (official) {
260         /* The document is changing name/uri. */
261         sp_document_change_uri_and_hrefs(doc, fileName);
262     }
264     // Update attributes:
265     {
266         bool const saved = sp_document_get_undo_sensitive(doc);
267         sp_document_set_undo_sensitive(doc, false);
268         {
269             // also save the extension for next use
270             repr->setAttribute("inkscape:output_extension", omod->get_id());
271             // set the "dataloss" attribute if the chosen extension is lossy
272             repr->setAttribute("inkscape:dataloss", NULL);
273             if (omod->causes_dataloss()) {
274                 repr->setAttribute("inkscape:dataloss", "true");
275             }
276         }
277         sp_document_set_undo_sensitive(doc, saved);
278         doc->setModifiedSinceSave(false);
279     }
281     try {
282         omod->save(doc, fileName);
283     }
284     catch(...) {
285         // revert attributes in case of official and overwrite
286         if(check_overwrite && official) {
287             bool const saved = sp_document_get_undo_sensitive(doc);
288             sp_document_set_undo_sensitive(doc, false);
289             {
290                 repr->setAttribute("inkscape:output_extension", saved_output_extension);
291                 repr->setAttribute("inkscape:dataloss", saved_dataloss);
292             }
293             sp_document_set_undo_sensitive(doc, saved);
294             sp_document_change_uri_and_hrefs(doc, saved_uri);
295         }
296         doc->setModifiedSinceSave(saved_modified);
297         // free used ressources
298         g_free(saved_output_extension);
299         g_free(saved_dataloss);
300         g_free(saved_uri);
302         g_free(fileName);
304         throw Inkscape::Extension::Output::save_failed();
305     }
307     // If it is an unofficial save, set the modified attributes back to what they were.
308     if ( !official) {
309         bool const saved = sp_document_get_undo_sensitive(doc);
310         sp_document_set_undo_sensitive(doc, false);
311         {
312             repr->setAttribute("inkscape:output_extension", saved_output_extension);
313             repr->setAttribute("inkscape:dataloss", saved_dataloss);
314         }
315         sp_document_set_undo_sensitive(doc, saved);
316         doc->setModifiedSinceSave(saved_modified);
318         g_free(saved_output_extension);
319         g_free(saved_dataloss);
320     }
322     g_free(fileName);
323     return;
326 /**
327  * \return   none
328  * \brief    This is the function that searches each module to see
329  *           if it matches the filename for autodetection.
330  * \param    in_plug  The module to be tested
331  * \param    in_data  An array of pointers containing the filename, and
332  *                    the place to put a successfully found module.
333  *
334  * Basically this function only looks at output modules as it is part of the open function.  If the
335  * module is an output module, it then starts to take it apart, and the data that is passed in.
336  * Because the data being passed in is in such a weird format, there are a few casts to make it
337  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
338  * compiler.
339  *
340  * First thing that is checked is if the filename is shorter than the extension itself.  There is
341  * no way for a match in that case.  If it's long enough then there is a string compare of the end
342  * of the filename (for the length of the extension), and the extension itself.  If this passes
343  * then the pointer passed in is set to the current module.
344  */
345 static void
346 save_internal(Extension *in_plug, gpointer in_data)
348     if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
349         gpointer *parray = (gpointer *)in_data;
350         gchar const *filename = (gchar const *)parray[0];
351         Output **pomod = (Output **)parray[1];
353         // skip all the rest if we already found someone to save it
354         // since they're ordered by preference now.
355         if (!*pomod) {
356             gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
358             gchar *filenamelower = g_utf8_strdown(filename, -1);
359             gchar *extensionlower = g_utf8_strdown(ext, -1);
361             if (g_str_has_suffix(filenamelower, extensionlower)) {
362                 *pomod = dynamic_cast<Output *>(in_plug);
363             }
365             g_free(filenamelower);
366             g_free(extensionlower);
367         }
368     }
370     return;
373 Print *
374 get_print(gchar const *key)
376     return dynamic_cast<Print *>(db.get(key));
379 /**
380  * \return   The built module
381  * \brief    Creates a module from a Inkscape::XML::Document describing the module
382  * \param    doc  The XML description of the module
383  *
384  * This function basically has two segments.  The first is that it goes through the Repr tree
385  * provided, and determines what kind of of module this is, and what kind of implementation to use.
386  * All of these are then stored in two enums that are defined in this function.  This makes it
387  * easier to add additional types (which will happen in the future, I'm sure).
388  *
389  * Second, there is case statements for these enums.  The first one is the type of module.  This is
390  * the one where the module is actually created.  After that, then the implementation is applied to
391  * get the load and unload functions.  If there is no implementation then these are not set.  This
392  * case could apply to modules that are built in (like the SVG load/save functions).
393  */
394 static Extension *
395 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
397     enum {
398         MODULE_EXTENSION,
399         MODULE_XSLT,
400         /* MODULE_PLUGIN, */
401         MODULE_UNKNOWN_IMP
402     } module_implementation_type = MODULE_UNKNOWN_IMP;
403     enum {
404         MODULE_INPUT,
405         MODULE_OUTPUT,
406         MODULE_FILTER,
407         MODULE_PRINT,
408         MODULE_PATH_EFFECT,
409         MODULE_UNKNOWN_FUNC
410     } module_functional_type = MODULE_UNKNOWN_FUNC;
412     g_return_val_if_fail(doc != NULL, NULL);
414     Inkscape::XML::Node *repr = doc->root();
416     if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
417         g_warning("Extension definition started with <%s> instead of <" INKSCAPE_EXTENSION_NS "inkscape-extension>.  Extension will not be created. See http://wiki.inkscape.org/wiki/index.php/Extensions for reference.\n", repr->name());
418         return NULL;
419     }
421     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
422     while (child_repr != NULL) {
423         char const *element_name = child_repr->name();
424         /* printf("Child: %s\n", child_repr->name()); */
425         if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
426             module_functional_type = MODULE_INPUT;
427         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
428             module_functional_type = MODULE_OUTPUT;
429         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
430             module_functional_type = MODULE_FILTER;
431         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
432             module_functional_type = MODULE_PRINT;
433         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
434             module_functional_type = MODULE_PATH_EFFECT;
435         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
436             module_implementation_type = MODULE_EXTENSION;
437         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
438             module_implementation_type = MODULE_XSLT;
439 #if 0
440         } else if (!strcmp(element_name, "plugin")) {
441             module_implementation_type = MODULE_PLUGIN;
442 #endif
443         }
445         //Inkscape::XML::Node *old_repr = child_repr;
446         child_repr = sp_repr_next(child_repr);
447         //Inkscape::GC::release(old_repr);
448     }
450     Implementation::Implementation *imp;
451     if (in_imp == NULL) {
452         switch (module_implementation_type) {
453             case MODULE_EXTENSION: {
454                 Implementation::Script *script = new Implementation::Script();
455                 imp = static_cast<Implementation::Implementation *>(script);
456                 break;
457             }
458             case MODULE_XSLT: {
459                 Implementation::XSLT *xslt = new Implementation::XSLT();
460                 imp = static_cast<Implementation::Implementation *>(xslt);
461                 break;
462             }
463 #if 0
464             case MODULE_PLUGIN: {
465                 Implementation::Plugin *plugin = new Implementation::Plugin();
466                 imp = static_cast<Implementation::Implementation *>(plugin);
467                 break;
468             }
469 #endif
470             default: {
471                 imp = NULL;
472                 break;
473             }
474         }
475     } else {
476         imp = in_imp;
477     }
479     Extension *module = NULL;
480     switch (module_functional_type) {
481         case MODULE_INPUT: {
482             module = new Input(repr, imp);
483             break;
484         }
485         case MODULE_OUTPUT: {
486             module = new Output(repr, imp);
487             break;
488         }
489         case MODULE_FILTER: {
490             module = new Effect(repr, imp);
491             break;
492         }
493         case MODULE_PRINT: {
494             module = new Print(repr, imp);
495             break;
496         }
497         case MODULE_PATH_EFFECT: {
498             module = new PathEffect(repr, imp);
499             break;
500         }
501         default: {
502             break;
503         }
504     }
506     return module;
509 /**
510  * \return   The module created
511  * \brief    This function creates a module from a filename of an
512  *           XML description.
513  * \param    filename  The file holding the XML description of the module.
514  *
515  * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
516  */
517 Extension *
518 build_from_file(gchar const *filename)
520     Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
521     Extension *ext = build_from_reprdoc(doc, NULL);
522     if (ext != NULL)
523         Inkscape::GC::release(doc);
524     else
525         g_warning("Unable to create extension from definition file %s.\n", filename);
526     return ext;
529 /**
530  * \return   The module created
531  * \brief    This function creates a module from a buffer holding an
532  *           XML description.
533  * \param    buffer  The buffer holding the XML description of the module.
534  *
535  * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
536  * finds the length of the buffer using strlen.
537  */
538 Extension *
539 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
541     Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
542     Extension *ext = build_from_reprdoc(doc, in_imp);
543     Inkscape::GC::release(doc);
544     return ext;
548 } } /* namespace Inkscape::Extension */
550 /*
551   Local Variables:
552   mode:c++
553   c-file-style:"stroustrup"
554   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
555   indent-tabs-mode:nil
556   fill-column:99
557   End:
558 */
559 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :