Code

null the sodipodi:modified before saving, not after; whitespace
[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  *
10  * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
11  * Copyright (C) 2002-2004 Authors
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
20 #include <interface.h>
22 #include "db.h"
23 #include "input.h"
24 #include "output.h"
25 #include "effect.h"
26 #include "patheffect.h"
27 #include "print.h"
28 #include "implementation/script.h"
29 #include "implementation/xslt.h"
30 /* #include "implementation/plugin.h" */
32 namespace Inkscape {
33 namespace Extension {
35 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
36 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
37 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
39 /**
40  * \return   A new document created from the filename passed in
41  * \brief    This is a generic function to use the open function of
42  *           a module (including Autodetect)
43  * \param    key       Identifier of which module to use
44  * \param    filename  The file that should be opened
45  *
46  * First things first, are we looking at an autodetection?  Well if that's the case then the module
47  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
48  * function is called, with the parameter being a gpointer array.  It contains both the filename
49  * (to find its extension) and where to write the module when it is found.
50  *
51  * If there is no autodetection, then the module database is queried with the key given.
52  *
53  * If everything is cool at this point, the module is loaded, and there is possibility for
54  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
55  * After it is finished the function continues.
56  *
57  * Lastly, the open function is called in the module itself.
58  */
59 SPDocument *
60 open(Extension *key, gchar const *filename)
61 {
62     Input *imod = NULL;
63     if (key == NULL) {
64         gpointer parray[2];
65         parray[0] = (gpointer)filename;
66         parray[1] = (gpointer)&imod;
67         db.foreach(open_internal, (gpointer)&parray);
68     } else {
69         imod = dynamic_cast<Input *>(key);
70     }
72     bool last_chance_svg = false;
73     if (key == NULL && imod == NULL) {
74         last_chance_svg = true;
75         imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
76     }
78     if (imod == NULL) {
79         throw Input::no_extension_found();
80     }
82     imod->set_state(Extension::STATE_LOADED);
84     if (!imod->loaded()) {
85         throw Input::open_failed();
86     }
88     if (!imod->prefs(filename))
89         return NULL;
91     SPDocument *doc = imod->open(filename);
92     if (!doc) {
93         return NULL;
94     }
96     if (last_chance_svg) {
97         /* We can't call sp_ui_error_dialog because we may be 
98            running from the console, in which case calling sp_ui
99            routines will cause a segfault.  See bug 1000350 - bryce */
100         // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
101         g_warning(_("Format autodetect failed. The file is being opened as SVG."));
102     }
104     /* This kinda overkill as most of these are already set, but I want
105        to make sure for this release -- TJG */
106     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
107     bool saved = sp_document_get_undo_sensitive(doc);
108     sp_document_set_undo_sensitive(doc, false);
109     repr->setAttribute("sodipodi:modified", NULL);
110     sp_document_set_undo_sensitive(doc, saved);
112     sp_document_set_uri(doc, filename);
114     return doc;
117 /**
118  * \return   none
119  * \brief    This is the function that searches each module to see
120  *           if it matches the filename for autodetection.
121  * \param    in_plug  The module to be tested
122  * \param    in_data  An array of pointers containing the filename, and
123  *                    the place to put a successfully found module.
124  *
125  * Basically this function only looks at input modules as it is part of the open function.  If the
126  * module is an input module, it then starts to take it apart, and the data that is passed in.
127  * Because the data being passed in is in such a weird format, there are a few casts to make it
128  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
129  * compiler.
130  *
131  * First thing that is checked is if the filename is shorter than the extension itself.  There is
132  * no way for a match in that case.  If it's long enough then there is a string compare of the end
133  * of the filename (for the length of the extension), and the extension itself.  If this passes
134  * then the pointer passed in is set to the current module.
135  */
136 static void
137 open_internal(Extension *in_plug, gpointer in_data)
139     if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
140         gpointer *parray = (gpointer *)in_data;
141         gchar const *filename = (gchar const *)parray[0];
142         Input **pimod = (Input **)parray[1];
144         // skip all the rest if we already found a function to open it
145         // since they're ordered by preference now.
146         if (!*pimod) {
147             gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
149             gchar *filenamelower = g_utf8_strdown(filename, -1);
150             gchar *extensionlower = g_utf8_strdown(ext, -1);
152             if (g_str_has_suffix(filenamelower, extensionlower)) {
153                 *pimod = dynamic_cast<Input *>(in_plug);
154             }
156             g_free(filenamelower);
157             g_free(extensionlower);
158         }
159     }
161     return;
164 /**
165  * \return   None
166  * \brief    This is a generic function to use the save function of
167  *           a module (including Autodetect)
168  * \param    key       Identifier of which module to use
169  * \param    doc       The document to be saved
170  * \param    filename  The file that the document should be saved to
171  * \param    official  (optional) whether to set :output_module and :modified in the
172  *                     document; is true for normal save, false for temporary saves
173  *
174  * First things first, are we looking at an autodetection?  Well if that's the case then the module
175  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
176  * function is called, with the parameter being a gpointer array.  It contains both the filename
177  * (to find its extension) and where to write the module when it is found.
178  *
179  * If there is no autodetection the module database is queried with the key given.
180  *
181  * If everything is cool at this point, the module is loaded, and there is possibility for
182  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
183  * After it is finished the function continues.
184  *
185  * Lastly, the save function is called in the module itself.
186  */
187 void
188 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
190     Output *omod;
191     if (key == NULL) {
192         gpointer parray[2];
193         parray[0] = (gpointer)filename;
194         parray[1] = (gpointer)&omod;
195         omod = NULL;
196         db.foreach(save_internal, (gpointer)&parray);
198         /* This is a nasty hack, but it is required to ensure that
199            autodetect will always save with the Inkscape extensions
200            if they are available. */
201         if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
202             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
203         }
204         /* If autodetect fails, save as Inkscape SVG */
205         if (omod == NULL) {
206             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
207         }
208     } else {
209         omod = dynamic_cast<Output *>(key);
210     }
212     if (!dynamic_cast<Output *>(omod)) {
213         g_warning("Unable to find output module to handle file: %s\n", filename);
214         throw Output::no_extension_found();
215         return;
216     }
218     omod->set_state(Extension::STATE_LOADED);
219     if (!omod->loaded()) {
220         throw Output::save_failed();
221     }
223     if (!omod->prefs())
224         return;
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     if (official) {
249         bool saved = sp_document_get_undo_sensitive(doc);
250         sp_document_set_undo_sensitive (doc, false);
251         Inkscape::XML::Node *repr = sp_document_repr_root(doc);
252         repr->setAttribute("sodipodi:modified", NULL);
253         sp_document_set_undo_sensitive (doc, saved);
254     }
256     omod->save(doc, fileName);
258     if (official) {
259         // save the filename for next use
260         sp_document_set_uri(doc, fileName);
262         bool saved = sp_document_get_undo_sensitive(doc);
263         sp_document_set_undo_sensitive (doc, false);
265         // also save the extension for next use
266         Inkscape::XML::Node *repr = sp_document_repr_root(doc);
267         repr->setAttribute("inkscape:output_extension", omod->get_id());
268         // set the "dataloss" attribute if the chosen extension is lossy
269         repr->setAttribute("inkscape:dataloss", NULL);
270         if ( omod->causes_dataloss() ) {
271             repr->setAttribute("inkscape:dataloss", "true");
272         }
274         sp_document_set_undo_sensitive (doc, saved);
275     }
276     
277     g_free(fileName);
278     return;
281 /**
282  * \return   none
283  * \brief    This is the function that searches each module to see
284  *           if it matches the filename for autodetection.
285  * \param    in_plug  The module to be tested
286  * \param    in_data  An array of pointers containing the filename, and
287  *                    the place to put a successfully found module.
288  *
289  * Basically this function only looks at output modules as it is part of the open function.  If the
290  * module is an output module, it then starts to take it apart, and the data that is passed in.
291  * Because the data being passed in is in such a weird format, there are a few casts to make it
292  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
293  * compiler.
294  *
295  * First thing that is checked is if the filename is shorter than the extension itself.  There is
296  * no way for a match in that case.  If it's long enough then there is a string compare of the end
297  * of the filename (for the length of the extension), and the extension itself.  If this passes
298  * then the pointer passed in is set to the current module.
299  */
300 static void
301 save_internal(Extension *in_plug, gpointer in_data)
303     if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
304         gpointer *parray = (gpointer *)in_data;
305         gchar const *filename = (gchar const *)parray[0];
306         Output **pomod = (Output **)parray[1];
308         // skip all the rest if we already found someone to save it
309         // since they're ordered by preference now.
310         if (!*pomod) {
311             gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
313             gchar *filenamelower = g_utf8_strdown(filename, -1);
314             gchar *extensionlower = g_utf8_strdown(ext, -1);
316             if (g_str_has_suffix(filenamelower, extensionlower)) {
317                 *pomod = dynamic_cast<Output *>(in_plug);
318             }
320             g_free(filenamelower);
321             g_free(extensionlower);
322         }
323     }
325     return;
328 Print *
329 get_print(gchar const *key)
331     return dynamic_cast<Print *>(db.get(key));
334 /**
335  * \return   The built module
336  * \brief    Creates a module from a Inkscape::XML::Document describing the module
337  * \param    doc  The XML description of the module
338  *
339  * This function basically has two segments.  The first is that it goes through the Repr tree
340  * provided, and determines what kind of of module this is, and what kind of implementation to use.
341  * All of these are then stored in two enums that are defined in this function.  This makes it
342  * easier to add additional types (which will happen in the future, I'm sure).
343  *
344  * Second, there is case statements for these enums.  The first one is the type of module.  This is
345  * the one where the module is actually created.  After that, then the implementation is applied to
346  * get the load and unload functions.  If there is no implementation then these are not set.  This
347  * case could apply to modules that are built in (like the SVG load/save functions).
348  */
349 static Extension *
350 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
352     enum {
353         MODULE_EXTENSION,
354         MODULE_XSLT,
355         /* MODULE_PLUGIN, */
356         MODULE_UNKNOWN_IMP
357     } module_implementation_type = MODULE_UNKNOWN_IMP;
358     enum {
359         MODULE_INPUT,
360         MODULE_OUTPUT,
361         MODULE_FILTER,
362         MODULE_PRINT,
363         MODULE_PATH_EFFECT,
364         MODULE_UNKNOWN_FUNC
365     } module_functional_type = MODULE_UNKNOWN_FUNC;
367     g_return_val_if_fail(doc != NULL, NULL);
369     Inkscape::XML::Node *repr = sp_repr_document_root(doc);
371     /* sp_repr_print(repr); */
373     if (strcmp(repr->name(), "inkscape-extension")) {
374         g_warning("Extension definition started with <%s> instead of <inkscape-extension>.  Extension will not be created.\n", repr->name());
375         return NULL;
376     }
378     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
379     while (child_repr != NULL) {
380         char const *element_name = child_repr->name();
381         /* printf("Child: %s\n", child_repr->name()); */
382         if (!strcmp(element_name, "input")) {
383             module_functional_type = MODULE_INPUT;
384         } else if (!strcmp(element_name, "output")) {
385             module_functional_type = MODULE_OUTPUT;
386         } else if (!strcmp(element_name, "effect")) {
387             module_functional_type = MODULE_FILTER;
388         } else if (!strcmp(element_name, "print")) {
389             module_functional_type = MODULE_PRINT;
390         } else if (!strcmp(element_name, "path-effect")) {
391             module_functional_type = MODULE_PATH_EFFECT;
392         } else if (!strcmp(element_name, "script")) {
393             module_implementation_type = MODULE_EXTENSION;
394         } else if (!strcmp(element_name, "xslt")) {
395             module_implementation_type = MODULE_XSLT;
396 #if 0
397         } else if (!strcmp(element_name, "plugin")) {
398             module_implementation_type = MODULE_PLUGIN;
399 #endif
400         }
402         //Inkscape::XML::Node *old_repr = child_repr;
403         child_repr = sp_repr_next(child_repr);
404         //Inkscape::GC::release(old_repr);
405     }
407     Implementation::Implementation *imp;
408     if (in_imp == NULL) {
409         switch (module_implementation_type) {
410             case MODULE_EXTENSION: {
411                 Implementation::Script *script = new Implementation::Script();
412                 imp = static_cast<Implementation::Implementation *>(script);
413                 break;
414             }
415             case MODULE_XSLT: {
416                 Implementation::XSLT *xslt = new Implementation::XSLT();
417                 imp = static_cast<Implementation::Implementation *>(xslt);
418                 break;
419             }
420 #if 0
421             case MODULE_PLUGIN: {
422                 Implementation::Plugin *plugin = new Implementation::Plugin();
423                 imp = static_cast<Implementation::Implementation *>(plugin);
424                 break;
425             }
426 #endif
427             default: {
428                 imp = NULL;
429                 break;
430             }
431         }
432     } else {
433         imp = in_imp;
434     }
436     Extension *module = NULL;
437     switch (module_functional_type) {
438         case MODULE_INPUT: {
439             module = new Input(repr, imp);
440             break;
441         }
442         case MODULE_OUTPUT: {
443             module = new Output(repr, imp);
444             break;
445         }
446         case MODULE_FILTER: {
447             module = new Effect(repr, imp);
448             break;
449         }
450         case MODULE_PRINT: {
451             module = new Print(repr, imp);
452             break;
453         }
454         case MODULE_PATH_EFFECT: {
455             module = new PathEffect(repr, imp);
456             break;
457         }
458         default: {
459             break;
460         }
461     }
463     return module;
466 /**
467  * \return   The module created
468  * \brief    This function creates a module from a filename of an
469  *           XML description.
470  * \param    filename  The file holding the XML description of the module.
471  *
472  * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
473  */
474 Extension *
475 build_from_file(gchar const *filename)
477     /* TODO: Need to define namespace here, need to write the
478        DTD in general for this stuff */
479     Inkscape::XML::Document *doc = sp_repr_read_file(filename, NULL);
480     Extension *ext = build_from_reprdoc(doc, NULL);
481     if (ext != NULL)
482         Inkscape::GC::release(doc);
483     else
484         g_warning("Unable to create extension from definition file %s.\n", filename);
485     return ext;
488 /**
489  * \return   The module created
490  * \brief    This function creates a module from a buffer holding an
491  *           XML description.
492  * \param    buffer  The buffer holding the XML description of the module.
493  *
494  * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
495  * finds the length of the buffer using strlen.
496  */
497 Extension *
498 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
500     Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL);
501     Extension *ext = build_from_reprdoc(doc, in_imp);
502     Inkscape::GC::release(doc);
503     return ext;
507 } } /* namespace Inkscape::Extension */
509 /*
510   Local Variables:
511   mode:c++
512   c-file-style:"stroustrup"
513   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
514   indent-tabs-mode:nil
515   fill-column:99
516   End:
517 */
518 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :