summaryrefslogtreecommitdiffstats
path: root/src/extension/system.cpp
blob: f4ba2dca53aff61600eb0fa94f7191efb1baa235 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * This is file is kind of the junk file.  Basically everything that
 * didn't fit in one of the other well defined areas, well, it's now
 * here.  Which is good in someways, but this file really needs some
 * definition.  Hopefully that will come ASAP.
 *
 * Authors:
 *   Ted Gould <ted@gould.cx>
 *   Johan Engelen <johan@shouraizou.nl>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Abhishek Sharma
 *
 * Copyright (C) 2006-2007 Johan Engelen
 * Copyright (C) 2002-2004 Ted Gould
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include "ui/interface.h"

#include "system.h"
#include "preferences.h"
#include "extension.h"
#include "db.h"
#include "input.h"
#include "output.h"
#include "effect.h"
#include "patheffect.h"
#include "print.h"
#include "implementation/script.h"
#include "implementation/xslt.h"
#include "xml/rebase-hrefs.h"
#include "io/sys.h"
#include "inkscape.h"
#include "document-undo.h"
#include "loader.h"

#include <glibmm/miscutils.h>

namespace Inkscape {
namespace Extension {

static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);

/**
 * \return   A new document created from the filename passed in
 * \brief    This is a generic function to use the open function of
 *           a module (including Autodetect)
 * \param    key       Identifier of which module to use
 * \param    filename  The file that should be opened
 *
 * First things first, are we looking at an autodetection?  Well if that's the case then the module
 * needs to be found, and that is done with a database lookup through the module DB.  The foreach
 * function is called, with the parameter being a gpointer array.  It contains both the filename
 * (to find its extension) and where to write the module when it is found.
 *
 * If there is no autodetection, then the module database is queried with the key given.
 *
 * If everything is cool at this point, the module is loaded, and there is possibility for
 * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
 * After it is finished the function continues.
 *
 * Lastly, the open function is called in the module itself.
 */
SPDocument *open(Extension *key, gchar const *filename)
{
    Input *imod = nullptr;

    if (key == nullptr) {
        gpointer parray[2];
        parray[0] = (gpointer)filename;
        parray[1] = (gpointer)&imod;
        db.foreach(open_internal, (gpointer)&parray);
    } else {
        imod = dynamic_cast<Input *>(key);
    }

    bool last_chance_svg = false;
    if (key == nullptr && imod == nullptr) {
        last_chance_svg = true;
        imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
    }

    if (imod == nullptr) {
        throw Input::no_extension_found();
    }

    // Hide pixbuf extensions depending on user preferences.
    //g_warning("Extension: %s", imod->get_id());

    bool show = true;
    if (strlen(imod->get_id()) > 21) {
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
        bool ask = prefs->getBool("/dialogs/import/ask");
        bool ask_svg = prefs->getBool("/dialogs/import/ask_svg");
        Glib::ustring id = Glib::ustring(imod->get_id(), 22);
        if (id.compare("org.inkscape.input.svg") == 0) {
            if (ask_svg && prefs->getBool("/options/onimport", false)) {
                show = true;
                imod->set_gui(true);
            } else {
                show = false;
                imod->set_gui(false);
            }
        } else if(strlen(imod->get_id()) > 27) {
            id = Glib::ustring(imod->get_id(), 28);
            if (!ask && id.compare( "org.inkscape.input.gdkpixbuf") == 0) {
                show = false;
                imod->set_gui(false);
            }
        }
    }
    imod->set_state(Extension::STATE_LOADED);

    if (!imod->loaded()) {
        throw Input::open_failed();
    }

    if (!imod->prefs(filename)) {
        throw Input::open_cancelled();
    }

    SPDocument *doc = imod->open(filename);

    if (!doc) {
        if (last_chance_svg) {
            if ( INKSCAPE.use_gui() ) {
                sp_ui_error_dialog(_("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
            } else {
                g_warning("%s", _("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
            }
        }
        throw Input::open_failed();
    }
    // If last_chance_svg is true here, it means we successfully opened a file as an svg
    // and there's no need to warn the user about it, just do it.

    doc->setDocumentFilename(filename);
    if (!show) {
        imod->set_gui(true);
    }

    return doc;
}

/**
 * \return   none
 * \brief    This is the function that searches each module to see
 *           if it matches the filename for autodetection.
 * \param    in_plug  The module to be tested
 * \param    in_data  An array of pointers containing the filename, and
 *                    the place to put a successfully found module.
 *
 * Basically this function only looks at input modules as it is part of the open function.  If the
 * module is an input module, it then starts to take it apart, and the data that is passed in.
 * Because the data being passed in is in such a weird format, there are a few casts to make it
 * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
 * compiler.
 *
 * First thing that is checked is if the filename is shorter than the extension itself.  There is
 * no way for a match in that case.  If it's long enough then there is a string compare of the end
 * of the filename (for the length of the extension), and the extension itself.  If this passes
 * then the pointer passed in is set to the current module.
 */
static void
open_internal(Extension *in_plug, gpointer in_data)
{
    auto imod = dynamic_cast<Input *>(in_plug);
    if (imod && !imod->deactivated()) {
        gpointer *parray = (gpointer *)in_data;
        gchar const *filename = (gchar const *)parray[0];
        Input **pimod = (Input **)parray[1];

        // skip all the rest if we already found a function to open it
        // since they're ordered by preference now.
        if (!*pimod) {
            gchar const *ext = imod->get_extension();

            gchar *filenamelower = g_utf8_strdown(filename, -1);
            gchar *extensionlower = g_utf8_strdown(ext, -1);

            if (g_str_has_suffix(filenamelower, extensionlower)) {
                *pimod = imod;
            }

            g_free(filenamelower);
            g_free(extensionlower);
        }
    }

    return;
}

/**
 * \return   None
 * \brief    This is a generic function to use the save function of
 *           a module (including Autodetect)
 * \param    key       Identifier of which module to use
 * \param    doc       The document to be saved
 * \param    filename  The file that the document should be saved to
 * \param    official  (optional) whether to set :output_module and :modified in the
 *                     document; is true for normal save, false for temporary saves
 *
 * First things first, are we looking at an autodetection?  Well if that's the case then the module
 * needs to be found, and that is done with a database lookup through the module DB.  The foreach
 * function is called, with the parameter being a gpointer array.  It contains both the filename
 * (to find its extension) and where to write the module when it is found.
 *
 * If there is no autodetection the module database is queried with the key given.
 *
 * If everything is cool at this point, the module is loaded, and there is possibility for
 * preferences.  If there is a function, then it is executed to get the dialog to be displayed.
 * After it is finished the function continues.
 *
 * Lastly, the save function is called in the module itself.
 */
void
save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official,
    Inkscape::Extension::FileSaveMethod save_method)
{
    Output *omod;
    if (key == nullptr) {
        gpointer parray[2];
        parray[0] = (gpointer)filename;
        parray[1] = (gpointer)&omod;
        omod = nullptr;
        db.foreach(save_internal, (gpointer)&parray);

        /* This is a nasty hack, but it is required to ensure that
           autodetect will always save with the Inkscape extensions
           if they are available. */
        if (omod != nullptr && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
            omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
        }
        /* If autodetect fails, save as Inkscape SVG */
        if (omod == nullptr) {
            // omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); use exception and let user choose
        }
    } else {
        omod = dynamic_cast<Output *>(key);
    }

    if (!dynamic_cast<Output *>(omod)) {
        g_warning("Unable to find output module to handle file: %s\n", filename);
        throw Output::no_extension_found();
    }

    omod->set_state(Extension::STATE_LOADED);
    if (!omod->loaded()) {
        throw Output::save_failed();
    }

    if (!omod->prefs()) {
        throw Output::save_cancelled();
    }

    gchar *fileName = g_strdup(filename);

    if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
        g_free(fileName);
        throw Output::no_overwrite();
    }

    // test if the file exists and is writable
    // the test only checks the file attributes and might pass where ACL does not allow writes
    if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_is_writable(filename)) {
        g_free(fileName);
        throw Output::file_read_only();
    }

    Inkscape::XML::Node *repr = doc->getReprRoot();


    // remember attributes in case this is an unofficial save and/or overwrite fails
    gchar *saved_filename = g_strdup(doc->getDocumentFilename());
    gchar *saved_output_extension = nullptr;
    gchar *saved_dataloss = nullptr;
    bool saved_modified = doc->isModifiedSinceSave();
    saved_output_extension = g_strdup(get_file_save_extension(save_method).c_str());
    saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
    if (official) {
        // The document is changing name/uri.
        doc->changeFilenameAndHrefs(fileName);
    }

    // Update attributes:
    {
        {
            DocumentUndo::ScopedInsensitive _no_undo(doc);
            // also save the extension for next use
            store_file_extension_in_prefs (omod->get_id(), save_method);
            // set the "dataloss" attribute if the chosen extension is lossy
            repr->removeAttribute("inkscape:dataloss");
            if (omod->causes_dataloss()) {
                repr->setAttribute("inkscape:dataloss", "true");
            }
        }
        doc->setModifiedSinceSave(false);
    }

    try {
        omod->save(doc, fileName);
    }
    catch(...) {
        // revert attributes in case of official and overwrite
        if(check_overwrite && official) {
            {
                DocumentUndo::ScopedInsensitive _no_undo(doc);
                store_file_extension_in_prefs (saved_output_extension, save_method);
                repr->setAttribute("inkscape:dataloss", saved_dataloss);
            }
            doc->changeFilenameAndHrefs(saved_filename);
        }
        doc->setModifiedSinceSave(saved_modified);
        // free used resources
        g_free(saved_output_extension);
        g_free(saved_dataloss);
        g_free(saved_filename);

        g_free(fileName);

        throw;
    }

    // If it is an unofficial save, set the modified attributes back to what they were.
    if ( !official) {
        {
            DocumentUndo::ScopedInsensitive _no_undo(doc);
            store_file_extension_in_prefs (saved_output_extension, save_method);
            repr->setAttribute("inkscape:dataloss", saved_dataloss);
        }
        doc->setModifiedSinceSave(saved_modified);

        g_free(saved_output_extension);
        g_free(saved_dataloss);
    }

    g_free(fileName);
    return;
}

/**
 * \return   none
 * \brief    This is the function that searches each module to see
 *           if it matches the filename for autodetection.
 * \param    in_plug  The module to be tested
 * \param    in_data  An array of pointers containing the filename, and
 *                    the place to put a successfully found module.
 *
 * Basically this function only looks at output modules as it is part of the open function.  If the
 * module is an output module, it then starts to take it apart, and the data that is passed in.
 * Because the data being passed in is in such a weird format, there are a few casts to make it
 * easier to use.  While it looks like a lot of local variables, they'll all get removed by the
 * compiler.
 *
 * First thing that is checked is if the filename is shorter than the extension itself.  There is
 * no way for a match in that case.  If it's long enough then there is a string compare of the end
 * of the filename (for the length of the extension), and the extension itself.  If this passes
 * then the pointer passed in is set to the current module.
 */
static void
save_internal(Extension *in_plug, gpointer in_data)
{
    auto omod = dynamic_cast<Output *>(in_plug);
    if (omod && !omod->deactivated()) {
        gpointer *parray = (gpointer *)in_data;
        gchar const *filename = (gchar const *)parray[0];
        Output **pomod = (Output **)parray[1];

        // skip all the rest if we already found someone to save it
        // since they're ordered by preference now.
        if (!*pomod) {
            gchar const *ext = omod->get_extension();

            gchar *filenamelower = g_utf8_strdown(filename, -1);
            gchar *extensionlower = g_utf8_strdown(ext, -1);

            if (g_str_has_suffix(filenamelower, extensionlower)) {
                *pomod = omod;
            }

            g_free(filenamelower);
            g_free(extensionlower);
        }
    }

    return;
}

Print *
get_print(gchar const *key)
{
    return dynamic_cast<Print *>(db.get(key));
}

/**
 * \return   true if extension successfully parsed, false otherwise
 *           A true return value does not guarantee an extension was actually registered,
 *           but indicates no errors occurred while parsing the extension.
 * \brief    Creates a module from a Inkscape::XML::Document describing the module
 * \param    doc  The XML description of the module
 *
 * This function basically has two segments.  The first is that it goes through the Repr tree
 * provided, and determines what kind of module this is, and what kind of implementation to use.
 * All of these are then stored in two enums that are defined in this function.  This makes it
 * easier to add additional types (which will happen in the future, I'm sure).
 *
 * Second, there is case statements for these enums.  The first one is the type of module.  This is
 * the one where the module is actually created.  After that, then the implementation is applied to
 * get the load and unload functions.  If there is no implementation then these are not set.  This
 * case could apply to modules that are built in (like the SVG load/save functions).
 */
bool
build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp, std::string* baseDir)
{
    ModuleImpType module_implementation_type = MODULE_UNKNOWN_IMP;
    ModuleFuncType module_functional_type = MODULE_UNKNOWN_FUNC;

    g_return_val_if_fail(doc != nullptr, false);

    Inkscape::XML::Node *repr = doc->root();

    if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
        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());
        return false;
    }

    Inkscape::XML::Node *child_repr = repr->firstChild();
    while (child_repr != nullptr) {
        char const *element_name = child_repr->name();
        /* printf("Child: %s\n", child_repr->name()); */
        if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
            module_functional_type = MODULE_INPUT;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
            module_functional_type = MODULE_OUTPUT;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
            module_functional_type = MODULE_FILTER;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
            module_functional_type = MODULE_PRINT;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
            module_functional_type = MODULE_PATH_EFFECT;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
            module_implementation_type = MODULE_EXTENSION;
        } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
            module_implementation_type = MODULE_XSLT;
        } else if (!strcmp(element_name,  INKSCAPE_EXTENSION_NS "plugin")) {
            module_implementation_type = MODULE_PLUGIN;
        }

        //Inkscape::XML::Node *old_repr = child_repr;
        child_repr = child_repr->next();
        //Inkscape::GC::release(old_repr);
    }

    Implementation::Implementation *imp;
    if (in_imp == nullptr) {
        switch (module_implementation_type) {
            case MODULE_EXTENSION: {
                Implementation::Script *script = new Implementation::Script();
                imp = static_cast<Implementation::Implementation *>(script);
                break;
            }
            case MODULE_XSLT: {
                Implementation::XSLT *xslt = new Implementation::XSLT();
                imp = static_cast<Implementation::Implementation *>(xslt);
                break;
            }
            case MODULE_PLUGIN: {
                Inkscape::Extension::Loader loader = Inkscape::Extension::Loader();
                if( baseDir != nullptr){
                    loader.set_base_directory ( *baseDir );
                }
                imp = loader.load_implementation(doc);
                break;
            }
            default: {
                imp = nullptr;
                break;
            }
        }
    } else {
        imp = in_imp;
    }

    Extension *module = nullptr;
    try {
        switch (module_functional_type) {
            case MODULE_INPUT: {
                module = new Input(repr, imp, baseDir);
                break;
            }
            case MODULE_OUTPUT: {
                module = new Output(repr, imp, baseDir);
                break;
            }
            case MODULE_FILTER: {
                module = new Effect(repr, imp, baseDir);
                break;
            }
            case MODULE_PRINT: {
                module = new Print(repr, imp, baseDir);
                break;
            }
            case MODULE_PATH_EFFECT: {
                module = new PathEffect(repr, imp, baseDir);
                break;
            }
            default: {
                g_warning("Extension of unknown type!"); // TODO: Should not happen! Is this even useful?
                module = new Extension(repr, imp, baseDir);
                break;
            }
        }
    } catch (const Extension::extension_no_id& e) {
        g_warning("Building extension failed. Extension does not have a valid ID");
    } catch (const Extension::extension_no_name& e) {
        g_warning("Building extension failed. Extension does not have a valid name");
    } catch (const Extension::extension_not_compatible& e) {
        return true; // This is not an actual error; just silently ignore the extension
    }

    if (module) {
        return true;
    }

    return false;
}

