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