From b951d841751ca46bf1797c629d15ec0d17bedc20 Mon Sep 17 00:00:00 2001 From: sasilver Date: Mon, 2 Jun 2008 08:23:40 +0000 Subject: [PATCH] basic support for and <desc> make the Title and Description fields on the Object Properties dialog work (bug 171024) --- src/dialogs/item-properties.cpp | 54 +++++++-- src/sp-object.cpp | 193 +++++++++++++++++++++++++++----- src/sp-object.h | 29 ++--- 3 files changed, 217 insertions(+), 59 deletions(-) diff --git a/src/dialogs/item-properties.cpp b/src/dialogs/item-properties.cpp index 2f93179e1..fb7753d00 100644 --- a/src/dialogs/item-properties.cpp +++ b/src/dialogs/item-properties.cpp @@ -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)); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index f6ddf27df..d26079260 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -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: diff --git a/src/sp-object.h b/src/sp-object.h index f30654c12..5d22e33e4 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -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); -- 2.30.2