Code

utilities and UI support for identifying truncated flowtext and text-on-path
authorbuliabyak <>
Mon, 21 Dec 2009 05:23:37 +0000 (01:23 -0400)
committerbuliabyak <>
Mon, 21 Dec 2009 05:23:37 +0000 (01:23 -0400)
src/libnrtype/Layout-TNG-Input.cpp
src/libnrtype/Layout-TNG.h
src/sp-flowtext.cpp
src/sp-text.cpp
src/text-context.cpp
src/text-editing.cpp
src/text-editing.h

index 2ee0051a4b80fd544a24670bd16db3d32cd6ac5f..33371ab1008928e1ff4751cc7054e2f224995fe8 100644 (file)
 #include "style.h"
 #include "svg/svg-length.h"
 #include "sp-object.h"
+#include "sp-string.h"
 #include "FontFactory.h"
 
+#include "text-editing.h" // for inputTruncated()
+
 namespace Inkscape {
 namespace Text {
 
@@ -321,5 +324,71 @@ Layout::InputStreamTextSource::~InputStreamTextSource()
     sp_style_unref(style);
 }
 
+bool
+Layout::inputTruncated() const
+{
+       if (!inputExists())
+               return false;
+
+       // Find out the SPObject to which the last visible character corresponds:
+       Layout::iterator last = end();
+       if (last == begin()) {
+           // FIXME: this returns a wrong "not truncated" when a flowtext is entirely
+           // truncated, so there are no visible characters. But how can I find out the
+           // originator SPObject without having anything to do getSourceOfCharacter
+           // from?
+               return false; 
+       }
+       last.prevCharacter();
+       void *source;
+       Glib::ustring::iterator offset;
+       getSourceOfCharacter(last, &source, &offset);
+       SPObject *obj = SP_OBJECT(source);
+
+       // if that is SPString, see if it has further characters beyond the last visible
+       if (obj && SP_IS_STRING(obj)) {
+               Glib::ustring::iterator offset_next = offset;
+               offset_next ++;
+               if (offset_next != SP_STRING(obj)->string.end()) {
+                       // truncated: source SPString has next char
+                       return true;
+               }
+       }
+
+       // otherwise, see if the SPObject at end() or any of its text-tree ancestors
+       // (excluding top-level SPText or SPFlowText) have a text-tree next sibling with
+       // visible text
+       if (obj) {
+               for (SPObject *ascend = obj; 
+                                ascend && (is_part_of_text_subtree (ascend) && !is_top_level_text_object(ascend));
+                                ascend = SP_OBJECT_PARENT(ascend)) {
+                       if (SP_OBJECT_NEXT(ascend)) {
+                               SPObject *next = SP_OBJECT_NEXT(ascend);
+                               if (next && is_part_of_text_subtree(next) && has_visible_text(next)) {
+                                       // truncated: source text object has next text sibling
+                                       return true;
+                               }
+                       }
+               }
+       }
+
+       // the above works for flowed text, but not for text on path.
+       // so now, we also check if the last of the _characters, if coming from a TEXT_SOURCE,
+       // has in_glyph different from -1
+       unsigned last_char = _characters.size() - 1;
+       unsigned span_index = _characters[last_char].in_span;
+       Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
+
+       if (_input_stream[_spans[span_index].in_input_stream_item]->Type() == TEXT_SOURCE) {
+               if (_characters[last_char].in_glyph == -1) {
+                       //truncated: last char has no glyph
+                       return true;
+               }
+       }
+
+       // not truncated
+       return false;
+}
+
 }//namespace Text
 }//namespace Inkscape
index 8cd26edefd7aa0e063e49c9ba4a484a307652348..05b5103fcc1407442063b9e3db7f7eba023c86c5 100644 (file)
@@ -212,6 +212,8 @@ public:
     bool inputExists() const
         {return !_input_stream.empty();}
 
