Code

now that selection description includes style (filtered, clipped), we need to update...
[inkscape.git] / src / file.cpp
index 51dce81cf7a8433a57dec6288bf3bb9584e9d151..5c729eeeb65b1032a8c61cb0ca255d6bdbcad34a 100644 (file)
@@ -8,17 +8,18 @@
  *   Chema Celorio <chema@celorio.com>
  *   bulia byak <buliabyak@users.sf.net>
  *   Bruno Dilly <bruno.dilly@gmail.com>
+ *   Stephen Silver <sasilver@users.sourceforge.net>
  *
  * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
- * Copyright (C) 1999-2005 Authors
+ * Copyright (C) 1999-2008 Authors
  * Copyright (C) 2004 David Turner
  * Copyright (C) 2001-2002 Ximian, Inc.
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
-/**
- * Note: This file needs to be cleaned up extensively.
+/** @file
+ * @note This file needs to be cleaned up extensively.
  * What it probably needs is to have one .h file for
  * the API, and two or more .cpp files for the implementations.
  */
@@ -27,6 +28,7 @@
 # include "config.h"
 #endif
 
+#include <gtk/gtk.h>
 #include <glib/gmem.h>
 #include <libnr/nr-pixops.h>
 
@@ -48,7 +50,7 @@
 #include "message-stack.h"
 #include "ui/dialog/filedialog.h"
 #include "ui/dialog/ocaldialogs.h"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "path-prefix.h"
 
 #include "sp-namedview.h"
@@ -65,6 +67,8 @@
 #include "application/editor.h"
 #include "inkscape.h"
 #include "uri.h"
+#include "id-clash.h"
+#include "dialogs/rdf.h"
 
 #ifdef WITH_GNOME_VFS
 # include <libgnomevfs/gnome-vfs.h>
 void dump_str(gchar const *str, gchar const *prefix);
 void dump_ustr(Glib::ustring const &ustr);
 
