Code

basic support for <title> and <desc>
authorsasilver <sasilver@users.sourceforge.net>
Mon, 2 Jun 2008 08:23:40 +0000 (08:23 +0000)
committersasilver <sasilver@users.sourceforge.net>
Mon, 2 Jun 2008 08:23:40 +0000 (08:23 +0000)
make the Title and Description fields on the Object Properties dialog work (bug 171024)

src/dialogs/item-properties.cpp
src/sp-object.cpp
src/sp-object.h

index 2f93179e109095abcf979e66b548542062e9e961..fb7753d0054f07f4798b6b47fea65b18a5c7232e 100644 (file)
@@ -191,7 +191,6 @@ sp_item_widget_new (void)
     gtk_table_attach ( GTK_TABLE (t), f, 0, 3, 3, 4,
                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ),
                        (GtkAttachOptions)( GTK_EXPAND | GTK_FILL ), 0, 0 );
-    gtk_object_set_data (GTK_OBJECT (spw), "desc_frame", l);
 
     /* Create the text view box for the object description */
     GtkWidget *textframe = gtk_frame_new(NULL);
@@ -199,8 +198,10 @@ sp_item_widget_new (void)
     gtk_widget_set_sensitive (GTK_WIDGET (textframe), FALSE);
     gtk_container_add (GTK_CONTAINER (f), textframe);
     gtk_frame_set_shadow_type (GTK_FRAME (textframe), GTK_SHADOW_IN);
+    gtk_object_set_data(GTK_OBJECT(spw), "desc_frame", textframe);
 
     tf = gtk_text_view_new();
+    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(tf), GTK_WRAP_WORD);
     desc_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tf));
     gtk_text_buffer_set_text(desc_buffer, "", -1);
     gtk_container_add (GTK_CONTAINER (textframe), tf);
@@ -324,7 +325,27 @@ sp_item_widget_setup ( SPWidget *spw, Inkscape::Selection *selection )
         w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label"));
         gtk_entry_set_text (GTK_ENTRY (w), obj->defaultLabel());
         gtk_widget_set_sensitive (w, TRUE);
-        w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "label_label"));
+
+        /* Title */
+        w = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "title"));
+        gchar *title = obj->title();
+        if (title) {
+            gtk_entry_set_text(GTK_ENTRY(w), title);
+            g_free(title);
+        }
+        else gtk_entry_set_text(GTK_ENTRY(w), "");
+        gtk_widget_set_sensitive(w, TRUE);
+
+        /* Description */
+        gchar *desc = obj->desc();
+        if (desc) {
+            w = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "desc"));
+            GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+            gtk_text_buffer_set_text(buf, desc, -1);
+            g_free(desc);
+        }
+        w = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "desc_frame"));
+        gtk_widget_set_sensitive(w, TRUE);
     }
 
     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
@@ -419,21 +440,32 @@ sp_item_widget_label_changed( GtkWidget */*widget*/, SPWidget *spw )
     }
 
     /* Retrieve the title */
-    GtkWidget *w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "title"));
-    gchar *title = (gchar *)gtk_entry_get_text (GTK_ENTRY (w));
-    if (title != NULL) {
+    GtkWidget *w = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(spw), "title"));
+    gchar *title = (gchar *)gtk_entry_get_text(GTK_ENTRY (w));
+    g_assert(title != NULL);
+    gchar *old_title = obj->title();
+    if (old_title == NULL || strcmp(title, old_title)) {
         obj->setTitle(title);
-        sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
-                                _("Set object title"));
+        sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
+                         _("Set object title"));
     }
+    g_free(old_title);
 
     /* Retrieve the description */
-    gchar *desc = NULL; /* TODO:  get text from text buffer */
-    if (desc != NULL) {
+    GtkTextView *tv = GTK_TEXT_VIEW(gtk_object_get_data(GTK_OBJECT(spw), "desc"));
+    GtkTextBuffer *buf = gtk_text_view_get_buffer(tv);
+    GtkTextIter start, end;
+    gtk_text_buffer_get_bounds(buf, &start, &end);
+    gchar *desc = gtk_text_buffer_get_text(buf, &start, &end, TRUE);
+    g_assert(desc != NULL);
+    gchar *old_desc = obj->desc();
+    if (old_desc == NULL || strcmp(desc, old_desc)) {
         obj->setDesc(desc);
-        sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
-                                _("Set object description"));
+        sp_document_done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM,
+                         _("Set object description"));
     }
+    g_free(old_desc);
+    g_free(desc);
 
     gtk_object_set_data (GTK_OBJECT (spw), "blocked", GUINT_TO_POINTER (FALSE));
 
