Code

patch from bug 1497803 by Gustav Broberg; allow to reflow an already flowed text
[inkscape.git] / src / text-chemistry.cpp
index 549db62282d5354dfde6bd44e92f59e09d3f2c40..4ea47fd278fd4caf19e1d9616d5e28126be4c798 100644 (file)
 #include "document.h"
 #include "message-stack.h"
 #include "selection.h"
+#include "style.h"
 #include "desktop-handles.h"
 #include "text-editing.h"
+#include "text-chemistry.h"
 #include "sp-flowtext.h"
 #include "sp-flowregion.h"
 #include "sp-flowdiv.h"
@@ -85,32 +87,47 @@ text_put_on_path()
     if (!desktop)
         return;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     SPItem *text = text_or_flowtext_in_selection(selection);
     SPItem *shape = shape_in_selection(selection);
 
     if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path."));
         return;
     }
 
     if (SP_IS_TEXT_TEXTPATH(text)) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put on a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path."));
-        return;
-    }
-
-    if (SP_IS_FLOWTEXT(text)) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put flowtext on a path. Convert flowtext to text first."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put on a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path."));
         return;
     }
 
     if (SP_IS_RECT(shape)) {
         // rect is the only SPShape which is not <path> yet, and thus SVG forbids us from putting text on it
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first."));
         return;
     }
 
+    // if a flowed text is selected, convert it to a regular text object
+    if (SP_IS_FLOWTEXT(text)) {
+        Inkscape::XML::Node *repr = SP_FLOWTEXT(text)->getAsText();
+        Inkscape::XML::Node *parent = SP_OBJECT_REPR(text)->parent();
+        parent->appendChild(repr);
+
+        SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
+        sp_item_write_transform(new_item, repr, text->transform);
+        SP_OBJECT(new_item)->updateRepr();
+
+        Inkscape::GC::release(repr);
+        text->deleteObject(); // delete the orignal flowtext
+
+        sp_document_ensure_up_to_date(sp_desktop_document(desktop));
+
+        selection->clear();
+
+        text = new_item; // point to the new text
+    }
+
     Inkscape::Text::Layout const *layout = te_get_layout(text);
     Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin());
 
@@ -153,7 +170,8 @@ text_put_on_path()
     SP_OBJECT_REPR(text)->setAttribute("x", NULL);
     SP_OBJECT_REPR(text)->setAttribute("y", NULL);
 
-    sp_document_done(SP_DT_DOCUMENT(desktop));
+    sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
+                     _("Put text on path"));
     g_slist_free(text_reprs);
 }
 
@@ -162,10 +180,10 @@ text_remove_from_path()
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text on path</b> to remove it from path."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text on path</b> to remove it from path."));
         return;
     }
 
@@ -187,10 +205,11 @@ text_remove_from_path()
     }
 
     if (!did) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> in the selection."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> in the selection."));
     } else {
         selection->setList(g_slist_copy((GSList *) selection->itemList())); // reselect to update statusbar description
-        sp_document_done(SP_DT_DOCUMENT(desktop));
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
+                         _("Remove text from path"));
     }
 }
 
@@ -212,10 +231,10 @@ text_remove_all_kerns()
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
         return;
     }
 
@@ -235,9 +254,10 @@ text_remove_all_kerns()
     }
 
     if (!did) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
     } else {
-        sp_document_done(SP_DT_DOCUMENT(desktop));
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
+                         _("Remove manual kerns"));
     }
 }
 
@@ -248,21 +268,23 @@ text_flow_into_shape()
     if (!desktop)
         return;
 
-    SPDocument *doc = SP_DT_DOCUMENT (desktop);
+    SPDocument *doc = sp_desktop_document (desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
-    SPItem *text = text_in_selection(selection);
+    SPItem *text = text_or_flowtext_in_selection(selection);
     SPItem *shape = shape_in_selection(selection);
 
     if (!text || !shape || g_slist_length((GSList *) selection->itemList()) < 2) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text into frame."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text into frame."));
         return;
     }
 
-    // remove transform from text, but recursively scale text's fontsize by the expansion
-    SP_TEXT(text)->_adjustFontsizeRecursive(text, NR::expansion(SP_ITEM(text)->transform));
-    SP_OBJECT_REPR(text)->setAttribute("transform", NULL);
+    if (SP_IS_TEXT(text)) {
+      // remove transform from text, but recursively scale text's fontsize by the expansion
+      SP_TEXT(text)->_adjustFontsizeRecursive(text, NR::expansion(SP_ITEM(text)->transform));
+      SP_OBJECT_REPR(text)->setAttribute("transform", NULL);
+    }
 
     Inkscape::XML::Node *root_repr = sp_repr_new("svg:flowRoot");
     root_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
@@ -292,27 +314,42 @@ text_flow_into_shape()
         }
     }
 