+// what gets passed here is not actually an URI... it is an UTF-8 encoded filename (!)
+static void sp_file_add_recent(gchar const *uri)
+{
+    GtkRecentManager *recent = gtk_recent_manager_get_default();
+    gchar *fn = g_filename_from_utf8(uri, -1, NULL, NULL, NULL);
+    if (fn) {
+        gchar *uri_to_add = g_filename_to_uri(fn, NULL, NULL);
+        if (uri_to_add) {
+            gtk_recent_manager_add_item(recent, uri_to_add);
+            g_free(uri_to_add);
+        }
+        g_free(fn);
+    }
+}
+
 
 /*######################
 ## N E W
@@ -179,6 +198,10 @@ sp_file_open(const Glib::ustring &uri,
              Inkscape::Extension::Extension *key,
              bool add_to_recent, bool replace_empty)
 {
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    if (desktop)
+        desktop->setWaitingCursor();
+
     SPDocument *doc = NULL;
     try {
         doc = Inkscape::Extension::open(key, uri.c_str());
@@ -188,8 +211,10 @@ sp_file_open(const Glib::ustring &uri,
         doc = NULL;
     }
 
+    if (desktop)
+        desktop->clearWaitingCursor();
+
     if (doc) {
-        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
         SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
 
         if (existing && existing->virgin && replace_empty) {
@@ -216,7 +241,7 @@ sp_file_open(const Glib::ustring &uri,
         sp_namedview_update_layers_from_document(desktop);
 
         if (add_to_recent) {
-            prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
+            sp_file_add_recent(SP_DOCUMENT_URI(doc));
         }
 
         return TRUE;
@@ -270,12 +295,12 @@ sp_file_revert_dialog()
 
         // remember current zoom and view
         double zoom = desktop->current_zoom();
-        NR::Point c = desktop->get_display_area().midpoint();
+        Geom::Point c = desktop->get_display_area().midpoint();
 
         reverted = sp_file_open(uri,NULL);
         if (reverted) {
             // restore zoom and view
-            desktop->zoom_absolute(c[NR::X], c[NR::Y], zoom);
+            desktop->zoom_absolute(c[Geom::X], c[Geom::Y], zoom);
         }
     } else {
         reverted = false;
@@ -379,12 +404,12 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d
 {
     //# Get the current directory for finding files
     static Glib::ustring open_path;
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
     if(open_path.empty())
     {
-        gchar const *attr = prefs_get_string_attribute("dialogs.open", "path");
-        if (attr)
-            open_path = attr;
+        Glib::ustring attr = prefs->getString("/dialogs/open/path");
+        if (!attr.empty()) open_path = attr;
     }
 
     //# Test if the open_path directory exists
@@ -493,9 +518,9 @@ sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d
         else
             g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
 
-        open_path = fileName;
+        open_path = Glib::path_get_dirname (fileName);
         open_path.append(G_DIR_SEPARATOR_S);
-        prefs_set_string_attribute("dialogs.open", "path", open_path.c_str());
+        prefs->setString("/dialogs/open/path", open_path);
 
         sp_file_open(fileName, selection);
     }
@@ -574,6 +599,9 @@ file_save(Gtk::Window &parentWindow, SPDocument *doc, const Glib::ustring &uri,
         g_free(text);
         g_free(safeUri);
         return FALSE;
+    } catch (Inkscape::Extension::Output::save_cancelled &e) {
+        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
+        return FALSE;
     } catch (Inkscape::Extension::Output::no_overwrite &e) {
         return sp_file_save_dialog(parentWindow, doc);
     }
@@ -679,16 +707,18 @@ sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
 {
 
     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
-
     Inkscape::Extension::Output *extension = 0;
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
     //# Get the default extension name
     Glib::ustring default_extension;
     char *attr = (char *)repr->attribute("inkscape:output_extension");
-    if (!attr)
-        attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
-    if (attr)
+    if (!attr) {
+        Glib::ustring attr2 = prefs->getString("/dialogs/save_as/default");
+        if(!attr2.empty()) default_extension = attr2;
+    } else {
         default_extension = attr;
+    }
     //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
 
     Glib::ustring save_path;
@@ -705,9 +735,9 @@ sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
         if (extension)
             filename_extension = extension->get_extension();
 
-        attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
-        if (attr)
-            save_path = attr;
+        Glib::ustring attr3 = prefs->getString("/dialogs/save_as/path");
+        if (!attr3.empty())
+            save_path = attr3;
 
         if (!Inkscape::IO::file_test(save_path.c_str(),
               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
@@ -747,13 +777,15 @@ sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
     } else {
         dialog_title = (char const *) _("Select file to save to");
     }
+    gchar* doc_title = doc->root->title();
     Inkscape::UI::Dialog::FileSaveDialog *saveDialog =
         Inkscape::UI::Dialog::FileSaveDialog::create(
             parentWindow,
             save_loc,
             Inkscape::UI::Dialog::SVG_TYPES,
             dialog_title,
-            default_extension
+            default_extension,
+            doc_title ? doc_title : ""
             );
 
     saveDialog->setSelectionType(extension);
@@ -764,6 +796,11 @@ sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
         return success;
     }
 
+    // set new title here (call RDF to ensure metadata and title element are updated)
+    rdf_set_work_entity(doc, rdf_find_entity("title"), saveDialog->getDocTitle().c_str());
+    // free up old string
+    if(doc_title) g_free(doc_title);
+
     Glib::ustring fileName = saveDialog->getFilename();
     Inkscape::Extension::Extension *selectionType = saveDialog->getSelectionType();
 
@@ -781,11 +818,13 @@ sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
 
         success = file_save(parentWindow, doc, fileName, selectionType, TRUE, !is_copy);
 
-        if (success)
-            prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
+        if (success) {
+            sp_file_add_recent(SP_DOCUMENT_URI(doc));
+        }
 
         save_path = Glib::path_get_dirname(fileName);
-        prefs_set_string_attribute("dialogs.save_as", "path", save_path.c_str());
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        prefs->setString("/dialogs/save_as/path", save_path);
 
         return success;
     }
@@ -891,80 +930,79 @@ file_import(SPDocument *in_doc, const Glib::ustring &uri,
     }
 
     if (doc != NULL) {
-        // move imported defs to our document's defs
-        SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
-        SPObject *defs = SP_DOCUMENT_DEFS(doc);
-
         Inkscape::IO::fixupHrefs(doc, in_doc->base, true);
-        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
+        Inkscape::XML::Document *xml_in_doc = sp_document_repr_doc(in_doc);
+
+        prevent_id_clashes(doc, in_doc);
 
+        SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
         Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
-        for (SPObject *child = sp_object_first_child(defs);
-             child != NULL; child = SP_OBJECT_NEXT(child))
-        {
-            // FIXME: in case of id conflict, newly added thing will be re-ided and thus likely break a reference to it from imported stuff
-            SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(child)->duplicate(xml_doc), last_def);
-        }
 
+        SPCSSAttr *style = sp_css_attr_from_object(SP_DOCUMENT_ROOT(doc));
+
+        // Count the number of top-level items in the imported document.
         guint items_count = 0;
         for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
-             child != NULL; child = SP_OBJECT_NEXT(child)) {
-            if (SP_IS_ITEM(child))
-                items_count ++;
+             child != NULL; child = SP_OBJECT_NEXT(child))
+        {
+            if (SP_IS_ITEM(child)) items_count++;
         }
-        SPCSSAttr *style = sp_css_attr_from_object (SP_DOCUMENT_ROOT (doc));
-
-        SPObject *new_obj = NULL;
 
+        // Create a new group if necessary.
+        Inkscape::XML::Node *newgroup = NULL;
         if ((style && style->firstChild()) || items_count > 1) {
-            // create group
-            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(in_doc);
-            Inkscape::XML::Node *newgroup = xml_doc->createElement("svg:g");
-            sp_repr_css_set (newgroup, style, "style");
+            newgroup = xml_in_doc->createElement("svg:g");
+            sp_repr_css_set(newgroup, style, "style");
+        }
 
-            for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
-                if (SP_IS_ITEM(child)) {
-                    Inkscape::XML::Node *newchild = SP_OBJECT_REPR(child)->duplicate(xml_doc);
+        // Determine the place to insert the new object.
+        // This will be the current layer, if possible.
+        // FIXME: If there's no desktop (command line run?) we need
+        //        a document:: method to return the current layer.
+        //        For now, we just use the root in this case.
+        SPObject *place_to_insert;
+        if (desktop) place_to_insert = desktop->currentLayer();
+        else         place_to_insert = SP_DOCUMENT_ROOT(in_doc);
+
+        // Construct a new object representing the imported image,
+        // and insert it into the current document.
+        SPObject *new_obj = NULL;
+        for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
+             child != NULL; child = SP_OBJECT_NEXT(child) )
+        {
+            if (SP_IS_ITEM(child)) {
+                Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate(xml_in_doc);
 
-                    // convert layers to groups; FIXME: add "preserve layers" mode where each layer
-                    // from impot is copied to the same-named layer in host
-                    newchild->setAttribute("inkscape:groupmode", NULL);
+                // convert layers to groups, and make sure they are unlocked
+                // FIXME: add "preserve layers" mode where each layer from
+                //        import is copied to the same-named layer in host
+                newitem->setAttribute("inkscape:groupmode", NULL);
+                newitem->setAttribute("sodipodi:insensitive", NULL);
 
-                    newgroup->appendChild(newchild);
-                }
+                if (newgroup) newgroup->appendChild(newitem);
+                else new_obj = place_to_insert->appendChildRepr(newitem);
             }
 
-            if (desktop) {
-                // Add it to the current layer
-                new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
-            } else {
-                // There's no desktop (command line run?)
-                // FIXME: For such cases we need a document:: method to return the current layer
-                new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newgroup);
-            }
-
-            Inkscape::GC::release(newgroup);
-        } else {
-            // just add one item
-            for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc)); child != NULL; child = SP_OBJECT_NEXT(child) ) {
-                if (SP_IS_ITEM(child)) {
-                    Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate(xml_doc);
-                    newitem->setAttribute("inkscape:groupmode", NULL);
-
-                    if (desktop) {
-                        // Add it to the current layer
-                        new_obj = desktop->currentLayer()->appendChildRepr(newitem);
-                    } else {
-                        // There's no desktop (command line run?)
-                        // FIXME: For such cases we need a document:: method to return the current layer
-                        new_obj = SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(newitem);
+            // don't lose top-level defs or style elements
+            else if (SP_OBJECT_REPR(child)->type() == Inkscape::XML::ELEMENT_NODE) {
+                const gchar *tag = SP_OBJECT_REPR(child)->name();
+                if (!strcmp(tag, "svg:defs")) {
+                    for (SPObject *x = sp_object_first_child(child);
+                         x != NULL; x = SP_OBJECT_NEXT(x))
+                    {
+                        SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(x)->duplicate(xml_in_doc), last_def);
                     }
-
+                }
+                else if (!strcmp(tag, "svg:style")) {
+                    SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(SP_OBJECT_REPR(child)->duplicate(xml_in_doc));
                 }
             }
         }
+        if (newgroup) new_obj = place_to_insert->appendChildRepr(newgroup);
 
-        if (style) sp_repr_css_attr_unref (style);
+        // release some stuff
+        if (newgroup) Inkscape::GC::release(newgroup);
+        if (style) sp_repr_css_attr_unref(style);
 
         // select and move the imported item
         if (new_obj && SP_IS_ITEM(new_obj)) {
@@ -974,15 +1012,16 @@ file_import(SPDocument *in_doc, const Glib::ustring &uri,
             // To move the imported object, we must temporarily set the "transform pattern with
             // object" option.
             {
-                int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
-                prefs_set_int_attribute("options.transform", "pattern", 1);
+                Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+                bool const saved_pref = prefs->getBool("/options/transform/pattern", true);
+                prefs->setBool("/options/transform/pattern", true);
                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
-                NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
+                Geom::OptRect sel_bbox = selection->bounds();
                 if (sel_bbox) {
-                    NR::Point m( desktop->point() - sel_bbox->midpoint() );
+                    Geom::Point m( desktop->point() - sel_bbox->midpoint() );
                     sp_selection_move_relative(selection, m);
                 }
-                prefs_set_int_attribute("options.transform", "pattern", saved_pref);
+                prefs->setBool("/options/transform/pattern", saved_pref);
             }
         }
 
@@ -1080,16 +1119,18 @@ sp_file_export_dialog(void *widget)
     Glib::ustring export_loc;
 
     Inkscape::XML::Node *repr = sp_document_repr_root(doc);
-
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     Inkscape::Extension::Output *extension;
 
     //# Get the default extension name
     Glib::ustring default_extension;
     char *attr = (char *)repr->attribute("inkscape:output_extension");
-    if (!attr)
-        attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
-    if (attr)
+    if (!attr) {
+        Glib::ustring attr2 = prefs->getString("/dialogs/save_as/default");
+        if(!attr2.empty()) default_extension = attr2;
+    } else {
         default_extension = attr;
+    }
     //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
 
     if (doc->uri == NULL)
@@ -1103,9 +1144,9 @@ sp_file_export_dialog(void *widget)
         if (extension)
             filename_extension = extension->get_extension();
 
-        attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
-        if (attr)
-            export_path = attr;
+        Glib::ustring attr3 = prefs->getString("/dialogs/save_as/path");
+        if (!attr3.empty())
+            export_path = attr3;
 
         if (!Inkscape::IO::file_test(export_path.c_str(),
               (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
@@ -1162,11 +1203,13 @@ sp_file_export_dialog(void *widget)
 
         success = file_save(doc, fileName, selectionType, TRUE, FALSE);
 
-        if (success)
-            prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
+        if (success) {
+            Glib::RefPtr<Gtk::RecentManager> recent = Gtk::RecentManager::get_default();
+            recent->add_item(SP_DOCUMENT_URI(doc));
+        }
 
         export_path = fileName;
-        prefs_set_string_attribute("dialogs.save_as", "path", export_path.c_str());
+        prefs->setString("/dialogs/save_as/path", export_path);
 
         return success;
     }
@@ -1284,10 +1327,11 @@ sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow)
     // Start now the submition
 
     // Create the uri
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     Glib::ustring uri = "dav://";
-    char *username = (char *)prefs_get_string_attribute("options.ocalusername", "str");
-    char *password = (char *)prefs_get_string_attribute("options.ocalpassword", "str");
-    if ((username == NULL) || (!strcmp(username, "")) || (password == NULL) || (!strcmp(password, "")))
+    Glib::ustring username = prefs->getString("/options/ocalusername/str");
+    Glib::ustring password = prefs->getString("/options/ocalpassword/str");
+    if (username.empty() || password.empty())
     {
         if(!gotSuccess)
         {
@@ -1299,14 +1343,14 @@ sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow)
             if (!success)
                 return success;
         }
-        username = (char *)exportPasswordDialogInstance->getUsername().c_str();
-        password = (char *)exportPasswordDialogInstance->getPassword().c_str();
+        username = exportPasswordDialogInstance->getUsername();
+        password = exportPasswordDialogInstance->getPassword();
     }
     uri.append(username);
     uri.append(":");
     uri.append(password);
     uri.append("@");
-    uri.append(prefs_get_string_attribute("options.ocalurl", "str"));
+    uri.append(prefs->getString("/options/ocalurl/str"));
     uri.append("/dav.php/");
     uri.append(Glib::path_get_basename(fileName));
 
@@ -1315,7 +1359,7 @@ sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow)
     remove(fileName.c_str());
     if (!success)
     {
-        gchar *text = g_strdup_printf(_("Error exporting the document. Verify if the server name, username and password are correct. If the server have support for webdav and verify if you didn't forget to choose a license too."));
+        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."));
         sp_ui_error_dialog(text);
     }
     else