index f6ddf27dfa0aae1414a756e7ddf5c2c0ac260e63..d260792608a50e288064ab8f1a136baa04de8dbc 100644 (file)
@@ -5,8 +5,9 @@
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   bulia byak <buliabyak@users.sf.net>
+ *   Stephen Silver <sasilver@users.sourceforge.net>
  *
- * Copyright (C) 1999-2005 authors
+ * Copyright (C) 1999-2008 authors
  * Copyright (C) 2001-2002 Ximian, Inc.
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
@@ -1362,36 +1363,6 @@ SPObject::emitModified(unsigned int flags)
     g_object_unref(G_OBJECT(this));
 }
 
-/*
- * Get and set descriptive parameters
- *
- * These are inefficent, so they are not intended to be used interactively
- */
-
-gchar const *
-sp_object_title_get(SPObject */*object*/)
-{
-    return NULL;
-}
-
-gchar const *
-sp_object_description_get(SPObject */*object*/)
-{
-    return NULL;
-}
-
-unsigned int
-sp_object_title_set(SPObject */*object*/, gchar const */*title*/)
-{
-    return FALSE;
-}
-
-unsigned int
-sp_object_description_set(SPObject */*object*/, gchar const */*desc*/)
-{
-    return FALSE;
-}
-
 gchar const *
 sp_object_tagName_get(SPObject const *object, SPException *ex)
 {
@@ -1591,6 +1562,166 @@ sp_object_prev(SPObject *child)
     return NULL;
 }
 
+/* Titles and descriptions */
+
+/* Note:
+   Titles and descriptions are stored in 'title' and 'desc' child elements
+   (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
+   an element to have more than one 'title' child element, but strongly
+   recommends against this and requires using the first one if a choice must
+   be made.  The same applies to 'desc' elements.  Therefore, these functions
+   ignore all but the first 'title' child element and first 'desc' child
+   element, except when deleting a title or description.
+*/
+
+/**
+ * Returns the title of this object, or NULL if there is none.
+ * The caller must free the returned string using g_free() - see comment
+ * for getTitleOrDesc() below.
+ */
+gchar *
+SPObject::title() const
+{
+    return getTitleOrDesc("svg:title");
+}
+
+/**
+ * Sets the title of this object
+ * A NULL or purely whitespace argument is interpreted as meaning that
+ * the existing title (if any) should be deleted.
+ */
+void
+SPObject::setTitle(gchar const *title)
+{
+    setTitleOrDesc(title, "svg:title");
+}
+
+/**
+ * Returns the description of this object, or NULL if there is none.
+ * The caller must free the returned string using g_free() - see comment
+ * for getTitleOrDesc() below.
+ */
+gchar *
+SPObject::desc() const
+{
+    return getTitleOrDesc("svg:desc");
+}
+
+/**
+ * Sets the description of this object.
+ * A NULL or purely whitespace argument is interpreted as meaning that
+ * the existing description (if any) should be deleted.
+ */
+void
+SPObject::setDesc(gchar const *desc)
+{
+    setTitleOrDesc(desc, "svg:desc");
+}
+
+/**
+ * Returns the title or description of this object, or NULL if there is none.
+ *
+ * The SVG spec allows 'title' and 'desc' elements to contain text marked up
+ * using elements from other namespaces.  Therefore, this function cannot
+ * in general just return a pointer to an existing string - it must instead
+ * construct a string containing the title or description without the mark-up.
+ * Consequently, the return value is a newly allocated string (or NULL), and
+ * must be freed (using g_free()) by the caller.
+ */
+gchar *
+SPObject::getTitleOrDesc(gchar const *svg_tagname) const
+{
+    SPObject *elem = findFirstChild(svg_tagname);
+    if (elem == NULL) return NULL;
+    return g_string_free(elem->textualContent(), FALSE);
+}
+
+/**
+ * Sets or deletes the title or description of this object.
+ */
+void
+SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname)
+{
+    SPObject *elem = findFirstChild(svg_tagname);
+
+    // if the new title/description is NULL, or just whitespace,
+    // then delete any existing title/description
+    bool just_whitespace = true;
+    if (value)
+        for (const gchar *cp = value; *cp; ++cp) {
+            if (!std::strchr("\r\n \t", *cp)) {
+                just_whitespace = false;
+                break;
+            }
+        }
+    if (just_whitespace) {
+        // delete the title/description(s)
+        while (elem) {
+            elem->deleteObject();
+            elem = findFirstChild(svg_tagname);
+        }
+        return;
+    }
+
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
+
+    if (elem == NULL) {
+        // create a new 'title' or 'desc' element, putting it at the
+        // beginning (in accordance with the spec's recommendations)
+        Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
+        repr->addChild(xml_elem, NULL);
+        elem = document->getObjectByRepr(xml_elem);
+    }
+    else {
+        // remove the current content of the 'text' or 'desc' element
+        SPObject *child;
+        while (NULL != (child = elem->firstChild())) child->deleteObject();
+    }
+
+    // add the new content
+    elem->appendChildRepr(xml_doc->createTextNode(value));
+}
+
+/**
+ * Find the first child of this object with a given tag name,
+ * and return it.  Returns NULL if there is no matching child.
+ */
+SPObject *
+SPObject::findFirstChild(gchar const *tagname) const
+{
+    for (SPObject *child = children; child; child = child->next)
+    {
+        if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
+            !strcmp(child->repr->name(), tagname)) return child;
+    }
+    return NULL;
+}
+
+/**
+ * Return the full textual content of an element (typically all the
+ * content except the tags).
+ * Must not be used on anything except elements.
+ */
+GString*
+SPObject::textualContent() const
+{
+    GString* text = g_string_new("");
+
+    for (const SPObject *child = firstChild(); child; child = child->next)
+    {
+        Inkscape::XML::NodeType child_type = child->repr->type();
+        
+        if (child_type == Inkscape::XML::ELEMENT_NODE) {
+            GString * new_text = child->textualContent();
+            g_string_append(text, new_text->str);
+            g_string_free(new_text, TRUE);
+        }
+        else if (child_type == Inkscape::XML::TEXT_NODE) {
+            g_string_append(text, child->repr->content());
+        }
+    }
+    return text;
+}
 
 /*
   Local Variables:
index f30654c12ebb214f862aa2f80f9baecf60ba17db..5d22e33e4290c41dbb38256c9121d1616c024ced 100644 (file)
@@ -53,8 +53,6 @@
 #define SP_OBJECT_PREV(o) (sp_object_prev((SPObject *) (o)))
 #define SP_OBJECT_HREFCOUNT(o) (((SPObject *) (o))->hrefcount)
 #define SP_OBJECT_STYLE(o) (((SPObject *) (o))->style)
-#define SP_OBJECT_TITLE(o) sp_object_title_get((SPObject *) (o))
-#define SP_OBJECT_DESCRIPTION(o) sp_object_description_get((SPObject *) (o))
 
 
 #include <glib-object.h>
@@ -240,14 +238,14 @@ struct SPObject : public GObject {
     void setLabel(gchar const *label);
 
     /** Retrieves the title of this object */
