Mercurial > dillo_port1.3
view dw/iterator.cc @ 1742:6ff5d5aeb9a5
inline setNonCssHint()
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> |
---|---|
date | Thu, 14 Oct 2010 09:04:52 +0200 |
parents | 7aca003dbfca |
children |
line wrap: on
line source
/* * Dillo Widget * * 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. * * 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::Object (), misc::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); } /** * \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); } } // ------------------- // 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 (misc::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 (misc::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::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::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::WIDGET); it2 = it->cloneIterator (); while (fromEnd ? it2->prev () : it2->next ()) { if (it2->getContent()->type == Content::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 (); if (it->getWidget()->getParent ()) { it2 = it->getWidget()->getParent()->iterator (mask, false); while (true) { if (!it2->next ()) misc::assertNotReached (); if (it2->getContent()->type == Content::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; } /** * \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) { //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::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. // Construct the iterators. int thisLevel = it->getWidget()->getLevel (), level; Widget *w; for (w = it->getWidget (), level = thisLevel; w->getParent() != NULL; w = w->getParent (), level--) { Iterator *it = w->getParent()->iterator (mask, false); stack.put (it, level - 1); while (true) { bool hasNext = it->next(); assert (hasNext); if (it->getContent()->type == Content::WIDGET && it->getContent()->widget == w) break; } } stack.put (it, thisLevel); content = *(it->getContent()); } } DeepIterator::~DeepIterator () { } 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 (misc::Comparable *other) { DeepIterator *otherDeepIterator = (DeepIterator*)other; // Search the highest level, where the widgets are the same. int level = 0; while (stack.get(level)->getWidget () == otherDeepIterator->stack.get(level)->getWidget ()) { if (level == stack.size() - 1 || level == otherDeepIterator->stack.size() - 1) break; level++; } while (stack.get(level)->getWidget () != otherDeepIterator->stack.get(level)->getWidget ()) 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::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::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; } CharIterator::CharIterator (Widget *widget) { Iterator *i = widget->iterator (Content::SELECTION_CONTENT, 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(misc::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 dw } // namespace core