diff options
author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
commit | 429d5f88b94ff28416cbfc6420b6389fa284df97 (patch) | |
tree | fb6fdaf7731de1ef396f98b748c56f3149801c84 /dw/iterator.cc |
Import RTFL 0.1.1v0.1.1
Diffstat (limited to 'dw/iterator.cc')
-rw-r--r-- | dw/iterator.cc | 920 |
1 files changed, 920 insertions, 0 deletions
diff --git a/dw/iterator.cc b/dw/iterator.cc new file mode 100644 index 0000000..de934d0 --- /dev/null +++ b/dw/iterator.cc @@ -0,0 +1,920 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + + +#include "core.hh" +#include <limits.h> + +using namespace lout; + +namespace dw { +namespace core { + +// -------------- +// Iterator +// -------------- + +Iterator::Iterator(Widget *widget, Content::Type mask, bool atEnd) +{ + this->widget = widget; + this->mask = mask; +} + +Iterator::Iterator(Iterator &it): object::Comparable () +{ + widget = it.widget; + content = it.content; +} + +Iterator::~Iterator() +{ +} + +bool Iterator::equals (Object *other) +{ + Iterator *otherIt = (Iterator*)other; + return + this == otherIt || + (getWidget() == otherIt->getWidget() && compareTo(otherIt) == 0); +} + +void Iterator::intoStringBuffer(misc::StringBuffer *sb) +{ + sb->append ("{ widget = "); + //widget->intoStringBuffer (sb); + sb->appendPointer (widget); + sb->append (" ("); + sb->append (widget->getClassName()); + sb->append (")>"); + + sb->append (", mask = "); + Content::maskIntoStringBuffer (mask, sb); + + sb->append (", content = "); + Content::intoStringBuffer (&content, sb); + + sb->append (" }"); +} + +/** + * \brief Delete the iterator. + * + * The destructor is hidden, implementations may use optimizations for + * the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.) + */ +void Iterator::unref () +{ + delete this; +} + +/** + * \brief Scrolls the viewport, so that the region between \em it1 and + * \em it2 is seen, according to \em hpos and \em vpos. + * + * The parameters \em start and \em end have the same meaning as in + * dw::core::Iterator::getAllocation, \em start refers + * to \em it1, while \em end rerers to \em it2. + * + * If \em it1 and \em it2 point to the same location (see code), only + * \em it1 is regarded, and both belowstart and belowend refer to it. + */ +void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, + HPosition hpos, VPosition vpos) +{ + Allocation alloc1, alloc2, alloc; + int x1, x2, y1, y2; + DeepIterator *eit1, *eit2, *eit3; + int curStart, curEnd, cmp; + bool atStart; + + if (it1->equals(it2)) { + it1->getAllocation (start, end, &alloc); + it1->getWidget()->getLayout()->scrollTo (hpos, vpos, alloc.x, alloc.y, + alloc.width, + alloc.ascent + alloc.descent); + } else { + // First, determine the rectangle all iterators from it1 and it2 + // allocate, i.e. the smallest rectangle containing all allocations of + // these iterators. + eit1 = new DeepIterator (it1); + eit2 = new DeepIterator (it2); + + x1 = INT_MAX; + x2 = INT_MIN; + y1 = INT_MAX; + y2 = INT_MIN; + + for (eit3 = (DeepIterator*)eit1->clone (), atStart = true; + (cmp = eit3->compareTo (eit2)) <= 0; + eit3->next (), atStart = false) { + if (atStart) + curStart = start; + else + curStart = 0; + + if (cmp == 0) + curEnd = end; + else + curEnd = INT_MAX; + + eit3->getAllocation (curStart, curEnd, &alloc); + x1 = misc::min (x1, alloc.x); + x2 = misc::max (x2, alloc.x + alloc.width); + y1 = misc::min (y1, alloc.y); + y2 = misc::max (y2, alloc.y + alloc.ascent + alloc.descent); + } + + delete eit3; + delete eit2; + delete eit1; + + it1->getAllocation (start, INT_MAX, &alloc1); + it2->getAllocation (0, end, &alloc2); + + if (alloc1.x > alloc2.x) { + // + // This is due to a line break within the region. When the line is + // longer than the viewport, and the region is actually quite short, + // the user would not see anything of the region, as in this figure + // (with region marked as "#"): + // + // +----------+ ,-- alloc1 + // | | V + // | | ### ### + // ### ### | | + // ^ | | <-- viewport + // | +----------+ + // `-- alloc2 + // |----------------------------| + // width + // + // Therefore, we make the region smaller, so that the region will be + // displayed like this: + // + // ,-- alloc1 + // +----|-----+ + // | V | + // | ### ###| + // ### ### | | + // ^ | | <-- viewport + // `-- alloc2 +----------+ + // |----------| + // width + // + + /** \todo Changes in the viewport size, until the idle function is + * called, are not regarded. */ + + if (it1->getWidget()->getLayout()->getUsesViewport() && + x2 - x1 > it1->getWidget()->getLayout()->getWidthViewport()) { + x1 = x2 - it1->getWidget()->getLayout()->getWidthViewport(); + x2 = x1 + it1->getWidget()->getLayout()->getWidthViewport(); + } + } + + if (alloc1.y > alloc2.y) { + // This is similar to the case above, e.g. if the region ends in + // another table column. + if (it1->getWidget()->getLayout()->getUsesViewport() && + y2 - y1 > it1->getWidget()->getLayout()->getHeightViewport()) { + y1 = y2 - it1->getWidget()->getLayout()->getHeightViewport(); + y2 = y1 + it1->getWidget()->getLayout()->getHeightViewport(); + } + } + + it1->getWidget()->getLayout()->scrollTo (hpos, vpos, + x1, y1, x2 - x1, y2 - y1); + } +} + + +void Iterator::print () +{ + misc::StringBuffer sb; + intoStringBuffer (&sb); + printf ("%s", sb.getChars ()); +} + +// ------------------- +// EmptyIterator +// ------------------- + +EmptyIterator::EmptyIterator (Widget *widget, Content::Type mask, bool atEnd): + Iterator (widget, mask, atEnd) +{ + this->content.type = (atEnd ? Content::END : Content::START); +} + +EmptyIterator::EmptyIterator (EmptyIterator &it): Iterator (it) +{ +} + +object::Object *EmptyIterator::clone () +{ + return new EmptyIterator (*this); +} + +int EmptyIterator::compareTo (object::Comparable *other) +{ + EmptyIterator *otherIt = (EmptyIterator*)other; + + if (content.type == otherIt->content.type) + return 0; + else if (content.type == Content::START) + return -1; + else + return +1; +} + +bool EmptyIterator::next () +{ + content.type = Content::END; + return false; +} + +bool EmptyIterator::prev () +{ + content.type = Content::START; + return false; +} + +void EmptyIterator::highlight (int start, int end, HighlightLayer layer) +{ +} + +void EmptyIterator::unhighlight (int direction, HighlightLayer layer) +{ +} + +void EmptyIterator::getAllocation (int start, int end, Allocation *allocation) +{ +} + +// ------------------ +// TextIterator +// ------------------ + +TextIterator::TextIterator (Widget *widget, Content::Type mask, bool atEnd, + const char *text): Iterator (widget, mask, atEnd) +{ + this->content.type = (atEnd ? Content::END : Content::START); + this->text = (mask & Content::TEXT) ? text : NULL; +} + +TextIterator::TextIterator (TextIterator &it): Iterator (it) +{ + text = it.text; +} + +int TextIterator::compareTo (object::Comparable *other) +{ + TextIterator *otherIt = (TextIterator*)other; + + if (content.type == otherIt->content.type) + return 0; + + switch (content.type) { + case Content::START: + return -1; + + case Content::TEXT: + if (otherIt->content.type == Content::START) + return +1; + else + return -1; + + case Content::END: + return +1; + + default: + misc::assertNotReached(); + return 0; + } +} + +bool TextIterator::next () +{ + if (content.type == Content::START && text != NULL) { + content.type = Content::TEXT; + content.text = text; + return true; + } else { + content.type = Content::END; + return false; + } +} + +bool TextIterator::prev () +{ + if (content.type == Content::END && text != NULL) { + content.type = Content::TEXT; + content.text = text; + return true; + } else { + content.type = Content::START; + return false; + } +} + +void TextIterator::getAllocation (int start, int end, Allocation *allocation) +{ + // Return the allocation of the widget. + *allocation = *(getWidget()->getAllocation ()); +} + +// ------------------ +// DeepIterator +// ------------------ + +DeepIterator::Stack::~Stack () +{ + for (int i = 0; i < size (); i++) + get(i)->unref (); +} + +/* + * The following two methods are used by dw::core::DeepIterator::DeepIterator, + * when the passed dw::core::Iterator points to a widget. Since a + * dw::core::DeepIterator never returns a widget, the dw::core::Iterator has + * to be corrected, by searching for the next content downwards (within the + * widget pointed to), forwards, and backwards (in the traversed tree). + */ + +/* + * Search downwards. If fromEnd is true, start search at the end, + * otherwise at the beginning. + */ +Iterator *DeepIterator::searchDownward (Iterator *it, Content::Type mask, + bool fromEnd) +{ + Iterator *it2, *it3; + + //DEBUG_MSG (1, "%*smoving down (%swards) from %s\n", + // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); + + assert (it->getContent()->type & Content::ANY_WIDGET); + it2 = it->getContent()->widget->iterator (mask, fromEnd); + + if (it2 == NULL) { + // Moving downwards failed. + //DEBUG_MSG (1, "%*smoving down failed\n", indent, ""); + return NULL; + } + + while (fromEnd ? it2->prev () : it2->next ()) { + //DEBUG_MSG (1, "%*sexamining %s\n", + // indent, "", a_Dw_iterator_text (it2)); + + if (it2->getContent()->type & Content::ANY_WIDGET) { + // Another widget. Search in it downwards. + it3 = searchDownward (it2, mask, fromEnd); + if (it3 != NULL) { + it2->unref (); + return it3; + } + // Else continue in this widget. + } else { + // Success! + //DEBUG_MSG (1, "%*smoving down succeeded: %s\n", + // indent, "", a_Dw_iterator_text (it2)); + return it2; + } + } + + // Nothing found. + it2->unref (); + //DEBUG_MSG (1, "%*smoving down failed (nothing found)\n", indent, ""); + return NULL; +} + +/* + * Search sidewards. fromEnd specifies the direction, false means forwards, + * true means backwards. + */ +Iterator *DeepIterator::searchSideward (Iterator *it, Content::Type mask, + bool fromEnd) +{ + Iterator *it2, *it3; + + //DEBUG_MSG (1, "%*smoving %swards from %s\n", + // indent, "", from_end ? "back" : "for", a_Dw_iterator_text (it)); + + assert (it->getContent()->type & Content::ANY_WIDGET); + it2 = it->cloneIterator (); + + while (fromEnd ? it2->prev () : it2->next ()) { + if (it2->getContent()->type & Content::ANY_WIDGET) { + // Search downwards in this widget. + it3 = searchDownward (it2, mask, fromEnd); + if (it3 != NULL) { + it2->unref (); + //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it3)); + return it3; + } + // Else continue in this widget. + } else { + // Success! + // DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it2)); + return it2; + } + } + + /* Nothing found, go upwards in the tree (if possible). */ + it2->unref (); + Widget *respParent = getRespectiveParent (it->getWidget(), it->getMask()); + if (respParent) { + it2 = respParent->iterator (mask, false); + while (true) { + if (!it2->next ()) + misc::assertNotReached (); + + if (it2->getContent()->type & Content::ANY_WIDGET && + it2->getContent()->widget == it->getWidget ()) { + it3 = searchSideward (it2, mask, fromEnd); + it2->unref (); + //DEBUG_MSG (1, "%*smoving %swards succeeded: %s\n", + // indent, "", from_end ? "back" : "for", + // a_Dw_iterator_text (it3)); + return it3; + } + } + } + + // Nothing found at all. + // DEBUG_MSG (1, "%*smoving %swards failed (nothing found)\n", + // indent, "", from_end ? "back" : "for"); + return NULL; +} + +Widget *DeepIterator::getRespectiveParent (Widget *widget, Content::Type mask) +{ + // Return, depending on which is requested indirectly (follow + // references or containments) the parent (container) or the + // generator. At this point, the type of the parent/generator is + // not known (since the parent/generator is not known), so we have + // to examine the mask. This is the reason why only one of + // WIDGET_OOF_CONT and WIDGET_OOF_REF is allowed. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGenerator() : widget->getParent(); +} + +int DeepIterator::getRespectiveLevel (Widget *widget, Content::Type mask) +{ + // Similar to getRespectiveParent. + + return (mask & Content::WIDGET_OOF_REF) ? + widget->getGeneratorLevel() : widget->getLevel(); +} + +/** + * \brief Create a new deep iterator from an existing dw::core::Iterator. + * + * The content of the return value will be the content of \em it. If within + * the widget tree, there is no non-widget content, the resulting deep + * iterator is empty (denoted by dw::core::DeepIterator::stack == NULL). + * + * Notes: + * + * <ol> + * <li> The mask of \em i" must include DW_CONTENT_WIDGET, but + * dw::core::DeepIterator::next will never return widgets. + * </ol> + */ +DeepIterator::DeepIterator (Iterator *it) +{ + //printf ("Starting creating DeepIterator %p ...\n", this); + //printf ("Initial iterator: "); + //it->print (); + //printf ("\n"); + + // Widgets out of flow are either followed widtin containers, or + // generators. Both (and also nothing at all) is not allowed. See + // also comment in getRespectiveParent. + int oofMask = + it->getMask() & (Content::WIDGET_OOF_CONT | Content::WIDGET_OOF_REF); + assert (oofMask == Content::WIDGET_OOF_CONT || + oofMask == Content::WIDGET_OOF_REF); + + //DEBUG_MSG (1, "a_Dw_ext_iterator_new: %s\n", a_Dw_iterator_text (it)); + + // Clone input iterator, so the iterator passed as parameter + // remains untouched. + it = it->cloneIterator (); + this->mask = it->getMask (); + + hasContents = true; + + // If it points to a widget, find a near non-widget content, + // since an DeepIterator should never return widgets. + if (it->getContent()->type & Content::ANY_WIDGET) { + Iterator *it2; + + // The second argument of searchDownward is actually a matter of + // taste :-) + if ((it2 = searchDownward (it, mask, false)) || + (it2 = searchSideward (it, mask, false)) || + (it2 = searchSideward (it, mask, true))) { + it->unref (); + it = it2; + } else { + // This may happen, when a page does not contain any non-widget + // content. + //DEBUG_MSG (1, "a_Dw_ext_iterator_new got totally helpless!\n"); + it->unref (); + hasContents = false; + } + } + + //DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it)); + + if (hasContents) { + // If this widget has parents, we must construct appropriate iterators. + // + // \todo There may be a faster way instead of iterating through the + // parent widgets. + + //printf ("Starting with: "); + //it->print (); + //printf ("\n"); + + // Construct the iterators. + int thisLevel = getRespectiveLevel (it->getWidget()), level; + Widget *w; + for (w = it->getWidget (), level = thisLevel; + getRespectiveParent (w) != NULL; + w = getRespectiveParent (w), level--) { + Iterator *it = getRespectiveParent(w)->iterator (mask, false); + + //printf (" parent: %s %p\n", w->getClassName (), w); + + stack.put (it, level - 1); + while (true) { + //printf (" "); + //it->print (); + //printf ("\n"); + + bool hasNext = it->next(); + assert (hasNext); + + if (it->getContent()->type & Content::ANY_WIDGET && + it->getContent()->widget == w) + break; + } + + //printf (" %d: ", level - 1); + //it->print (); + //printf ("\n"); + } + + stack.put (it, thisLevel); + content = *(it->getContent()); + } + + //printf ("... done creating DeepIterator %p.\n", this); +} + + +DeepIterator::~DeepIterator () +{ + //printf ("Deleting DeepIterator %p ...\n", this); +} + +object::Object *DeepIterator::clone () +{ + DeepIterator *it = new DeepIterator (); + + for (int i = 0; i < stack.size (); i++) + it->stack.put (stack.get(i)->cloneIterator (), i); + + it->mask = mask; + it->content = content; + it->hasContents = hasContents; + + return it; +} + +int DeepIterator::compareTo (object::Comparable *other) +{ + DeepIterator *otherDeepIterator = (DeepIterator*)other; + + //printf ("Compare: %s\n", stack.toString ()); + //printf (" to: %s\n", otherDeepIterator->stack.toString ()); + + // Search the highest level, where the widgets are the same. + int level = 0; + + // The Comparable interface does not define "uncomparable". Deep + // iterators are only comparable if they belong to the same widget + // tree, so have the same widget at the bottom at the + // stack. If this is not the case, we abort. + + assert (stack.size() > 0); + assert (otherDeepIterator->stack.size() > 0); + + //printf ("Equal? The %s %p (of %p) and the %s %p (of %p)?\n", + // stack.get(0)->getWidget()->getClassName(), + // stack.get(0)->getWidget(), this, + // otherDeepIterator->stack.get(0)->getWidget()->getClassName(), + // otherDeepIterator->stack.get(0)->getWidget(), otherDeepIterator); + + assert (stack.get(0)->getWidget() + == otherDeepIterator->stack.get(level)->getWidget()); + + while (stack.get(level)->getWidget () + == otherDeepIterator->stack.get(level)->getWidget ()) { + if (level == stack.size() - 1 || + level == otherDeepIterator->stack.size() - 1) + break; + level++; + } + + //printf (" => level = %d (temorally)\n", level); + + while (stack.get(level)->getWidget () + != otherDeepIterator->stack.get(level)->getWidget ()) + level--; + + //printf (" => level = %d (finally)\n", level); + + return stack.get(level)->compareTo (otherDeepIterator->stack.get(level)); +} + +DeepIterator *DeepIterator::createVariant(Iterator *it) +{ + /** \todo Not yet implemented, and actually not yet needed very much. */ + return new DeepIterator (it); +} + +bool DeepIterator::isEmpty () { + return !hasContents; +} + +/** + * \brief Move iterator forward and store content it. + * + * Returns true on success. + */ +bool DeepIterator::next () +{ + Iterator *it = stack.getTop (); + + if (it->next ()) { + if (it->getContent()->type & Content::ANY_WIDGET) { + // Widget: new iterator on stack, to search in this widget. + stack.push (it->getContent()->widget->iterator (mask, false)); + return next (); + } else { + // Simply return the content of the iterartor. + content = *(it->getContent ()); + return true; + } + } else { + // No more data in the top-most widget. + if (stack.size () > 1) { + // Pop iterator from stack, and move to next item in the old one. + stack.pop (); + return next (); + } else { + // Stack is empty. + content.type = Content::END; + return false; + } + } +} + +/** + * \brief Move iterator backward and store content it. + * + * Returns true on success. + */ +bool DeepIterator::prev () +{ + Iterator *it = stack.getTop (); + + if (it->prev ()) { + if (it->getContent()->type & Content::ANY_WIDGET) { + // Widget: new iterator on stack, to search in this widget. + stack.push (it->getContent()->widget->iterator (mask, true)); + return prev (); + } else { + // Simply return the content of the iterartor. + content = *(it->getContent ()); + return true; + } + } else { + // No more data in the top-most widget. + if (stack.size () > 1) { + // Pop iterator from stack, and move to next item in the old one. + stack.pop (); + return prev (); + } else { + // Stack is empty. + content.type = Content::START; + return false; + } + } +} + +// ----------------- +// CharIterator +// ----------------- + +CharIterator::CharIterator () +{ + it = NULL; +} + +/** + * \brief ... + * + * If followReferences is true, only the reference are followed, when + * the container and generator for a widget is different. If false, + * only the container is followed. + */ +CharIterator::CharIterator (Widget *widget, bool followReferences) +{ + Iterator *i = + widget->iterator (Content::maskForSelection (followReferences), false); + it = new DeepIterator (i); + i->unref (); + ch = START; +} + +CharIterator::~CharIterator () +{ + if (it) + delete it; +} + +object::Object *CharIterator::clone() +{ + CharIterator *cloned = new CharIterator (); + cloned->it = it->cloneDeepIterator (); + cloned->ch = ch; + cloned->pos = pos; + return cloned; +} + +int CharIterator::compareTo(object::Comparable *other) +{ + CharIterator *otherIt = (CharIterator*)other; + int c = it->compareTo(otherIt->it); + if (c != 0) + return c; + else + return pos - otherIt->pos; +} + +bool CharIterator::next () +{ + if (ch == START || it->getContent()->type == Content::BREAK || + (it->getContent()->type == Content::TEXT && + it->getContent()->text[pos] == 0)) { + if (it->next()) { + if (it->getContent()->type == Content::BREAK) + ch = '\n'; + else { // if (it->getContent()->type == Content::TEXT) + pos = 0; + ch = it->getContent()->text[pos]; + if (ch == 0) + // should not happen, actually + return next (); + } + return true; + } + else { + ch = END; + return false; + } + } else if (ch == END) + return false; + else { + // at this point, it->getContent()->type == Content::TEXT + pos++; + ch = it->getContent()->text[pos]; + if (ch == 0) { + if (it->getContent()->space) { + ch = ' '; + } else { + return next (); + } + } + + return true; + } +} + +bool CharIterator::prev () +{ + if (ch == END || it->getContent()->type == Content::BREAK || + (it->getContent()->type == Content::TEXT && pos == 0)) { + if (it->prev()) { + if (it->getContent()->type == Content::BREAK) + ch = '\n'; + else { // if (it->getContent()->type == Content::TEXT) + if (it->getContent()->text[0] == 0) + return prev (); + else { + pos = strlen (it->getContent()->text); + if (it->getContent()->space) { + ch = ' '; + } else { + pos--; + ch = it->getContent()->text[pos]; + } + } + } + return true; + } + else { + ch = START; + return false; + } + } else if (ch == START) + return false; + else { + // at this point, it->getContent()->type == Content::TEXT + pos--; + ch = it->getContent()->text[pos]; + return true; + } +} + +void CharIterator::highlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer) +{ + if (it2->getChar () == CharIterator::END) + it2->prev (); + + if (it1->it->compareTo (it2->it) == 0) + // Only one content => highlight part of it. + it1->it->highlight (it1->pos, it2->pos, layer); + else { + DeepIterator *it = it1->it->cloneDeepIterator (); + int c; + bool start; + for (start = true; + (c = it->compareTo (it2->it)) <= 0; + it->next (), start = false) { + int endOfWord = + it->getContent()->type == Content::TEXT ? + strlen (it->getContent()->text) : 1; + if (start) // first iteration + it->highlight (it1->pos, endOfWord, layer); + else if (c == 0) // last iteration + it->highlight (0, it2->pos, layer); + else + it->highlight (0, endOfWord, layer); + } + delete it; + } +} + +void CharIterator::unhighlight (CharIterator *it1, CharIterator *it2, + HighlightLayer layer) +{ + if (it1->it->compareTo (it2->it) == 0) + // Only one content => unhighlight it (only for efficiency). + it1->it->unhighlight (0, layer); + else { + DeepIterator *it = it1->it->cloneDeepIterator (); + for (; it->compareTo (it2->it) <= 0; it->next ()) + it->unhighlight (-1, layer); + delete it; + } +} + +} // namespace core +} // namespace dw |