-    gchar const *title() const { return NULL; /* TODO */ }
+    gchar *title() const;
     /** Sets the title of this object */
-    void setTitle(gchar const *title) { (void)title; /* TODO */ }
+    void setTitle(gchar const *title);
 
     /** Retrieves the description of this object */
-    gchar const *desc() const { return NULL; /* TODO */ }
+    gchar *desc() const;
     /** Sets the description of this object */
-    void setDesc(gchar const *desc) { (void)desc; /* TODO */ }
+    void setDesc(gchar const *desc);
 
     /** @brief Set the policy under which this object will be
      *         orphan-collected.
@@ -488,6 +486,14 @@ struct SPObject : public GObject {
     CollectionPolicy _collection_policy;
     gchar *_label;
     mutable gchar *_default_label;
+
+private:
+    // Private member functions used in the definitions of setTitle(),
+    // setDesc(), title() and desc().
+    void setTitleOrDesc(gchar const *value, gchar const *svg_tagname);
+    gchar * getTitleOrDesc(gchar const *svg_tagname) const;
+    SPObject * findFirstChild(gchar const *tagname) const;
+    GString * textualContent() const;
 };
 
 /// The SPObject vtable.
@@ -535,17 +541,6 @@ void sp_object_set(SPObject *object, unsigned int key, gchar const *value);
 
 void sp_object_read_attr(SPObject *object, gchar const *key);
 
-/*
- * Get and set descriptive parameters.
- *
- * These are inefficent, so they are not intended to be used interactively.
- */
-
-gchar const *sp_object_title_get(SPObject *object);
-gchar const *sp_object_description_get(SPObject *object);
-unsigned int sp_object_title_set(SPObject *object, gchar const *title);
-unsigned int sp_object_description_set(SPObject *object, gchar const *desc);
-
 /* Public */
 
 gchar const *sp_object_tagName_get(SPObject const *object, SPException *ex);