Code

peeled back the gboolean code as it hit on some complexity theory principles...
[inkscape.git] / src / file.cpp
1 #define __SP_FILE_C__
3 /*
4  * File/Print operations
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Chema Celorio <chema@celorio.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
12  * Copyright (C) 1999-2005 Authors
13  * Copyright (C) 2004 David Turner
14  * Copyright (C) 2001-2002 Ximian, Inc.
15  *
16  * Released under GNU GPL, read the file 'COPYING' for more information
17  */
19 /**
20  * Note: This file needs to be cleaned up extensively.
21  * What it probably needs is to have one .h file for
22  * the API, and two or more .cpp files for the implementations.
23  */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <glib/gmem.h>
30 #include <libnr/nr-pixops.h>
32 #include "document-private.h"
33 #include "selection-chemistry.h"
34 #include "ui/view/view-widget.h"
35 #include "dir-util.h"
36 #include "helper/png-write.h"
37 #include "dialogs/export.h"
38 #include <glibmm/i18n.h>
39 #include "inkscape.h"
40 #include "desktop.h"
41 #include "selection.h"
42 #include "interface.h"
43 #include "style.h"
44 #include "print.h"
45 #include "file.h"
46 #include "message-stack.h"
47 #include "ui/dialog/filedialog.h"
48 #include "prefs-utils.h"
49 #include "path-prefix.h"
51 #include "sp-namedview.h"
52 #include "desktop-handles.h"
54 #include "extension/db.h"
55 #include "extension/input.h"
56 #include "extension/output.h"
57 /* #include "extension/menu.h"  */
58 #include "extension/system.h"
60 #include "io/sys.h"
61 #include "application/application.h"
62 #include "application/editor.h"
63 #include "inkscape.h"
64 #include "uri.h"
66 #ifdef WITH_INKBOARD
67 #include "jabber_whiteboard/session-manager.h"
68 #endif
71 //#define INK_DUMP_FILENAME_CONV 1
72 #undef INK_DUMP_FILENAME_CONV
74 //#define INK_DUMP_FOPEN 1
75 #undef INK_DUMP_FOPEN
77 void dump_str(gchar const *str, gchar const *prefix);
78 void dump_ustr(Glib::ustring const &ustr);
81 /*######################
82 ## N E W
83 ######################*/
85 /**
86  * Create a blank document and add it to the desktop
87  */
88 SPDesktop*
89 sp_file_new(const Glib::ustring &templ)
90 {
91     char *templName = NULL;
92     if (templ.size()>0)
93         templName = (char *)templ.c_str();
94     SPDocument *doc = sp_document_new(templName, TRUE, true);
95     g_return_val_if_fail(doc != NULL, NULL);
97     SPDesktop *dt;
98     if (Inkscape::NSApplication::Application::getNewGui())
99     {
100         dt = Inkscape::NSApplication::Editor::createDesktop (doc);
101     } else {
102         SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
103         g_return_val_if_fail(dtw != NULL, NULL);
104         sp_document_unref(doc);
106         sp_create_window(dtw, TRUE);
107         dt = static_cast<SPDesktop*>(dtw->view);
108         sp_namedview_window_from_document(dt);
109     }
110     return dt;
113 SPDesktop*
114 sp_file_new_default()
116     std::list<gchar *> sources;
117     sources.push_back( profile_path("templates") ); // first try user's local dir
118     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
120     while (!sources.empty()) {
121         gchar *dirname = sources.front();
122         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
124             // TRANSLATORS: default.svg is localizable - this is the name of the default document
125             //  template. This way you can localize the default pagesize, translate the name of
126             //  the default layer, etc. If you wish to localize this file, please create a
127             //  localized share/templates/default.xx.svg file, where xx is your language code.
128             char *default_template = g_build_filename(dirname, _("default.svg"), NULL);
129             if (Inkscape::IO::file_test(default_template, G_FILE_TEST_IS_REGULAR)) {
130                 return sp_file_new(default_template);
131             }
132         }
133         g_free(dirname);
134         sources.pop_front();
135     }
137     return sp_file_new("");
141 /*######################
142 ## D E L E T E
143 ######################*/
145 /**
146  *  Perform document closures preceding an exit()
147  */
148 void
149 sp_file_exit()
151     sp_ui_close_all();
152     // no need to call inkscape_exit here; last document being closed will take care of that
156 /*######################
157 ## O P E N
158 ######################*/
160 /**
161  *  Open a file, add the document to the desktop
162  *
163  *  \param replace_empty if true, and the current desktop is empty, this document
164  *  will replace the empty one.
165  */
166 bool
167 sp_file_open(const Glib::ustring &uri,
168              Inkscape::Extension::Extension *key,
169              bool add_to_recent, bool replace_empty)
171     SPDocument *doc = NULL;
172     try {
173         doc = Inkscape::Extension::open(key, uri.c_str());
174     } catch (Inkscape::Extension::Input::no_extension_found &e) {
175         doc = NULL;
176     } catch (Inkscape::Extension::Input::open_failed &e) {
177         doc = NULL;
178     }
180     if (doc) {
181         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
182         SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
184         if (existing && existing->virgin && replace_empty) {
185             // If the current desktop is empty, open the document there
186             sp_document_ensure_up_to_date (doc);
187             desktop->change_document(doc);
188             sp_document_resized_signal_emit (doc, sp_document_width(doc), sp_document_height(doc));
189         } else {
190             if (!Inkscape::NSApplication::Application::getNewGui()) {
191                 // create a whole new desktop and window
192                 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
193                 sp_create_window(dtw, TRUE);
194                 desktop = static_cast<SPDesktop*>(dtw->view);
195             } else {
196                 desktop = Inkscape::NSApplication::Editor::createDesktop (doc);
197             }
198         }
200         doc->virgin = FALSE;
201         // everyone who cares now has a reference, get rid of ours
202         sp_document_unref(doc);
203         // resize the window to match the document properties
204         // (this may be redundant for new windows... if so, move to the "virgin"
205         //  section above)
206         sp_namedview_window_from_document(desktop);
208         if (add_to_recent) {
209             prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
210         }
212         return TRUE;
213     } else {
214         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
215         gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri);
216         sp_ui_error_dialog(text);
217         g_free(text);
218         g_free(safeUri);
219         return FALSE;
220     }
223 /**
224  *  Handle prompting user for "do you want to revert"?  Revert on "OK"
225  */
226 void
227 sp_file_revert_dialog()
229     SPDesktop  *desktop = SP_ACTIVE_DESKTOP;
230     g_assert(desktop != NULL);
232     SPDocument *doc = sp_desktop_document(desktop);
233     g_assert(doc != NULL);
235     Inkscape::XML::Node     *repr = sp_document_repr_root(doc);
236     g_assert(repr != NULL);
238     gchar const *uri = doc->uri;
239     if (!uri) {
240         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet.  Cannot revert."));
241         return;
242     }
244     bool do_revert = true;
245     if (repr->attribute("sodipodi:modified") != NULL) {
246         gchar *text = g_strdup_printf(_("Changes will be lost!  Are you sure you want to reload document %s?"), uri);
248         bool response = desktop->warnDialog (text);
249         g_free(text);
251         if (!response) {
252             do_revert = false;
253         }
254     }
256     bool reverted;
257     if (do_revert) {
258         // Allow overwriting of current document.
259         doc->virgin = TRUE;
260         reverted = sp_file_open(uri,NULL);
261     } else {
262         reverted = false;
263     }
265     if (reverted) {
266         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted."));
267     } else {
268         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted."));
269     }
272 void dump_str(gchar const *str, gchar const *prefix)
274     Glib::ustring tmp;
275     tmp = prefix;
276     tmp += " [";
277     size_t const total = strlen(str);
278     for (unsigned i = 0; i < total; i++) {
279         gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
280         tmp += tmp2;
281         g_free(tmp2);
282     }
284     tmp += "]";
285     g_message(tmp.c_str());
288 void dump_ustr(Glib::ustring const &ustr)
290     char const *cstr = ustr.c_str();
291     char const *data = ustr.data();
292     Glib::ustring::size_type const byteLen = ustr.bytes();
293     Glib::ustring::size_type const dataLen = ustr.length();
294     Glib::ustring::size_type const cstrLen = strlen(cstr);
296     g_message("   size: %lu\n   length: %lu\n   bytes: %lu\n    clen: %lu",
297               gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) );
298     g_message( "  ASCII? %s", (ustr.is_ascii() ? "yes":"no") );
299     g_message( "  UTF-8? %s", (ustr.validate() ? "yes":"no") );
301     try {
302         Glib::ustring tmp;
303         for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) {
304             tmp = "    ";
305             if (i < dataLen) {
306                 Glib::ustring::value_type val = ustr.at(i);
307                 gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? "  %02x" : "%04x"), val );
308                 tmp += tmp2;
309                 g_free( tmp2 );
310             } else {
311                 tmp += "    ";
312             }
314             if (i < byteLen) {
315                 int val = (0x0ff & data[i]);
316                 gchar *tmp2 = g_strdup_printf("    %02x", val);
317                 tmp += tmp2;
318                 g_free( tmp2 );
319                 if ( val > 32 && val < 127 ) {
320                     tmp2 = g_strdup_printf( "   '%c'", (gchar)val );
321                     tmp += tmp2;
322                     g_free( tmp2 );
323                 } else {
324                     tmp += "    . ";
325                 }
326             } else {
327                 tmp += "       ";
328             }
330             if ( i < cstrLen ) {
331                 int val = (0x0ff & cstr[i]);
332                 gchar* tmp2 = g_strdup_printf("    %02x", val);
333                 tmp += tmp2;
334                 g_free(tmp2);
335                 if ( val > 32 && val < 127 ) {
336                     tmp2 = g_strdup_printf("   '%c'", (gchar) val);
337                     tmp += tmp2;
338                     g_free( tmp2 );
339                 } else {
340                     tmp += "    . ";
341                 }
342             } else {
343                 tmp += "            ";
344             }
346             g_message( tmp.c_str() );
347         }
348     } catch (...) {
349         g_message("XXXXXXXXXXXXXXXXXX Exception" );
350     }
351     g_message("---------------");
354 static Inkscape::UI::Dialog::FileOpenDialog *openDialogInstance = NULL;
356 /**
357  *  Display an file Open selector.  Open a document if OK is pressed.
358  *  Can select single or multiple files for opening.
359  */
360 void
361 sp_file_open_dialog(gpointer object, gpointer data)
364     //# Get the current directory for finding files
365     Glib::ustring open_path;
366     char *attr = (char *)prefs_get_string_attribute("dialogs.open", "path");
367     if (attr)
368         open_path = attr;
371     //# Test if the open_path directory exists  
372     if (!Inkscape::IO::file_test(open_path.c_str(),
373               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
374         open_path = "";
376     //# If no open path, default to our home directory
377     if (open_path.size() < 1)
378         {
379         open_path = g_get_home_dir();
380         open_path.append(G_DIR_SEPARATOR_S);
381         }
383     //# Create a dialog if we don't already have one
384     if (!openDialogInstance) {
385         openDialogInstance =
386               Inkscape::UI::Dialog::FileOpenDialog::create(
387                  open_path,
388                  Inkscape::UI::Dialog::SVG_TYPES,
389                  (char const *)_("Select file to open"));
390     }
392     //# Show the dialog
393     bool const success = openDialogInstance->show();
394     if (!success)
395         return;
397     //# User selected something.  Get name and type
398     Glib::ustring fileName = openDialogInstance->getFilename();
399     Inkscape::Extension::Extension *selection =
400             openDialogInstance->getSelectionType();
402     //# Code to check & open iff multiple files.
403     std::vector<Glib::ustring> flist=openDialogInstance->getFilenames();
405     //# Iterate through filenames if more than 1
406     if (flist.size() > 1)
407         {
408         for (unsigned int i=1 ; i<flist.size() ; i++)
409             {
410             Glib::ustring fName = flist[i];
412             if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
413             Glib::ustring newFileName = Glib::filename_to_utf8(fName);
414             if ( newFileName.size() > 0 )
415                 fName = newFileName;
416             else
417                 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
419 #ifdef INK_DUMP_FILENAME_CONV
420             g_message("Opening File %s\n",fileName);
421 #endif
422             sp_file_open(fileName, selection);
423             }
424         }
425         return;
426     }
429     if (fileName.size() > 0) {
431         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
433         if ( newFileName.size() > 0)
434             fileName = newFileName;
435         else
436             g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
438         open_path = fileName;
439         open_path.append(G_DIR_SEPARATOR_S);
440         prefs_set_string_attribute("dialogs.open", "path", open_path.c_str());
442         sp_file_open(fileName, selection);
443     }
445     return;
449 /*######################
450 ## V A C U U M
451 ######################*/
453 /**
454  * Remove unreferenced defs from the defs section of the document.
455  */
458 void
459 sp_file_vacuum()
461     SPDocument *doc = SP_ACTIVE_DOCUMENT;
463     unsigned int diff = vacuum_document (doc);
465     sp_document_done(doc, SP_VERB_FILE_VACUUM, 
466                      /* TODO: annotate */ "file.cpp:515");
468     SPDesktop *dt = SP_ACTIVE_DESKTOP;
469     if (diff > 0) {
470         dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
471                 ngettext("Removed <b>%i</b> unused definition in &lt;defs&gt;.",
472                          "Removed <b>%i</b> unused definitions in &lt;defs&gt;.",
473                          diff),
474                 diff);
475     } else {
476         dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE,  _("No unused definitions in &lt;defs&gt;."));
477     }
482 /*######################
483 ## S A V E
484 ######################*/
486 /**
487  * This 'save' function called by the others below
488  *
489  * \param    official  whether to set :output_module and :modified in the
490  *                     document; is true for normal save, false for temporary saves
491  */
492 static bool
493 file_save(SPDocument *doc, const Glib::ustring &uri,
494           Inkscape::Extension::Extension *key, bool saveas, bool official)
496     if (!doc || uri.size()<1) //Safety check
497         return false;
499     try {
500         Inkscape::Extension::save(key, doc, uri.c_str(),
501                  saveas && prefs_get_int_attribute("dialogs.save_as", "append_extension", 1),
502                  saveas, official); 
503     } catch (Inkscape::Extension::Output::no_extension_found &e) {
504         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
505         gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s).  This may have been caused by an unknown filename extension."), safeUri);
506         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
507         sp_ui_error_dialog(text);
508         g_free(text);
509         g_free(safeUri);
510         return FALSE;
511     } catch (Inkscape::Extension::Output::save_failed &e) {
512         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
513         gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri);
514         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
515         sp_ui_error_dialog(text);
516         g_free(text);
517         g_free(safeUri);
518         return FALSE;
519     } catch (Inkscape::Extension::Output::no_overwrite &e) {
520         return sp_file_save_dialog(doc);
521     }
523     SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document saved."));
524     return true;
531 static Inkscape::UI::Dialog::FileSaveDialog *saveDialogInstance = NULL;
533 /**
534  *  Display a SaveAs dialog.  Save the document if OK pressed.
535  *
536  * \param    ascopy  (optional) wether to set the documents->uri to the new filename or not
537  */
538 bool
539 sp_file_save_dialog(SPDocument *doc, bool is_copy)
542     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
544     Inkscape::Extension::Output *extension;
546     //# Get the default extension name
547     Glib::ustring default_extension;
548     char *attr = (char *)repr->attribute("inkscape:output_extension");
549     if (!attr)
550         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
551     if (attr)
552         default_extension = attr;
553     //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
555     Glib::ustring save_path;
556     Glib::ustring save_loc;
558     if (doc->uri == NULL) {
559         char formatBuf[256];
560         int i = 1;
562         Glib::ustring filename_extension = ".svg";
563         extension = dynamic_cast<Inkscape::Extension::Output *>
564               (Inkscape::Extension::db.get(default_extension.c_str()));
565         //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
566         if (extension)
567             filename_extension = extension->get_extension();
569         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
570         if (attr)
571             save_path = attr;
573         if (!Inkscape::IO::file_test(save_path.c_str(),
574               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
575             save_path = "";
577         if (save_path.size()<1)
578             save_path = g_get_home_dir();
580         save_loc = save_path;
581         save_loc.append(G_DIR_SEPARATOR_S);
582         snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
583         save_loc.append(formatBuf);
585         while (Inkscape::IO::file_test(save_loc.c_str(), G_FILE_TEST_EXISTS)) {
586             save_loc = save_path;
587             save_loc.append(G_DIR_SEPARATOR_S);
588             snprintf(formatBuf, 255, _("drawing-%d%s"), i++, filename_extension.c_str());
589             save_loc.append(formatBuf);
590         }
591     } else {
592         save_loc = Glib::path_get_dirname(doc->uri);
593     }
595     // convert save_loc from utf-8 to locale
596     // is this needed any more, now that everything is handled in
597     // Inkscape::IO?
598     Glib::ustring save_loc_local = Glib::filename_from_utf8(save_loc);
600     if ( save_loc_local.size() > 0) 
601         save_loc = save_loc_local;
603     //# Show the SaveAs dialog
604     char const * dialog_title;
605     if (is_copy) {
606         dialog_title = (char const *) _("Select file to save a copy to");
607     } else {
608         dialog_title = (char const *) _("Select file to save to");
609     }
610     if (!saveDialogInstance)
611         saveDialogInstance =
612              Inkscape::UI::Dialog::FileSaveDialog::create(
613                  save_loc,
614                  Inkscape::UI::Dialog::SVG_TYPES,
615                  (char const *) _("Select file to save to"),
616                  default_extension
617             );
618     saveDialogInstance->change_title(dialog_title);
619     
620     bool success = saveDialogInstance->show();
621     if (!success)
622         return success;
624     Glib::ustring fileName = saveDialogInstance->getFilename();
626     Inkscape::Extension::Extension *selectionType =
627         saveDialogInstance->getSelectionType();
630     if (fileName.size() > 0) {
631         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
633         if ( newFileName.size()>0 )
634             fileName = newFileName;
635         else
636             g_warning( "Error converting save filename to UTF-8." );
638         success = file_save(doc, fileName, selectionType, TRUE, !is_copy);
640         if (success)
641             prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
643         save_path = fileName;
644         prefs_set_string_attribute("dialogs.save_as", "path", save_path.c_str());
646         return success;
647     }
650     return false;
654 /**
655  * Save a document, displaying a SaveAs dialog if necessary.
656  */
657 bool
658 sp_file_save_document(SPDocument *doc)
660     bool success = true;
662     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
664     gchar const *fn = repr->attribute("sodipodi:modified");
665     if (fn != NULL) {
666         if ( doc->uri == NULL
667             || repr->attribute("inkscape:output_extension") == NULL )
668         {
669             return sp_file_save_dialog(doc, FALSE);
670         } else {
671             fn = g_strdup(doc->uri);
672             gchar const *ext = repr->attribute("inkscape:output_extension");
673             success = file_save(doc, fn, Inkscape::Extension::db.get(ext), FALSE, TRUE);
674             g_free((void *) fn);
675         }
676     } else {
677         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No changes need to be saved."));
678         success = TRUE;
679     }
681     return success;
685 /**
686  * Save a document.
687  */
688 bool
689 sp_file_save(gpointer object, gpointer data)
691     if (!SP_ACTIVE_DOCUMENT)
692         return false;
693     sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
694     return sp_file_save_document(SP_ACTIVE_DOCUMENT);
698 /**
699  *  Save a document, always displaying the SaveAs dialog.
700  */
701 bool
702 sp_file_save_as(gpointer object, gpointer data)
704     if (!SP_ACTIVE_DOCUMENT)
705         return false;
706     sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
707     return sp_file_save_dialog(SP_ACTIVE_DOCUMENT, FALSE);
712 /**
713  *  Save a copy of a document, always displaying a sort of SaveAs dialog.
714  */
715 bool
716 sp_file_save_a_copy(gpointer object, gpointer data)
718     if (!SP_ACTIVE_DOCUMENT)
719         return false;
720     sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
721     return sp_file_save_dialog(SP_ACTIVE_DOCUMENT, TRUE);
725 /*######################
726 ## I M P O R T
727 ######################*/
729 /**
730  *  Import a resource.  Called by sp_file_import()
731  */
732 void
733 file_import(SPDocument *in_doc, const Glib::ustring &uri,
734                Inkscape::Extension::Extension *key)
736     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
738     //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key );
739     SPDocument *doc;
740     try {
741         doc = Inkscape::Extension::open(key, uri.c_str());
742     } catch (Inkscape::Extension::Input::no_extension_found &e) {
743         doc = NULL;
744     } catch (Inkscape::Extension::Input::open_failed &e) {
745         doc = NULL;
746     }
748     if (doc != NULL) {
749         // the import extension has passed us a document, now we need to embed it into our document
750         if ( 0 ) {
751 //            const gchar *docbase = (sp_repr_document_root( sp_repr_document( repr ))->attribute("sodipodi:docbase" );
752             g_message(" settings  uri  [%s]", doc->uri );
753             g_message("           base [%s]", doc->base );
754             g_message("           name [%s]", doc->name );
755             Inkscape::IO::fixupHrefs( doc, doc->base, TRUE );
756             g_message("        mid-fixup");
757             Inkscape::IO::fixupHrefs( doc, in_doc->base, TRUE );
758         }
760         // move imported defs to our document's defs
761         SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
762         SPObject *defs = SP_DOCUMENT_DEFS(doc);
763         Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
764         for (SPObject *child = sp_object_first_child(defs);
765              child != NULL; child = SP_OBJECT_NEXT(child))
766         {
767             // FIXME: in case of id conflict, newly added thing will be re-ided and thus likely break a reference to it from imported stuff
768             SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(child)->duplicate(), last_def);
769         }
771         guint items_count = 0;
772         for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
773              child != NULL; child = SP_OBJECT_NEXT(child)) {
774             if (SP_IS_ITEM(child))
775                 items_count ++;
776         }
777         SPCSSAttr *style = sp_css_attr_from_object (SP_DOCUMENT_ROOT (doc));
779         SPObject *new_obj = NULL;
781         if ((style && style->firstChild()) || items_count > 1) {
782             // create group
783             Inkscape::XML::Node *newgroup = sp_repr_new("svg:g");
784             sp_repr_css_set (newgroup, style, "style");
786             for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
787                 if (SP_IS_ITEM(child)) {
788                     Inkscape::XML::Node *newchild = SP_OBJECT_REPR(child)->duplicate();
790                     // convert layers to groups; FIXME: add "preserve layers" mode where each layer
791                     // from impot is copied to the same-named layer in host
792                     newchild->setAttribute("inkscape:groupmode", NULL);
794                     newgroup->appendChild(newchild);
795                 }
796             }
798             if (desktop) {
799                 // Add it to the current layer
800                 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
801             } else {
802                 // There's no desktop (command line run?)
803                 // FIXME: For such cases we need a document:: method to return the current layer
804                 new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newgroup);
805             }
807             Inkscape::GC::release(newgroup);
808         } else {
809             // just add one item
810             for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
811                 if (SP_IS_ITEM(child)) {
812                     Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate();
813                     newitem->setAttribute("inkscape:groupmode", NULL);
815                     if (desktop) {
816                         // Add it to the current layer
817                         new_obj = desktop->currentLayer()->appendChildRepr(newitem);
818                     } else {
819                         // There's no desktop (command line run?)
820                         // FIXME: For such cases we need a document:: method to return the current layer
821                         new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newitem);
822                     }
824                 }
825             }
826         }
828         if (style) sp_repr_css_attr_unref (style);
830         // select and move the imported item
831         if (new_obj && SP_IS_ITEM(new_obj)) {
832             Inkscape::Selection *selection = sp_desktop_selection(desktop);
833             selection->set(SP_ITEM(new_obj));
835             // To move the imported object, we must temporarily set the "transform pattern with
836             // object" option.
837             {
838                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
839                 prefs_set_int_attribute("options.transform", "pattern", 1);
840                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
841                 NR::Point m( desktop->point() - selection->bounds().midpoint() );
842                 sp_selection_move_relative(selection, m);
843                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
844             }
845         }
847         sp_document_unref(doc);
848         sp_document_done(in_doc, SP_VERB_FILE_IMPORT,
849                          /* TODO: annotate */ "file.cpp:900");
851     } else {
852         gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri.c_str());
853         sp_ui_error_dialog(text);
854         g_free(text);
855     }
857     return;
861 static Inkscape::UI::Dialog::FileOpenDialog *importDialogInstance = NULL;
863 /**
864  *  Display an Open dialog, import a resource if OK pressed.
865  */
866 void
867 sp_file_import(GtkWidget *widget)
869     static Glib::ustring import_path;
871     SPDocument *doc = SP_ACTIVE_DOCUMENT;
872     if (!doc)
873         return;
875     if (!importDialogInstance) {
876         importDialogInstance =
877              Inkscape::UI::Dialog::FileOpenDialog::create(
878                  import_path,
879                  Inkscape::UI::Dialog::IMPORT_TYPES,
880                  (char const *)_("Select file to import"));
881     }
883     bool success = importDialogInstance->show();
884     if (!success)
885         return;
887     //# Get file name and extension type
888     Glib::ustring fileName = importDialogInstance->getFilename();
889     Inkscape::Extension::Extension *selection =
890         importDialogInstance->getSelectionType();
893     if (fileName.size() > 0) {
894  
895         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
897         if ( newFileName.size() > 0)
898             fileName = newFileName;
899         else
900             g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
902         import_path = fileName;
903         if (import_path.size()>0)
904             import_path.append(G_DIR_SEPARATOR_S);
906         file_import(doc, fileName, selection);
907     }
909     return;
914 /*######################
915 ## E X P O R T
916 ######################*/
918 //#define NEW_EXPORT_DIALOG
922 #ifdef NEW_EXPORT_DIALOG
924 static Inkscape::UI::Dialog::FileExportDialog *exportDialogInstance = NULL;
926 /**
927  *  Display an Export dialog, export as the selected type if OK pressed
928  */
929 bool
930 sp_file_export_dialog(void *widget)
932     //# temp hack for 'doc' until we can switch to this dialog
933     SPDocument *doc = SP_ACTIVE_DOCUMENT;
935     Glib::ustring export_path; 
936     Glib::ustring export_loc; 
938     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
940     Inkscape::Extension::Output *extension;
942     //# Get the default extension name
943     Glib::ustring default_extension;
944     char *attr = (char *)repr->attribute("inkscape:output_extension");
945     if (!attr)
946         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
947     if (attr)
948         default_extension = attr;
949     //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
951     if (doc->uri == NULL)
952         {
953         char formatBuf[256];
955         Glib::ustring filename_extension = ".svg";
956         extension = dynamic_cast<Inkscape::Extension::Output *>
957               (Inkscape::Extension::db.get(default_extension.c_str()));
958         //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
959         if (extension)
960             filename_extension = extension->get_extension();
962         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
963         if (attr)
964             export_path = attr;
966         if (!Inkscape::IO::file_test(export_path.c_str(),
967               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
968             export_path = "";
970         if (export_path.size()<1)
971             export_path = g_get_home_dir();
973         export_loc = export_path;
974         export_loc.append(G_DIR_SEPARATOR_S);
975         snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
976         export_loc.append(formatBuf);
978         }
979     else
980         {
981         export_path = Glib::path_get_dirname(doc->uri);
982         }
984     // convert save_loc from utf-8 to locale
985     // is this needed any more, now that everything is handled in
986     // Inkscape::IO?
987     Glib::ustring export_path_local = Glib::filename_from_utf8(export_path);
988     if ( export_path_local.size() > 0) 
989         export_path = export_path_local;
991     //# Show the SaveAs dialog
992     if (!exportDialogInstance)
993         exportDialogInstance =
994              Inkscape::UI::Dialog::FileExportDialog::create(
995                  export_path,
996                  Inkscape::UI::Dialog::EXPORT_TYPES,
997                  (char const *) _("Select file to export to"),
998                  default_extension
999             );
1001     bool success = exportDialogInstance->show();
1002     if (!success)
1003         return success;
1005     Glib::ustring fileName = exportDialogInstance->getFilename();
1007     Inkscape::Extension::Extension *selectionType =
1008         exportDialogInstance->getSelectionType();
1011     if (fileName.size() > 0) {
1012         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1014         if ( newFileName.size()>0 )
1015             fileName = newFileName;
1016         else
1017             g_warning( "Error converting save filename to UTF-8." );
1019         success = file_save(doc, fileName, selectionType, TRUE, FALSE);
1021         if (success)
1022             prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
1024         export_path = fileName;
1025         prefs_set_string_attribute("dialogs.save_as", "path", export_path.c_str());
1027         return success;
1028     }
1031     return false;
1040 #else
1044 /**
1045  *
1046  */
1047 bool
1048 sp_file_export_dialog(void *widget)
1050     sp_export_dialog();
1051     return true;
1054 #endif
1056 /*######################
1057 ## P R I N T
1058 ######################*/
1061 /**
1062  *  Print the current document, if any.
1063  */
1064 void
1065 sp_file_print()
1067     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1068     if (doc)
1069         sp_print_document(doc, FALSE);
1073 /**
1074  *  Print the current document, if any.  Do not use
1075  *  the machine's print drivers.
1076  */
1077 void
1078 sp_file_print_direct()
1080     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1081     if (doc)
1082         sp_print_document(doc, TRUE);
1086 /**
1087  * Display what the drawing would look like, if
1088  * printed.
1089  */
1090 void
1091 sp_file_print_preview(gpointer object, gpointer data)
1094     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1095     if (doc)
1096         sp_print_preview_document(doc);
1100 void Inkscape::IO::fixupHrefs( SPDocument *doc, const gchar *base, gboolean spns )
1102     //g_message("Inkscape::IO::fixupHrefs( , [%s], )", base );
1104     if ( 0 ) {
1105         gchar const* things[] = {
1106             "data:foo,bar",
1107             "http://www.google.com/image.png",
1108             "ftp://ssd.com/doo",
1109             "/foo/dee/bar.svg",
1110             "foo.svg",
1111             "file:/foo/dee/bar.svg",
1112             "file:///foo/dee/bar.svg",
1113             "file:foo.svg",
1114             "/foo/bar\xe1\x84\x92.svg",
1115             "file:///foo/bar\xe1\x84\x92.svg",
1116             "file:///foo/bar%e1%84%92.svg",
1117             "/foo/bar%e1%84%92.svg",
1118             "bar\xe1\x84\x92.svg",
1119             "bar%e1%84%92.svg",
1120             NULL
1121         };
1122         g_message("+------");
1123         for ( int i = 0; things[i]; i++ )
1124         {
1125             try
1126             {
1127                 URI uri(things[i]);
1128                 gboolean isAbs = g_path_is_absolute( things[i] );
1129                 gchar *str = uri.toString();
1130                 g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
1131                            (int)uri.isRelative(),
1132                            uri.getScheme(),
1133                            uri.getPath(),
1134                            uri.getOpaque(),
1135                            things[i],
1136                            str );
1137                 g_free(str);
1138             }
1139             catch ( MalformedURIException err )
1140             {
1141                 dump_str( things[i], "MalformedURIException" );
1142                 xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
1143                 g_message("    gone from [%s] to [%s]", things[i], redo );
1144                 if ( redo == NULL )
1145                 {
1146                     URI again = URI::fromUtf8( things[i] );
1147                     gboolean isAbs = g_path_is_absolute( things[i] );
1148                     gchar *str = again.toString();
1149                     g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
1150                                (int)again.isRelative(),
1151                                again.getScheme(),
1152                                again.getPath(),
1153                                again.getOpaque(),
1154                                things[i],
1155                                str );
1156                     g_free(str);
1157                     g_message("    ----");
1158                 }
1159             }
1160         }
1161         g_message("+------");
1162     }
1164     GSList const *images = sp_document_get_resource_list(doc, "image");
1165     for (GSList const *l = images; l != NULL; l = l->next) {
1166         Inkscape::XML::Node *ir = SP_OBJECT_REPR(l->data);
1168         const gchar *href = ir->attribute("xlink:href");
1170         // First try to figure out an absolute path to the asset
1171         //g_message("image href [%s]", href );
1172         if (spns && !g_path_is_absolute(href)) {
1173             const gchar *absref = ir->attribute("sodipodi:absref");
1174             const gchar *base_href = g_build_filename(base, href, NULL);
1175             //g_message("      absr [%s]", absref );
1177             if ( absref && Inkscape::IO::file_test(absref, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_test(base_href, G_FILE_TEST_EXISTS))
1178             {
1179                 // only switch over if the absref is valid while href is not
1180                 href = absref;
1181                 //g_message("     copied absref to href");
1182             }
1183         }
1185         // Once we have an absolute path, convert it relative to the new location
1186         if (href && g_path_is_absolute(href)) {
1187             const gchar *relname = sp_relative_path_from_path(href, base);
1188             //g_message("     setting to [%s]", relname );
1189             ir->setAttribute("xlink:href", relname);
1190         }
1191 // TODO next refinement is to make the first choice keeping the relative path as-is if
1192 //      based on the new location it gives us a valid file.
1193     }
1197 /*
1198   Local Variables:
1199   mode:c++
1200   c-file-style:"stroustrup"
1201   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1202   indent-tabs-mode:nil
1203   fill-column:99
1204   End:
1205 */
1206 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :