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 * Bruno Dilly <bruno.dilly@gmail.com>
11 * Stephen Silver <sasilver@users.sourceforge.net>
12 *
13 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
14 * Copyright (C) 1999-2008 Authors
15 * Copyright (C) 2004 David Turner
16 * Copyright (C) 2001-2002 Ximian, Inc.
17 *
18 * Released under GNU GPL, read the file 'COPYING' for more information
19 */
21 /**
22 * Note: This file needs to be cleaned up extensively.
23 * What it probably needs is to have one .h file for
24 * the API, and two or more .cpp files for the implementations.
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <glib/gmem.h>
32 #include <libnr/nr-pixops.h>
34 #include "document-private.h"
35 #include "selection-chemistry.h"
36 #include "ui/view/view-widget.h"
37 #include "dir-util.h"
38 #include "helper/png-write.h"
39 #include "dialogs/export.h"
40 #include <glibmm/i18n.h>
41 #include "inkscape.h"
42 #include "desktop.h"
43 #include "selection.h"
44 #include "interface.h"
45 #include "style.h"
46 #include "print.h"
47 #include "file.h"
48 #include "message.h"
49 #include "message-stack.h"
50 #include "ui/dialog/filedialog.h"
51 #include "ui/dialog/ocaldialogs.h"
52 #include "prefs-utils.h"
53 #include "path-prefix.h"
55 #include "sp-namedview.h"
56 #include "desktop-handles.h"
58 #include "extension/db.h"
59 #include "extension/input.h"
60 #include "extension/output.h"
61 /* #include "extension/menu.h" */
62 #include "extension/system.h"
64 #include "io/sys.h"
65 #include "application/application.h"
66 #include "application/editor.h"
67 #include "inkscape.h"
68 #include "uri.h"
69 #include "extract-uri.h"
71 #ifdef WITH_GNOME_VFS
72 # include <libgnomevfs/gnome-vfs.h>
73 #endif
75 #ifdef WITH_INKBOARD
76 #include "jabber_whiteboard/session-manager.h"
77 #endif
79 #ifdef WIN32
80 #include <windows.h>
81 #endif
83 //#define INK_DUMP_FILENAME_CONV 1
84 #undef INK_DUMP_FILENAME_CONV
86 //#define INK_DUMP_FOPEN 1
87 #undef INK_DUMP_FOPEN
89 void dump_str(gchar const *str, gchar const *prefix);
90 void dump_ustr(Glib::ustring const &ustr);
93 /*######################
94 ## N E W
95 ######################*/
97 /**
98 * Create a blank document and add it to the desktop
99 */
100 SPDesktop*
101 sp_file_new(const Glib::ustring &templ)
102 {
103 char *templName = NULL;
104 if (templ.size()>0)
105 templName = (char *)templ.c_str();
106 SPDocument *doc = sp_document_new(templName, TRUE, true);
107 g_return_val_if_fail(doc != NULL, NULL);
109 SPDesktop *dt;
110 if (Inkscape::NSApplication::Application::getNewGui())
111 {
112 dt = Inkscape::NSApplication::Editor::createDesktop (doc);
113 } else {
114 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
115 g_return_val_if_fail(dtw != NULL, NULL);
116 sp_document_unref(doc);
118 sp_create_window(dtw, TRUE);
119 dt = static_cast<SPDesktop*>(dtw->view);
120 sp_namedview_window_from_document(dt);
121 sp_namedview_update_layers_from_document(dt);
122 }
123 return dt;
124 }
126 SPDesktop*
127 sp_file_new_default()
128 {
129 std::list<gchar *> sources;
130 sources.push_back( profile_path("templates") ); // first try user's local dir
131 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
133 while (!sources.empty()) {
134 gchar *dirname = sources.front();
135 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
137 // TRANSLATORS: default.svg is localizable - this is the name of the default document
138 // template. This way you can localize the default pagesize, translate the name of
139 // the default layer, etc. If you wish to localize this file, please create a
140 // localized share/templates/default.xx.svg file, where xx is your language code.
141 char *default_template = g_build_filename(dirname, _("default.svg"), NULL);
142 if (Inkscape::IO::file_test(default_template, G_FILE_TEST_IS_REGULAR)) {
143 return sp_file_new(default_template);
144 }
145 }
146 g_free(dirname);
147 sources.pop_front();
148 }
150 return sp_file_new("");
151 }
154 /*######################
155 ## D E L E T E
156 ######################*/
158 /**
159 * Perform document closures preceding an exit()
160 */
161 void
162 sp_file_exit()
163 {
164 sp_ui_close_all();
165 // no need to call inkscape_exit here; last document being closed will take care of that
166 }
169 /*######################
170 ## O P E N
171 ######################*/
173 /**
174 * Open a file, add the document to the desktop
175 *
176 * \param replace_empty if true, and the current desktop is empty, this document
177 * will replace the empty one.
178 */
179 bool
180 sp_file_open(const Glib::ustring &uri,
181 Inkscape::Extension::Extension *key,
182 bool add_to_recent, bool replace_empty)
183 {
184 SPDocument *doc = NULL;
185 try {
186 doc = Inkscape::Extension::open(key, uri.c_str());
187 } catch (Inkscape::Extension::Input::no_extension_found &e) {
188 doc = NULL;
189 } catch (Inkscape::Extension::Input::open_failed &e) {
190 doc = NULL;
191 }
193 if (doc) {
194 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
195 SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
197 if (existing && existing->virgin && replace_empty) {
198 // If the current desktop is empty, open the document there
199 sp_document_ensure_up_to_date (doc);
200 desktop->change_document(doc);
201 sp_document_resized_signal_emit (doc, sp_document_width(doc), sp_document_height(doc));
202 } else {
203 if (!Inkscape::NSApplication::Application::getNewGui()) {
204 // create a whole new desktop and window
205 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
206 sp_create_window(dtw, TRUE);
207 desktop = static_cast<SPDesktop*>(dtw->view);
208 } else {
209 desktop = Inkscape::NSApplication::Editor::createDesktop (doc);
210 }
211 }
213 doc->virgin = FALSE;
214 // everyone who cares now has a reference, get rid of ours
215 sp_document_unref(doc);
216 // resize the window to match the document properties
217 sp_namedview_window_from_document(desktop);
218 sp_namedview_update_layers_from_document(desktop);
220 if (add_to_recent) {
221 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
222 }
224 return TRUE;
225 } else {
226 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
227 gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri);
228 sp_ui_error_dialog(text);
229 g_free(text);
230 g_free(safeUri);
231 return FALSE;
232 }
233 }
235 /**
236 * Handle prompting user for "do you want to revert"? Revert on "OK"
237 */
238 void
239 sp_file_revert_dialog()
240 {
241 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
242 g_assert(desktop != NULL);
244 SPDocument *doc = sp_desktop_document(desktop);
245 g_assert(doc != NULL);
247 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
248 g_assert(repr != NULL);
250 gchar const *uri = doc->uri;
251 if (!uri) {
252 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet. Cannot revert."));
253 return;
254 }
256 bool do_revert = true;
257 if (doc->isModifiedSinceSave()) {
258 gchar *text = g_strdup_printf(_("Changes will be lost! Are you sure you want to reload document %s?"), uri);
260 bool response = desktop->warnDialog (text);
261 g_free(text);
263 if (!response) {
264 do_revert = false;
265 }
266 }
268 bool reverted;
269 if (do_revert) {
270 // Allow overwriting of current document.
271 doc->virgin = TRUE;
273 // remember current zoom and view
274 double zoom = desktop->current_zoom();
275 NR::Point c = desktop->get_display_area().midpoint();
277 reverted = sp_file_open(uri,NULL);
278 if (reverted) {
279 // restore zoom and view
280 desktop->zoom_absolute(c[NR::X], c[NR::Y], zoom);
281 }
282 } else {
283 reverted = false;
284 }
286 if (reverted) {
287 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted."));
288 } else {
289 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted."));
290 }
291 }
293 void dump_str(gchar const *str, gchar const *prefix)
294 {
295 Glib::ustring tmp;
296 tmp = prefix;
297 tmp += " [";
298 size_t const total = strlen(str);
299 for (unsigned i = 0; i < total; i++) {
300 gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
301 tmp += tmp2;
302 g_free(tmp2);
303 }
305 tmp += "]";
306 g_message("%s", tmp.c_str());
307 }
309 void dump_ustr(Glib::ustring const &ustr)
310 {
311 char const *cstr = ustr.c_str();
312 char const *data = ustr.data();
313 Glib::ustring::size_type const byteLen = ustr.bytes();
314 Glib::ustring::size_type const dataLen = ustr.length();
315 Glib::ustring::size_type const cstrLen = strlen(cstr);
317 g_message(" size: %lu\n length: %lu\n bytes: %lu\n clen: %lu",
318 gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) );
319 g_message( " ASCII? %s", (ustr.is_ascii() ? "yes":"no") );
320 g_message( " UTF-8? %s", (ustr.validate() ? "yes":"no") );
322 try {
323 Glib::ustring tmp;
324 for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) {
325 tmp = " ";
326 if (i < dataLen) {
327 Glib::ustring::value_type val = ustr.at(i);
328 gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? " %02x" : "%04x"), val );
329 tmp += tmp2;
330 g_free( tmp2 );
331 } else {
332 tmp += " ";
333 }
335 if (i < byteLen) {
336 int val = (0x0ff & data[i]);
337 gchar *tmp2 = g_strdup_printf(" %02x", val);
338 tmp += tmp2;
339 g_free( tmp2 );
340 if ( val > 32 && val < 127 ) {
341 tmp2 = g_strdup_printf( " '%c'", (gchar)val );
342 tmp += tmp2;
343 g_free( tmp2 );
344 } else {
345 tmp += " . ";
346 }
347 } else {
348 tmp += " ";
349 }
351 if ( i < cstrLen ) {
352 int val = (0x0ff & cstr[i]);
353 gchar* tmp2 = g_strdup_printf(" %02x", val);
354 tmp += tmp2;
355 g_free(tmp2);
356 if ( val > 32 && val < 127 ) {
357 tmp2 = g_strdup_printf(" '%c'", (gchar) val);
358 tmp += tmp2;
359 g_free( tmp2 );
360 } else {
361 tmp += " . ";
362 }
363 } else {
364 tmp += " ";
365 }
367 g_message( "%s", tmp.c_str() );
368 }
369 } catch (...) {
370 g_message("XXXXXXXXXXXXXXXXXX Exception" );
371 }
372 g_message("---------------");
373 }
375 /**
376 * Display an file Open selector. Open a document if OK is pressed.
377 * Can select single or multiple files for opening.
378 */
379 void
380 sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
381 {
382 //# Get the current directory for finding files
383 static Glib::ustring open_path;
385 if(open_path.empty())
386 {
387 gchar const *attr = prefs_get_string_attribute("dialogs.open", "path");
388 if (attr)
389 open_path = attr;
390 }
392 //# Test if the open_path directory exists
393 if (!Inkscape::IO::file_test(open_path.c_str(),
394 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
395 open_path = "";
397 #ifdef WIN32
398 //# If no open path, default to our win32 documents folder
399 if (open_path.empty())
400 {
401 // The path to the My Documents folder is read from the
402 // value "HKEY_CURRENT_USER\Software\Windows\CurrentVersion\Explorer\Shell Folders\Personal"
403 HKEY key = NULL;
404 if(RegOpenKeyExA(HKEY_CURRENT_USER,
405 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
406 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
407 {
408 WCHAR utf16path[_MAX_PATH];
409 DWORD value_type;
410 DWORD data_size = sizeof(utf16path);
411 if(RegQueryValueExW(key, L"Personal", NULL, &value_type,
412 (BYTE*)utf16path, &data_size) == ERROR_SUCCESS)
413 {
414 g_assert(value_type == REG_SZ);
415 gchar *utf8path = g_utf16_to_utf8(
416 (const gunichar2*)utf16path, -1, NULL, NULL, NULL);
417 if(utf8path)
418 {
419 open_path = Glib::ustring(utf8path);
420 g_free(utf8path);
421 }
422 }
423 }
424 }
425 #endif
427 //# If no open path, default to our home directory
428 if (open_path.empty())
429 {
430 open_path = g_get_home_dir();
431 open_path.append(G_DIR_SEPARATOR_S);
432 }
434 //# Create a dialog if we don't already have one
435 Inkscape::UI::Dialog::FileOpenDialog *openDialogInstance =
436 Inkscape::UI::Dialog::FileOpenDialog::create(
437 parentWindow, open_path,
438 Inkscape::UI::Dialog::SVG_TYPES,
439 _("Select file to open"));
441 //# Show the dialog
442 bool const success = openDialogInstance->show();
444 //# Save the folder the user selected for later
445 open_path = openDialogInstance->getCurrentDirectory();
447 if (!success)
448 {
449 delete openDialogInstance;
450 return;
451 }
453 //# User selected something. Get name and type
454 Glib::ustring fileName = openDialogInstance->getFilename();
456 Inkscape::Extension::Extension *selection =
457 openDialogInstance->getSelectionType();
459 //# Code to check & open if multiple files.
460 std::vector<Glib::ustring> flist = openDialogInstance->getFilenames();
462 //# We no longer need the file dialog object - delete it
463 delete openDialogInstance;
464 openDialogInstance = NULL;
466 //# Iterate through filenames if more than 1
467 if (flist.size() > 1)
468 {
469 for (unsigned int i = 0; i < flist.size(); i++)
470 {
471 fileName = flist[i];
473 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
474 if ( newFileName.size() > 0 )
475 fileName = newFileName;
476 else
477 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
479 #ifdef INK_DUMP_FILENAME_CONV
480 g_message("Opening File %s\n", fileName.c_str());
481 #endif
482 sp_file_open(fileName, selection);
483 }
485 return;
486 }
489 if (!fileName.empty())
490 {
491 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
493 if ( newFileName.size() > 0)
494 fileName = newFileName;
495 else
496 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
498 open_path = Glib::path_get_dirname (fileName);
499 open_path.append(G_DIR_SEPARATOR_S);
500 prefs_set_string_attribute("dialogs.open", "path", open_path.c_str());
502 sp_file_open(fileName, selection);
503 }
505 return;
506 }
509 /*######################
510 ## V A C U U M
511 ######################*/
513 /**
514 * Remove unreferenced defs from the defs section of the document.
515 */
518 void
519 sp_file_vacuum()
520 {
521 SPDocument *doc = SP_ACTIVE_DOCUMENT;
523 unsigned int diff = vacuum_document (doc);
525 sp_document_done(doc, SP_VERB_FILE_VACUUM,
526 _("Vacuum <defs>"));
528 SPDesktop *dt = SP_ACTIVE_DESKTOP;
529 if (diff > 0) {
530 dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
531 ngettext("Removed <b>%i</b> unused definition in <defs>.",
532 "Removed <b>%i</b> unused definitions in <defs>.",
533 diff),
534 diff);
535 } else {
536 dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("No unused definitions in <defs>."));
537 }
538 }
542 /*######################
543 ## S A V E
544 ######################*/
546 /**
547 * This 'save' function called by the others below
548 *
549 * \param official whether to set :output_module and :modified in the
550 * document; is true for normal save, false for temporary saves
551 */
552 static bool
553 file_save(Gtk::Window &parentWindow, SPDocument *doc, const Glib::ustring &uri,
554 Inkscape::Extension::Extension *key, bool saveas, bool official)
555 {
556 if (!doc || uri.size()<1) //Safety check
557 return false;
559 try {
560 Inkscape::Extension::save(key, doc, uri.c_str(),
561 false,
562 saveas, official);
563 } catch (Inkscape::Extension::Output::no_extension_found &e) {
564 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
565 gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s). This may have been caused by an unknown filename extension."), safeUri);
566 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
567 sp_ui_error_dialog(text);
568 g_free(text);
569 g_free(safeUri);
570 return FALSE;
571 } catch (Inkscape::Extension::Output::save_failed &e) {
572 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
573 gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri);
574 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
575 sp_ui_error_dialog(text);
576 g_free(text);
577 g_free(safeUri);
578 return FALSE;
579 } catch (Inkscape::Extension::Output::no_overwrite &e) {
580 return sp_file_save_dialog(parentWindow, doc);
581 }
583 SP_ACTIVE_DESKTOP->event_log->rememberFileSave();
584 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document saved."));
585 return true;
586 }
588 /*
589 * Used only for remote saving using VFS and a specific uri. Gets the file at the /tmp.
590 */
591 bool
592 file_save_remote(SPDocument */*doc*/,
593 #ifdef WITH_GNOME_VFS
594 const Glib::ustring &uri,
595 #else
596 const Glib::ustring &/*uri*/,
597 #endif
598 Inkscape::Extension::Extension */*key*/, bool /*saveas*/, bool /*official*/)
599 {
600 #ifdef WITH_GNOME_VFS
602 #define BUF_SIZE 8192
603 gnome_vfs_init();
605 GnomeVFSHandle *from_handle = NULL;
606 GnomeVFSHandle *to_handle = NULL;
607 GnomeVFSFileSize bytes_read;
608 GnomeVFSFileSize bytes_written;
609 GnomeVFSResult result;
610 guint8 buffer[8192];
612 gchar* uri_local = g_filename_from_utf8( uri.c_str(), -1, NULL, NULL, NULL);
614 if ( uri_local == NULL ) {
615 g_warning( "Error converting filename to locale encoding.");
616 }
618 // Gets the temp file name.
619 Glib::ustring fileName = Glib::get_tmp_dir ();
620 fileName.append(G_DIR_SEPARATOR_S);
621 fileName.append((gnome_vfs_uri_extract_short_name(gnome_vfs_uri_new(uri_local))));
623 // Open the temp file to send.
624 result = gnome_vfs_open (&from_handle, fileName.c_str(), GNOME_VFS_OPEN_READ);
626 if (result != GNOME_VFS_OK) {
627 g_warning("Could not find the temp saving.");
628 return false;
629 }
631 result = gnome_vfs_create (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL);
632 result = gnome_vfs_open (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE);
634 if (result != GNOME_VFS_OK) {
635 g_warning("file creating: %s", gnome_vfs_result_to_string(result));
636 return false;
637 }
639 while (1) {
641 result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read);
643 if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){
644 result = gnome_vfs_close (from_handle);
645 result = gnome_vfs_close (to_handle);
646 return true;
647 }
649 if (result != GNOME_VFS_OK) {
650 g_warning("%s", gnome_vfs_result_to_string(result));
651 return false;
652 }
653 result = gnome_vfs_write (to_handle, buffer, bytes_read, &bytes_written);
654 if (result != GNOME_VFS_OK) {
655 g_warning("%s", gnome_vfs_result_to_string(result));
656 return false;
657 }
660 if (bytes_read != bytes_written){
661 return false;
662 }
664 }
665 return true;
666 #else
667 // in case we do not have GNOME_VFS
668 return false;
669 #endif
671 }
674 /**
675 * Display a SaveAs dialog. Save the document if OK pressed.
676 *
677 * \param ascopy (optional) wether to set the documents->uri to the new filename or not
678 */
679 bool
680 sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
681 {
683 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
685 Inkscape::Extension::Output *extension = 0;
687 //# Get the default extension name
688 Glib::ustring default_extension;
689 char *attr = (char *)repr->attribute("inkscape:output_extension");
690 if (!attr)
691 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
692 if (attr)
693 default_extension = attr;
694 //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
696 Glib::ustring save_path;
697 Glib::ustring save_loc;
699 if (doc->uri == NULL) {
700 char formatBuf[256];
701 int i = 1;
703 Glib::ustring filename_extension = ".svg";
704 extension = dynamic_cast<Inkscape::Extension::Output *>
705 (Inkscape::Extension::db.get(default_extension.c_str()));
706 //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
707 if (extension)
708 filename_extension = extension->get_extension();
710 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
711 if (attr)
712 save_path = attr;
714 if (!Inkscape::IO::file_test(save_path.c_str(),
715 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
716 save_path = "";
718 if (save_path.size()<1)
719 save_path = g_get_home_dir();
721 save_loc = save_path;
722 save_loc.append(G_DIR_SEPARATOR_S);
723 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
724 save_loc.append(formatBuf);
726 while (Inkscape::IO::file_test(save_loc.c_str(), G_FILE_TEST_EXISTS)) {
727 save_loc = save_path;
728 save_loc.append(G_DIR_SEPARATOR_S);
729 snprintf(formatBuf, 255, _("drawing-%d%s"), i++, filename_extension.c_str());
730 save_loc.append(formatBuf);
731 }
732 } else {
733 save_loc = Glib::build_filename(Glib::path_get_dirname(doc->uri),
734 Glib::path_get_basename(doc->uri));
735 }
737 // convert save_loc from utf-8 to locale
738 // is this needed any more, now that everything is handled in
739 // Inkscape::IO?
740 Glib::ustring save_loc_local = Glib::filename_from_utf8(save_loc);
742 if ( save_loc_local.size() > 0)
743 save_loc = save_loc_local;
745 //# Show the SaveAs dialog
746 char const * dialog_title;
747 if (is_copy) {
748 dialog_title = (char const *) _("Select file to save a copy to");
749 } else {
750 dialog_title = (char const *) _("Select file to save to");
751 }
752 Inkscape::UI::Dialog::FileSaveDialog *saveDialog =
753 Inkscape::UI::Dialog::FileSaveDialog::create(
754 parentWindow,
755 save_loc,
756 Inkscape::UI::Dialog::SVG_TYPES,
757 dialog_title,
758 default_extension
759 );
761 saveDialog->setSelectionType(extension);
763 bool success = saveDialog->show();
764 if (!success) {
765 delete saveDialog;
766 return success;
767 }
769 Glib::ustring fileName = saveDialog->getFilename();
770 Inkscape::Extension::Extension *selectionType = saveDialog->getSelectionType();
772 delete saveDialog;
774 saveDialog = 0;
776 if (fileName.size() > 0) {
777 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
779 if ( newFileName.size()>0 )
780 fileName = newFileName;
781 else
782 g_warning( "Error converting save filename to UTF-8." );
784 success = file_save(parentWindow, doc, fileName, selectionType, TRUE, !is_copy);
786 if (success)
787 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
789 save_path = Glib::path_get_dirname(fileName);
790 prefs_set_string_attribute("dialogs.save_as", "path", save_path.c_str());
792 return success;
793 }
796 return false;
797 }
800 /**
801 * Save a document, displaying a SaveAs dialog if necessary.
802 */
803 bool
804 sp_file_save_document(Gtk::Window &parentWindow, SPDocument *doc)
805 {
806 bool success = true;
808 if (doc->isModifiedSinceSave()) {
809 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
810 if ( doc->uri == NULL
811 || repr->attribute("inkscape:output_extension") == NULL )
812 {
813 return sp_file_save_dialog(parentWindow, doc, FALSE);
814 } else {
815 gchar const *fn = g_strdup(doc->uri);
816 gchar const *ext = repr->attribute("inkscape:output_extension");
817 success = file_save(parentWindow, doc, fn, Inkscape::Extension::db.get(ext), FALSE, TRUE);
818 g_free((void *) fn);
819 }
820 } else {
821 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No changes need to be saved."));
822 success = TRUE;
823 }
825 return success;
826 }
829 /**
830 * Save a document.
831 */
832 bool
833 sp_file_save(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
834 {
835 if (!SP_ACTIVE_DOCUMENT)
836 return false;
838 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Saving document..."));
840 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
841 return sp_file_save_document(parentWindow, SP_ACTIVE_DOCUMENT);
842 }
845 /**
846 * Save a document, always displaying the SaveAs dialog.
847 */
848 bool
849 sp_file_save_as(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
850 {
851 if (!SP_ACTIVE_DOCUMENT)
852 return false;
853 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
854 return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, FALSE);
855 }
859 /**
860 * Save a copy of a document, always displaying a sort of SaveAs dialog.
861 */
862 bool
863 sp_file_save_a_copy(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
864 {
865 if (!SP_ACTIVE_DOCUMENT)
866 return false;
867 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
868 return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, TRUE);
869 }
872 /*######################
873 ## I M P O R T
874 ######################*/
876 typedef enum { REF_HREF, REF_STYLE, REF_URL } ID_REF_TYPE;
878 struct IdReference {
879 ID_REF_TYPE type;
880 SPObject *elem;
881 const char *attr; // property or href-like attribute
882 };
884 typedef std::map<std::string, std::list<IdReference> > refmap_type;
886 typedef std::pair<SPObject*, std::string> id_changeitem_type;
887 typedef std::list<id_changeitem_type> id_changelist_type;
889 const char *href_like_attributes[] = {
890 "inkscape:href",
891 "inkscape:path-effect",
892 "inkscape:perspectiveID",
893 "inkscape:tiled-clone-of",
894 "xlink:href",
895 };
896 #define NUM_HREF_LIKE_ATTRIBUTES (sizeof(href_like_attributes) / sizeof(*href_like_attributes))
898 const SPIPaint SPStyle::* SPIPaint_members[] = {
899 &SPStyle::color,
900 &SPStyle::fill,
901 &SPStyle::stroke,
902 };
903 const char* SPIPaint_properties[] = {
904 "color",
905 "fill",
906 "stroke",
907 };
908 #define NUM_SPIPAINT_PROPERTIES (sizeof(SPIPaint_properties) / sizeof(*SPIPaint_properties))
910 const char* other_url_properties[] = {
911 "clip-path",
912 "color-profile",
913 "cursor",
914 "marker-end",
915 "marker-mid",
916 "marker-start",
917 "mask",
918 };
919 #define NUM_OTHER_URL_PROPERTIES (sizeof(other_url_properties) / sizeof(*other_url_properties))
921 /**
922 * Build a table of places where ids are referenced, for a given element.
923 * FIXME: There are some types of references not yet dealt with here
924 * (e.g., ID selectors in CSS stylesheets).
925 */
926 static void
927 find_references(SPObject *elem, refmap_type *refmap)
928 {
929 Inkscape::XML::Node *repr_elem = SP_OBJECT_REPR(elem);
930 SPStyle *style = SP_OBJECT_STYLE(elem);
932 /* check for xlink:href="#..." and similar */
933 for (unsigned i = 0; i < NUM_HREF_LIKE_ATTRIBUTES; ++i) {
934 const char *attr = href_like_attributes[i];
935 const gchar *val = repr_elem->attribute(attr);
936 if (val && val[0] == '#') {
937 std::string id(val+1);
938 IdReference idref = { REF_HREF, elem, attr };
939 (*refmap)[id].push_back(idref);
940 }
941 }
943 /* check for url(#...) references in 'fill' or 'stroke' */
944 for (unsigned i = 0; i < NUM_SPIPAINT_PROPERTIES; ++i) {
945 const SPIPaint SPStyle::*prop = SPIPaint_members[i];
946 const SPIPaint *paint = &(style->*prop);
947 if (paint->isPaintserver()) {
948 const gchar *id = SP_OBJECT_ID(paint->value.href->getObject());
949 IdReference idref = { REF_STYLE, elem, SPIPaint_properties[i] };
950 (*refmap)[id].push_back(idref);
951 }
952 }
954 /* check for url(#...) references in 'filter' */
955 const SPIFilter *filter = &(style->filter);
956 if (filter->href) {
957 const gchar *id = SP_OBJECT_ID(filter->href->getObject());
958 IdReference idref = { REF_STYLE, elem, "filter" };
959 (*refmap)[id].push_back(idref);
960 }
962 /* check for other url(#...) references */
963 for (unsigned i = 0; i < NUM_OTHER_URL_PROPERTIES; ++i) {
964 const char *attr = other_url_properties[i];
965 const gchar *value = repr_elem->attribute(attr);
966 if (value) {
967 const gchar *uri = extract_uri(value);
968 if (uri && uri[0] == '#') {
969 IdReference idref = { REF_URL, elem, attr };
970 (*refmap)[uri+1].push_back(idref);
971 }
972 }
973 }
975 /* recurse */
976 for (SPObject *child = sp_object_first_child(elem);
977 child; child = SP_OBJECT_NEXT(child) )
978 {
979 find_references(child, refmap);
980 }
981 }
983 /**
984 * Change any ids that clash with ids in the current document, and make
985 * a list of those changes that will require fixing up references.
986 */
987 static void
988 change_clashing_ids(SPDocument *imported_doc, SPDocument *current_doc,
989 SPObject *elem, const refmap_type *refmap,
990 id_changelist_type *id_changes)
991 {
992 const gchar *id = SP_OBJECT_ID(elem);
994 if (id && current_doc->getObjectById(id)) {
995 // Choose a new id.
996 // To try to preserve any meaningfulness that the original id
997 // may have had, the new id is the old id followed by a hyphen
998 // and one or more digits.
999 std::string old_id(id);
1000 std::string new_id(old_id + '-');
1001 for (;;) {
1002 new_id += "0123456789"[std::rand() % 10];
1003 const char *str = new_id.c_str();
1004 if (current_doc->getObjectById(str) == NULL &&
1005 imported_doc->getObjectById(str) == NULL) break;
1006 }
1007 // Change to the new id
1008 SP_OBJECT_REPR(elem)->setAttribute("id", new_id.c_str());
1009 // Make a note of this change, if we need to fix up refs to it
1010 if (refmap->find(old_id) != refmap->end())
1011 id_changes->push_back(id_changeitem_type(elem, old_id));
1012 }
1014 /* recurse */
1015 for (SPObject *child = sp_object_first_child(elem);
1016 child; child = SP_OBJECT_NEXT(child) )
1017 {
1018 change_clashing_ids(imported_doc, current_doc, child, refmap, id_changes);
1019 }
1020 }
1022 /**
1023 * Fix up references to changed ids.
1024 */
1025 static void
1026 fix_up_refs(const refmap_type *refmap, const id_changelist_type &id_changes)
1027 {
1028 id_changelist_type::const_iterator pp;
1029 const id_changelist_type::const_iterator pp_end = id_changes.end();
1030 for (pp = id_changes.begin(); pp != pp_end; ++pp) {
1031 SPObject *obj = pp->first;
1032 refmap_type::const_iterator pos = refmap->find(pp->second);
1033 std::list<IdReference>::const_iterator it;
1034 const std::list<IdReference>::const_iterator it_end = pos->second.end();
1035 for (it = pos->second.begin(); it != it_end; ++it) {
1036 if (it->type == REF_HREF) {
1037 gchar *new_uri = g_strdup_printf("#%s", SP_OBJECT_ID(obj));
1038 SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, new_uri);
1039 g_free(new_uri);
1040 }
1041 else if (it->type == REF_STYLE) {
1042 sp_style_set_property_url(it->elem, it->attr, obj, false);
1043 }
1044 else if (it->type == REF_URL) {
1045 gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj));
1046 SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, url);
1047 g_free(url);
1048 }
1049 else g_assert(0); // shouldn't happen
1050 }
1051 }
1052 }
1054 /**
1055 * This function resolves ID clashes between the document being imported
1056 * and the current open document: IDs in the imported document that would
1057 * clash with IDs in the existing document are changed, and references to
1058 * those IDs are updated accordingly.
1059 */
1060 void
1061 prevent_id_clashes(SPDocument *imported_doc, SPDocument *current_doc)
1062 {
1063 refmap_type *refmap = new refmap_type;
1064 id_changelist_type id_changes;
1065 SPObject *imported_root = SP_DOCUMENT_ROOT(imported_doc);
1067 find_references(imported_root, refmap);
1068 change_clashing_ids(imported_doc, current_doc, imported_root, refmap,
1069 &id_changes);
1070 fix_up_refs(refmap, id_changes);
1072 delete refmap;
1073 }
1076 /**
1077 * Import a resource. Called by sp_file_import()
1078 */
1079 void
1080 file_import(SPDocument *in_doc, const Glib::ustring &uri,
1081 Inkscape::Extension::Extension *key)
1082 {
1083 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1085 //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key );
1086 SPDocument *doc;
1087 try {
1088 doc = Inkscape::Extension::open(key, uri.c_str());
1089 } catch (Inkscape::Extension::Input::no_extension_found &e) {
1090 doc = NULL;
1091 } catch (Inkscape::Extension::Input::open_failed &e) {
1092 doc = NULL;
1093 }
1095 if (doc != NULL) {
1096 Inkscape::IO::fixupHrefs(doc, in_doc->base, true);
1097 Inkscape::XML::Document *xml_in_doc = sp_document_repr_doc(in_doc);
1099 prevent_id_clashes(doc, in_doc);
1101 SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
1102 Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
1104 SPCSSAttr *style = sp_css_attr_from_object(SP_DOCUMENT_ROOT(doc));
1106 // Count the number of top-level items in the imported document.
1107 guint items_count = 0;
1108 for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
1109 child != NULL; child = SP_OBJECT_NEXT(child))
1110 {
1111 if (SP_IS_ITEM(child)) items_count++;
1112 }
1114 // Create a new group if necessary.
1115 Inkscape::XML::Node *newgroup = NULL;
1116 if ((style && style->firstChild()) || items_count > 1) {
1117 newgroup = xml_in_doc->createElement("svg:g");
1118 sp_repr_css_set(newgroup, style, "style");
1119 }
1121 // Determine the place to insert the new object.
1122 // This will be the current layer, if possible.
1123 // FIXME: If there's no desktop (command line run?) we need
1124 // a document:: method to return the current layer.
1125 // For now, we just use the root in this case.
1126 SPObject *place_to_insert;
1127 if (desktop) place_to_insert = desktop->currentLayer();
1128 else place_to_insert = SP_DOCUMENT_ROOT(in_doc);
1130 // Construct a new object representing the imported image,
1131 // and insert it into the current document.
1132 SPObject *new_obj = NULL;
1133 for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
1134 child != NULL; child = SP_OBJECT_NEXT(child) )
1135 {
1136 if (SP_IS_ITEM(child)) {
1137 Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate(xml_in_doc);
1139 // convert layers to groups, and make sure they are unlocked
1140 // FIXME: add "preserve layers" mode where each layer from
1141 // import is copied to the same-named layer in host
1142 newitem->setAttribute("inkscape:groupmode", NULL);
1143 newitem->setAttribute("sodipodi:insensitive", NULL);
1145 if (newgroup) newgroup->appendChild(newitem);
1146 else new_obj = place_to_insert->appendChildRepr(newitem);
1147 }
1149 // don't lose top-level defs or style elements
1150 else if (SP_OBJECT_REPR(child)->type() == Inkscape::XML::ELEMENT_NODE) {
1151 const gchar *tag = SP_OBJECT_REPR(child)->name();
1152 if (!strcmp(tag, "svg:defs")) {
1153 for (SPObject *x = sp_object_first_child(child);
1154 x != NULL; x = SP_OBJECT_NEXT(x))
1155 {
1156 SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(x)->duplicate(xml_in_doc), last_def);
1157 }
1158 }
1159 else if (!strcmp(tag, "svg:style")) {
1160 SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(SP_OBJECT_REPR(child)->duplicate(xml_in_doc));
1161 }
1162 }
1163 }
1164 if (newgroup) new_obj = place_to_insert->appendChildRepr(newgroup);
1166 // release some stuff
1167 if (newgroup) Inkscape::GC::release(newgroup);
1168 if (style) sp_repr_css_attr_unref(style);
1170 // select and move the imported item
1171 if (new_obj && SP_IS_ITEM(new_obj)) {
1172 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1173 selection->set(SP_ITEM(new_obj));
1175 // To move the imported object, we must temporarily set the "transform pattern with
1176 // object" option.
1177 {
1178 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1179 prefs_set_int_attribute("options.transform", "pattern", 1);
1180 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1181 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1182 if (sel_bbox) {
1183 NR::Point m( desktop->point() - sel_bbox->midpoint() );
1184 sp_selection_move_relative(selection, m);
1185 }
1186 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1187 }
1188 }
1190 sp_document_unref(doc);
1191 sp_document_done(in_doc, SP_VERB_FILE_IMPORT,
1192 _("Import"));
1194 } else {
1195 gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri.c_str());
1196 sp_ui_error_dialog(text);
1197 g_free(text);
1198 }
1200 return;
1201 }
1204 static Inkscape::UI::Dialog::FileOpenDialog *importDialogInstance = NULL;
1206 /**
1207 * Display an Open dialog, import a resource if OK pressed.
1208 */
1209 void
1210 sp_file_import(Gtk::Window &parentWindow)
1211 {
1212 static Glib::ustring import_path;
1214 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1215 if (!doc)
1216 return;
1218 if (!importDialogInstance) {
1219 importDialogInstance =
1220 Inkscape::UI::Dialog::FileOpenDialog::create(
1221 parentWindow,
1222 import_path,
1223 Inkscape::UI::Dialog::IMPORT_TYPES,
1224 (char const *)_("Select file to import"));
1225 }
1227 bool success = importDialogInstance->show();
1228 if (!success)
1229 return;
1231 //# Get file name and extension type
1232 Glib::ustring fileName = importDialogInstance->getFilename();
1233 Inkscape::Extension::Extension *selection =
1234 importDialogInstance->getSelectionType();
1237 if (fileName.size() > 0) {
1239 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1241 if ( newFileName.size() > 0)
1242 fileName = newFileName;
1243 else
1244 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
1247 import_path = fileName;
1248 if (import_path.size()>0)
1249 import_path.append(G_DIR_SEPARATOR_S);
1251 file_import(doc, fileName, selection);
1252 }
1254 return;
1255 }
1259 /*######################
1260 ## E X P O R T
1261 ######################*/
1263 //#define NEW_EXPORT_DIALOG
1267 #ifdef NEW_EXPORT_DIALOG
1269 static Inkscape::UI::Dialog::FileExportDialog *exportDialogInstance = NULL;
1271 /**
1272 * Display an Export dialog, export as the selected type if OK pressed
1273 */
1274 bool
1275 sp_file_export_dialog(void *widget)
1276 {
1277 //# temp hack for 'doc' until we can switch to this dialog
1278 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1280 Glib::ustring export_path;
1281 Glib::ustring export_loc;
1283 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
1285 Inkscape::Extension::Output *extension;
1287 //# Get the default extension name
1288 Glib::ustring default_extension;
1289 char *attr = (char *)repr->attribute("inkscape:output_extension");
1290 if (!attr)
1291 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
1292 if (attr)
1293 default_extension = attr;
1294 //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
1296 if (doc->uri == NULL)
1297 {
1298 char formatBuf[256];
1300 Glib::ustring filename_extension = ".svg";
1301 extension = dynamic_cast<Inkscape::Extension::Output *>
1302 (Inkscape::Extension::db.get(default_extension.c_str()));
1303 //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
1304 if (extension)
1305 filename_extension = extension->get_extension();
1307 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
1308 if (attr)
1309 export_path = attr;
1311 if (!Inkscape::IO::file_test(export_path.c_str(),
1312 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
1313 export_path = "";
1315 if (export_path.size()<1)
1316 export_path = g_get_home_dir();
1318 export_loc = export_path;
1319 export_loc.append(G_DIR_SEPARATOR_S);
1320 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
1321 export_loc.append(formatBuf);
1323 }
1324 else
1325 {
1326 export_path = Glib::path_get_dirname(doc->uri);
1327 }
1329 // convert save_loc from utf-8 to locale
1330 // is this needed any more, now that everything is handled in
1331 // Inkscape::IO?
1332 Glib::ustring export_path_local = Glib::filename_from_utf8(export_path);
1333 if ( export_path_local.size() > 0)
1334 export_path = export_path_local;
1336 //# Show the SaveAs dialog
1337 if (!exportDialogInstance)
1338 exportDialogInstance =
1339 Inkscape::UI::Dialog::FileExportDialog::create(
1340 export_path,
1341 Inkscape::UI::Dialog::EXPORT_TYPES,
1342 (char const *) _("Select file to export to"),
1343 default_extension
1344 );
1346 bool success = exportDialogInstance->show();
1347 if (!success)
1348 return success;
1350 Glib::ustring fileName = exportDialogInstance->getFilename();
1352 Inkscape::Extension::Extension *selectionType =
1353 exportDialogInstance->getSelectionType();
1356 if (fileName.size() > 0) {
1357 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1359 if ( newFileName.size()>0 )
1360 fileName = newFileName;
1361 else
1362 g_warning( "Error converting save filename to UTF-8." );
1364 success = file_save(doc, fileName, selectionType, TRUE, FALSE);
1366 if (success)
1367 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
1369 export_path = fileName;
1370 prefs_set_string_attribute("dialogs.save_as", "path", export_path.c_str());
1372 return success;
1373 }
1376 return false;
1377 }
1379 #else
1381 /**
1382 *
1383 */
1384 bool
1385 sp_file_export_dialog(void */*widget*/)
1386 {
1387 sp_export_dialog();
1388 return true;
1389 }
1391 #endif
1393 /*######################
1394 ## E X P O R T T O O C A L
1395 ######################*/
1397 /**
1398 * Display an Export dialog, export as the selected type if OK pressed
1399 */
1400 bool
1401 sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow)
1402 {
1404 if (!SP_ACTIVE_DOCUMENT)
1405 return false;
1407 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1409 Glib::ustring export_path;
1410 Glib::ustring export_loc;
1411 Glib::ustring fileName;
1412 Inkscape::Extension::Extension *selectionType;
1414 bool success = false;
1416 static Inkscape::UI::Dialog::FileExportToOCALDialog *exportDialogInstance = NULL;
1417 static Inkscape::UI::Dialog::FileExportToOCALPasswordDialog *exportPasswordDialogInstance = NULL;
1418 static bool gotSuccess = false;
1420 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
1421 (void)repr;
1423 if (!doc->uri && !doc->isModifiedSinceSave())
1424 return false;
1426 // Get the default extension name
1427 Glib::ustring default_extension = "org.inkscape.output.svg.inkscape";
1428 char formatBuf[256];
1430 Glib::ustring filename_extension = ".svg";
1431 selectionType = Inkscape::Extension::db.get(default_extension.c_str());
1433 export_path = Glib::get_tmp_dir ();
1435 export_loc = export_path;
1436 export_loc.append(G_DIR_SEPARATOR_S);
1437 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
1438 export_loc.append(formatBuf);
1440 // convert save_loc from utf-8 to locale
1441 // is this needed any more, now that everything is handled in
1442 // Inkscape::IO?
1443 Glib::ustring export_path_local = Glib::filename_from_utf8(export_path);
1444 if ( export_path_local.size() > 0)
1445 export_path = export_path_local;
1447 // Show the Export To OCAL dialog
1448 if (!exportDialogInstance)
1449 exportDialogInstance = new Inkscape::UI::Dialog::FileExportToOCALDialog(
1450 parentWindow,
1451 Inkscape::UI::Dialog::EXPORT_TYPES,
1452 (char const *) _("Select file to export to")
1453 );
1455 success = exportDialogInstance->show();
1456 if (!success)
1457 return success;
1459 fileName = exportDialogInstance->getFilename();
1461 fileName.append(filename_extension.c_str());
1462 if (fileName.size() > 0) {
1463 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1465 if ( newFileName.size()>0 )
1466 fileName = newFileName;
1467 else
1468 g_warning( "Error converting save filename to UTF-8." );
1469 }
1470 Glib::ustring filePath = export_path;
1471 filePath.append(G_DIR_SEPARATOR_S);
1472 filePath.append(Glib::path_get_basename(fileName));
1474 fileName = filePath;
1476 success = file_save(parentWindow, doc, filePath, selectionType, FALSE, FALSE);
1478 if (!success){
1479 gchar *text = g_strdup_printf(_("Error saving a temporary copy"));
1480 sp_ui_error_dialog(text);
1482 return success;
1483 }
1485 // Start now the submition
1487 // Create the uri
1488 Glib::ustring uri = "dav://";
1489 char *username = (char *)prefs_get_string_attribute("options.ocalusername", "str");
1490 char *password = (char *)prefs_get_string_attribute("options.ocalpassword", "str");
1491 if ((username == NULL) || (!strcmp(username, "")) || (password == NULL) || (!strcmp(password, "")))
1492 {
1493 if(!gotSuccess)
1494 {
1495 if (!exportPasswordDialogInstance)
1496 exportPasswordDialogInstance = new Inkscape::UI::Dialog::FileExportToOCALPasswordDialog(
1497 parentWindow,
1498 (char const *) _("Open Clip Art Login"));
1499 success = exportPasswordDialogInstance->show();
1500 if (!success)
1501 return success;
1502 }
1503 username = (char *)exportPasswordDialogInstance->getUsername().c_str();
1504 password = (char *)exportPasswordDialogInstance->getPassword().c_str();
1505 }
1506 uri.append(username);
1507 uri.append(":");
1508 uri.append(password);
1509 uri.append("@");
1510 uri.append(prefs_get_string_attribute("options.ocalurl", "str"));
1511 uri.append("/dav.php/");
1512 uri.append(Glib::path_get_basename(fileName));
1514 // Save as a remote file using the dav protocol.
1515 success = file_save_remote(doc, uri, selectionType, FALSE, FALSE);
1516 remove(fileName.c_str());
1517 if (!success)
1518 {
1519 gchar *text = g_strdup_printf(_("Error exporting the document. Verify if the server name, username and password are correct, if the server has support for webdav and verify if you didn't forget to choose a license."));
1520 sp_ui_error_dialog(text);
1521 }
1522 else
1523 gotSuccess = true;
1525 return success;
1526 }
1528 /**
1529 * Export the current document to OCAL
1530 */
1531 void
1532 sp_file_export_to_ocal(Gtk::Window &parentWindow)
1533 {
1535 // Try to execute the new code and return;
1536 if (!SP_ACTIVE_DOCUMENT)
1537 return;
1538 bool success = sp_file_export_to_ocal_dialog(parentWindow);
1539 if (success)
1540 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Document exported..."));
1541 }
1544 /*######################
1545 ## I M P O R T F R O M O C A L
1546 ######################*/
1548 /**
1549 * Display an ImportToOcal Dialog, and the selected document from OCAL
1550 */
1551 void
1552 sp_file_import_from_ocal(Gtk::Window &parentWindow)
1553 {
1554 static Glib::ustring import_path;
1556 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1557 if (!doc)
1558 return;
1560 static Inkscape::UI::Dialog::FileImportFromOCALDialog *importDialogInstance = NULL;
1562 if (!importDialogInstance) {
1563 importDialogInstance = new
1564 Inkscape::UI::Dialog::FileImportFromOCALDialog(
1565 parentWindow,
1566 import_path,
1567 Inkscape::UI::Dialog::IMPORT_TYPES,
1568 (char const *)_("Import From Open Clip Art Library"));
1569 }
1571 bool success = importDialogInstance->show();
1572 if (!success)
1573 return;
1575 // Get file name and extension type
1576 Glib::ustring fileName = importDialogInstance->getFilename();
1577 Inkscape::Extension::Extension *selection =
1578 importDialogInstance->getSelectionType();
1580 if (fileName.size() > 0) {
1582 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1584 if ( newFileName.size() > 0)
1585 fileName = newFileName;
1586 else
1587 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
1589 import_path = fileName;
1590 if (import_path.size()>0)
1591 import_path.append(G_DIR_SEPARATOR_S);
1593 file_import(doc, fileName, selection);
1594 }
1596 return;
1597 }
1599 /*######################
1600 ## P R I N T
1601 ######################*/
1604 /**
1605 * Print the current document, if any.
1606 */
1607 void
1608 sp_file_print(Gtk::Window& parentWindow)
1609 {
1610 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1611 if (doc)
1612 sp_print_document(parentWindow, doc);
1613 }
1615 /**
1616 * Display what the drawing would look like, if
1617 * printed.
1618 */
1619 void
1620 sp_file_print_preview(gpointer /*object*/, gpointer /*data*/)
1621 {
1623 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1624 if (doc)
1625 sp_print_preview_document(doc);
1627 }
1629 void Inkscape::IO::fixupHrefs( SPDocument *doc, const gchar *base, gboolean spns )
1630 {
1631 //g_message("Inkscape::IO::fixupHrefs( , [%s], )", base );
1633 if ( 0 ) {
1634 gchar const* things[] = {
1635 "data:foo,bar",
1636 "http://www.google.com/image.png",
1637 "ftp://ssd.com/doo",
1638 "/foo/dee/bar.svg",
1639 "foo.svg",
1640 "file:/foo/dee/bar.svg",
1641 "file:///foo/dee/bar.svg",
1642 "file:foo.svg",
1643 "/foo/bar\xe1\x84\x92.svg",
1644 "file:///foo/bar\xe1\x84\x92.svg",
1645 "file:///foo/bar%e1%84%92.svg",
1646 "/foo/bar%e1%84%92.svg",
1647 "bar\xe1\x84\x92.svg",
1648 "bar%e1%84%92.svg",
1649 NULL
1650 };
1651 g_message("+------");
1652 for ( int i = 0; things[i]; i++ )
1653 {
1654 try
1655 {
1656 URI uri(things[i]);
1657 gboolean isAbs = g_path_is_absolute( things[i] );
1658 gchar *str = uri.toString();
1659 g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
1660 (int)uri.isRelative(),
1661 uri.getScheme(),
1662 uri.getPath(),
1663 uri.getOpaque(),
1664 things[i],
1665 str );
1666 g_free(str);
1667 }
1668 catch ( MalformedURIException err )
1669 {
1670 dump_str( things[i], "MalformedURIException" );
1671 xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
1672 g_message(" gone from [%s] to [%s]", things[i], redo );
1673 if ( redo == NULL )
1674 {
1675 URI again = URI::fromUtf8( things[i] );
1676 gboolean isAbs = g_path_is_absolute( things[i] );
1677 gchar *str = again.toString();
1678 g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
1679 (int)again.isRelative(),
1680 again.getScheme(),
1681 again.getPath(),
1682 again.getOpaque(),
1683 things[i],
1684 str );
1685 g_free(str);
1686 g_message(" ----");
1687 }
1688 }
1689 }
1690 g_message("+------");
1691 }
1693 GSList const *images = sp_document_get_resource_list(doc, "image");
1694 for (GSList const *l = images; l != NULL; l = l->next) {
1695 Inkscape::XML::Node *ir = SP_OBJECT_REPR(l->data);
1697 const gchar *href = ir->attribute("xlink:href");
1699 // First try to figure out an absolute path to the asset
1700 //g_message("image href [%s]", href );
1701 if (spns && !g_path_is_absolute(href)) {
1702 const gchar *absref = ir->attribute("sodipodi:absref");
1703 const gchar *base_href = g_build_filename(base, href, NULL);
1704 //g_message(" absr [%s]", absref );
1706 if ( absref && Inkscape::IO::file_test(absref, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_test(base_href, G_FILE_TEST_EXISTS))
1707 {
1708 // only switch over if the absref is valid while href is not
1709 href = absref;
1710 //g_message(" copied absref to href");
1711 }
1712 }
1714 // Once we have an absolute path, convert it relative to the new location
1715 if (href && g_path_is_absolute(href)) {
1716 const gchar *relname = sp_relative_path_from_path(href, base);
1717 //g_message(" setting to [%s]", relname );
1718 ir->setAttribute("xlink:href", relname);
1719 }
1720 // TODO next refinement is to make the first choice keeping the relative path as-is if
1721 // based on the new location it gives us a valid file.
1722 }
1723 }
1726 /*
1727 Local Variables:
1728 mode:c++
1729 c-file-style:"stroustrup"
1730 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1731 indent-tabs-mode:nil
1732 fill-column:99
1733 End:
1734 */
1735 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :