905 lines
25 KiB
C++
905 lines
25 KiB
C++
/*
|
|
* 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::Comparable ()
|
|
{
|
|
widget = it.widget;
|
|
content = it.content;
|
|
}
|
|
|
|
Iterator::~Iterator()
|
|
{
|
|
}
|
|
|
|
bool
|
|
Iterator::equals( const Object *const other ) const
|
|
{
|
|
Iterator *const otherIt = const_cast< Iterator * >( dynamic_cast< const Iterator * >( other ) );
|
|
return
|
|
this == otherIt ||
|
|
(const_cast< Iterator * >( this )->getWidget() == otherIt->getWidget() && const_cast< Iterator * >( this )->compareTo(otherIt) == 0);
|
|
}
|
|
|
|
void
|
|
Iterator::intoStringBuffer(misc::StringBuffer *sb) const
|
|
{
|
|
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 (const_cast< Content * >( &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 (atStart = true, eit3 = (DeepIterator*)eit1->clone ();
|
|
(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 = std::min (x1, alloc.x);
|
|
x2 = std::max (x2, alloc.x + alloc.width);
|
|
y1 = std::min (y1, alloc.y);
|
|
y2 = std::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 () const
|
|
{
|
|
return new EmptyIterator (const_cast< 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()->getWidget()->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()->getWidget () == 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()->getWidget () == 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 () const
|
|
{
|
|
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()->getWidget()->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()->getWidget()->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() const
|
|
{
|
|
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
|