/* * Dillo Widget * * Copyright 2013-2014 Sebastian Geerken * * 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 . */ #include "ooffloatsmgr.hh" #include "oofawarewidget.hh" #include "../lout/debug.hh" #include using namespace lout::object; using namespace lout::container::typed; using namespace lout::misc; using namespace dw::core; using namespace dw::core::style; namespace dw { namespace oof { OOFFloatsMgr::WidgetInfo::WidgetInfo (OOFFloatsMgr *oofm, Widget *widget) { this->oofm = oofm; this->widget = widget; } // ---------------------------------------------------------------------- OOFFloatsMgr::Float::Float (OOFFloatsMgr *oofm, Widget *widget, OOFAwareWidget *generatingBlock, int externalIndex) : WidgetInfo (oofm, widget) { this->generator = generatingBlock; this->externalIndex = externalIndex; yReq = yReal = size.width = size.ascent = size.descent = 0; dirty = true; index = -1; // Sometimes a float with widget = NULL is created as a key; this // is not interesting for RTFL. if (widget) { DBG_OBJ_SET_PTR_O (widget, ".generatingBlock", generatingBlock); DBG_OBJ_SET_NUM_O (widget, ".externalIndex", externalIndex); DBG_OBJ_SET_NUM_O (widget, ".yReq", yReq); DBG_OBJ_SET_NUM_O (widget, ".yReal", yReal); DBG_OBJ_SET_NUM_O (widget, ".size.width", size.width); DBG_OBJ_SET_NUM_O (widget, ".size.ascent", size.ascent); DBG_OBJ_SET_NUM_O (widget, ".size.descent", size.descent); DBG_OBJ_SET_BOOL_O (widget, ".dirty", dirty); } } void OOFFloatsMgr::Float::intoStringBuffer(StringBuffer *sb) { sb->append ("{ widget = "); sb->appendPointer (getWidget ()); if (getWidget ()) { sb->append (" ("); sb->append (getWidget()->getClassName ()); sb->append (")"); } sb->append (", index = "); sb->appendInt (index); sb->append (", sideSpanningIndex = "); sb->appendInt (sideSpanningIndex); sb->append (", generator = "); sb->appendPointer (generator); sb->append (", yReq = "); sb->appendInt (yReq); sb->append (", yReal = "); sb->appendInt (yReal); sb->append (", size = { "); sb->appendInt (size.width); sb->append (" * "); sb->appendInt (size.ascent); sb->append (" + "); sb->appendInt (size.descent); sb->append (" }, dirty = "); sb->appendBool (dirty); sb->append (", sizeChangedSinceLastAllocation = "); sb->append (" }"); } /** * `y` is given relative to the container. */ bool OOFFloatsMgr::Float::covers (int y, int h) { DBG_OBJ_ENTER_O ("border", 0, getOOFFloatsMgr (), "covers", "%d, %d [vloat: %p]", y, h, getWidget ()); getOOFFloatsMgr()->ensureFloatSize (this); bool b = yReal + size.ascent + size.descent > y && yReal < y + h; DBG_OBJ_LEAVE_VAL_O (getOOFFloatsMgr (), "%s", b ? "true" : "false"); return b; } int OOFFloatsMgr::Float::ComparePosition::compare (Object *o1, Object *o2) { return ((Float*)o1)->yReal - ((Float*)o2)->yReal; } int OOFFloatsMgr::Float::CompareSideSpanningIndex::compare (Object *o1, Object *o2) { return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex; } int OOFFloatsMgr::Float::CompareGBAndExtIndex::compare (Object *o1, Object *o2) { Float *f1 = (Float*)o1, *f2 = (Float*)o2; int r = -123; // Compiler happiness: GCC 4.7 does not handle this?; DBG_OBJ_ENTER_O ("border", 1, oofm, "CompareGBAndExtIndex::compare", "#%d -> %p/%d, #%d -> %p/#%d", f1->index, f1->generator, f1->externalIndex, f2->index, f2->generator, f2->externalIndex); if (f1->generator == f2->generator) { r = f1->externalIndex - f2->externalIndex; DBG_OBJ_MSGF_O ("border", 2, oofm, "(a) generating blocks equal => %d - %d = %d", f1->externalIndex, f2->externalIndex, r); } else { TBInfo *t1 = oofm->getOOFAwareWidget (f1->generator), *t2 = oofm->getOOFAwareWidget (f2->generator); bool rdef = false; for (TBInfo *t = t1; t != NULL; t = t->parent) if (t->parent == t2) { rdef = true; r = t->parentExtIndex - f2->externalIndex; DBG_OBJ_MSGF_O ("border", 2, oofm, "(b) %p is an achestor of %p; direct child is " "%p (%d) => %d - %d = %d\n", t2->getOOFAwareWidget (), t1->getOOFAwareWidget (), t->getOOFAwareWidget (), t->parentExtIndex, t->parentExtIndex, f2->externalIndex, r); } for (TBInfo *t = t2; !rdef && t != NULL; t = t->parent) if (t->parent == t1) { r = f1->externalIndex - t->parentExtIndex; rdef = true; DBG_OBJ_MSGF_O ("border", 2, oofm, "(c) %p is an achestor of %p; direct child is %p " "(%d) => %d - %d = %d\n", t1->getOOFAwareWidget (), t2->getOOFAwareWidget (), t->getOOFAwareWidget (), t->parentExtIndex, f1->externalIndex, t->parentExtIndex, r); } if (!rdef) { r = t1->index - t2->index; DBG_OBJ_MSGF_O ("border", 2, oofm, "(d) other => %d - %d = %d", t1->index, t2->index, r); } } DBG_OBJ_LEAVE_VAL_O (oofm, "%d", r); return r; } int OOFFloatsMgr::SortedFloatsVector::findFloatIndex (OOFAwareWidget *lastGB, int lastExtIndex) { DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d", lastGB, lastExtIndex); Float key (oofm, NULL, lastGB, lastExtIndex); key.index = -1; // for debugging Float::CompareGBAndExtIndex comparator (oofm); int i = bsearch (&key, false, &comparator); // At position i is the next larger element, so element i should // not included, but i - 1 returned; except if the exact element is // found: then include it and so return i. int r; if (i == size()) r = i - 1; else { Float *f = get (i); if (comparator.compare (f, &key) == 0) r = i; else r = i - 1; } //printf ("[%p] findFloatIndex (%p, %d) => i = %d, r = %d (size = %d); " // "in %s list %p on the %s side\n", // oofm->container, lastGB, lastExtIndex, i, r, size (), // type == GB ? "GB" : "CB", this, side == LEFT ? "left" : "right"); //for (int i = 0; i < size (); i++) { // Float *f = get(i); // TBInfo *t = oofm->getOOFAwareWidget(f->generatingBlock); // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock, // t->index, t->parent ? t->parent->textblock : NULL, // get(i)->externalIndex); //} DBG_OBJ_LEAVE_VAL_O (oofm, "%d", r); return r; } /** * `y` is given relative to the container. */ int OOFFloatsMgr::SortedFloatsVector::find (int y, int start, int end) { DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%d, %d, %d", y, start, end); Float key (oofm, NULL, NULL, 0); key.yReal = y; Float::ComparePosition comparator; int result = bsearch (&key, false, start, end, &comparator); DBG_OBJ_LEAVE_VAL_O (oofm, "%d", result); return result; } int OOFFloatsMgr::SortedFloatsVector::findFirst (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex, int *lastReturn) { DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%d, %d, %p, %d", y, h, lastGB, lastExtIndex); int last = findFloatIndex (lastGB, lastExtIndex); DBG_OBJ_MSGF_O ("border", 1, oofm, "last = %d", last); assert (last < size()); // If the caller wants to reuse this value: if (lastReturn) *lastReturn = last; int i = find (y, 0, last), result; DBG_OBJ_MSGF_O ("border", 1, oofm, "i = %d", i); // Note: The smallest value of "i" is 0, which means that "y" is before or // equal to the first float. The largest value is "last + 1", which means // that "y" is after the last float. In both cases, the first or last, // respectively, float is a candidate. Generally, both floats, before and // at the search position, are candidates. if (i > 0 && get(i - 1)->covers (y, h)) result = i - 1; else if (i <= last && get(i)->covers (y, h)) result = i; else result = -1; DBG_OBJ_LEAVE_VAL_O (oofm, "%d", result); return result; } int OOFFloatsMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex (int sideSpanningIndex) { OOFFloatsMgr::Float::CompareSideSpanningIndex comparator; Float key (NULL, NULL, NULL, 0); key.sideSpanningIndex = sideSpanningIndex; return bsearch (&key, false, &comparator) - 1; } void OOFFloatsMgr::SortedFloatsVector::put (Float *vloat) { lout::container::typed::Vector::put (vloat); vloat->index = size() - 1; } int OOFFloatsMgr::TBInfo::ComparePosition::compare (Object *o1, Object *o2) { TBInfo *tbInfo1 = (TBInfo*)o1, *tbInfo2 = (TBInfo*)o2; int y1 = tbInfo1->getOOFAwareWidget () == NULL ? tbInfo1->y : tbInfo1->getOOFAwareWidget()->getGeneratorY (oofmIndex); int y2 = tbInfo2->getOOFAwareWidget () == NULL ? tbInfo2->y : tbInfo2->getOOFAwareWidget()->getGeneratorY (oofmIndex); return y1 - y2; } OOFFloatsMgr::TBInfo::TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock, TBInfo *parent, int parentExtIndex) : WidgetInfo (oofm, textblock) { this->parent = parent; this->parentExtIndex = parentExtIndex; leftFloats = new Vector (1, false); rightFloats = new Vector (1, false); } OOFFloatsMgr::TBInfo::~TBInfo () { delete leftFloats; delete rightFloats; } OOFFloatsMgr::OOFFloatsMgr (OOFAwareWidget *container, int oofmIndex) { DBG_OBJ_CREATE ("dw::oof::OOFFloatsMgr"); this->container = container; this->oofmIndex = oofmIndex; leftFloats = new SortedFloatsVector (this, LEFT, true); rightFloats = new SortedFloatsVector (this, RIGHT, true); DBG_OBJ_SET_NUM ("leftFloats.size", leftFloats->size()); DBG_OBJ_SET_NUM ("rightFloats.size", rightFloats->size()); floatsByWidget = new HashTable , Float> (true, false); tbInfos = new Vector (1, false); tbInfosByOOFAwareWidget = new HashTable , TBInfo> (true, true); leftFloatsMark = rightFloatsMark = 0; lastLeftTBIndex = lastRightTBIndex = 0; SizeChanged = true; DBG_OBJ_SET_BOOL ("SizeChanged", SizeChanged); containerAllocation = *(container->getAllocation()); addWidgetInFlow (this->container, NULL, 0); } OOFFloatsMgr::~OOFFloatsMgr () { // Order is important: tbInfosByOOFAwareWidget is owner of the instances // of TBInfo.tbInfosByOOFAwareWidget. Also, leftFloats and rightFloats are // owners of the floats. delete tbInfos; delete tbInfosByOOFAwareWidget; delete floatsByWidget; delete leftFloats; delete rightFloats; DBG_OBJ_DELETE (); } void OOFFloatsMgr::sizeAllocateStart (OOFAwareWidget *caller, Allocation *allocation) { DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateStart", "%p, (%d, %d, %d * (%d + %d))", caller, allocation->x, allocation->y, allocation->width, allocation->ascent, allocation->descent); if (caller == container) containerAllocation = *allocation; DBG_OBJ_LEAVE (); } void OOFFloatsMgr::sizeAllocateEnd (OOFAwareWidget *caller) { DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); if (caller == container) { sizeAllocateFloats (LEFT); sizeAllocateFloats (RIGHT); } DBG_OBJ_LEAVE (); } void OOFFloatsMgr::containerSizeChangedForChildren () { DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); for (int i = 0; i < leftFloats->size (); i++) leftFloats->get(i)->getWidget()->containerSizeChanged (); for (int i = 0; i < rightFloats->size (); i++) rightFloats->get(i)->getWidget()->containerSizeChanged (); DBG_OBJ_LEAVE (); } void OOFFloatsMgr::sizeAllocateFloats (Side side) { SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", "%s", side == LEFT ? "LEFT" : "RIGHT"); for (int i = 0; i < list->size (); i++) { Float *vloat = list->get(i); ensureFloatSize (vloat); Allocation childAllocation; childAllocation.x = containerAllocation.x + calcFloatX (vloat); childAllocation.y = containerAllocation.y + vloat->yReal; childAllocation.width = vloat->size.width; childAllocation.ascent = vloat->size.ascent; childAllocation.descent = vloat->size.descent; vloat->getWidget()->sizeAllocate (&childAllocation); } DBG_OBJ_LEAVE (); } /** * \brief Return position of a float relative to the container. */ int OOFFloatsMgr::calcFloatX (Float *vloat) { DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p", vloat->getWidget ()); int x, effGeneratorWidth; OOFAwareWidget *generator = vloat->generator; ensureFloatSize (vloat); switch (vloat->getWidget()->getStyle()->vloat) { case FLOAT_LEFT: // Left floats are always aligned on the left side of the generator // (content, not allocation) ... x = generator->getGeneratorX (oofmIndex) + generator->getStyle()->boxOffsetX(); // ... but when the float exceeds the line break width of the container, // it is corrected (but not left of the container). This way, we save // space and, especially within tables, avoid some problems. if (x + vloat->size.width > container->getMaxGeneratorWidth ()) x = max (0, container->getMaxGeneratorWidth () - vloat->size.width); break; case FLOAT_RIGHT: // Similar for right floats, but in this case, floats are shifted to the // right when they are too big (instead of shifting the generator to the // right). // (The following code for calculating effGeneratorWidth, is quite // specific for textblocks; this also applies for the comments. Both may // be generalized, but actually, only textblocks play a role here.) if (vloat->generator->usesMaxGeneratorWidth ()) // For most textblocks, the line break width is used for calculating // the x position. (This changed for GROWS, where the width of a // textblock is often smaller that the line break.) effGeneratorWidth = vloat->generator->getMaxGeneratorWidth (); else // For some textblocks, like inline blocks, the line break width would // be too large for right floats in some cases. // // (i) Consider a small inline block with only a few words in one // line, narrower that line break width minus float width. In this // case, the sum should be used. // // (ii) If there is more than one line, the line break will already be // exceeded, and so be smaller that GB width + float width. effGeneratorWidth = min (vloat->generator->getGeneratorWidth () + vloat->size.width, vloat->generator->getMaxGeneratorWidth ()); x = max (generator->getGeneratorX (oofmIndex) + effGeneratorWidth - vloat->size.width - generator->getStyle()->boxRestWidth(), // Do not exceed container allocation: 0); break; default: assertNotReached (); x = 0; break; } DBG_OBJ_LEAVE_VAL ("%d", x); return x; } void OOFFloatsMgr::draw (View *view, Rectangle *area, DrawingContext *context) { DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", area->x, area->y, area->width, area->height); drawFloats (leftFloats, view, area, context); drawFloats (rightFloats, view, area, context); DBG_OBJ_LEAVE (); } void OOFFloatsMgr::drawFloats (SortedFloatsVector *list, View *view, Rectangle *area, DrawingContext *context) { // This could be improved, since the list is sorted: search the // first float fitting into the area, and iterate until one is // found below the area. for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); Widget *childWidget = vloat->getWidget (); Rectangle childArea; if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && !StackingContextMgr::handledByStackingContextMgr (childWidget) && childWidget->intersects (container, area, &childArea)) childWidget->draw (view, &childArea, context); } } void OOFFloatsMgr::addWidgetInFlow (OOFAwareWidget *textblock, OOFAwareWidget *parentBlock, int externalIndex) { //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n", // container, textblock, parentBlock, externalIndex); TBInfo *tbInfo = new TBInfo (this, textblock, parentBlock ? getOOFAwareWidget (parentBlock) : NULL, externalIndex); tbInfo->index = tbInfos->size(); tbInfos->put (tbInfo); tbInfosByOOFAwareWidget->put (new TypedPointer (textblock), tbInfo); } int OOFFloatsMgr::addWidgetOOF (Widget *widget, OOFAwareWidget *generatingBlock, int externalIndex) { DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", widget, generatingBlock, externalIndex); int subRef; TBInfo *tbInfo = getOOFAwareWidget (generatingBlock); Float *vloat = new Float (this, widget, generatingBlock, externalIndex); // Note: Putting the float first in the GB list, and then, possibly // into the CB list (in that order) will trigger setting // Float::inCBList to the right value. switch (widget->getStyle()->vloat) { case FLOAT_LEFT: leftFloats->put (vloat); DBG_OBJ_SET_NUM ("leftFloats.size", leftFloats->size()); DBG_OBJ_ARRATTRSET_PTR ("leftFloats", leftFloats->size() - 1, "widget", vloat->getWidget ()); tbInfo->leftFloats->put (vloat); subRef = createSubRefLeftFloat (leftFloats->size() - 1); break; case FLOAT_RIGHT: rightFloats->put (vloat); DBG_OBJ_SET_NUM ("rightFloats.size", rightFloats->size()); DBG_OBJ_ARRATTRSET_PTR ("rightFloats", rightFloats->size() - 1, "widget", vloat->getWidget ()); tbInfo->rightFloats->put (vloat); subRef = createSubRefRightFloat (rightFloats->size() - 1); break; default: assertNotReached(); } // "sideSpanningIndex" is only compared, so this simple assignment // is sufficient; differenciation between GB and CB lists is not // neccessary. vloat->sideSpanningIndex = leftFloats->size() + rightFloats->size() - 1; floatsByWidget->put (new TypedPointer (widget), vloat); DBG_OBJ_LEAVE_VAL ("%d", subRef); return subRef; } void OOFFloatsMgr::calcWidgetRefSize (Widget *widget, Requisition *size) { size->width = size->ascent = size->descent = 0; } void OOFFloatsMgr::moveExternalIndices (OOFAwareWidget *generatingBlock, int oldStartIndex, int diff) { TBInfo *tbInfo = getOOFAwareWidget (generatingBlock); moveExternalIndices (tbInfo->leftFloats, oldStartIndex, diff); moveExternalIndices (tbInfo->rightFloats, oldStartIndex, diff); } void OOFFloatsMgr::moveExternalIndices (Vector *list, int oldStartIndex, int diff) { // Could be faster with binary search, but the number of floats per generator // should be rather small. for (int i = 0; i < list->size (); i++) { Float *vloat = list->get (i); if (vloat->externalIndex >= oldStartIndex) { vloat->externalIndex += diff; DBG_OBJ_SET_NUM_O (vloat->getWidget (), ".externalIndex", vloat->externalIndex); } } } OOFFloatsMgr::Float *OOFFloatsMgr::findFloatByWidget (Widget *widget) { TypedPointer key (widget); Float *vloat = floatsByWidget->get (&key); assert (vloat != NULL); return vloat; } /* * Currently this is a compound recursion for textblocks: * markSizeChange -> updateReference -> queueResize -> markSizeChange * One way to see it is as a widget tree coverage problem. i.e. to cover all * the nodes that need a resize when a float changes its size. * The coverage logic of it is shared between resize code and mark code (here). * * This implementation works for all the test cases so far. It relies on the * fact that Widget::queueResize should be called whenever a widget changes its * size. When "SizeChanged" is true, we notify the parent, when not, just the * following textblocks. */ void OOFFloatsMgr::markSizeChange (int ref) { DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); // When "SizeChanged" is true, we know this float changed its size. // This helps to prune redundant passes. // "SizeChanged" is set by getSize(), which is called by sizeRequest(). SortedFloatsVector *list; list = isSubRefLeftFloat(ref) ? leftFloats : rightFloats; Float *vloat = list->get (getFloatIndexFromSubRef (ref)); vloat->dirty = true; DBG_OBJ_SET_BOOL_O (vloat->getWidget (), ".dirty", vloat->dirty); updateGenerators (vloat); DBG_OBJ_LEAVE (); } /** * \brief Update all generators which are affected by a given float. */ void OOFFloatsMgr::updateGenerators (Float *vloat) { DBG_OBJ_ENTER ("resize.oofm", 0, "updateGenerators", "#%d [%p]", vloat->index, vloat->getWidget ()); assert (vloat->getWidget()->getWidgetReference() != NULL); int first = getOOFAwareWidget(vloat->generator)->index; DBG_OBJ_MSGF ("resize.oofm", 1, "updating from %d", first); //printf("IN markSizeChange %p ref %d SzCh=%d\n", this, ref, // (int)SizeChanged); if (SizeChanged) tbInfos->get(first)->getOOFAwareWidget() ->updateReference (vloat->getWidget()->getWidgetReference() ->parentRef); for (int i = first + 1; i < tbInfos->size(); i++) tbInfos->get(i)->getOOFAwareWidget()->updateReference(0); SizeChanged = false; // Done. DBG_OBJ_LEAVE (); } /** * `y` is given relative to the container. */ int OOFFloatsMgr::findTBInfo (int y) { DBG_OBJ_ENTER ("findTBInfo", 0, "findTBInfo", "%d", y); TBInfo key (this, NULL, NULL, 0); key.y = y; TBInfo::ComparePosition comparator (oofmIndex); int index = tbInfos->bsearch (&key, false, &comparator); // "bsearch" returns next greater, but we are interrested in the last which // is less or equal. int result = index > 0 ? index - 1 : index; DBG_OBJ_LEAVE_VAL ("%d", result); return result; } void OOFFloatsMgr::markExtremesChange (int ref) { // Nothing to do here. } Widget *OOFFloatsMgr::getWidgetAtPoint (int x, int y, GettingWidgetAtPointContext *context) { Widget *widgetAtPoint = NULL; widgetAtPoint = getFloatWidgetAtPoint (rightFloats, x, y, context); if (widgetAtPoint == NULL) widgetAtPoint = getFloatWidgetAtPoint (leftFloats, x, y, context); return widgetAtPoint; } Widget *OOFFloatsMgr::getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, GettingWidgetAtPointContext *context) { // Could use binary search to be faster (similar to drawing). Widget *widgetAtPoint = NULL; for (int i = list->size() - 1; widgetAtPoint == NULL && i >= 0; i--) { Widget *childWidget = list->get(i)->getWidget (); if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && !StackingContextMgr::handledByStackingContextMgr (childWidget)) widgetAtPoint = childWidget->getWidgetAtPoint (x, y, context); } return widgetAtPoint; } void OOFFloatsMgr::tellPosition1 (Widget *widget, int x, int y) { DBG_OBJ_ENTER ("resize.oofm", 0, "tellPosition1", "%p, %d, %d", widget, x, y); assert (y >= 0); Float *vloat = findFloatByWidget(widget); SortedFloatsVector *listSame, *listOpp; Side side; getFloatsListsAndSide (vloat, &listSame, &listOpp, &side); ensureFloatSize (vloat); int oldYReal = vloat->yReal; // "yReal" may change due to collisions (see below). vloat->yReq = vloat->yReal = y; DBG_OBJ_SET_NUM_O (vloat->getWidget (), ".yReq", vloat->yReq); DBG_OBJ_SET_NUM_O (vloat->getWidget (), ".yReal", vloat->yReal); // Test collisions (on this side). Although there are (rare) cases // where it could make sense, the horizontal dimensions are not // tested; especially since searching and border calculation would // be confused. For this reaspn, only the previous float is // relevant. (Cf. below, collisions on the other side.) int yRealNew; if (vloat->index >= 1 && collidesV (vloat, listSame->get (vloat->index - 1), &yRealNew)) { vloat->yReal = yRealNew; DBG_OBJ_SET_NUM_O (vloat->getWidget (), ".yReal", vloat->yReal); } // Test collisions (on the opposite side). There are cases when // more than one float has to be tested. Consider the following // HTML snippet ("id" attribute only used for simple reference // below, as #f1, #f2, and #f3): // //
// Left left left left left left left left left left. //
//
Also left.
//
Right.
// // When displayed with a suitable window width (only slightly wider // than the text within #f1), this should look like this: // // --------------------------------------------------------- // | Left left left left left left left left left left. | // | Also left. Right. | // --------------------------------------------------------- // // Consider float #f3: a collision test with #f2, considering // vertical dimensions, is positive, but not the test with // horizontal dimensions (because #f2 and #f3 are too // narrow). However, a collision has to be tested with #f1; // otherwise #f3 and #f1 would overlap. int oppFloatIndex = listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex); // Generally, the rules are simple: loop as long as the vertical // dimensions test is positive (and, of course, there are floats), // ... for (bool foundColl = false; !foundColl && oppFloatIndex >= 0 && collidesV (vloat, listOpp->get (oppFloatIndex), &yRealNew); oppFloatIndex--) { // ... but stop the loop as soon as the horizontal dimensions // test is positive. if (collidesH (vloat, listOpp->get (oppFloatIndex))) { vloat->yReal = yRealNew; DBG_OBJ_SET_NUM_O (vloat->getWidget (), ".yReal", vloat->yReal); foundColl = true; } } DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", vloat->yReq, vloat->yReal); // In some cases, an explicit update is neccessary, as in this example: // // //
//
main
//
//
x
//
footer
// // // Without an explicit update, #c would keep an old value for extraSpace.top, // based on the old value of vloat->yReal. // // Notice that #c would be updated otherwise, if it had at least one word // content. if (vloat->yReal != oldYReal) updateGenerators (vloat); DBG_OBJ_LEAVE (); } void OOFFloatsMgr::tellPosition2 (Widget *widget, int x, int y) { // Nothing to do. } void OOFFloatsMgr::tellIncompletePosition1 (Widget *generator, Widget *widget, int x, int y) { notImplemented ("OOFFloatsMgr::tellIncompletePosition1"); } void OOFFloatsMgr::tellIncompletePosition2 (Widget *generator, Widget *widget, int x, int y) { notImplemented ("OOFFloatsMgr::tellIncompletePosition2"); } bool OOFFloatsMgr::collidesV (Float *vloat, Float *other, int *yReal) { DBG_OBJ_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...", vloat->index, vloat->getWidget (), other->index, other->getWidget ()); bool result; DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); ensureFloatSize (other); int otherBottomGB = other->yReal + other->size.ascent + other->size.descent; DBG_OBJ_MSGF ("resize.oofm", 1, "otherBottomGB = %d + (%d + %d) = %d", other->yReal, other->size.ascent, other->size.descent, otherBottomGB); if (vloat->yReal < otherBottomGB) { *yReal = otherBottomGB; result = true; } else result = false; if (result) DBG_OBJ_LEAVE_VAL ("%s, %d", "true", *yReal); else DBG_OBJ_LEAVE_VAL ("%s", "false"); return result; } bool OOFFloatsMgr::collidesH (Float *vloat, Float *other) { // Only checks horizontal collision. For a complete test, use collidesV (...) // && collidesH (...). bool collidesH; int vloatX = calcFloatX (vloat), otherX = calcFloatX (other); // Generally: right border of the left float > left border of the right float // (all in canvas coordinates). if (vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT) { // "vloat" is left, "other" is right ensureFloatSize (vloat); collidesH = vloatX + vloat->size.width > otherX; } else { // "other" is left, "vloat" is right ensureFloatSize (other); collidesH = otherX + other->size.width > vloatX; } return collidesH; } void OOFFloatsMgr::getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, SortedFloatsVector **listOpp, Side *side) { switch (vloat->getWidget()->getStyle()->vloat) { case FLOAT_LEFT: if (listSame) *listSame = leftFloats; if (listOpp) *listOpp = rightFloats; if (side) *side = LEFT; break; case FLOAT_RIGHT: if (listSame) *listSame = rightFloats; if (listOpp) *listOpp = leftFloats; if (side) *side = RIGHT; break; default: assertNotReached(); } } void OOFFloatsMgr::getSize (Requisition *cbReq, int *oofWidth, int *oofHeight) { DBG_OBJ_ENTER0 ("resize.oofm", 0, "getSize"); int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; getFloatsSize (cbReq, LEFT, &oofWidthtLeft, &oofHeightLeft); getFloatsSize (cbReq, RIGHT, &oofWidthRight, &oofHeightRight); // Floats must be within the *content* area of the containing // block, not its *margin* area (which is equivalent to the // requisition / allocation). For this reason, boxRestWidth() and // boxRestHeight() are added here. *oofWidth = max (oofWidthtLeft, oofWidthRight) + container->boxRestWidth (); *oofHeight = max (oofHeightLeft, oofHeightRight) + container->boxRestHeight (); SizeChanged = true; DBG_OBJ_SET_NUM ("floatRef", floatRef); DBG_OBJ_MSGF ("resize.oofm", 1, "=> (l: %d, r: %d => %d) * (l: %d, r: %d => %d)", oofWidthtLeft, oofWidthRight, *oofWidth, oofHeightLeft, oofHeightRight, *oofHeight); DBG_OBJ_LEAVE (); } void OOFFloatsMgr::getFloatsSize (Requisition *cbReq, Side side, int *width, int *height) { DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsSize", "(%d * (%d + %d), %s, ...", cbReq->width, cbReq->ascent, cbReq->descent, side == LEFT ? "LEFT" : "RIGHT"); SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; *width = *height = 0; DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats on this side", list->size()); for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); DBG_OBJ_MSGF ("resize.oofm", 1, "float %p has generator %p (container is %p)", vloat->getWidget (), vloat->generator, container); ensureFloatSize (vloat); *width = max (*width, calcFloatX (vloat) + vloat->size.width); *height = max (*height, vloat->yReal + vloat->size.ascent + vloat->size.descent); } DBG_OBJ_LEAVE (); } bool OOFFloatsMgr::containerMustAdjustExtraSpace () { return false; } void OOFFloatsMgr::getExtremes (Extremes *cbExtr, int *oofMinWidth, int *oofMaxWidth) { DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", cbExtr->minWidth, cbExtr->maxWidth); int oofMinWidthtLeft, oofMinWidthRight, oofMaxWidthLeft, oofMaxWidthRight; getFloatsExtremes (cbExtr, LEFT, &oofMinWidthtLeft, &oofMaxWidthLeft); getFloatsExtremes (cbExtr, RIGHT, &oofMinWidthRight, &oofMaxWidthRight); *oofMinWidth = max (oofMinWidthtLeft, oofMinWidthRight); *oofMaxWidth = max (oofMaxWidthLeft, oofMaxWidthRight); DBG_OBJ_MSGF ("resize.oofm", 1, "=> (l: %d, r: %d => %d) / (l: %d, r: %d => %d)", oofMinWidthtLeft, oofMinWidthRight, *oofMinWidth, oofMaxWidthLeft, oofMaxWidthRight, *oofMaxWidth); DBG_OBJ_LEAVE (); } void OOFFloatsMgr::getFloatsExtremes (Extremes *cbExtr, Side side, int *minWidth, int *maxWidth) { DBG_OBJ_ENTER ("resize.oofm", 0, "getFloatsExtremes", "(%d / %d), %s, ...", cbExtr->minWidth, cbExtr->maxWidth, side == LEFT ? "LEFT" : "RIGHT"); *minWidth = *maxWidth = 0; SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; DBG_OBJ_MSGF ("resize.oofm", 1, "%d floats to be examined", list->size()); for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); DBG_OBJ_MSGF ("resize.oofm", 1, "float %p has generator %p (container is %p)", vloat->getWidget (), vloat->generator, container); Extremes extr; vloat->getWidget()->getExtremes (&extr); // The calculation of extremes must be kept consistent with // getFloatsSize(). Especially this means for the *minimal* width: // // - The right border (difference between float and container) does not // have to be considered (see getFloatsSize()). // // - This is also the case for the left border, as seen in calcFloatX() // ("... but when the float exceeds the line break width" ...). *minWidth = max (*minWidth, extr.minWidth); // For the maximal width, borders must be considered. *maxWidth = max (*maxWidth, extr.maxWidth + vloat->generator->getStyle()->boxDiffWidth(), + max (container->getGeneratorWidth () - vloat->generator->getGeneratorWidth (), 0)); DBG_OBJ_MSGF ("resize.oofm", 1, "%d / %d => %d / %d", extr.minWidth, extr.maxWidth, *minWidth, *maxWidth); } DBG_OBJ_LEAVE (); } OOFFloatsMgr::TBInfo *OOFFloatsMgr::getOOFAwareWidgetWhenRegistered (OOFAwareWidget *widget) { DBG_OBJ_ENTER ("oofm.common", 0, "getOOFAwareWidgetWhenRegistered", "%p", widget); TypedPointer key (widget); TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (&key); DBG_OBJ_MSGF ("oofm.common", 1, "found? %s", tbInfo ? "yes" : "no"); DBG_OBJ_LEAVE (); return tbInfo; } OOFFloatsMgr::TBInfo *OOFFloatsMgr::getOOFAwareWidget (OOFAwareWidget *widget) { DBG_OBJ_ENTER ("oofm.common", 0, "getOOFAwareWidget", "%p", widget); TBInfo *tbInfo = getOOFAwareWidgetWhenRegistered (widget); assert (tbInfo); DBG_OBJ_LEAVE (); return tbInfo; } int OOFFloatsMgr::getLeftBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { int b = getBorder (LEFT, y, h, lastGB, lastExtIndex); DBG_OBJ_MSGF ("border", 0, "left border (%d, %d, %p, %d) => %d", y, h, lastGB, lastExtIndex, b); return b; } int OOFFloatsMgr::getRightBorder (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { int b = getBorder (RIGHT, y, h, lastGB, lastExtIndex); DBG_OBJ_MSGF ("border", 0, "right border (%d, %d, %p, %d) => %d", y, h, lastGB, lastExtIndex, b); return b; } int OOFFloatsMgr::getBorder (Side side, int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { DBG_OBJ_ENTER ("border", 0, "getBorder", "%s, %d, %d, %p, %d", side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex); SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; int last; int first = list->findFirst (y, h, lastGB, lastExtIndex, &last); int border = 0; DBG_OBJ_MSGF ("border", 1, "first = %d", first); if (first != -1) { // It is not sufficient to find the first float, since a line // (with height h) may cover the region of multiple float, of // which the widest has to be choosen. bool covers = true; // We are not searching until the end of the list, but until the // float defined by lastGB and lastExtIndex. for (int i = first; covers && i <= last; i++) { Float *vloat = list->get(i); covers = vloat->covers (y, h); DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", i, vloat->getWidget(), covers ? "yes" : "no"); if (covers) { int d; switch (side) { case LEFT: d = vloat->generator->getGeneratorX (oofmIndex) + vloat->generator->getStyle()->boxOffsetX (); break; case RIGHT: // There is no simple possibility to get the difference between // container and generator at the right border (as it is at the // left border, see above). We have to calculate the difference // between the maximal widths. d = container->getMaxGeneratorWidth () - (vloat->generator->getGeneratorX (oofmIndex) + vloat->generator->getMaxGeneratorWidth ()) + vloat->generator->getStyle()->boxRestWidth (); break; default: assertNotReached (); d = 0; break; } int thisBorder = vloat->size.width + d; DBG_OBJ_MSGF ("border", 1, "thisBorder = %d + %d = %d", vloat->size.width, d, thisBorder); border = max (border, thisBorder); } } } DBG_OBJ_LEAVE_VAL ("%d", border); return border; } bool OOFFloatsMgr::hasFloatLeft (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { bool b = hasFloat (LEFT, y, h, lastGB, lastExtIndex); DBG_OBJ_MSGF ("border", 0, "has float left (%d, %d, %p, %d) => %s", y, h, lastGB, lastExtIndex, b ? "true" : "false"); return b; } bool OOFFloatsMgr::hasFloatRight (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { bool b = hasFloat (RIGHT, y, h, lastGB, lastExtIndex); DBG_OBJ_MSGF ("border", 0, "has float right (%d, %d, %p, %d) => %s", y, h, lastGB, lastExtIndex, b ? "true" : "false"); return b; } bool OOFFloatsMgr::hasFloat (Side side, int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { DBG_OBJ_ENTER ("border", 0, "hasFloat", "%s, %d, %d, %p, %d", side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex); SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; int first = list->findFirst (y, h, lastGB, lastExtIndex, NULL); DBG_OBJ_MSGF ("border", 1, "first = %d", first); DBG_OBJ_LEAVE (); return first != -1; } int OOFFloatsMgr::getLeftFloatHeight (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { return getFloatHeight (LEFT, y, h, lastGB, lastExtIndex); } int OOFFloatsMgr::getRightFloatHeight (int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { return getFloatHeight (RIGHT, y, h, lastGB, lastExtIndex); } int OOFFloatsMgr::getFloatHeight (Side side, int y, int h, OOFAwareWidget *lastGB, int lastExtIndex) { DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%s, %d, %d, %p, %d", side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex); SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; int first = list->findFirst (y, h, lastGB, lastExtIndex, NULL); assert (first != -1); /* This method must not be called when there is no float on the respective side. */ Float *vloat = list->get (first); int yRelToFloat = y - vloat->yReal; DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d", y, vloat->yReal, yRelToFloat); ensureFloatSize (vloat); int height = vloat->size.ascent + vloat->size.descent - yRelToFloat; DBG_OBJ_MSGF ("border", 1, "=> (%d + %d) - %d = %d", vloat->size.ascent, vloat->size.descent, yRelToFloat, height); DBG_OBJ_LEAVE (); return height; } int OOFFloatsMgr::getClearPosition (OOFAwareWidget *widget) { DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", widget); int pos; if (widget->getStyle()) { bool left = false, right = false; switch (widget->getStyle()->clear) { case CLEAR_NONE: break; case CLEAR_LEFT: left = true; break; case CLEAR_RIGHT: right = true; break; case CLEAR_BOTH: left = right = true; break; default: assertNotReached (); } pos = max (left ? getClearPosition (widget, LEFT) : 0, right ? getClearPosition (widget, RIGHT) : 0); } else pos = 0; DBG_OBJ_LEAVE_VAL ("%d", pos); return pos; } bool OOFFloatsMgr::affectsLeftBorder (core::Widget *widget) { return widget->getStyle()->vloat == core::style::FLOAT_LEFT; } bool OOFFloatsMgr::affectsRightBorder (core::Widget *widget) { return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; }; bool OOFFloatsMgr::mayAffectBordersAtAll () { return true; } int OOFFloatsMgr::getClearPosition (OOFAwareWidget *widget, Side side) { DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", widget, side == LEFT ? "LEFT" : "RIGHT"); int pos; SortedFloatsVector *list = side == LEFT ? leftFloats : rightFloats; // Search the last float before (therfore -1) this widget. int i = list->findFloatIndex (widget, -1); if (i < 0) pos = 0; else { Float *vloat = list->get(i); assert (vloat->generator != widget); ensureFloatSize (vloat); int yRel = widget->getGeneratorY (oofmIndex); pos = max (vloat->yReal + vloat->size.ascent + vloat->size.descent - yRel, 0); DBG_OBJ_MSGF ("resize.oofm", 1, "pos = max (%d + %d + %d - %d, 0)", vloat->yReal, vloat->size.ascent, vloat->size.descent, yRel); } DBG_OBJ_LEAVE_VAL ("%d", pos); return pos; } void OOFFloatsMgr::ensureFloatSize (Float *vloat) { // Historical note: relative sizes (e. g. percentages) are already // handled by (at this time) Layout::containerSizeChanged, so // Float::dirty will be set. DBG_OBJ_ENTER ("resize.oofm", 0, "ensureFloatSize", "%p", vloat->getWidget ()); if (vloat->dirty) { DBG_OBJ_MSG ("resize.oofm", 1, "dirty: recalculation"); vloat->getWidget()->sizeRequest (&vloat->size); vloat->dirty = false; DBG_OBJ_SET_BOOL_O (vloat->getWidget (), ".dirty", vloat->dirty); DBG_OBJ_MSGF ("resize.oofm", 1, "size: %d * (%d + %d)", vloat->size.width, vloat->size.ascent, vloat->size.descent); DBG_OBJ_SET_NUM_O (vloat->getWidget(), ".size.width", vloat->size.width); DBG_OBJ_SET_NUM_O (vloat->getWidget(), ".size.ascent", vloat->size.ascent); DBG_OBJ_SET_NUM_O (vloat->getWidget(), ".size.descent", vloat->size.descent); // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() } DBG_OBJ_LEAVE (); } bool OOFFloatsMgr::dealingWithSizeOfChild (core::Widget *child) { return false; } int OOFFloatsMgr::getAvailWidthOfChild (Widget *child, bool forceValue) { notImplemented ("OOFFloatsMgr::getAvailWidthOfChild"); return 0; } int OOFFloatsMgr::getAvailHeightOfChild (Widget *child, bool forceValue) { notImplemented ("OOFFloatsMgr::getAvailHeightOfChild"); return 0; } int OOFFloatsMgr::getNumWidgets () { return leftFloats->size() + rightFloats->size(); } Widget *OOFFloatsMgr::getWidget (int i) { if (i < leftFloats->size()) return leftFloats->get(i)->getWidget (); else return rightFloats->get(i - leftFloats->size())->getWidget (); } } // namespace oof } // namespace dw