/**
 * \brief    This function creates a module from a filename of an
 *           XML description.
 * \param    filename  The file holding the XML description of the module.
 *
 * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
 */
void
build_from_file(gchar const *filename)
{
    std::string dir = Glib::path_get_dirname(filename);

    Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
    if (!doc) {
        g_critical("Inkscape::Extension::build_from_file() - XML description loaded from '%s' not valid.", filename);
        return;
    }

    if (!build_from_reprdoc(doc, nullptr, &dir)) {
        g_warning("Inkscape::Extension::build_from_file() - Could not parse extension from '%s'.", filename);
    }

    Inkscape::GC::release(doc);
}

/**
 * \brief    This function creates a module from a buffer holding an
 *           XML description.
 * \param    buffer  The buffer holding the XML description of the module.
 *
 * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc.  It
 * finds the length of the buffer using strlen.
 */
void
build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
{
    Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
    if (!doc) {
        g_critical("Inkscape::Extension::build_from_mem() - XML description loaded from memory buffer not valid.");
        return;
    }

    if (!build_from_reprdoc(doc, in_imp, nullptr)) {
        g_critical("Inkscape::Extension::build_from_mem() - Could not parse extension from memory buffer.");
    }

    Inkscape::GC::release(doc);
}

/*
 * TODO: Is it guaranteed that the returned extension is valid? If so, we can remove the check for
 * filename_extension in sp_file_save_dialog().
 */