+    bool inputTruncated() const;
+
     /** adds a new piece of text to the end of the current list of text to
     be processed. This method can only add text of a consistent style.
     To add lots of different styles, call it lots of times.
index 6af2f7169eaf3c9eb34ed451470c0d9ee4fa0aa1..e4259e52ca9bb0f3adf9a8cc63f5d26f49975147 100644 (file)
@@ -372,10 +372,16 @@ static gchar *sp_flowtext_description(SPItem *item)
 {
     Inkscape::Text::Layout const &layout = SP_FLOWTEXT(item)->layout;
     int const nChars = layout.iteratorToCharIndex(layout.end());
+
+    char *trunc = "";
+    if (layout.inputTruncated()) {
+        trunc = _(" [truncated]");
+    }
+
     if (SP_FLOWTEXT(item)->has_internal_frame())
-        return g_strdup_printf(ngettext("<b>Flowed text</b> (%d character)", "<b>Flowed text</b> (%d characters)", nChars), nChars);
+        return g_strdup_printf(ngettext("<b>Flowed text</b> (%d character%s)", "<b>Flowed text</b> (%d characters%s)", nChars), nChars, trunc);
     else
-        return g_strdup_printf(ngettext("<b>Linked flowed text</b> (%d character)", "<b>Linked flowed text</b> (%d characters)", nChars), nChars);
+        return g_strdup_printf(ngettext("<b>Linked flowed text</b> (%d character%s)", "<b>Linked flowed text</b> (%d characters%s)", nChars), nChars, trunc);
 }
 
 static NRArenaItem *
index 423922b80c991a11207973b3ab800b1185a18ee3..b45f8cbb607d3a67d6aac7f7f39bc2384364a0d1 100644 (file)
@@ -421,9 +421,15 @@ sp_text_description(SPItem *item)
 
     GString *xs = SP_PX_TO_METRIC_STRING(style->font_size.computed, sp_desktop_namedview(SP_ACTIVE_DESKTOP)->getDefaultMetric());
 
+    char *trunc = "";
+    Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item);
+    if (layout && layout->inputTruncated()) {
+        trunc = _(" [truncated]");
+    }
+
     char *ret = ( SP_IS_TEXT_TEXTPATH(item)
-                  ? g_strdup_printf(_("<b>Text on path</b> (%s, %s)"), n, xs->str)
-                  : g_strdup_printf(_("<b>Text</b> (%s, %s)"), n, xs->str) );
+                  ? g_strdup_printf(_("<b>Text on path</b>%s (%s, %s)"), trunc, n, xs->str)
+                  : g_strdup_printf(_("<b>Text</b>%s (%s, %s)"), trunc, n, xs->str) );
     g_free(n);
     return ret;
 }
index e6f4f083b2945b006b0496707f328f5204255aac..fc28dc8e473bd66244e1e294b5ed31447e5cb193 100644 (file)
@@ -425,11 +425,18 @@ sp_text_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEve
             // find out item under mouse, disregarding groups
             item_ungrouped = desktop->item_at_point(Geom::Point(event->button.x, event->button.y), TRUE);
             if (SP_IS_TEXT(item_ungrouped) || SP_IS_FLOWTEXT(item_ungrouped)) {
-                sp_canvas_item_show(tc->indicator);
+
+                Inkscape::Text::Layout const *layout = te_get_layout(item_ungrouped);
+                if (layout->inputTruncated()) {
+                    SP_CTRLRECT(tc->indicator)->setColor(0xff0000ff, false, 0);
+                } else {
+                    SP_CTRLRECT(tc->indicator)->setColor(0x0000ff7f, false, 0);
+                }
                 Geom::OptRect ibbox = sp_item_bbox_desktop(item_ungrouped);
                 if (ibbox) {
                     SP_CTRLRECT(tc->indicator)->setRectangle(*ibbox);
                 }
+                sp_canvas_item_show(tc->indicator);
 
                 event_context->cursor_shape = cursor_text_insert_xpm;
                 event_context->hot_x = 7;
@@ -1590,18 +1597,30 @@ sp_text_context_update_cursor(SPTextContext *tc,  bool scroll_to_see)
 
         Inkscape::Text::Layout const *layout = te_get_layout(tc->text);
         int const nChars = layout->iteratorToCharIndex(layout->end());
+        char *trunc = "";
+        bool truncated = false;
+        if (layout->inputTruncated()) {
+            truncated = true;
+            trunc = _(" [truncated]");
+        }
         if (SP_IS_FLOWTEXT(tc->text)) {
             SPItem *frame = SP_FLOWTEXT(tc->text)->get_frame (NULL); // first frame only
             if (frame) {
+                if (truncated) {
+                    SP_CTRLRECT(tc->frame)->setColor(0xff0000ff, false, 0);
+                } else {
+                    SP_CTRLRECT(tc->frame)->setColor(0x0000ff7f, false, 0);
+                }
                 sp_canvas_item_show(tc->frame);
                 Geom::OptRect frame_bbox = sp_item_bbox_desktop(frame);
                 if (frame_bbox) {
                     SP_CTRLRECT(tc->frame)->setRectangle(*frame_bbox);
                 }
             }
-            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit flowed text (%d characters); <b>Enter</b> to start new paragraph."), nChars);
+
+            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit flowed text (%d characters%s); <b>Enter</b> to start new paragraph."), nChars, trunc);
         } else {
-            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters); <b>Enter</b> to start new line."), nChars);
+            SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters%s); <b>Enter</b> to start new line."), nChars, trunc);
         }
 
     } else {
index 2bdee4c102a643ff8302d7142fd8c8113a35cadc..e93ebdffada702e11ceb028c2c4a4f9c91b96bb9 100644 (file)
@@ -1843,6 +1843,37 @@ void sp_te_apply_style(SPItem *text, Inkscape::Text::Layout::iterator const &sta
     text->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
 }
 
+bool is_part_of_text_subtree (SPObject *obj)
+{
+    return (SP_IS_TSPAN(obj) 
+            || SP_IS_TEXT(obj) 
+            || SP_IS_FLOWTEXT(obj)
+            || SP_IS_FLOWTSPAN(obj)
+            || SP_IS_FLOWDIV(obj)
+            || SP_IS_FLOWPARA(obj)
+            || SP_IS_FLOWLINE(obj)
+            || SP_IS_FLOWREGIONBREAK(obj));
+}
+
+bool is_top_level_text_object (SPObject *obj)
+{
+    return (SP_IS_TEXT(obj) 
+            || SP_IS_FLOWTEXT(obj));
+}
+
+bool has_visible_text (SPObject *obj)
+{
+    if (SP_IS_STRING(obj) && !SP_STRING(obj)->string.empty()) 
+        return true; // maybe we should also check that it's not all whitespace?
+
+    for (SPObject const *child = obj->firstChild() ; child ; child = SP_OBJECT_NEXT(child)) {
+        if (has_visible_text((SPObject *) child))
+            return true;
+    }
+
+    return false;
+}
+
 /*
   Local Variables:
   mode:c++
index 83ddae77fc6e74781c8d9c78a79491e30170704e..7e845dbc905dff38e9d6d1b93ee341de6f727261 100644 (file)
@@ -59,4 +59,8 @@ void sp_te_adjust_tspan_letterspacing_screen(SPItem *text, Inkscape::Text::Layou
 void sp_te_adjust_linespacing_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble by);
 void sp_te_apply_style(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPCSSAttr const *css);
 
+bool is_part_of_text_subtree (SPObject *obj);
+bool is_top_level_text_object (SPObject *obj);
+bool has_visible_text (SPObject *obj);
+
 #endif