-    Inkscape::XML::Node *para_repr = sp_repr_new("svg:flowPara");
-    root_repr->appendChild(para_repr);
-    object = doc->getObjectByRepr(para_repr);
-    g_return_if_fail(SP_IS_FLOWPARA(object));
+    if (SP_IS_TEXT(text)) { // flow from text, as string
+        Inkscape::XML::Node *para_repr = sp_repr_new("svg:flowPara");
+        root_repr->appendChild(para_repr);
+        object = doc->getObjectByRepr(para_repr);
+        g_return_if_fail(SP_IS_FLOWPARA(object));
 
-    Inkscape::Text::Layout const *layout = te_get_layout(text);
-    Glib::ustring text_ustring = sp_te_get_string_multiline(text, layout->begin(), layout->end());
+        Inkscape::Text::Layout const *layout = te_get_layout(text);
+        Glib::ustring text_ustring = sp_te_get_string_multiline(text, layout->begin(), layout->end());
+
+        Inkscape::XML::Node *text_repr = sp_repr_new_text(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas!
+        para_repr->appendChild(text_repr);
 
-    Inkscape::XML::Node *text_repr = sp_repr_new_text(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas!
-    para_repr->appendChild(text_repr);
+        Inkscape::GC::release(para_repr);
+        Inkscape::GC::release(text_repr);
+
+    } else { // reflow an already flowed text, preserving paras
+        for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) {
+            if (SP_IS_FLOWPARA(o)) {
+                Inkscape::XML::Node *para_repr = SP_OBJECT_REPR(o)->duplicate();
+                root_repr->appendChild(para_repr);
+                object = doc->getObjectByRepr(para_repr);
+                g_return_if_fail(SP_IS_FLOWPARA(object));
+                Inkscape::GC::release(para_repr);
+            }
+        }
+    }
 
     SP_OBJECT(text)->deleteObject (true);
 
-    sp_document_done(doc);
+    sp_document_done(doc, SP_VERB_CONTEXT_TEXT,
+                     _("Flow text into shape"));
 
-    SP_DT_SELECTION(desktop)->set(SP_ITEM(root_object));
+    sp_desktop_selection(desktop)->set(SP_ITEM(root_object));
 
     Inkscape::GC::release(root_repr);
     Inkscape::GC::release(region_repr);
-    Inkscape::GC::release(para_repr);
-    Inkscape::GC::release(text_repr);
 }
 
 void
@@ -322,13 +359,13 @@ text_unflow ()
     if (!desktop)
         return;
 
-    SPDocument *doc = SP_DT_DOCUMENT (desktop);
+    SPDocument *doc = sp_desktop_document (desktop);
 
-    Inkscape::Selection *selection = SP_DT_SELECTION(desktop);
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
 
     if (!flowtext_in_selection(selection) || g_slist_length((GSList *) selection->itemList()) < 1) {
-        SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it."));
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it."));
         return;
     }
 
@@ -390,9 +427,65 @@ text_unflow ()
     g_slist_free (old_objs);
     g_slist_free (new_objs);
 
-    sp_document_done(doc);
+    sp_document_done(doc, SP_VERB_CONTEXT_TEXT, 
+                     _("Unflow flowed text"));
 }
 
+void
+flowtext_to_text()
+{
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+
+    Inkscape::Selection *selection = sp_desktop_selection(desktop);
+
+    if (selection->isEmpty()) {
+        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, 
+                                                 _("Select <b>flowed text(s)</b> to convert."));
+        return;
+    }
+
+    bool did = false;
+
+    GSList *reprs = NULL;
+    GSList *items = g_slist_copy((GSList *) selection->itemList());
+    for (; items != NULL; items = items->next) {
+        
+        SPItem *item = (SPItem *) items->data;
+
+        if (!SP_IS_FLOWTEXT(item))
+            continue;
+
+        did = true;
+
+        Inkscape::XML::Node *repr = SP_FLOWTEXT(item)->getAsText();
+        Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent();
+        parent->appendChild(repr);
+
+        SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
+        sp_item_write_transform(new_item, repr, item->transform);
+        SP_OBJECT(new_item)->updateRepr();
+    
+        Inkscape::GC::release(repr);
+        item->deleteObject();
+
+        reprs = g_slist_prepend(reprs, repr);
+    }
+
+    g_slist_free(items);
+
+    if (did) {
+        sp_document_done(sp_desktop_document(desktop), 
+                         SP_VERB_OBJECT_FLOWTEXT_TO_TEXT,
+                         _("Convert flowed text to text"));
+        selection->setReprList(reprs);        
+    } else {
+        sp_desktop_message_stack(desktop)->
+            flash(Inkscape::ERROR_MESSAGE,
+                  _("<b>No flowed text(s)</b> to convert in the selection."));
+    }
+
+    g_slist_free(reprs);
+}
 
 
 /*