Glib::ustring
get_file_save_extension (Inkscape::Extension::FileSaveMethod method) {
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    Glib::ustring extension;
    switch (method) {
        case FILE_SAVE_METHOD_SAVE_AS:
        case FILE_SAVE_METHOD_TEMPORARY:
            extension = prefs->getString("/dialogs/save_as/default");
            break;
        case FILE_SAVE_METHOD_SAVE_COPY:
            extension = prefs->getString("/dialogs/save_copy/default");
            break;
        case FILE_SAVE_METHOD_INKSCAPE_SVG:
            extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
            break;
        case FILE_SAVE_METHOD_EXPORT:
            /// \todo no default extension set for Export? defaults to SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE is ok?
            break;
    }

    if(extension.empty()) {
        extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
    }

    return extension;
}

Glib::ustring
get_file_save_path (SPDocument *doc, FileSaveMethod method) {
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    Glib::ustring path;
    bool use_current_dir = true;
    switch (method) {
        case FILE_SAVE_METHOD_SAVE_AS:
        {
            use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir", true);
            if (doc->getDocumentFilename() && use_current_dir) {
                path = Glib::path_get_dirname(doc->getDocumentFilename());
            } else {
                path = prefs->getString("/dialogs/save_as/path");
            }
            break;
        }
        case FILE_SAVE_METHOD_TEMPORARY:
            path = prefs->getString("/dialogs/save_as/path");
            break;
        case FILE_SAVE_METHOD_SAVE_COPY:
            use_current_dir = prefs->getBool("/dialogs/save_copy/use_current_dir", prefs->getBool("/dialogs/save_as/use_current_dir", true));
            if (doc->getDocumentFilename() && use_current_dir) {
                path = Glib::path_get_dirname(doc->getDocumentFilename());
            } else {
                path = prefs->getString("/dialogs/save_copy/path");
            }
            break;
        case FILE_SAVE_METHOD_INKSCAPE_SVG:
            if (doc->getDocumentFilename()) {
                path = Glib::path_get_dirname(doc->getDocumentFilename());
            } else {
                // FIXME: should we use the save_as path here or something else? Maybe we should
                // leave this as a choice to the user.
                path = prefs->getString("/dialogs/save_as/path");
            }
            break;
        case FILE_SAVE_METHOD_EXPORT:
            /// \todo no default path set for Export?
            // defaults to g_get_home_dir()
            break;
    }

    if(path.empty()) {
        path = g_get_home_dir(); // Is this the most sensible solution? Note that we should avoid
                                 // g_get_current_dir because this leads to problems on OS X where
                                 // Inkscape opens the dialog inside application bundle when it is
                                 // invoked for the first teim.
    }

    return path;
}

void
store_file_extension_in_prefs (Glib::ustring extension, FileSaveMethod method) {
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    switch (method) {
        case FILE_SAVE_METHOD_SAVE_AS:
        case FILE_SAVE_METHOD_TEMPORARY:
            prefs->setString("/dialogs/save_as/default", extension);
            break;
        case FILE_SAVE_METHOD_SAVE_COPY:
            prefs->setString("/dialogs/save_copy/default", extension);
            break;
        case FILE_SAVE_METHOD_INKSCAPE_SVG:
        case FILE_SAVE_METHOD_EXPORT:
            // do nothing
            break;
    }
}

void
store_save_path_in_prefs (Glib::ustring path, FileSaveMethod method) {
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    switch (method) {
        case FILE_SAVE_METHOD_SAVE_AS:
        case FILE_SAVE_METHOD_TEMPORARY:
            prefs->setString("/dialogs/save_as/path", path);
            break;
        case FILE_SAVE_METHOD_SAVE_COPY:
            prefs->setString("/dialogs/save_copy/path", path);
            break;
        case FILE_SAVE_METHOD_INKSCAPE_SVG:
        case FILE_SAVE_METHOD_EXPORT:
            // do nothing
            break;
    }
}

} } /* namespace Inkscape::Extension */

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :