Code

more string cleanup
[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) 1999-2005 Authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 /**
19  * Note: This file needs to be cleaned up extensively.
20  * What it probably needs is to have one .h file for
21  * the API, and two or more .cpp files for the implementations.
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <glib/gmem.h>
29 #include <libnr/nr-pixops.h>
31 #include "document-private.h"
32 #include "selection-chemistry.h"
33 #include "ui/view/view-widget.h"
34 #include "dir-util.h"
35 #include "helper/png-write.h"
36 #include "dialogs/export.h"
37 #include <glibmm/i18n.h>
38 #include "inkscape.h"
39 #include "desktop.h"
40 #include "selection.h"
41 #include "interface.h"
42 #include "style.h"
43 #include "print.h"
44 #include "file.h"
45 #include "message-stack.h"
46 #include "ui/dialog/filedialog.h"
47 #include "prefs-utils.h"
48 #include "path-prefix.h"
50 #include "sp-namedview.h"
51 #include "desktop-handles.h"
53 #include "extension/db.h"
54 #include "extension/input.h"
55 #include "extension/output.h"
56 /* #include "extension/menu.h"  */
57 #include "extension/system.h"
59 #include "io/sys.h"
60 #include "application/application.h"
61 #include "application/editor.h"
62 #include "inkscape.h"
63 #include "uri.h"
65 #ifdef WITH_INKBOARD
66 #include "jabber_whiteboard/session-manager.h"
67 #endif
70 //#define INK_DUMP_FILENAME_CONV 1
71 #undef INK_DUMP_FILENAME_CONV
73 //#define INK_DUMP_FOPEN 1
74 #undef INK_DUMP_FOPEN
76 void dump_str(gchar const *str, gchar const *prefix);
77 void dump_ustr(Glib::ustring const &ustr);
80 /*######################
81 ## N E W
82 ######################*/
84 /**
85  * Create a blank document and add it to the desktop
86  */
87 SPDesktop*
88 sp_file_new(const Glib::ustring &templ)
89 {
90     SPDocument *doc = sp_document_new(templ.c_str(), TRUE, true);
91     g_return_val_if_fail(doc != NULL, NULL);
93     SPDesktop *dt;
94     if (Inkscape::NSApplication::Application::getNewGui())
95     {
96         dt = Inkscape::NSApplication::Editor::createDesktop (doc);
97     } else {
98         SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
99         g_return_val_if_fail(dtw != NULL, NULL);
100         sp_document_unref(doc);
102         sp_create_window(dtw, TRUE);
103         dt = static_cast<SPDesktop*>(dtw->view);
104         sp_namedview_window_from_document(dt);
105     }
106     return dt;
109 SPDesktop*
110 sp_file_new_default()
112     std::list<gchar *> sources;
113     sources.push_back( profile_path("templates") ); // first try user's local dir
114     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
116     while (!sources.empty()) {
117         gchar *dirname = sources.front();
118         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
120             // TRANSLATORS: default.svg is localizable - this is the name of the default document
121             //  template. This way you can localize the default pagesize, translate the name of
122             //  the default layer, etc. If you wish to localize this file, please create a
123             //  localized share/templates/default.xx.svg file, where xx is your language code.
124             char *default_template = g_build_filename(dirname, _("default.svg"), NULL);
125             if (Inkscape::IO::file_test(default_template, G_FILE_TEST_IS_REGULAR)) {
126                 return sp_file_new(default_template);
127             }
128         }
129         g_free(dirname);
130         sources.pop_front();
131     }
133     return sp_file_new(NULL);
137 /*######################
138 ## D E L E T E
139 ######################*/
141 /**
142  *  Perform document closures preceding an exit()
143  */
144 void
145 sp_file_exit()
147     sp_ui_close_all();
148     // no need to call inkscape_exit here; last document being closed will take care of that
152 /*######################
153 ## O P E N
154 ######################*/
156 /**
157  *  Open a file, add the document to the desktop
158  *
159  *  \param replace_empty if true, and the current desktop is empty, this document
160  *  will replace the empty one.
161  */
162 bool
163 sp_file_open(const Glib::ustring &uri,
164              Inkscape::Extension::Extension *key,
165              bool add_to_recent, bool replace_empty)
167     SPDocument *doc = NULL;
168     try {
169         doc = Inkscape::Extension::open(key, uri.c_str());
170     } catch (Inkscape::Extension::Input::no_extension_found &e) {
171         doc = NULL;
172     } catch (Inkscape::Extension::Input::open_failed &e) {
173         doc = NULL;
174     }
176     if (doc) {
177         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
178         SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
180         if (existing && existing->virgin && replace_empty) {
181             // If the current desktop is empty, open the document there
182             sp_document_ensure_up_to_date (doc);
183             desktop->change_document(doc);
184             sp_document_resized_signal_emit (doc, sp_document_width(doc), sp_document_height(doc));
185         } else {
186             if (!Inkscape::NSApplication::Application::getNewGui()) {
187                 // create a whole new desktop and window
188                 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
189                 sp_create_window(dtw, TRUE);
190                 desktop = static_cast<SPDesktop*>(dtw->view);
191             } else {
192                 desktop = Inkscape::NSApplication::Editor::createDesktop (doc);
193             }
194         }
196         doc->virgin = FALSE;
197         // everyone who cares now has a reference, get rid of ours
198         sp_document_unref(doc);
199         // resize the window to match the document properties
200         // (this may be redundant for new windows... if so, move to the "virgin"
201         //  section above)
202         sp_namedview_window_from_document(desktop);
204         if (add_to_recent) {
205             prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
206         }
208         return TRUE;
209     } else {
210         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
211         gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri);
212         sp_ui_error_dialog(text);
213         g_free(text);
214         g_free(safeUri);
215         return FALSE;
216     }
219 /**
220  *  Handle prompting user for "do you want to revert"?  Revert on "OK"
221  */
222 void
223 sp_file_revert_dialog()
225     SPDesktop  *desktop = SP_ACTIVE_DESKTOP;
226     g_assert(desktop != NULL);
228     SPDocument *doc = sp_desktop_document(desktop);
229     g_assert(doc != NULL);
231     Inkscape::XML::Node     *repr = sp_document_repr_root(doc);
232     g_assert(repr != NULL);
234     gchar const *uri = doc->uri;
235     if (!uri) {
236         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet.  Cannot revert."));
237         return;
238     }
240     bool do_revert = true;
241     if (repr->attribute("sodipodi:modified") != NULL) {
242         gchar *text = g_strdup_printf(_("Changes will be lost!  Are you sure you want to reload document %s?"), uri);
244         bool response = desktop->warnDialog (text);
245         g_free(text);
247         if (!response) {
248             do_revert = false;
249         }
250     }
252     bool reverted;
253     if (do_revert) {
254         // Allow overwriting of current document.
255         doc->virgin = TRUE;
256         reverted = sp_file_open(uri,NULL);
257     } else {
258         reverted = false;
259     }
261     if (reverted) {
262         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted."));
263     } else {
264         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted."));
265     }
268 void dump_str(gchar const *str, gchar const *prefix)
270     Glib::ustring tmp;
271     tmp = prefix;
272     tmp += " [";
273     size_t const total = strlen(str);
274     for (unsigned i = 0; i < total; i++) {
275         gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
276         tmp += tmp2;
277         g_free(tmp2);
278     }
280     tmp += "]";
281     g_message(tmp.c_str());
284 void dump_ustr(Glib::ustring const &ustr)
286     char const *cstr = ustr.c_str();
287     char const *data = ustr.data();
288     Glib::ustring::size_type const byteLen = ustr.bytes();
289     Glib::ustring::size_type const dataLen = ustr.length();
290     Glib::ustring::size_type const cstrLen = strlen(cstr);
292     g_message("   size: %lu\n   length: %lu\n   bytes: %lu\n    clen: %lu",
293               gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) );
294     g_message( "  ASCII? %s", (ustr.is_ascii() ? "yes":"no") );
295     g_message( "  UTF-8? %s", (ustr.validate() ? "yes":"no") );
297     try {
298         Glib::ustring tmp;
299         for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) {
300             tmp = "    ";
301             if (i < dataLen) {
302                 Glib::ustring::value_type val = ustr.at(i);
303                 gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? "  %02x" : "%04x"), val );
304                 tmp += tmp2;
305                 g_free( tmp2 );
306             } else {
307                 tmp += "    ";
308             }
310             if (i < byteLen) {
311                 int val = (0x0ff & data[i]);
312                 gchar *tmp2 = g_strdup_printf("    %02x", val);
313                 tmp += tmp2;
314                 g_free( tmp2 );
315                 if ( val > 32 && val < 127 ) {
316                     tmp2 = g_strdup_printf( "   '%c'", (gchar)val );
317                     tmp += tmp2;
318                     g_free( tmp2 );
319                 } else {
320                     tmp += "    . ";
321                 }
322             } else {
323                 tmp += "       ";
324             }
326             if ( i < cstrLen ) {
327                 int val = (0x0ff & cstr[i]);
328                 gchar* tmp2 = g_strdup_printf("    %02x", val);
329                 tmp += tmp2;
330                 g_free(tmp2);
331                 if ( val > 32 && val < 127 ) {
332                     tmp2 = g_strdup_printf("   '%c'", (gchar) val);
333                     tmp += tmp2;
334                     g_free( tmp2 );
335                 } else {
336                     tmp += "    . ";
337                 }
338             } else {
339                 tmp += "            ";
340             }
342             g_message( tmp.c_str() );
343         }
344     } catch (...) {
345         g_message("XXXXXXXXXXXXXXXXXX Exception" );
346     }
347     g_message("---------------");
350 static Inkscape::UI::Dialog::FileOpenDialog *openDialogInstance = NULL;
352 /**
353  *  Display an file Open selector.  Open a document if OK is pressed.
354  *  Can select single or multiple files for opening.
355  */
356 void
357 sp_file_open_dialog(gpointer object, gpointer data)
360     //# Get the current directory for finding files
361     Glib::ustring open_path;
362     char *attr = (char *)prefs_get_string_attribute("dialogs.open", "path");
363     if (attr)
364         open_path = attr;
367     //# Test if the open_path directory exists  
368     if (!Inkscape::IO::file_test(open_path.c_str(),
369               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
370         open_path = "";
372     //# If no open path, default to our home directory
373     if (open_path.size() < 1)
374         {
375         open_path = g_get_home_dir();
376         open_path.append(G_DIR_SEPARATOR_S);
377         }
379     //# Create a dialog if we don't already have one
380     if (!openDialogInstance) {
381         openDialogInstance =
382               Inkscape::UI::Dialog::FileOpenDialog::create(
383                  open_path,
384                  Inkscape::UI::Dialog::SVG_TYPES,
385                  (char const *)_("Select file to open"));
386     }
388     //# Show the dialog
389     bool const success = openDialogInstance->show();
390     if (!success)
391         return;
393     //# User selected something.  Get name and type
394     Glib::ustring fileName = openDialogInstance->getFilename();
395     Inkscape::Extension::Extension *selection =
396             openDialogInstance->getSelectionType();
398     //# Code to check & open iff multiple files.
399     std::vector<Glib::ustring> flist=openDialogInstance->getFilenames();
401     //# Iterate through filenames if more than 1
402     if (flist.size() > 1)
403         {
404         for (unsigned int i=1 ; i<flist.size() ; i++)
405             {
406             Glib::ustring fName = flist[i];
408             if (Glib::file_test(fileName, Glib::FILE_TEST_IS_DIR)) {
409             Glib::ustring newFileName = Glib::filename_to_utf8(fName);
410             if ( newFileName.size() > 0 )
411                 fName = newFileName;
412             else
413                 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
415 #ifdef INK_DUMP_FILENAME_CONV
416             g_message("Opening File %s\n",fileName);
417 #endif
418             sp_file_open(fileName, selection);
419             }
420         }
421         return;
422     }
425     if (fileName.size() > 0) {
427         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
429         if ( newFileName.size() > 0)
430             fileName = newFileName;
431         else
432             g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
434         open_path = fileName;
435         open_path.append(G_DIR_SEPARATOR_S);
436         prefs_set_string_attribute("dialogs.open", "path", open_path.c_str());
438         sp_file_open(fileName, selection);
439     }
441     return;
445 /*######################
446 ## V A C U U M
447 ######################*/
449 /**
450  * Remove unreferenced defs from the defs section of the document.
451  */
454 void
455 sp_file_vacuum()
457     SPDocument *doc = SP_ACTIVE_DOCUMENT;
459     unsigned int diff = vacuum_document (doc);
461     sp_document_done(doc, SP_VERB_FILE_VACUUM, 
462                      /* TODO: annotate */ "file.cpp:515");
464     SPDesktop *dt = SP_ACTIVE_DESKTOP;
465     if (diff > 0) {
466         dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
467                 ngettext("Removed <b>%i</b> unused definition in &lt;defs&gt;.",
468                          "Removed <b>%i</b> unused definitions in &lt;defs&gt;.",
469                          diff),
470                 diff);
471     } else {
472         dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE,  _("No unused definitions in &lt;defs&gt;."));
473     }
478 /*######################
479 ## S A V E
480 ######################*/
482 /**
483  * This 'save' function called by the others below
484  */
485 static bool
486 file_save(SPDocument *doc, const Glib::ustring &uri,
487           Inkscape::Extension::Extension *key, bool saveas)
489     if (!doc || uri.size()<1) //Safety check
490         return false;
492     try {
493         Inkscape::Extension::save(key, doc, uri.c_str(),
494                  saveas && prefs_get_int_attribute("dialogs.save_as", "append_extension", 1),
495                  saveas, TRUE); // save officially, with inkscape: attributes set
496     } catch (Inkscape::Extension::Output::no_extension_found &e) {
497         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
498         gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s).  This may have been caused by an unknown filename extension."), safeUri);
499         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
500         sp_ui_error_dialog(text);
501         g_free(text);
502         g_free(safeUri);
503         return FALSE;
504     } catch (Inkscape::Extension::Output::save_failed &e) {
505         gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
506         gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri);
507         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
508         sp_ui_error_dialog(text);
509         g_free(text);
510         g_free(safeUri);
511         return FALSE;
512     } catch (Inkscape::Extension::Output::no_overwrite &e) {
513         return sp_file_save_dialog(doc);
514     }
516     SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document saved."));
517     return true;
524 static Inkscape::UI::Dialog::FileSaveDialog *saveDialogInstance = NULL;
526 /**
527  *  Display a SaveAs dialog.  Save the document if OK pressed.
528  */
529 bool
530 sp_file_save_dialog(SPDocument *doc)
533     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
535     Inkscape::Extension::Output *extension;
537     //# Get the default extension name
538     Glib::ustring default_extension;
539     char *attr = (char *)repr->attribute("inkscape:output_extension");
540     if (!attr)
541         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
542     if (attr)
543         default_extension = attr;
544     //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
546     Glib::ustring save_path;
547     Glib::ustring save_loc;
549     if (doc->uri == NULL) {
550         char formatBuf[256];
551         int i = 1;
553         Glib::ustring filename_extension = ".svg";
554         extension = dynamic_cast<Inkscape::Extension::Output *>
555               (Inkscape::Extension::db.get(default_extension.c_str()));
556         //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
557         if (extension)
558             filename_extension = extension->get_extension();
560         attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
561         if (attr)
562             save_path = attr;
564         if (!Inkscape::IO::file_test(save_path.c_str(),
565               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
566             save_path = "";
568         if (save_path.size()<1)
569             save_path = g_get_home_dir();
571         save_loc = save_path;
572         save_loc.append(G_DIR_SEPARATOR_S);
573         snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
574         save_loc.append(formatBuf);
576         while (Inkscape::IO::file_test(save_loc.c_str(), G_FILE_TEST_EXISTS)) {
577             save_loc = save_path;
578             save_loc.append(G_DIR_SEPARATOR_S);
579             snprintf(formatBuf, 255, _("drawing-%d%s"), i++, filename_extension.c_str());
580             save_loc.append(formatBuf);
581         }
582     } else {
583         save_loc = Glib::path_get_dirname(doc->uri);
584     }
586     // convert save_loc from utf-8 to locale
587     // is this needed any more, now that everything is handled in
588     // Inkscape::IO?
589     Glib::ustring save_loc_local = Glib::filename_from_utf8(save_loc);
591     if ( save_loc_local.size() > 0) 
592         save_loc = save_loc_local;
594     //# Show the SaveAs dialog
595     if (!saveDialogInstance)
596         saveDialogInstance =
597              Inkscape::UI::Dialog::FileSaveDialog::create(
598                  save_loc,
599                  Inkscape::UI::Dialog::SVG_TYPES,
600                  (char const *) _("Select file to save to"),
601                  default_extension
602             );
604     bool success = saveDialogInstance->show();
605     if (!success)
606         return success;
608     Glib::ustring fileName = saveDialogInstance->getFilename();
610     Inkscape::Extension::Extension *selectionType =
611         saveDialogInstance->getSelectionType();
614     if (fileName.size() > 0) {
615         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
617         if ( newFileName.size()>0 )
618             fileName = newFileName;
619         else
620             g_warning( "Error converting save filename to UTF-8." );
622         success = file_save(doc, fileName, selectionType, TRUE);
624         if (success)
625             prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
627         save_path = fileName;
628         prefs_set_string_attribute("dialogs.save_as", "path", save_path.c_str());
630         return success;
631     }
634     return false;
638 /**
639  * Save a document, displaying a SaveAs dialog if necessary.
640  */
641 bool
642 sp_file_save_document(SPDocument *doc)
644     bool success = true;
646     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
648     gchar const *fn = repr->attribute("sodipodi:modified");
649     if (fn != NULL) {
650         if (doc->uri == NULL
651             || repr->attribute("inkscape:output_extension") == NULL)
652         {
653             return sp_file_save_dialog(doc);
654         } else {
655             fn = g_strdup(doc->uri);
656             gchar const *ext = repr->attribute("inkscape:output_extension");
657             success = file_save(doc, fn, Inkscape::Extension::db.get(ext), FALSE);
658             g_free((void *) fn);
659         }
660     } else {
661         SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No changes need to be saved."));
662         success = TRUE;
663     }
665     return success;
669 /**
670  * Save a document.
671  */
672 bool
673 sp_file_save(gpointer object, gpointer data)
675     if (!SP_ACTIVE_DOCUMENT)
676         return false;
677     sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
678     return sp_file_save_document(SP_ACTIVE_DOCUMENT);
682 /**
683  *  Save a document, always displaying the SaveAs dialog.
684  */
685 bool
686 sp_file_save_as(gpointer object, gpointer data)
688     if (!SP_ACTIVE_DOCUMENT)
689         return false;
690     sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
691     return sp_file_save_dialog(SP_ACTIVE_DOCUMENT);
697 /*######################
698 ## I M P O R T
699 ######################*/
701 /**
702  *  Import a resource.  Called by sp_file_import()
703  */
704 void
705 file_import(SPDocument *in_doc, const Glib::ustring &uri,
706                Inkscape::Extension::Extension *key)
708     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
710     //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key );
711     SPDocument *doc;
712     try {
713         doc = Inkscape::Extension::open(key, uri.c_str());
714     } catch (Inkscape::Extension::Input::no_extension_found &e) {
715         doc = NULL;
716     } catch (Inkscape::Extension::Input::open_failed &e) {
717         doc = NULL;
718     }
720     if (doc != NULL) {
721         // the import extension has passed us a document, now we need to embed it into our document
722         if ( 0 ) {
723 //            const gchar *docbase = (sp_repr_document_root( sp_repr_document( repr ))->attribute("sodipodi:docbase" );
724             g_message(" settings  uri  [%s]", doc->uri );
725             g_message("           base [%s]", doc->base );
726             g_message("           name [%s]", doc->name );
727             Inkscape::IO::fixupHrefs( doc, doc->base, TRUE );
728             g_message("        mid-fixup");
729             Inkscape::IO::fixupHrefs( doc, in_doc->base, TRUE );
730         }
732         // move imported defs to our document's defs
733         SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
734         SPObject *defs = SP_DOCUMENT_DEFS(doc);
735         Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
736         for (SPObject *child = sp_object_first_child(defs);
737              child != NULL; child = SP_OBJECT_NEXT(child))
738         {
739             // FIXME: in case of id conflict, newly added thing will be re-ided and thus likely break a reference to it from imported stuff
740             SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(child)->duplicate(), last_def);
741         }
743         guint items_count = 0;
744         for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
745              child != NULL; child = SP_OBJECT_NEXT(child)) {
746             if (SP_IS_ITEM(child))
747                 items_count ++;
748         }
749         SPCSSAttr *style = sp_css_attr_from_object (SP_DOCUMENT_ROOT (doc));
751         SPObject *new_obj = NULL;
753         if ((style && style->firstChild()) || items_count > 1) {
754             // create group
755             Inkscape::XML::Node *newgroup = sp_repr_new("svg:g");
756             sp_repr_css_set (newgroup, style, "style");
758             for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
759                 if (SP_IS_ITEM(child)) {
760                     Inkscape::XML::Node *newchild = SP_OBJECT_REPR(child)->duplicate();
762                     // convert layers to groups; FIXME: add "preserve layers" mode where each layer
763                     // from impot is copied to the same-named layer in host
764                     newchild->setAttribute("inkscape:groupmode", NULL);
766                     newgroup->appendChild(newchild);
767                 }
768             }
770             if (desktop) {
771                 // Add it to the current layer
772                 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
773             } else {
774                 // There's no desktop (command line run?)
775                 // FIXME: For such cases we need a document:: method to return the current layer
776                 new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newgroup);
777             }
779             Inkscape::GC::release(newgroup);
780         } else {
781             // just add one item
782             for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
783                 if (SP_IS_ITEM(child)) {
784                     Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate();
785                     newitem->setAttribute("inkscape:groupmode", NULL);
787                     if (desktop) {
788                         // Add it to the current layer
789                         new_obj = desktop->currentLayer()->appendChildRepr(newitem);
790                     } else {
791                         // There's no desktop (command line run?)
792                         // FIXME: For such cases we need a document:: method to return the current layer
793                         new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newitem);
794                     }
796                 }
797             }
798         }
800         if (style) sp_repr_css_attr_unref (style);
802         // select and move the imported item
803         if (new_obj && SP_IS_ITEM(new_obj)) {
804             Inkscape::Selection *selection = sp_desktop_selection(desktop);
805             selection->set(SP_ITEM(new_obj));
807             // To move the imported object, we must temporarily set the "transform pattern with
808             // object" option.
809             {
810                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
811                 prefs_set_int_attribute("options.transform", "pattern", 1);
812                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
813                 NR::Point m( desktop->point() - selection->bounds().midpoint() );
814                 sp_selection_move_relative(selection, m);
815                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
816             }
817         }
819         sp_document_unref(doc);
820         sp_document_done(in_doc, SP_VERB_FILE_IMPORT,
821                          /* TODO: annotate */ "file.cpp:900");
823     } else {
824         gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri.c_str());
825         sp_ui_error_dialog(text);
826         g_free(text);
827     }
829     return;
833 static Inkscape::UI::Dialog::FileOpenDialog *importDialogInstance = NULL;
835 /**
836  *  Display an Open dialog, import a resource if OK pressed.
837  */
838 void
839 sp_file_import(GtkWidget *widget)
841     static Glib::ustring import_path;
843     SPDocument *doc = SP_ACTIVE_DOCUMENT;
844     if (!doc)
845         return;
847     if (!importDialogInstance) {
848         importDialogInstance =
849              Inkscape::UI::Dialog::FileOpenDialog::create(
850                  import_path,
851                  Inkscape::UI::Dialog::IMPORT_TYPES,
852                  (char const *)_("Select file to import"));
853     }
855     bool success = importDialogInstance->show();
856     if (!success)
857         return;
859     //# Get file name and extension type
860     Glib::ustring fileName = importDialogInstance->getFilename();
861     Inkscape::Extension::Extension *selection =
862         importDialogInstance->getSelectionType();
865     if (fileName.size() > 0) {
866  
867         Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
869         if ( newFileName.size() > 0)
870             fileName = newFileName;
871         else
872             g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
874         import_path = fileName;
875         if (import_path.size()>0)
876             import_path.append(G_DIR_SEPARATOR_S);
878         file_import(doc, fileName, selection);
879     }
881     return;
886 /*######################
887 ## E X P O R T
888 ######################*/
890 /**
891  *
892  */
893 void
894 sp_file_export_dialog(void *widget)
896     sp_export_dialog();
899 #include <display/nr-arena-item.h>
900 #include <display/nr-arena.h>
902 struct SPEBP {
903     int width, height, sheight;
904     guchar r, g, b, a;
905     NRArenaItem *root; // the root arena item to show; it is assumed that all unneeded items are hidden
906     guchar *px;
907     unsigned (*status)(float, void *);
908     void *data;
909 };
912 /**
913  *
914  */
915 static int
916 sp_export_get_rows(guchar const **rows, int row, int num_rows, void *data)
918     struct SPEBP *ebp = (struct SPEBP *) data;
920     if (ebp->status) {
921         if (!ebp->status((float) row / ebp->height, ebp->data)) return 0;
922     }
924     num_rows = MIN(num_rows, ebp->sheight);
925     num_rows = MIN(num_rows, ebp->height - row);
927     /* Set area of interest */
928     NRRectL bbox;
929     bbox.x0 = 0;
930     bbox.y0 = row;
931     bbox.x1 = ebp->width;
932     bbox.y1 = row + num_rows;
933     /* Update to renderable state */
934     NRGC gc(NULL);
935     nr_matrix_set_identity(&gc.transform);
937     nr_arena_item_invoke_update(ebp->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
939     NRPixBlock pb;
940     nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,
941                              bbox.x0, bbox.y0, bbox.x1, bbox.y1,
942                              ebp->px, 4 * ebp->width, FALSE, FALSE);
944     for (int r = 0; r < num_rows; r++) {
945         guchar *p = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
946         for (int c = 0; c < ebp->width; c++) {
947             *p++ = ebp->r;
948             *p++ = ebp->g;
949             *p++ = ebp->b;
950             *p++ = ebp->a;
951         }
952     }
954     /* Render */
955     nr_arena_item_invoke_render(ebp->root, &bbox, &pb, 0);
957     for (int r = 0; r < num_rows; r++) {
958         rows[r] = NR_PIXBLOCK_PX(&pb) + r * pb.rs;
959     }
961     nr_pixblock_release(&pb);
963     return num_rows;
966 /**
967 Hide all items which are not listed in list, recursively, skipping groups and defs
968 */
969 void
970 hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
972     if (SP_IS_ITEM(o)
973         && !SP_IS_DEFS(o)
974         && !SP_IS_ROOT(o)
975         && !SP_IS_GROUP(o)
976         && !g_slist_find(list, o))
977     {
978         sp_item_invoke_hide(SP_ITEM(o), dkey);
979     }
981      // recurse
982     if (!g_slist_find(list, o)) {
983         for (SPObject *child = sp_object_first_child(o) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
984             hide_other_items_recursively(child, list, dkey);
985         }
986     }
990 /**
991  *  Render the SVG drawing onto a PNG raster image, then save to
992  *  a file.  Returns TRUE if succeeded in writing the file,
993  *  FALSE otherwise.
994  */
995 int
996 sp_export_png_file(SPDocument *doc, gchar const *filename,
997                    double x0, double y0, double x1, double y1,
998                    unsigned width, unsigned height, double xdpi, double ydpi,
999                    unsigned long bgcolor,
1000                    unsigned (*status)(float, void *),
1001                    void *data, bool force_overwrite,
1002                    GSList *items_only)
1004     int write_status = TRUE;
1005     g_return_val_if_fail(doc != NULL, FALSE);
1006     g_return_val_if_fail(filename != NULL, FALSE);
1007     g_return_val_if_fail(width >= 1, FALSE);
1008     g_return_val_if_fail(height >= 1, FALSE);
1010     if (!force_overwrite && !sp_ui_overwrite_file(filename)) {
1011         return FALSE;
1012     }
1014     sp_document_ensure_up_to_date(doc);
1016     /* Go to document coordinates */
1017     gdouble t = y0;
1018     y0 = sp_document_height(doc) - y1;
1019     y1 = sp_document_height(doc) - t;
1021     /*
1022      * 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0
1023      * 2) a[1] * x0 + a[3] * y1 + a[5] = 0.0
1024      * 3) a[0] * x1 + a[2] * y1 + a[4] = width
1025      * 4) a[1] * x0 + a[3] * y0 + a[5] = height
1026      * 5) a[1] = 0.0;
1027      * 6) a[2] = 0.0;
1028      *
1029      * (1,3) a[0] * x1 - a[0] * x0 = width
1030      * a[0] = width / (x1 - x0)
1031      * (2,4) a[3] * y0 - a[3] * y1 = height
1032      * a[3] = height / (y0 - y1)
1033      * (1) a[4] = -a[0] * x0
1034      * (2) a[5] = -a[3] * y1
1035      */
1037     NRMatrix affine;
1038     affine.c[0] = width / (x1 - x0);
1039     affine.c[1] = 0.0;
1040     affine.c[2] = 0.0;
1041     affine.c[3] = height / (y1 - y0);
1042     affine.c[4] = -affine.c[0] * x0;
1043     affine.c[5] = -affine.c[3] * y0;
1045     //SP_PRINT_MATRIX("SVG2PNG", &affine);
1047     struct SPEBP ebp;
1048     ebp.width  = width;
1049     ebp.height = height;
1050     ebp.r      = NR_RGBA32_R(bgcolor);
1051     ebp.g      = NR_RGBA32_G(bgcolor);
1052     ebp.b      = NR_RGBA32_B(bgcolor);
1053     ebp.a      = NR_RGBA32_A(bgcolor);
1055     /* Create new arena */
1056     NRArena *arena = NRArena::create();
1057     unsigned dkey = sp_item_display_key_new(1);
1059     /* Create ArenaItems and set transform */
1060     ebp.root = sp_item_invoke_show(SP_ITEM(sp_document_root(doc)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
1061     nr_arena_item_set_transform(NR_ARENA_ITEM(ebp.root), NR::Matrix(&affine));
1063     // We show all and then hide all items we don't want, instead of showing only requested items,
1064     // because that would not work if the shown item references something in defs
1065     if (items_only) {
1066         hide_other_items_recursively(sp_document_root(doc), items_only, dkey);
1067     }
1069     ebp.status = status;
1070     ebp.data   = data;
1072     if ((width < 256) || ((width * height) < 32768)) {
1073         ebp.px = nr_pixelstore_64K_new(FALSE, 0);
1074         ebp.sheight = 65536 / (4 * width);
1075         write_status = sp_png_write_rgba_striped(filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
1076         nr_pixelstore_64K_free(ebp.px);
1077     } else {
1078         ebp.px = g_new(guchar, 4 * 64 * width);
1079         ebp.sheight = 64;
1080         write_status = sp_png_write_rgba_striped(filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
1081         g_free(ebp.px);
1082     }
1084     // Hide items
1085     sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), dkey);
1087     /* Free Arena and ArenaItem */
1088     nr_arena_item_unref(ebp.root);
1089     nr_object_unref((NRObject *) arena);
1090     return write_status;
1094 /*######################
1095 ## P R I N T
1096 ######################*/
1099 /**
1100  *  Print the current document, if any.
1101  */
1102 void
1103 sp_file_print()
1105     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1106     if (doc)
1107         sp_print_document(doc, FALSE);
1111 /**
1112  *  Print the current document, if any.  Do not use
1113  *  the machine's print drivers.
1114  */
1115 void
1116 sp_file_print_direct()
1118     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1119     if (doc)
1120         sp_print_document(doc, TRUE);
1124 /**
1125  * Display what the drawing would look like, if
1126  * printed.
1127  */
1128 void
1129 sp_file_print_preview(gpointer object, gpointer data)
1132     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1133     if (doc)
1134         sp_print_preview_document(doc);
1138 void Inkscape::IO::fixupHrefs( SPDocument *doc, const gchar *base, gboolean spns )
1140     //g_message("Inkscape::IO::fixupHrefs( , [%s], )", base );
1142     if ( 0 ) {
1143         gchar const* things[] = {
1144             "data:foo,bar",
1145             "http://www.google.com/image.png",
1146             "ftp://ssd.com/doo",
1147             "/foo/dee/bar.svg",
1148             "foo.svg",
1149             "file:/foo/dee/bar.svg",
1150             "file:///foo/dee/bar.svg",
1151             "file:foo.svg",
1152             "/foo/bar\xe1\x84\x92.svg",
1153             "file:///foo/bar\xe1\x84\x92.svg",
1154             "file:///foo/bar%e1%84%92.svg",
1155             "/foo/bar%e1%84%92.svg",
1156             "bar\xe1\x84\x92.svg",
1157             "bar%e1%84%92.svg",
1158             NULL
1159         };
1160         g_message("+------");
1161         for ( int i = 0; things[i]; i++ )
1162         {
1163             try
1164             {
1165                 URI uri(things[i]);
1166                 gboolean isAbs = g_path_is_absolute( things[i] );
1167                 gchar *str = uri.toString();
1168                 g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
1169                            (int)uri.isRelative(),
1170                            uri.getScheme(),
1171                            uri.getPath(),
1172                            uri.getOpaque(),
1173                            things[i],
1174                            str );
1175                 g_free(str);
1176             }
1177             catch ( MalformedURIException err )
1178             {
1179                 dump_str( things[i], "MalformedURIException" );
1180                 xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
1181                 g_message("    gone from [%s] to [%s]", things[i], redo );
1182                 if ( redo == NULL )
1183                 {
1184                     URI again = URI::fromUtf8( things[i] );
1185                     gboolean isAbs = g_path_is_absolute( things[i] );
1186                     gchar *str = again.toString();
1187                     g_message( "abs:%d  isRel:%d  scheme:[%s]  path:[%s][%s]   uri[%s] / [%s]", (int)isAbs,
1188                                (int)again.isRelative(),
1189                                again.getScheme(),
1190                                again.getPath(),
1191                                again.getOpaque(),
1192                                things[i],
1193                                str );
1194                     g_free(str);
1195                     g_message("    ----");
1196                 }
1197             }
1198         }
1199         g_message("+------");
1200     }
1202     GSList const *images = sp_document_get_resource_list(doc, "image");
1203     for (GSList const *l = images; l != NULL; l = l->next) {
1204         Inkscape::XML::Node *ir = SP_OBJECT_REPR(l->data);
1206         const gchar *href = ir->attribute("xlink:href");
1208         // First try to figure out an absolute path to the asset
1209         //g_message("image href [%s]", href );
1210         if (spns && !g_path_is_absolute(href)) {
1211             const gchar *absref = ir->attribute("sodipodi:absref");
1212             const gchar *base_href = g_build_filename(base, href, NULL);
1213             //g_message("      absr [%s]", absref );
1215             if ( absref && Inkscape::IO::file_test(absref, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_test(base_href, G_FILE_TEST_EXISTS))
1216             {
1217                 // only switch over if the absref is valid while href is not
1218                 href = absref;
1219                 //g_message("     copied absref to href");
1220             }
1221         }
1223         // Once we have an absolute path, convert it relative to the new location
1224         if (href && g_path_is_absolute(href)) {
1225             const gchar *relname = sp_relative_path_from_path(href, base);
1226             //g_message("     setting to [%s]", relname );
1227             ir->setAttribute("xlink:href", relname);
1228         }
1229 // TODO next refinement is to make the first choice keeping the relative path as-is if
1230 //      based on the new location it gives us a valid file.
1231     }
1235 /*
1236   Local Variables:
1237   mode:c++
1238   c-file-style:"stroustrup"
1239   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1240   indent-tabs-mode:nil
1241   fill-column:99
1242   End:
1243 */
1244 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :