Code

Fixed saving bug, now attributes are correctly handled for both normal saves and...
[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 "db.h"
24 #include "input.h"
25 #include "output.h"
26 #include "effect.h"
27 #include "patheffect.h"
28 #include "print.h"
29 #include "implementation/script.h"
30 #include "implementation/xslt.h"
31 /* #include "implementation/plugin.h" */
33 namespace Inkscape {
34 namespace Extension {
36 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
37 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
38 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
40 /**
41  * \return   A new document created from the filename passed in
42  * \brief    This is a generic function to use the open function of
43  *           a module (including Autodetect)
44  * \param    key       Identifier of which module to use
45  * \param    filename  The file that should be opened
46  *
47  * First things first, are we looking at an autodetection?  Well if that's the case then the module
48  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
49  * function is called, with the parameter being a gpointer array.  It contains both the filename
50  * (to find its extension) and where to write the module when it is found.
51  *
52  * If there is no autodetection, then the module database is queried with the key given.
53  *
54  * If everything is cool at this point, the module is loaded, and there is possibility for
55  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
56  * After it is finished the function continues.
57  *
58  * Lastly, the open function is called in the module itself.
59  */
60 SPDocument *
61 open(Extension *key, gchar const *filename)
62 {
63     Input *imod = NULL;
64     if (key == NULL) {
65         gpointer parray[2];
66         parray[0] = (gpointer)filename;
67         parray[1] = (gpointer)&imod;
68         db.foreach(open_internal, (gpointer)&parray);
69     } else {
70         imod = dynamic_cast<Input *>(key);
71     }
73     bool last_chance_svg = false;
74     if (key == NULL && imod == NULL) {
75         last_chance_svg = true;
76         imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
77     }
79     if (imod == NULL) {
80         throw Input::no_extension_found();
81     }
83     imod->set_state(Extension::STATE_LOADED);
85     if (!imod->loaded()) {
86         throw Input::open_failed();
87     }
89     if (!imod->prefs(filename))
90         return NULL;
92     SPDocument *doc = imod->open(filename);
93     if (!doc) {
94         return NULL;
95     }
97     if (last_chance_svg) {
98         /* We can't call sp_ui_error_dialog because we may be 
99            running from the console, in which case calling sp_ui
100            routines will cause a segfault.  See bug 1000350 - bryce */
101         // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
102         g_warning(_("Format autodetect failed. The file is being opened as SVG."));
103     }
105     /* This kinda overkill as most of these are already set, but I want
106        to make sure for this release -- TJG */
107     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
108     bool saved = sp_document_get_undo_sensitive(doc);
109     sp_document_set_undo_sensitive(doc, false);
110     repr->setAttribute("sodipodi:modified", NULL);
111     sp_document_set_undo_sensitive(doc, saved);
113     sp_document_set_uri(doc, filename);
115     return doc;
118 /**
119  * \return   none
120  * \brief    This is the function that searches each module to see
121  *           if it matches the filename for autodetection.
122  * \param    in_plug  The module to be tested
123  * \param    in_data  An array of pointers containing the filename, and
124  *                    the place to put a successfully found module.
125  *
126  * Basically this function only looks at input modules as it is part of the open function.  If the
127  * module is an input module, it then starts to take it apart, and the data that is passed in.
128  * Because the data being passed in is in such a weird format, there are a few casts to make it
129  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
130  * compiler.
131  *
132  * First thing that is checked is if the filename is shorter than the extension itself.  There is
133  * no way for a match in that case.  If it's long enough then there is a string compare of the end
134  * of the filename (for the length of the extension), and the extension itself.  If this passes
135  * then the pointer passed in is set to the current module.
136  */
137 static void
138 open_internal(Extension *in_plug, gpointer in_data)
140     if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
141         gpointer *parray = (gpointer *)in_data;
142         gchar const *filename = (gchar const *)parray[0];
143         Input **pimod = (Input **)parray[1];
145         // skip all the rest if we already found a function to open it
146         // since they're ordered by preference now.
147         if (!*pimod) {
148             gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
150             gchar *filenamelower = g_utf8_strdown(filename, -1);
151             gchar *extensionlower = g_utf8_strdown(ext, -1);
153             if (g_str_has_suffix(filenamelower, extensionlower)) {
154                 *pimod = dynamic_cast<Input *>(in_plug);
155             }
157             g_free(filenamelower);
158             g_free(extensionlower);
159         }
160     }
162     return;
165 /**
166  * \return   None
167  * \brief    This is a generic function to use the save function of
168  *           a module (including Autodetect)
169  * \param    key       Identifier of which module to use
170  * \param    doc       The document to be saved
171  * \param    filename  The file that the document should be saved to
172  * \param    official  (optional) whether to set :output_module and :modified in the
173  *                     document; is true for normal save, false for temporary saves
174  *
175  * First things first, are we looking at an autodetection?  Well if that's the case then the module
176  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
177  * function is called, with the parameter being a gpointer array.  It contains both the filename
178  * (to find its extension) and where to write the module when it is found.
179  *
180  * If there is no autodetection the module database is queried with the key given.
181  *
182  * If everything is cool at this point, the module is loaded, and there is possibility for
183  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
184  * After it is finished the function continues.
185  *
186  * Lastly, the save function is called in the module itself.
187  */
188 void
189 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
191     Output *omod;
192     if (key == NULL) {
193         gpointer parray[2];
194         parray[0] = (gpointer)filename;
195         parray[1] = (gpointer)&omod;
196         omod = NULL;
197         db.foreach(save_internal, (gpointer)&parray);
199         /* This is a nasty hack, but it is required to ensure that
200            autodetect will always save with the Inkscape extensions
201            if they are available. */
202         if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
203             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
204         }
205         /* If autodetect fails, save as Inkscape SVG */
206         if (omod == NULL) {
207             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
208         }
209     } else {
210         omod = dynamic_cast<Output *>(key);
211     }
213     if (!dynamic_cast<Output *>(omod)) {
214         g_warning("Unable to find output module to handle file: %s\n", filename);
215         throw Output::no_extension_found();
216         return;
217     }
219     omod->set_state(Extension::STATE_LOADED);
220     if (!omod->loaded()) {
221         throw Output::save_failed();
222     }
224     if (!omod->prefs())
225         return;
227     gchar *fileName = NULL;
228     if (setextension) {
229         gchar *lowerfile = g_utf8_strdown(filename, -1);
230         gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
232         if (!g_str_has_suffix(lowerfile, lowerext)) {
233             fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
234         }
236         g_free(lowerfile);
237         g_free(lowerext);
238     }
240     if (fileName == NULL) {
241         fileName = g_strdup(filename);
242     }
244     if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
245         g_free(fileName);
246         throw Output::no_overwrite();
247     }
249     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
251     // remember attributes in case this is an unofficial save
252     gchar *saved_sodipodi_modified = NULL;
253     gchar *saved_output_extension = NULL;
254     gchar *saved_dataloss = NULL;
255     gchar *saved_uri = NULL;
256     if (!official) {
257         if (repr->attribute("sodipodi:modified")) {
258             saved_sodipodi_modified = g_strdup(repr->attribute("sodipodi:modified"));
259         }
260         if (repr->attribute("inkscape:output_extension")) {
261             saved_output_extension = g_strdup(repr->attribute("inkscape:output_extension"));
262         }
263         if (repr->attribute("inkscape:dataloss")) {
264             saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
265         }
266         if (doc->uri) {
267             saved_uri = g_strdup(doc->uri);    
268         }
269     }    
271     // update attributes:
272     bool saved = sp_document_get_undo_sensitive(doc);
273     sp_document_set_undo_sensitive (doc, false); 
274         repr->setAttribute("sodipodi:modified", NULL); 
275         // save the filename for next use
276         sp_document_set_uri(doc, fileName);
277         // also save the extension for next use
278         repr->setAttribute("inkscape:output_extension", omod->get_id());
279         // set the "dataloss" attribute if the chosen extension is lossy
280         repr->setAttribute("inkscape:dataloss", NULL);
281         if ( omod->causes_dataloss() ) {
282             repr->setAttribute("inkscape:dataloss", "true");
283         }
284     sp_document_set_undo_sensitive (doc, saved);
286     omod->save(doc, fileName);
287     
288     // if it is an unofficial save, set the modified attributes back to what they were    
289     if ( !official) {
290         saved = sp_document_get_undo_sensitive(doc);
291         sp_document_set_undo_sensitive (doc, false);
292             repr->setAttribute("sodipodi:modified", saved_sodipodi_modified);
293             repr->setAttribute("inkscape:output_extension", saved_output_extension);
294             repr->setAttribute("inkscape:dataloss", saved_dataloss);
295             sp_document_set_uri(doc, saved_uri);
296         sp_document_set_undo_sensitive (doc, saved);
297     }
298     
299     if (saved_sodipodi_modified) g_free(saved_sodipodi_modified);
300     if (saved_output_extension)  g_free(saved_output_extension);
301     if (saved_dataloss)          g_free(saved_dataloss);
302     if (saved_uri)               g_free(saved_uri);    
303     
304     g_free(fileName);
305     return;
308 /**
309  * \return   none
310  * \brief    This is the function that searches each module to see
311  *           if it matches the filename for autodetection.
312  * \param    in_plug  The module to be tested
313  * \param    in_data  An array of pointers containing the filename, and
314  *                    the place to put a successfully found module.
315  *
316  * Basically this function only looks at output modules as it is part of the open function.  If the
317  * module is an output module, it then starts to take it apart, and the data that is passed in.
318  * Because the data being passed in is in such a weird format, there are a few casts to make it
319  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
320  * compiler.
321  *
322  * First thing that is checked is if the filename is shorter than the extension itself.  There is
323  * no way for a match in that case.  If it's long enough then there is a string compare of the end
324  * of the filename (for the length of the extension), and the extension itself.  If this passes
325  * then the pointer passed in is set to the current module.
326  */
327 static void
328 save_internal(Extension *in_plug, gpointer in_data)
330     if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
331         gpointer *parray = (gpointer *)in_data;
332         gchar const *filename = (gchar const *)parray[0];
333         Output **pomod = (Output **)parray[1];
335         // skip all the rest if we already found someone to save it
336         // since they're ordered by preference now.
337         if (!*pomod) {
338             gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
340             gchar *filenamelower = g_utf8_strdown(filename, -1);
341             gchar *extensionlower = g_utf8_strdown(ext, -1);
343             if (g_str_has_suffix(filenamelower, extensionlower)) {
344                 *pomod = dynamic_cast<Output *>(in_plug);
345             }
347             g_free(filenamelower);
348             g_free(extensionlower);
349         }
350     }
352     return;
355 Print *
356 get_print(gchar const *key)
358     return dynamic_cast<Print *>(db.get(key));
361 /**
362  * \return   The built module
363  * \brief    Creates a module from a Inkscape::XML::Document describing the module
364  * \param    doc  The XML description of the module
365  *
366  * This function basically has two segments.  The first is that it goes through the Repr tree
367  * provided, and determines what kind of of module this is, and what kind of implementation to use.
368  * All of these are then stored in two enums that are defined in this function.  This makes it
369  * easier to add additional types (which will happen in the future, I'm sure).
370  *
371  * Second, there is case statements for these enums.  The first one is the type of module.  This is
372  * the one where the module is actually created.  After that, then the implementation is applied to
373  * get the load and unload functions.  If there is no implementation then these are not set.  This
374  * case could apply to modules that are built in (like the SVG load/save functions).
375  */
376 static Extension *
377 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
379     enum {
380         MODULE_EXTENSION,
381         MODULE_XSLT,
382         /* MODULE_PLUGIN, */
383         MODULE_UNKNOWN_IMP
384     } module_implementation_type = MODULE_UNKNOWN_IMP;
385     enum {
386         MODULE_INPUT,
387         MODULE_OUTPUT,
388         MODULE_FILTER,
389         MODULE_PRINT,
390         MODULE_PATH_EFFECT,
391         MODULE_UNKNOWN_FUNC
392     } module_functional_type = MODULE_UNKNOWN_FUNC;
394     g_return_val_if_fail(doc != NULL, NULL);
396     Inkscape::XML::Node *repr = sp_repr_document_root(doc);
398     /* sp_repr_print(repr); */
400     if (strcmp(repr->name(), "inkscape-extension")) {
401         g_warning("Extension definition started with <%s> instead of <inkscape-extension>.  Extension will not be created.\n", repr->name());
402         return NULL;
403     }
405     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
406     while (child_repr != NULL) {
407         char const *element_name = child_repr->name();
408         /* printf("Child: %s\n", child_repr->name()); */
409         if (!strcmp(element_name, "input")) {
410             module_functional_type = MODULE_INPUT;
411         } else if (!strcmp(element_name, "output")) {
412             module_functional_type = MODULE_OUTPUT;
413         } else if (!strcmp(element_name, "effect")) {
414             module_functional_type = MODULE_FILTER;
415         } else if (!strcmp(element_name, "print")) {
416             module_functional_type = MODULE_PRINT;
417         } else if (!strcmp(element_name, "path-effect")) {
418             module_functional_type = MODULE_PATH_EFFECT;
419         } else if (!strcmp(element_name, "script")) {
420             module_implementation_type = MODULE_EXTENSION;
421         } else if (!strcmp(element_name, "xslt")) {
422             module_implementation_type = MODULE_XSLT;
423 #if 0
424         } else if (!strcmp(element_name, "plugin")) {
425             module_implementation_type = MODULE_PLUGIN;
426 #endif
427         }
429         //Inkscape::XML::Node *old_repr = child_repr;
430         child_repr = sp_repr_next(child_repr);
431         //Inkscape::GC::release(old_repr);
432     }
434     Implementation::Implementation *imp;
435     if (in_imp == NULL) {
436         switch (module_implementation_type) {
437             case MODULE_EXTENSION: {
438                 Implementation::Script *script = new Implementation::Script();
439                 imp = static_cast<Implementation::Implementation *>(script);
440                 break;
441             }
442             case MODULE_XSLT: {
443                 Implementation::XSLT *xslt = new Implementation::XSLT();
444                 imp = static_cast<Implementation::Implementation *>(xslt);
445                 break;
446             }
447 #if 0
448             case MODULE_PLUGIN: {
449                 Implementation::Plugin *plugin = new Implementation::Plugin();
450                 imp = static_cast<Implementation::Implementation *>(plugin);
451                 break;
452             }
453 #endif
454             default: {
455                 imp = NULL;
456                 break;
457             }
458         }
459     } else {
460         imp = in_imp;
461     }
463     Extension *module = NULL;
464     switch (module_functional_type) {
465         case MODULE_INPUT: {
466             module = new Input(repr, imp);
467             break;
468         }
469         case MODULE_OUTPUT: {
470             module = new Output(repr, imp);
471             break;
472         }
473         case MODULE_FILTER: {
474             module = new Effect(repr, imp);
475             break;
476         }
477         case MODULE_PRINT: {
478             module = new Print(repr, imp);
479             break;
480         }
481         case MODULE_PATH_EFFECT: {
482             module = new PathEffect(repr, imp);
483             break;
484         }
485         default: {
486             break;
487         }
488     }
490     return module;
493 /**
494  * \return   The module created
495  * \brief    This function creates a module from a filename of an
496  *           XML description.
497  * \param    filename  The file holding the XML description of the module.
498  *
499  * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
500  */
501 Extension *
502 build_from_file(gchar const *filename)
504     /* TODO: Need to define namespace here, need to write the
505        DTD in general for this stuff */
506     Inkscape::XML::Document *doc = sp_repr_read_file(filename, NULL);
507     Extension *ext = build_from_reprdoc(doc, NULL);
508     if (ext != NULL)
509         Inkscape::GC::release(doc);
510     else
511         g_warning("Unable to create extension from definition file %s.\n", filename);
512     return ext;
515 /**
516  * \return   The module created
517  * \brief    This function creates a module from a buffer holding an
518  *           XML description.
519  * \param    buffer  The buffer holding the XML description of the module.
520  *
521  * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
522  * finds the length of the buffer using strlen.
523  */
524 Extension *
525 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
527     Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL);
528     Extension *ext = build_from_reprdoc(doc, in_imp);
529     Inkscape::GC::release(doc);
530     return ext;
534 } } /* namespace Inkscape::Extension */
536 /*
537   Local Variables:
538   mode:c++
539   c-file-style:"stroustrup"
540   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
541   indent-tabs-mode:nil
542   fill-column:99
543   End:
544 */
545 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :