Code

Pot and Dutch translation update
[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 "system.h"
24 #include "preferences.h"
25 #include "extension.h"
26 #include "db.h"
27 #include "input.h"
28 #include "output.h"
29 #include "effect.h"
30 #include "patheffect.h"
31 #include "print.h"
32 #include "implementation/script.h"
33 #include "implementation/xslt.h"
34 #include "xml/rebase-hrefs.h"
35 #include "io/sys.h"
36 /* #include "implementation/plugin.h" */
38 namespace Inkscape {
39 namespace Extension {
41 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
42 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
43 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
45 /**
46  * \return   A new document created from the filename passed in
47  * \brief    This is a generic function to use the open function of
48  *           a module (including Autodetect)
49  * \param    key       Identifier of which module to use
50  * \param    filename  The file that should be opened
51  *
52  * First things first, are we looking at an autodetection?  Well if that's the case then the module
53  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
54  * function is called, with the parameter being a gpointer array.  It contains both the filename
55  * (to find its extension) and where to write the module when it is found.
56  *
57  * If there is no autodetection, then the module database is queried with the key given.
58  *
59  * If everything is cool at this point, the module is loaded, and there is possibility for
60  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
61  * After it is finished the function continues.
62  *
63  * Lastly, the open function is called in the module itself.
64  */
65 SPDocument *
66 open(Extension *key, gchar const *filename)
67 {
68     Input *imod = NULL;
69     if (key == NULL) {
70         gpointer parray[2];
71         parray[0] = (gpointer)filename;
72         parray[1] = (gpointer)&imod;
73         db.foreach(open_internal, (gpointer)&parray);
74     } else {
75         imod = dynamic_cast<Input *>(key);
76     }
78     bool last_chance_svg = false;
79     if (key == NULL && imod == NULL) {
80         last_chance_svg = true;
81         imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
82     }
84     if (imod == NULL) {
85         throw Input::no_extension_found();
86     }
88     imod->set_state(Extension::STATE_LOADED);
90     if (!imod->loaded()) {
91         throw Input::open_failed();
92     }
94     if (!imod->prefs(filename))
95         return NULL;
97     SPDocument *doc = imod->open(filename);
98     if (!doc) {
99         throw Input::open_failed();
100     }
102     if (last_chance_svg) {
103         /* We can't call sp_ui_error_dialog because we may be
104            running from the console, in which case calling sp_ui
105            routines will cause a segfault.  See bug 1000350 - bryce */
106         // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
107         g_warning(_("Format autodetect failed. The file is being opened as SVG."));
108     }
110     /* This kinda overkill as most of these are already set, but I want
111        to make sure for this release -- TJG */
112     doc->setModifiedSinceSave(false);
114     sp_document_set_uri(doc, filename);
116     return doc;
119 /**
120  * \return   none
121  * \brief    This is the function that searches each module to see
122  *           if it matches the filename for autodetection.
123  * \param    in_plug  The module to be tested
124  * \param    in_data  An array of pointers containing the filename, and
125  *                    the place to put a successfully found module.
126  *
127  * Basically this function only looks at input modules as it is part of the open function.  If the
128  * module is an input module, it then starts to take it apart, and the data that is passed in.
129  * Because the data being passed in is in such a weird format, there are a few casts to make it
130  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
131  * compiler.
132  *
133  * First thing that is checked is if the filename is shorter than the extension itself.  There is
134  * no way for a match in that case.  If it's long enough then there is a string compare of the end
135  * of the filename (for the length of the extension), and the extension itself.  If this passes
136  * then the pointer passed in is set to the current module.
137  */
138 static void
139 open_internal(Extension *in_plug, gpointer in_data)
141     if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
142         gpointer *parray = (gpointer *)in_data;
143         gchar const *filename = (gchar const *)parray[0];
144         Input **pimod = (Input **)parray[1];
146         // skip all the rest if we already found a function to open it
147         // since they're ordered by preference now.
148         if (!*pimod) {
149             gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
151             gchar *filenamelower = g_utf8_strdown(filename, -1);
152             gchar *extensionlower = g_utf8_strdown(ext, -1);
154             if (g_str_has_suffix(filenamelower, extensionlower)) {
155                 *pimod = dynamic_cast<Input *>(in_plug);
156             }
158             g_free(filenamelower);
159             g_free(extensionlower);
160         }
161     }
163     return;
166 /**
167  * \return   None
168  * \brief    This is a generic function to use the save function of
169  *           a module (including Autodetect)
170  * \param    key       Identifier of which module to use
171  * \param    doc       The document to be saved
172  * \param    filename  The file that the document should be saved to
173  * \param    official  (optional) whether to set :output_module and :modified in the
174  *                     document; is true for normal save, false for temporary saves
175  *
176  * First things first, are we looking at an autodetection?  Well if that's the case then the module
177  * needs to be found, and that is done with a database lookup through the module DB.  The foreach
178  * function is called, with the parameter being a gpointer array.  It contains both the filename
179  * (to find its extension) and where to write the module when it is found.
180  *
181  * If there is no autodetection the module database is queried with the key given.
182  *
183  * If everything is cool at this point, the module is loaded, and there is possibility for
184  * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
185  * After it is finished the function continues.
186  *
187  * Lastly, the save function is called in the module itself.
188  */
189 void
190 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official,
191     Inkscape::Extension::FileSaveMethod save_method)
193     Output *omod;
194     if (key == NULL) {
195         gpointer parray[2];
196         parray[0] = (gpointer)filename;
197         parray[1] = (gpointer)&omod;
198         omod = NULL;
199         db.foreach(save_internal, (gpointer)&parray);
201         /* This is a nasty hack, but it is required to ensure that
202            autodetect will always save with the Inkscape extensions
203            if they are available. */
204         if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
205             omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
206         }
207         /* If autodetect fails, save as Inkscape SVG */
208         if (omod == NULL) {
209             // omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); use exception and let user choose
210         }
211     } else {
212         omod = dynamic_cast<Output *>(key);
213     }
215     if (!dynamic_cast<Output *>(omod)) {
216         g_warning("Unable to find output module to handle file: %s\n", filename);
217         throw Output::no_extension_found();
218         return;
219     }
221     omod->set_state(Extension::STATE_LOADED);
222     if (!omod->loaded()) {
223         throw Output::save_failed();
224     }
226     if (!omod->prefs()) {
227         throw Output::save_cancelled();
228     }
230     gchar *fileName = NULL;
231     if (setextension) {
232         gchar *lowerfile = g_utf8_strdown(filename, -1);
233         gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
235         if (!g_str_has_suffix(lowerfile, lowerext)) {
236             fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
237         }
239         g_free(lowerfile);
240         g_free(lowerext);
241     }
243     if (fileName == NULL) {
244         fileName = g_strdup(filename);
245     }
247     if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
248         g_free(fileName);
249         throw Output::no_overwrite();
250     }
252     // test if the file exists and is writable
253     // the test only checks the file attributes and might pass where ACL does not allow to write
254     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_is_writable(filename)) {
255         g_free(fileName);
256         throw Output::file_read_only();
257     }
259     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
262     // remember attributes in case this is an unofficial save and/or overwrite fails
263     gchar *saved_uri = g_strdup(doc->uri);
264     bool saved_modified = false;
265     gchar *saved_output_extension = NULL;
266     gchar *saved_dataloss = NULL;
267     saved_modified = doc->isModifiedSinceSave();
268     saved_output_extension = g_strdup(get_file_save_extension(save_method).c_str());
269     saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
270     if (official) {
271         /* The document is changing name/uri. */
272         sp_document_change_uri_and_hrefs(doc, fileName);
273     }
275     // Update attributes:
276     {
277         bool const saved = sp_document_get_undo_sensitive(doc);
278         sp_document_set_undo_sensitive(doc, false);
279         {
280             // also save the extension for next use
281             store_file_extension_in_prefs (omod->get_id(), save_method);
282             // set the "dataloss" attribute if the chosen extension is lossy
283             repr->setAttribute("inkscape:dataloss", NULL);
284             if (omod->causes_dataloss()) {
285                 repr->setAttribute("inkscape:dataloss", "true");
286             }
287         }
288         sp_document_set_undo_sensitive(doc, saved);
289         doc->setModifiedSinceSave(false);
290     }
292     try {
293         omod->save(doc, fileName);
294     }
295     catch(...) {
296         // revert attributes in case of official and overwrite
297         if(check_overwrite && official) {
298             bool const saved = sp_document_get_undo_sensitive(doc);
299             sp_document_set_undo_sensitive(doc, false);
300             {
301                 store_file_extension_in_prefs (saved_output_extension, save_method);
302                 repr->setAttribute("inkscape:dataloss", saved_dataloss);
303             }
304             sp_document_set_undo_sensitive(doc, saved);
305             sp_document_change_uri_and_hrefs(doc, saved_uri);
306         }
307         doc->setModifiedSinceSave(saved_modified);
308         // free used ressources
309         g_free(saved_output_extension);
310         g_free(saved_dataloss);
311         g_free(saved_uri);
313         g_free(fileName);
315         throw Inkscape::Extension::Output::save_failed();
316     }
318     // If it is an unofficial save, set the modified attributes back to what they were.
319     if ( !official) {
320         bool const saved = sp_document_get_undo_sensitive(doc);
321         sp_document_set_undo_sensitive(doc, false);
322         {
323             store_file_extension_in_prefs (saved_output_extension, save_method);
324             repr->setAttribute("inkscape:dataloss", saved_dataloss);
325         }
326         sp_document_set_undo_sensitive(doc, saved);
327         doc->setModifiedSinceSave(saved_modified);
329         g_free(saved_output_extension);
330         g_free(saved_dataloss);
331     }
333     g_free(fileName);
334     return;
337 /**
338  * \return   none
339  * \brief    This is the function that searches each module to see
340  *           if it matches the filename for autodetection.
341  * \param    in_plug  The module to be tested
342  * \param    in_data  An array of pointers containing the filename, and
343  *                    the place to put a successfully found module.
344  *
345  * Basically this function only looks at output modules as it is part of the open function.  If the
346  * module is an output module, it then starts to take it apart, and the data that is passed in.
347  * Because the data being passed in is in such a weird format, there are a few casts to make it
348  * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
349  * compiler.
350  *
351  * First thing that is checked is if the filename is shorter than the extension itself.  There is
352  * no way for a match in that case.  If it's long enough then there is a string compare of the end
353  * of the filename (for the length of the extension), and the extension itself.  If this passes
354  * then the pointer passed in is set to the current module.
355  */
356 static void
357 save_internal(Extension *in_plug, gpointer in_data)
359     if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
360         gpointer *parray = (gpointer *)in_data;
361         gchar const *filename = (gchar const *)parray[0];
362         Output **pomod = (Output **)parray[1];
364         // skip all the rest if we already found someone to save it
365         // since they're ordered by preference now.
366         if (!*pomod) {
367             gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
369             gchar *filenamelower = g_utf8_strdown(filename, -1);
370             gchar *extensionlower = g_utf8_strdown(ext, -1);
372             if (g_str_has_suffix(filenamelower, extensionlower)) {
373                 *pomod = dynamic_cast<Output *>(in_plug);
374             }
376             g_free(filenamelower);
377             g_free(extensionlower);
378         }
379     }
381     return;
384 Print *
385 get_print(gchar const *key)
387     return dynamic_cast<Print *>(db.get(key));
390 /**
391  * \return   The built module
392  * \brief    Creates a module from a Inkscape::XML::Document describing the module
393  * \param    doc  The XML description of the module
394  *
395  * This function basically has two segments.  The first is that it goes through the Repr tree
396  * provided, and determines what kind of of module this is, and what kind of implementation to use.
397  * All of these are then stored in two enums that are defined in this function.  This makes it
398  * easier to add additional types (which will happen in the future, I'm sure).
399  *
400  * Second, there is case statements for these enums.  The first one is the type of module.  This is
401  * the one where the module is actually created.  After that, then the implementation is applied to
402  * get the load and unload functions.  If there is no implementation then these are not set.  This
403  * case could apply to modules that are built in (like the SVG load/save functions).
404  */
405 static Extension *
406 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
408     enum {
409         MODULE_EXTENSION,
410         MODULE_XSLT,
411         /* MODULE_PLUGIN, */
412         MODULE_UNKNOWN_IMP
413     } module_implementation_type = MODULE_UNKNOWN_IMP;
414     enum {
415         MODULE_INPUT,
416         MODULE_OUTPUT,
417         MODULE_FILTER,
418         MODULE_PRINT,
419         MODULE_PATH_EFFECT,
420         MODULE_UNKNOWN_FUNC
421     } module_functional_type = MODULE_UNKNOWN_FUNC;
423     g_return_val_if_fail(doc != NULL, NULL);
425     Inkscape::XML::Node *repr = doc->root();
427     if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
428         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());
429         return NULL;
430     }
432     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
433     while (child_repr != NULL) {
434         char const *element_name = child_repr->name();
435         /* printf("Child: %s\n", child_repr->name()); */
436         if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
437             module_functional_type = MODULE_INPUT;
438         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
439             module_functional_type = MODULE_OUTPUT;
440         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
441             module_functional_type = MODULE_FILTER;
442         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
443             module_functional_type = MODULE_PRINT;
444         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
445             module_functional_type = MODULE_PATH_EFFECT;
446         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
447             module_implementation_type = MODULE_EXTENSION;
448         } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
449             module_implementation_type = MODULE_XSLT;
450 #if 0
451         } else if (!strcmp(element_name, "plugin")) {
452             module_implementation_type = MODULE_PLUGIN;
453 #endif
454         }
456         //Inkscape::XML::Node *old_repr = child_repr;
457         child_repr = sp_repr_next(child_repr);
458         //Inkscape::GC::release(old_repr);
459     }
461     Implementation::Implementation *imp;
462     if (in_imp == NULL) {
463         switch (module_implementation_type) {
464             case MODULE_EXTENSION: {
465                 Implementation::Script *script = new Implementation::Script();
466                 imp = static_cast<Implementation::Implementation *>(script);
467                 break;
468             }
469             case MODULE_XSLT: {
470                 Implementation::XSLT *xslt = new Implementation::XSLT();
471                 imp = static_cast<Implementation::Implementation *>(xslt);
472                 break;
473             }
474 #if 0
475             case MODULE_PLUGIN: {
476                 Implementation::Plugin *plugin = new Implementation::Plugin();
477                 imp = static_cast<Implementation::Implementation *>(plugin);
478                 break;
479             }
480 #endif
481             default: {
482                 imp = NULL;
483                 break;
484             }
485         }
486     } else {
487         imp = in_imp;
488     }
490     Extension *module = NULL;
491     switch (module_functional_type) {
492         case MODULE_INPUT: {
493             module = new Input(repr, imp);
494             break;
495         }
496         case MODULE_OUTPUT: {
497             module = new Output(repr, imp);
498             break;
499         }
500         case MODULE_FILTER: {
501             module = new Effect(repr, imp);
502             break;
503         }
504         case MODULE_PRINT: {
505             module = new Print(repr, imp);
506             break;
507         }
508         case MODULE_PATH_EFFECT: {
509             module = new PathEffect(repr, imp);
510             break;
511         }
512         default: {
513             break;
514         }
515     }
517     return module;
520 /**
521  * \return   The module created
522  * \brief    This function creates a module from a filename of an
523  *           XML description.
524  * \param    filename  The file holding the XML description of the module.
525  *
526  * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
527  */
528 Extension *
529 build_from_file(gchar const *filename)
531     Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
532     Extension *ext = build_from_reprdoc(doc, NULL);
533     if (ext != NULL)
534         Inkscape::GC::release(doc);
535     else
536         g_warning("Unable to create extension from definition file %s.\n", filename);
537     return ext;
540 /**
541  * \return   The module created
542  * \brief    This function creates a module from a buffer holding an
543  *           XML description.
544  * \param    buffer  The buffer holding the XML description of the module.
545  *
546  * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
547  * finds the length of the buffer using strlen.
548  */
549 Extension *
550 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
552     Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
553     Extension *ext = build_from_reprdoc(doc, in_imp);
554     Inkscape::GC::release(doc);
555     return ext;
558 /*
559  * TODO: Is it guaranteed that the returned extension is valid? If so, we can remove the check for
560  * filename_extension in sp_file_save_dialog().
561  */
562 Glib::ustring
563 get_file_save_extension (Inkscape::Extension::FileSaveMethod method) {
564     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
565     Glib::ustring extension;
566     switch (method) {
567         case FILE_SAVE_METHOD_SAVE_AS:
568         case FILE_SAVE_METHOD_TEMPORARY:
569             extension = prefs->getString("/dialogs/save_as/default");
570             break;
571         case FILE_SAVE_METHOD_SAVE_COPY:
572             extension = prefs->getString("/dialogs/save_copy/default");
573             break;
574         case FILE_SAVE_METHOD_INKSCAPE_SVG:
575             extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
576             break;
577     }
579     if(extension.empty())
580         extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
582     return extension;
585 Glib::ustring
586 get_file_save_path (SPDocument *doc, FileSaveMethod method) {
587     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
588     Glib::ustring path;
589     switch (method) {
590         case FILE_SAVE_METHOD_SAVE_AS:
591         {
592             bool use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir", true);
593             if (doc->uri && use_current_dir) {
594                 path = Glib::path_get_dirname(doc->uri);
595             } else {
596                 path = prefs->getString("/dialogs/save_as/path");
597             }
598             break;
599         }
600         case FILE_SAVE_METHOD_TEMPORARY:
601             path = prefs->getString("/dialogs/save_as/path");
602             break;
603         case FILE_SAVE_METHOD_SAVE_COPY:
604             path = prefs->getString("/dialogs/save_copy/path");
605             break;
606         case FILE_SAVE_METHOD_INKSCAPE_SVG:
607             if (doc->uri) {
608                 path = Glib::path_get_dirname(doc->uri);
609             } else {
610                 // FIXME: should we use the save_as path here or something else? Maybe we should
611                 // leave this as a choice to the user.
612                 path = prefs->getString("/dialogs/save_as/path");
613             }
614     }
616     if(path.empty())
617         path = g_get_home_dir(); // Is this the most sensible solution? Note that we should avoid
618                                  // g_get_current_dir because this leads to problems on OS X where
619                                  // Inkscape opens the dialog inside application bundle when it is
620                                  // invoked for the first teim.
622     return path;
625 void
626 store_file_extension_in_prefs (Glib::ustring extension, FileSaveMethod method) {
627     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
628     switch (method) {
629         case FILE_SAVE_METHOD_SAVE_AS:
630         case FILE_SAVE_METHOD_TEMPORARY:
631             prefs->setString("/dialogs/save_as/default", extension);
632             break;
633         case FILE_SAVE_METHOD_SAVE_COPY:
634             prefs->setString("/dialogs/save_copy/default", extension);
635             break;
636         case FILE_SAVE_METHOD_INKSCAPE_SVG:
637             // do nothing
638             break;
639     }
642 void
643 store_save_path_in_prefs (Glib::ustring path, FileSaveMethod method) {
644     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
645     switch (method) {
646         case FILE_SAVE_METHOD_SAVE_AS:
647         case FILE_SAVE_METHOD_TEMPORARY:
648             prefs->setString("/dialogs/save_as/path", path);
649             break;
650         case FILE_SAVE_METHOD_SAVE_COPY:
651             prefs->setString("/dialogs/save_copy/path", path);
652             break;
653         case FILE_SAVE_METHOD_INKSCAPE_SVG:
654             // do nothing
655             break;
656     }
659 } } /* namespace Inkscape::Extension */
661 /*
662   Local Variables:
663   mode:c++
664   c-file-style:"stroustrup"
665   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
666   indent-tabs-mode:nil
667   fill-column:99
668   End:
669 */
670 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :