From 950ccc6c98dbab31603a66a04b6101ab5a4752fc Mon Sep 17 00:00:00 2001 From: buliabyak <> Date: Mon, 21 Dec 2009 01:23:37 -0400 Subject: [PATCH] utilities and UI support for identifying truncated flowtext and text-on-path --- src/libnrtype/Layout-TNG-Input.cpp | 69 ++++++++++++++++++++++++++++++ src/libnrtype/Layout-TNG.h | 2 + src/sp-flowtext.cpp | 10 ++++- src/sp-text.cpp | 10 ++++- src/text-context.cpp | 25 +++++++++-- src/text-editing.cpp | 31 ++++++++++++++ src/text-editing.h | 4 ++ 7 files changed, 144 insertions(+), 7 deletions(-) diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 2ee0051a4..33371ab10 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -16,8 +16,11 @@ #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 diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 8cd26edef..05b5103fc 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -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. diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index 6af2f7169..e4259e52c 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -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("Flowed text (%d character)", "Flowed text (%d characters)", nChars), nChars); + return g_strdup_printf(ngettext("Flowed text (%d character%s)", "Flowed text (%d characters%s)", nChars), nChars, trunc); else - return g_strdup_printf(ngettext("Linked flowed text (%d character)", "Linked flowed text (%d characters)", nChars), nChars); + return g_strdup_printf(ngettext("Linked flowed text (%d character%s)", "Linked flowed text (%d characters%s)", nChars), nChars, trunc); } static NRArenaItem * diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 423922b80..b45f8cbb6 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -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(_("Text on path (%s, %s)"), n, xs->str) - : g_strdup_printf(_("Text (%s, %s)"), n, xs->str) ); + ? g_strdup_printf(_("Text on path%s (%s, %s)"), trunc, n, xs->str) + : g_strdup_printf(_("Text%s (%s, %s)"), trunc, n, xs->str) ); g_free(n); return ret; } diff --git a/src/text-context.cpp b/src/text-context.cpp index e6f4f083b..fc28dc8e4 100644 --- a/src/text-context.cpp +++ b/src/text-context.cpp @@ -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); Enter to start new paragraph."), nChars); + + SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit flowed text (%d characters%s); Enter to start new paragraph."), nChars, trunc); } else { - SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters); Enter to start new line."), nChars); + SP_EVENT_CONTEXT(tc)->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Type or edit text (%d characters%s); Enter to start new line."), nChars, trunc); } } else { diff --git a/src/text-editing.cpp b/src/text-editing.cpp index 2bdee4c10..e93ebdffa 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -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++ diff --git a/src/text-editing.h b/src/text-editing.h index 83ddae77f..7e845dbc9 100644 --- a/src/text-editing.h +++ b/src/text-editing.h @@ -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 -- 2.30.2