/* * Dillo Widget * * Copyright 2013 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 "outofflowmgr.hh" #include "textblock.hh" #include "../lout/debug.hh" using namespace lout::object; using namespace lout::container::typed; using namespace lout::misc; using namespace dw::core; using namespace dw::core::style; namespace dw { OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget, Textblock *generatingBlock, int externalIndex) { this->oofm = oofm; this->widget = widget; this->generatingBlock = generatingBlock; this->externalIndex = externalIndex; yReq = yReal = size.width = size.ascent = size.descent = 0; dirty = sizeChangedSinceLastAllocation = true; inCBList = false; } void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) { sb->append ("{ widget = "); sb->appendPointer (widget); if (widget) { sb->append (" ("); sb->append (widget->getClassName ()); sb->append (")"); } sb->append (", index = "); sb->appendInt (index); sb->append (", sideSpanningIndex = "); sb->appendInt (sideSpanningIndex); sb->append (", generatingBlock = "); sb->appendPointer (generatingBlock); 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->appendBool (sizeChangedSinceLastAllocation); sb->append (", inCBList = "); sb->appendBool (inCBList); sb->append (" }"); } int OutOfFlowMgr::Float::compareTo(Comparable *other) { Float *otherFloat = (Float*)other; if (oofm->wasAllocated (generatingBlock)) { assert (oofm->wasAllocated (otherFloat->generatingBlock)); return yForContainer() - otherFloat->yForContainer(); } else { assert (generatingBlock == otherFloat->generatingBlock); return yReal - otherFloat->yReal; } } int OutOfFlowMgr::Float::yForTextblock (Textblock *textblock, int y) { if (oofm->wasAllocated (generatingBlock)) { assert (oofm->wasAllocated (textblock)); return oofm->getAllocation(generatingBlock)->y + y - oofm->getAllocation(textblock)->y; } else { assert (textblock == generatingBlock); return y; } } int OutOfFlowMgr::Float::yForContainer (int y) { assert (oofm->wasAllocated (generatingBlock)); return y + oofm->getAllocation(generatingBlock)->y - oofm->getAllocation(oofm->containingBlock)->y; } bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) { int reqy, fly; // either widget or canvas coordinates if (oofm->wasAllocated (generatingBlock)) { assert (oofm->wasAllocated (textblock)); reqy = oofm->getAllocation(textblock)->y + y; fly = oofm->getAllocation(generatingBlock)->y + yReal; } else { assert (textblock == generatingBlock); reqy = y; fly = yReal; } oofm->ensureFloatSize (this); //printf ("[%p] COVERS (%p, %d, %d) => %d + %d + %d > %d && %d < %d + %d? " // "%s.\n", oofm->containingBlock, textblock, y, h, fly, size.ascent, // size.descent, reqy, fly, reqy, h, // (fly + size.ascent + size.descent > reqy && fly < reqy + h) ? // "yes" : "no"); return fly + size.ascent + size.descent > reqy && fly < reqy + h; } int OutOfFlowMgr::Float::CompareSideSpanningIndex::compare(Object *o1, Object *o2) { return ((Float*)o1)->sideSpanningIndex - ((Float*)o2)->sideSpanningIndex; } int OutOfFlowMgr::Float::CompareGBAndExtIndex::compare(Object *o1, Object *o2) { Float *f1 = (Float*)o1, *f2 = (Float*)o2; //printf ("[%p] comparing (%p, %d) with (%p, %d) ...\n", // oofm->containingBlock, f1->generatingBlock, f1->externalIndex, // f2->generatingBlock, f2->externalIndex); if (f1->generatingBlock == f2->generatingBlock) { //printf (" (a) generating blocks equal => %d - %d = %d\n", // f1->externalIndex, f2->externalIndex, // f1->externalIndex - f2->externalIndex); return f1->externalIndex - f2->externalIndex; } else { TBInfo *t1 = oofm->getTextblock (f1->generatingBlock), *t2 = oofm->getTextblock (f2->generatingBlock); for (TBInfo *t = t1; t != NULL; t = t->parent) if (t->parent == t2) { //printf (" (b) %p is an achestor of %p; direct child is %p (%d)" // " => %d - %d = %d\n", t2->textblock, t1->textblock, // t->textblock, t->parentExtIndex, t->parentExtIndex, // f2->externalIndex, t->parentExtIndex - f2->externalIndex); return t->parentExtIndex - f2->externalIndex; } for (TBInfo *t = t2; t != NULL; t = t->parent) if (t->parent == t1) { //printf (" (c) %p is an achestor of %p; direct child is %p (%d)" // " => %d - %d = %d\n", t1->textblock, t2->textblock, // t->textblock, t->parentExtIndex, f1->externalIndex, // t->parentExtIndex, f1->externalIndex - t->parentExtIndex); return f1->externalIndex - t->parentExtIndex; } //printf (" (d) other => %d - %d = %d\n", // t1->index, t2->index, t1->index - t2->index); return t1->index - t2->index; } } int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, int lastExtIndex) { Float key (oofm, NULL, lastGB, lastExtIndex); Float::CompareGBAndExtIndex cmp (oofm); int i = bsearch (&key, false, &cmp); // 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 (cmp.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->containingBlock, 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->getTextblock(f->generatingBlock); // printf (" %d: (%p [%d, %p], %d)\n", i, f->generatingBlock, // t->index, t->parent ? t->parent->textblock : NULL, // get(i)->externalIndex); //} return r; } int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, int start, int end) { Float key (oofm, NULL, NULL, 0); key.generatingBlock = textblock; key.yReal = y; return bsearch (&key, false, start, end); } int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, int y, int h, Textblock *lastGB, int lastExtIndex) { int last = findFloatIndex (lastGB, lastExtIndex); assert (last < size()); int i = find (textblock, y, 0, last); //printf ("[%p] FIND (%s, %p, allocated: %s, %d, %p, %d) => last = %d, " // "result = %d (of %d)\n", oofm->containingBlock, // type == GB ? "GB" : "CB", textblock, // oofm->wasAllocated (textblock) ? "true" : "false", y, lastGB, // lastExtIndex, last, i, size()); // 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 (textblock, y, h)) return i - 1; else if (i <= last && get(i)->covers (textblock, y, h)) return i; else return -1; } int OutOfFlowMgr::SortedFloatsVector::findLastBeforeSideSpanningIndex (int sideSpanningIndex) { OutOfFlowMgr::Float::CompareSideSpanningIndex comparator; Float key (NULL, NULL, NULL, 0); key.sideSpanningIndex = sideSpanningIndex; return bsearch (&key, false, &comparator) - 1; } void OutOfFlowMgr::SortedFloatsVector::put (Float *vloat) { lout::container::typed::Vector::put (vloat); vloat->index = size() - 1; vloat->inCBList = type == CB; } OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, TBInfo *parent, int parentExtIndex) { this->textblock = textblock; this->parent = parent; this->parentExtIndex = parentExtIndex; leftFloatsGB = new SortedFloatsVector (oofm, LEFT, SortedFloatsVector::GB); rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, SortedFloatsVector::GB); } OutOfFlowMgr::TBInfo::~TBInfo () { delete leftFloatsGB; delete rightFloatsGB; } OutOfFlowMgr::AbsolutelyPositioned::AbsolutelyPositioned (OutOfFlowMgr *oofm, core::Widget *widget, Textblock *generatingBlock, int externalIndex) { this->widget = widget; dirty = true; } OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) { DBG_OBJ_CREATE ("dw::OutOfFlowMgr"); this->containingBlock = containingBlock; leftFloatsCB = new SortedFloatsVector (this, LEFT, SortedFloatsVector::CB); rightFloatsCB = new SortedFloatsVector (this, RIGHT, SortedFloatsVector::CB); leftFloatsAll = new Vector (1, true); rightFloatsAll = new Vector (1, true); floatsByWidget = new HashTable , Float> (true, false); tbInfos = new Vector (1, false); tbInfosByTextblock = new HashTable , TBInfo> (true, true); leftFloatsMark = rightFloatsMark = 0; lastLeftTBIndex = lastRightTBIndex = 0; absolutelyPositioned = new Vector (1, true); containingBlockWasAllocated = containingBlock->wasAllocated (); if (containingBlockWasAllocated) containingBlockAllocation = *(containingBlock->getAllocation()); addWidgetInFlow (containingBlock, NULL, 0); } OutOfFlowMgr::~OutOfFlowMgr () { //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); delete leftFloatsCB; delete rightFloatsCB; // Order is important: tbInfosByTextblock is owner of the instances // of TBInfo.tbInfosByTextblock delete tbInfos; delete tbInfosByTextblock; delete floatsByWidget; // Order is important, since the instances of Float are owned by // leftFloatsAll and rightFloatsAll, so these should be deleted // last. delete leftFloatsAll; delete rightFloatsAll; delete absolutelyPositioned; } void OutOfFlowMgr::sizeAllocateStart (Allocation *containingBlockAllocation) { DBG_OBJ_MSG ("resize.floats", 0, "sizeAllocateStart"); this->containingBlockAllocation = *containingBlockAllocation; containingBlockWasAllocated = true; } void OutOfFlowMgr::sizeAllocateEnd () { DBG_OBJ_MSG ("resize.floats", 0, "sizeAllocateEnd"); DBG_OBJ_MSG_START (); // 1. Move floats from GB lists to the one CB list. moveFromGBToCB (LEFT); moveFromGBToCB (RIGHT); // 2. Floats and absolutely positioned blocks have to be allocated sizeAllocateFloats (LEFT); sizeAllocateFloats (RIGHT); sizeAllocateAbsolutelyPositioned (); // 3. Textblocks have already been allocated, but we (i) check // allocation change of textblocks, and (ii) store some information // for later use. for (lout::container::typed::Iterator > it = tbInfosByTextblock->iterator (); it.hasNext (); ) { TypedPointer *key = it.getNext (); TBInfo *tbInfo = tbInfosByTextblock->get (key); Textblock *tb = key->getTypedValue(); Allocation *tbAllocation = getAllocation (tb); int xCB = tbAllocation->x - containingBlockAllocation.x; int yCB = tbAllocation->y - containingBlockAllocation.y; int width = tbAllocation->width; int height = tbAllocation->ascent + tbAllocation->descent; #if 0 // (i) Check allocation change of textblocks. if ((!tbInfo->wasAllocated || tbInfo->xCB != xCB || tbInfo->yCB != yCB || tbInfo->width != width || tbInfo->height != height)) { if (tbInfo->wasAllocated) DBG_OBJ_MSGF ("resize.floats", 1, "%p: old allocation (within CB): %d, %d; %d * %d", tb, tbInfo->xCB, tbInfo->yCB, tbInfo->width, tbInfo->height); else DBG_OBJ_MSGF ("resize.floats", 0, "%p: not allocated before", tb); DBG_OBJ_MSGF ("resize.floats", 1, "%p: new allocation (within CB): %d, %d; %d * %d", tb, xCB, yCB, width, height); // Changed: change borders when covered by floats. int oldPos, newPos; Widget *oldFloat, *newFloat; // To calculate the minimum, both allocations, old and new, // have to be tested. // Old allocation: bool c1 = isTextblockCoveredByFloats (tb, tbInfo->xCB + containingBlockAllocation.x, tbInfo->yCB + containingBlockAllocation.y, tbInfo->width, tbInfo->height, &oldPos, &oldFloat); // new allocation: int c2 = isTextblockCoveredByFloats (tb, tbAllocation->x, tbAllocation->y, width, height, &newPos, &newFloat); DBG_OBJ_MSGF ("resize.floats", 1, "%p covered? then: %s, now: %s.", tb, c1 ? "yes" : "no", c2 ? "yes" : "no"); if (c1 || c2) { if (!c1) tb->borderChanged (newPos, newFloat); else if (!c2) tb->borderChanged (oldPos, oldFloat); else { if (oldPos < newPos) tb->borderChanged (oldPos, oldFloat); else tb->borderChanged (newPos, newFloat); } } } #else int minFloatPos; Widget *minFloat; if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) tb->borderChanged (minFloatPos, minFloat); #endif // TODO Comment and re-number. checkChangedFloatSizes (); } // (ii) Store some information for later use. for (lout::container::typed::Iterator > it = tbInfosByTextblock->iterator (); it.hasNext (); ) { TypedPointer *key = it.getNext (); TBInfo *tbInfo = tbInfosByTextblock->get (key); Textblock *tb = key->getTypedValue(); Allocation *tbAllocation = getAllocation (tb); tbInfo->wasAllocated = true; tbInfo->xCB = tbAllocation->x - containingBlockAllocation.x;; tbInfo->yCB = tbAllocation->y - containingBlockAllocation.y;; tbInfo->width = tbAllocation->width; tbInfo->height = tbAllocation->ascent + tbAllocation->descent; tbInfo->availWidth = tb->getAvailWidth (); } DBG_OBJ_MSG_END (); } bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, Widget **minFloat) { int leftMinPos, rightMinPos; Widget *leftMinFloat, *rightMinFloat; bool c1 = hasRelationChanged (tbInfo, LEFT, &leftMinPos, &leftMinFloat); bool c2 = hasRelationChanged (tbInfo, RIGHT, &rightMinPos, &rightMinFloat); if (c1 || c2) { if (!c1) { *minFloatPos = rightMinPos; *minFloat = rightMinFloat; } else if (!c2) { *minFloatPos = leftMinPos; *minFloat = leftMinFloat; } else { if (leftMinPos < rightMinPos) { *minFloatPos = leftMinPos; *minFloat = leftMinFloat; } else{ *minFloatPos = rightMinPos; *minFloat = rightMinFloat; } } } return c1 || c2; } bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, Widget **minFloat) { SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; bool covered = false; for (int i = 0; i < list->size(); i++) { // TODO binary search? Float *vloat = list->get(i); int floatPos; // TODO Clarify the old condition: tb != v->generatingBlock. Neccessary? if (tbInfo->textblock != vloat->generatingBlock) { Allocation *tba = getAllocation (tbInfo->textblock); Allocation *gba = getAllocation (vloat->generatingBlock); TBInfo *gbInfo = getTextblock (vloat->generatingBlock); int oldFlx, oldFly, newFlx, newFly; if (gbInfo->wasAllocated) { oldFlx= calcFloatX (vloat, side, gbInfo->xCB, gbInfo->width, gbInfo->availWidth); oldFly = gbInfo->xCB + vloat->yReal; } else oldFlx = oldFly = 0; newFlx = calcFloatX (vloat, side, vloat->generatingBlock->getAllocation()->x - containingBlockAllocation.x, gba->width, vloat->generatingBlock->getAvailWidth ()); newFly = vloat->generatingBlock->getAllocation()->y - containingBlockAllocation.y + vloat->yReal; DBG_OBJ_MSGF ("resize.floats", 0, "Has relation changed between textblock %p and " "float %p?", tbInfo->textblock, vloat->widget); DBG_OBJ_MSG_START (); if (hasRelationChanged (tbInfo->wasAllocated, tbInfo->xCB, tbInfo->yCB, tbInfo->width, tbInfo->height, tba->x - containingBlockAllocation.x, tba->y - containingBlockAllocation.x, tba->width, tba->ascent + tba->descent, gbInfo->wasAllocated, oldFlx, oldFly, vloat->size.width, vloat->size.ascent + vloat->size.descent, newFlx, newFly, vloat->size.width, vloat->size.ascent + vloat->size.descent, side, &floatPos)) { if (!covered || floatPos < *minFloatPos) { *minFloatPos = floatPos; *minFloat = vloat->widget; } covered = true; DBG_OBJ_MSG ("resize.floats", 0, "Yes."); } else DBG_OBJ_MSG ("resize.floats", 0, "No."); DBG_OBJ_MSG_END (); } // All floarts are searched, to find the minimum. TODO: Are // floats sorted, so this can be shortened? (The first is the // minimum?) } return covered; } /** * \brief ... * * All coordinates are given relative to the CB. *floatPos is relative * to the TB, and may be negative. */ bool OutOfFlowMgr::hasRelationChanged (bool oldTBAlloc, int oldTBx, int oldTBy, int oldTBw, int oldTBh, int newTBx, int newTBy, int newTBw, int newTBh, bool oldFlAlloc, int oldFlx, int oldFly, int oldFlw, int oldFlh, int newFlx, int newFly, int newFlw, int newFlh, Side side, int *floatPos) { DBG_OBJ_MSG ("resize.floats", 0, "hasRelationChanged (...)"); DBG_OBJ_MSG_START (); if (oldTBAlloc) DBG_OBJ_MSGF ("resize.floats", 0, "old TB: %d, %d; %d * %d", oldTBx, oldTBy, oldTBw, oldTBh); else DBG_OBJ_MSG ("resize.floats", 0, "old TB: undefined"); DBG_OBJ_MSGF ("resize.floats", 0, "new TB: %d, %d; %d * %d", newTBx, newTBy, newTBw, newTBh); if (oldFlAlloc) DBG_OBJ_MSGF ("resize.floats", 0, "old Fl: %d, %d; %d * %d", oldFlx, oldFly, oldFlw, oldFlh); else DBG_OBJ_MSG ("resize.floats", 0, "old Fl: undefined"); DBG_OBJ_MSGF ("resize.floats", 0, "new Fl: %d, %d; %d * %d", newFlx, newFly, newFlw, newFlh); bool result; if (oldTBAlloc && oldFlAlloc) { bool oldCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; if (oldCov && newCov) { int yOld = oldFly - oldTBy, yNew = newFly - newTBy; if (yOld == yNew) { // Float position has not changed, but perhaps the amout // how far the float reaches into the TB. (TODO: // Generally, not only here, it could be tested whether // the float reaches into the TB at all.) int wOld, wNew; if (side == LEFT) { wOld = oldFlx + oldFlw - oldTBx; wNew = newFlx + newFlw - newTBx; } else { wOld = oldTBx + oldTBw - oldFlx; wNew = newTBx + newTBw - newFlx; } if (wOld == wNew) { if (oldFlh == newFlh) result = false; else { // Only heights of floats changed. Relevant only // from bottoms of float. result = min (yOld + oldFlh, yNew + newFlh); } } else { *floatPos = yOld; result = true; } } else { *floatPos = min (yOld, yNew); result = true; } } else if (oldCov) { *floatPos = oldFly - oldTBy; result = true; } else if (newCov) { *floatPos = newFly - newTBy; result = true; } else result = false; } else { // Not allocated before: ignore all old values, only check whether // TB is covered by Float. if (newFly + newFlh > newTBy && newFly < newTBy + newTBh) { *floatPos = newFly - newTBy; result = true; } else result = false; } DBG_OBJ_MSG_END (); return result; } bool OutOfFlowMgr::isTextblockCoveredByFloats (Textblock *tb, int tbx, int tby, int tbWidth, int tbHeight, int *minFloatPos, Widget **minFloat) { int leftMinPos, rightMinPos; Widget *leftMinFloat, *rightMinFloat; bool c1 = isTextblockCoveredByFloats (leftFloatsCB, tb, tbx, tby, tbWidth, tbHeight, &leftMinPos, &leftMinFloat); bool c2 = isTextblockCoveredByFloats (rightFloatsCB, tb, tbx, tby, tbWidth, tbHeight, &rightMinPos, &rightMinFloat); if (c1 || c2) { if (!c1) { *minFloatPos = rightMinPos; *minFloat = rightMinFloat; } else if (!c2) { *minFloatPos = leftMinPos; *minFloat = leftMinFloat; } else { if (leftMinPos < rightMinPos) { *minFloatPos = leftMinPos; *minFloat = leftMinFloat; } else{ *minFloatPos = rightMinPos; *minFloat = rightMinFloat; } } } return c1 || c2; } bool OutOfFlowMgr::isTextblockCoveredByFloats (SortedFloatsVector *list, Textblock *tb, int tbx, int tby, int tbWidth, int tbHeight, int *minFloatPos, Widget **minFloat) { bool covered = false; for (int i = 0; i < list->size(); i++) { // TODO binary search Float *vloat = list->get(i); int floatPos; // TODO Clarify the old condition: tb != v->generatingBlock. Neccessary? if (tb != vloat->generatingBlock && isTextblockCoveredByFloat (vloat, tb, tbx, tby, tbWidth, tbHeight, &floatPos)) { if (!covered || floatPos < *minFloatPos) { *minFloatPos = floatPos; *minFloat = vloat->widget; } covered = true; } // All floarts are searched, to find the minimum. TODO: Are // floats sorted, so this can be shortened? (The first is the // minimum?) } return covered; } bool OutOfFlowMgr::isTextblockCoveredByFloat (Float *vloat, Textblock *tb, int tbx, int tby, int tbWidth, int tbHeight, int *floatPos) { assert (wasAllocated (vloat->generatingBlock)); int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; int y1 = getAllocation(vloat->generatingBlock)->y + vloat->yReal; int y2 = y1 + flh; // TODO: Also regard horizontal dimension (same for tellFloatPosition)? if (y2 > tby && y1 < tby + tbHeight) { *floatPos = y1 - tby; return true; } else return false; } void OutOfFlowMgr::checkChangedFloatSizes () { DBG_OBJ_MSG ("resize.floats", 0, "checkChangedFloatSizes"); DBG_OBJ_MSG_START (); checkChangedFloatSizes (leftFloatsCB); checkChangedFloatSizes (rightFloatsCB); DBG_OBJ_MSG_END (); } void OutOfFlowMgr::checkChangedFloatSizes (SortedFloatsVector *list) { DBG_OBJ_MSG ("resize.floats", 0, "checkChangedFloatSizes (list)"); DBG_OBJ_MSG_START (); // TODO (i) Comment (ii) linear search? for (int i = 0; i < list->size(); i++) { // TODO binary search Float *vloat = list->get(i); if (vloat->sizeChangedSinceLastAllocation && wasAllocated (vloat->generatingBlock)) { DBG_OBJ_MSGF ("resize.floats", 1, "float %p: checking textblocks", vloat->widget); DBG_OBJ_MSG_START (); for (lout::container::typed::Iterator > it = tbInfosByTextblock->iterator (); it.hasNext (); ) { Textblock *tb = it.getNext()->getTypedValue(); if (wasAllocated (tb)) { Allocation *tba = getAllocation (tb); int floatPos; if (isTextblockCoveredByFloat (vloat, tb, tba->x - containingBlockAllocation.x, tba->y - containingBlockAllocation.y, tba->width, tba->ascent + tba->descent, &floatPos)) { DBG_OBJ_MSGF ("resize.floats", 2, "%p: covereds", tb); tb->borderChanged (floatPos, vloat->widget); } else DBG_OBJ_MSGF ("resize.floats", 2, "%p: not covered", tb); } else DBG_OBJ_MSGF ("resize.floats", 2, "%p: not allocated", tb); } vloat->sizeChangedSinceLastAllocation = false; DBG_OBJ_MSG_END (); } } DBG_OBJ_MSG_END (); } void OutOfFlowMgr::moveFromGBToCB (Side side) { SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; for (int mark = 0; mark <= *floatsMark; mark++) for (lout::container::typed::Iterator it = tbInfos->iterator (); it.hasNext (); ) { TBInfo *tbInfo = it.getNext (); SortedFloatsVector *src = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; for (int i = 0; i < src->size (); i++) { Float *vloat = src->get(i); if (!vloat->inCBList && vloat->mark == mark) { dest->put (vloat); //printf("[%p] moving %s float %p (%s %p, mark %d) to CB list\n", // containingBlock, side == LEFT ? "left" : "right", // vloat, vloat->widget->getClassName(), vloat->widget, // vloat->mark); } } } *floatsMark = 0; /* Old code: GB lists do not have to be cleared, but their contents are still useful after allocation. Soon to be deleted, not only uncommented. for (lout::container::typed::Iterator it = tbInfos->iterator (); it.hasNext (); ) { TBInfo *tbInfo = it.getNext (); SortedFloatsVector *src = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; src->clear (); } */ //printf ("[%p] new %s list:\n", // containingBlock, side == LEFT ? "left" : "right"); //for (int i = 0; i < dest->size(); i++) // printf (" %d: %s\n", i, dest->get(i)->toString()); } void OutOfFlowMgr::sizeAllocateFloats (Side side) { SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; for (int i = 0; i < list->size(); i++) { // TODO Missing: check newly calculated positions, collisions, // and queue resize, when neccessary. TODO: See step 2? Float *vloat = list->get(i); ensureFloatSize (vloat); Allocation *gbAllocation = getAllocation(vloat->generatingBlock); Allocation *cbAllocation = getAllocation(containingBlock); Allocation childAllocation; childAllocation.x = cbAllocation->x + calcFloatX (vloat, side, gbAllocation->x - cbAllocation->x, gbAllocation->width, vloat->generatingBlock->getAvailWidth()); childAllocation.y = gbAllocation->y + vloat->yReal; childAllocation.width = vloat->size.width; childAllocation.ascent = vloat->size.ascent; childAllocation.descent = vloat->size.descent; vloat->widget->sizeAllocate (&childAllocation); //printf (" allocate %s float #%d -> (%d, %d), %d x (%d + %d)\n", // side == LEFT ? "left" : "right", i, childAllocation.x, // childAllocation.y, childAllocation.width, // childAllocation.ascent, childAllocation.descent); } } /** * \brief ... * * gbX is given relative to the CB, as is the return value. */ int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth, int gbAvailWidth) { int gbActualWidth; switch (side) { case LEFT: // Left floats are always aligned on the left side of the // generator (content, not allocation). return gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); break; case RIGHT: // In some cases, the actual (allocated) width is too large; we // use the "available" width here. gbActualWidth = min (gbWidth, gbAvailWidth); // 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). return max (gbX + gbActualWidth - vloat->size.width - vloat->generatingBlock->getStyle()->boxRestWidth(), // Do not exceed CB allocation: 0); default: assertNotReached (); return 0; } } void OutOfFlowMgr::draw (View *view, Rectangle *area) { drawFloats (leftFloatsCB, view, area); drawFloats (rightFloatsCB, view, area); drawAbsolutelyPositioned (view, area); } void OutOfFlowMgr::drawFloats (SortedFloatsVector *list, View *view, Rectangle *area) { // 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); core::Rectangle childArea; if (vloat->widget->intersects (area, &childArea)) vloat->widget->draw (view, &childArea); } } void OutOfFlowMgr::drawAbsolutelyPositioned (View *view, Rectangle *area) { for (int i = 0; i < absolutelyPositioned->size(); i++) { AbsolutelyPositioned *abspos = absolutelyPositioned->get(i); core::Rectangle childArea; if (abspos->widget->intersects (area, &childArea)) abspos->widget->draw (view, &childArea); } } /** * This method consideres also the attributes not yet considered by * dillo, so that the containing block is determined correctly, which * leads sometimes to a cleaner rendering. */ bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget) { return widget->getStyle()->vloat != core::style::FLOAT_NONE || widget->getStyle()->position == core::style::POSITION_ABSOLUTE || widget->getStyle()->position == core::style::POSITION_FIXED; } bool OutOfFlowMgr::isWidgetHandledByOOFM (core::Widget *widget) { // May be extended for fixed (and relative?) positions. return isWidgetFloat (widget); // TODO temporary disabled: || isWidgetAbsolutelyPositioned (widget); } void OutOfFlowMgr::addWidgetInFlow (Textblock *textblock, Textblock *parentBlock, int externalIndex) { //printf ("[%p] addWidgetInFlow (%p, %p, %d)\n", // containingBlock, textblock, parentBlock, externalIndex); TBInfo *tbInfo = new TBInfo (this, textblock, parentBlock ? getTextblock (parentBlock) : NULL, externalIndex); tbInfo->wasAllocated = false; tbInfo->index = tbInfos->size(); tbInfos->put (tbInfo); tbInfosByTextblock->put (new TypedPointer (textblock), tbInfo); } void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, int externalIndex) { if (isWidgetFloat (widget)) { TBInfo *tbInfo = getTextblock (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: leftFloatsAll->put (vloat); widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1); tbInfo->leftFloatsGB->put (vloat); if (wasAllocated (generatingBlock)) { leftFloatsCB->put (vloat); //printf ("[%p] adding left float %p (%s %p) to CB list\n", // containingBlock, vloat, widget->getClassName(), widget); } else { if (tbInfo->index < lastLeftTBIndex) leftFloatsMark++; vloat->mark = leftFloatsMark; //printf ("[%p] adding left float %p (%s %p, mark %d) to GB list " // "(index %d, last = %d)\n", // containingBlock, vloat, widget->getClassName(), widget, // vloat->mark, tbInfo->index, lastLeftTBIndex); lastLeftTBIndex = tbInfo->index; } break; case FLOAT_RIGHT: rightFloatsAll->put (vloat); widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1); tbInfo->rightFloatsGB->put (vloat); if (wasAllocated (generatingBlock)) { rightFloatsCB->put (vloat); //printf ("[%p] adding right float %p (%s %p) to CB list\n", // containingBlock, vloat, widget->getClassName(), widget); } else { if (tbInfo->index < lastRightTBIndex) rightFloatsMark++; vloat->mark = rightFloatsMark; //printf ("[%p] adding right float %p (%s %p, mark %d) to GB list " // "(index %d, last = %d)\n", // containingBlock, vloat, widget->getClassName(), widget, // vloat->mark, tbInfo->index, lastRightTBIndex); lastRightTBIndex = tbInfo->index; } break; default: assertNotReached(); } // "sideSpanningIndex" is only compared, so this simple // assignment is sufficient; differenciation between GB and CB // lists is not neccessary. TODO: Can this also be applied to // "index", to simplify the current code? Check: where is // "index" used. vloat->sideSpanningIndex = leftFloatsAll->size() + rightFloatsAll->size() - 1; floatsByWidget->put (new TypedPointer (widget), vloat); } else if (isWidgetAbsolutelyPositioned (widget)) { AbsolutelyPositioned *abspos = new AbsolutelyPositioned (this, widget, generatingBlock, externalIndex); absolutelyPositioned->put (abspos); widget->parentRef = createRefAbsolutelyPositioned (absolutelyPositioned->size() - 1); } else // May be extended. assertNotReached(); } void OutOfFlowMgr::moveExternalIndices (Textblock *generatingBlock, int oldStartIndex, int diff) { TBInfo *tbInfo = getTextblock (generatingBlock); moveExternalIndices (tbInfo->leftFloatsGB, oldStartIndex, diff); moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff); } void OutOfFlowMgr::moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, int diff) { // Could be faster with binary search, but the GB (not CB!) lists // should be rather small. for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); if (vloat->externalIndex >= oldStartIndex) vloat->externalIndex += diff; } } OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) { TypedPointer key (widget); Float *vloat = floatsByWidget->get (&key); assert (vloat != NULL); return vloat; } void OutOfFlowMgr::markSizeChange (int ref) { //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref); if (isRefFloat (ref)) { Float *vloat; if (isRefLeftFloat (ref)) { int i = getFloatIndexFromRef (ref); vloat = leftFloatsAll->get (i); //printf (" => left float %d\n", i); } else if (isRefRightFloat (ref)) { int i = getFloatIndexFromRef (ref); vloat = rightFloatsAll->get (i); //printf (" => right float %d\n", i); } else { assertNotReached(); vloat = NULL; // compiler happiness } vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; // Effects take place in ensureFloatSize. } else if (isRefAbsolutelyPositioned (ref)) { int i = getAbsolutelyPositionedIndexFromRef (ref); absolutelyPositioned->get(i)->dirty = true; } else assertNotReached(); } void OutOfFlowMgr::markExtremesChange (int ref) { // Nothing to do here. } Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level) { Widget *childAtPoint = getFloatWidgetAtPoint (leftFloatsCB, x, y, level); if (childAtPoint == NULL) childAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, level); if (childAtPoint == NULL) childAtPoint = getAbsolutelyPositionedWidgetAtPoint (x, y, level); return childAtPoint; } Widget *OutOfFlowMgr::getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, int level) { for (int i = 0; i < list->size(); i++) { // Could use binary search to be faster. Float *vloat = list->get(i); Widget *childAtPoint = vloat->widget->getWidgetAtPoint (x, y, level + 1); if (childAtPoint) return childAtPoint; } return NULL; } Widget *OutOfFlowMgr::getAbsolutelyPositionedWidgetAtPoint (int x, int y, int level) { for (int i = 0; i < absolutelyPositioned->size(); i++) { AbsolutelyPositioned *abspos = absolutelyPositioned->get(i); Widget *childAtPoint = abspos->widget->getWidgetAtPoint (x, y, level + 1); if (childAtPoint) return childAtPoint; } return NULL; } void OutOfFlowMgr::tellPosition (Widget *widget, int yReq) { if (isWidgetFloat (widget)) tellFloatPosition (widget, yReq); // Nothing to do for absolutely positioned blocks. } void OutOfFlowMgr::tellFloatPosition (Widget *widget, int yReq) { DBG_OBJ_MSGF ("resize.floats", 0, "tellFloatPosition (%p, %d)", widget, yReq); DBG_OBJ_MSG_START (); assert (yReq >= 0); Float *vloat = findFloatByWidget(widget); //printf ("[%p] TELL_FLOAT_POSITION (%p (%s), %d)\n", // containingBlock, widget, widget->getClassName (), yReq); //printf (" this float: %s\n", vloat->toString()); SortedFloatsVector *listSame, *listOpp; getFloatsLists (vloat, &listSame, &listOpp); ensureFloatSize (vloat); //printf (" ensured size: %s\n", vloat->toString()); //printf (" all floats on same side (%s):\n", // listSame->type == SortedFloatsVector::GB ? "GB" : "CB"); //for (int i = 0; i < listSame->size(); i++) // printf (" %d: %p, %s\n", i, listSame->get(i), // listSame->get(i)->toString()); int oldY = vloat->yReal; // "yReal" may change due to collisions (see below). vloat->yReq = vloat->yReal = yReq; // Test collisions (on this side). Only previous float is relevant. int yRealNew; if (vloat->index >= 1 && collides (vloat, listSame->get (vloat->index - 1), &yRealNew)) { vloat->yReal = yRealNew; //printf (" collides; yReal = %d\n", vloat->yReal); } // Test collisions (on the opposite side). Search the last float on // the other size before this float; only this is relevant. int lastOppFloat = listOpp->findLastBeforeSideSpanningIndex (vloat->sideSpanningIndex); if (lastOppFloat >= 0) { Float *last = listOpp->get (lastOppFloat); if (collides (vloat, last, &yRealNew)) { // Here, test also horizontal values. bool collidesH; if (vloat->generatingBlock == last->generatingBlock) collidesH = vloat->size.width + last->size.width + vloat->generatingBlock->getStyle()->boxDiffWidth() > vloat->generatingBlock->getAvailWidth(); else { // Here (different generating blocks) it can be assumed // that the allocations are defined, otherwise, the float // "last" would not be found in "listOpp". assert (wasAllocated (vloat->generatingBlock)); assert (wasAllocated (last->generatingBlock)); Float *left, *right; if (widget->getStyle()->vloat == FLOAT_LEFT) { left = vloat; right = last; } else { left = last; right = vloat; } // right border of the left float (canvas coordinates) int rightOfLeft = left->generatingBlock->getAllocation()->x + left->generatingBlock->getStyle()->boxOffsetX() + left->size.width; // left border of the right float (canvas coordinates) int leftOfRight = right->generatingBlock->getAllocation()->x + min (right->generatingBlock->getAllocation()->width, right->generatingBlock->getAvailWidth()) - right->generatingBlock->getStyle()->boxRestWidth() - right->size.width; collidesH = rightOfLeft > leftOfRight; } if (collidesH) vloat->yReal = yRealNew; } } DBG_OBJ_MSGF ("resize.floats", 1, "oldY = %d, vloat->yReq = %d, vloat->yReal = %d", oldY, vloat->yReq, vloat->yReal); // No call neccessary when yReal has not changed. (Notice that // checking for yReq is wrong: yReq may remain the same, when yReal // changes, e. g. when previous float has changes its size. if (vloat->yReal != oldY) checkCoveragePosChanged (vloat, oldY); DBG_OBJ_MSG_END (); } bool OutOfFlowMgr::collides (Float *vloat, Float *other, int *yReal) { ensureFloatSize (other); int otherH = other->size.ascent + other->size.descent; if (wasAllocated (other->generatingBlock)) { assert (wasAllocated (vloat->generatingBlock)); if (getAllocation(vloat->generatingBlock)->y + vloat->yReal < getAllocation(other->generatingBlock)->y + other->yReal + otherH) { *yReal = getAllocation(other->generatingBlock)->y + other->yReal + otherH - getAllocation(vloat->generatingBlock)->y; return true; } } else { assert (vloat->generatingBlock == other->generatingBlock); if (vloat->yReal < other->yReal + otherH) { *yReal = other->yReal + otherH; return true; } } return false; } void OutOfFlowMgr::checkCoveragePosChanged (Float *vloat, int oldY) { DBG_OBJ_MSGF ("resize.floats", 0, "checkCoveragePosChanged (, %d)", vloat->widget, oldY); DBG_OBJ_MSG_START (); // Only this float has been changed (see tellFloatPosition), so // only this float has to be tested against all textblocks. if (wasAllocated (vloat->generatingBlock)) { // TODO This (and similar code) is not very efficient. for (lout::container::typed::Iterator > it = tbInfosByTextblock->iterator (); it.hasNext (); ) { TypedPointer *key = it.getNext (); Textblock *textblock = key->getTypedValue(); if (textblock != vloat->generatingBlock && wasAllocated (textblock)) { Allocation *tba = getAllocation (textblock); Allocation *gba = getAllocation (vloat->generatingBlock); int tby1 = tba->y; int tby2 = tba->y + tba->ascent + tba->descent; int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; int y1old = gba->y + oldY, y2old = y1old + flh; int y1new = gba->y + vloat->yReal, y2new = y1new + flh; bool covered = (y2old > tby1 && y1old < tby2) || (y2new > tby1 && y1new < tby2); DBG_OBJ_MSGF ("resize.floats", 0, "%p covered: (%d > %d && %d < %d) || " "(%d > %d && %d < %d? %s.\n", textblock, y2old, tby1, y1old, tby2, y2new, tby1, y1new,tby2, covered ? "Yes" : "No"); if (covered) { int yTextblock = gba->y + min (oldY, vloat->yReal) - tba->y; textblock->borderChanged (yTextblock, vloat->widget); } } } } DBG_OBJ_MSG_END (); } void OutOfFlowMgr::getFloatsLists (Float *vloat, SortedFloatsVector **listSame, SortedFloatsVector **listOpp) { TBInfo *tbInfo = getTextblock (vloat->generatingBlock); switch (vloat->widget->getStyle()->vloat) { case FLOAT_LEFT: if (wasAllocated (vloat->generatingBlock)) { if (listSame) *listSame = leftFloatsCB; if (listOpp) *listOpp = rightFloatsCB; } else { if (listSame) *listSame = tbInfo->leftFloatsGB; if (listOpp) *listOpp = tbInfo->rightFloatsGB; } break; case FLOAT_RIGHT: if (wasAllocated (vloat->generatingBlock)) { if (listSame) *listSame = rightFloatsCB; if (listOpp) *listOpp = leftFloatsCB; } else { if (listSame) *listSame = tbInfo->rightFloatsGB; if (listOpp) *listOpp = tbInfo->leftFloatsGB; } break; default: assertNotReached(); } } void OutOfFlowMgr::getSize (int cbWidth, int cbHeight, int *oofWidth, int *oofHeight) { // CbWidth and cbHeight *do* contain padding, border, and // margin. See call in dw::Textblock::sizeRequest. (Notice that // this has changed from an earlier version.) // Also notice that Float::y includes margins etc. // TODO Is it correct to add padding, border, and margin to the // containing block? Check CSS spec. //printf ("[%p] GET_SIZE (%d, %d, ...): %d / %d floats...\n", // containingBlock, cbWidth, cbHeight, // leftFloatsCB->size(), rightFloatsCB->size()); int oofWidthAbsPos, oofHeightAbsPos; getAbsolutelyPositionedSize (&oofWidthAbsPos, &oofHeightAbsPos); int oofWidthtLeft, oofWidthRight, oofHeightLeft, oofHeightRight; getFloatsSize (leftFloatsCB, LEFT, &oofWidthtLeft, &oofHeightLeft); getFloatsSize (rightFloatsCB, RIGHT, &oofWidthRight, &oofHeightRight); *oofWidth = max (oofWidthtLeft, oofWidthRight, oofWidthAbsPos); *oofHeight = max (oofHeightLeft, oofHeightRight, oofHeightAbsPos); //printf (" => %d x %d => %d x %d (%d / %d)\n", // cbWidth, cbHeight, *oofWidth, *oofHeight, // oofHeightLeft, oofHeightRight); } void OutOfFlowMgr::getFloatsSize (SortedFloatsVector *list, Side side, int *width, int *height) { *width = 0; *height = containingBlock->getStyle()->boxDiffHeight(); // Idea for a faster implementation: find the last float; this // should be the relevant one, since the list is sorted. for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); ensureFloatSize (vloat); // 1. Height: May play a role when the float is too wide. // Minimum taken, since the relevant case is when the containing // block is otherwise not wide enough. int borderDiff = getMinBorderDiff (vloat, side); *width = max (*width, vloat->size.width + borderDiff); // 2. Height: Plays a role for floats hanging over at the bottom // of the page. // Notice that all positions are relative to the generating // block, but we need them relative to the containing block. // Position of generating block, relative to containing // block. Greater or equal than 0, so dealing with 0 when it // cannot yet be calculated is safe. (No distiction whether it // is defined or not is necessary.) int yGBinCB; if (vloat->generatingBlock == containingBlock) // Simplest case: the generator is the container. yGBinCB = 0; else { if (wasAllocated (containingBlock)) { if (wasAllocated (vloat->generatingBlock)) // Simple case: both containing block and generating // block are defined. yGBinCB = getAllocation(vloat->generatingBlock)->y - containingBlock->getAllocation()->y; else // Generating block not yet allocation; the next // allocation will, when necessary, trigger // sizeRequest. (TODO: Is this really the case?) yGBinCB = 0; } else // Nothing can be done now, but the next allocation // will trigger sizeAllocate. (TODO: Is this really the // case?) yGBinCB = 0; } *height = max (*height, yGBinCB + vloat->yReal + vloat->size.ascent + vloat->size.descent + containingBlock->getStyle()->boxRestHeight()); //printf (" float %d: (%d + %d) + (%d + %d + %d) => %d\n", // i, yGBinCB, vloat->yReal, vloat->size.ascent, // vloat->size.descent, // containingBlock->getStyle()->boxRestHeight(), *height); } } void OutOfFlowMgr::getExtremes (int cbMinWidth, int cbMaxWidth, int *oofMinWidth, int *oofMaxWidth) { *oofMinWidth = *oofMaxWidth = 0; accumExtremes (leftFloatsCB, LEFT, oofMinWidth, oofMaxWidth); accumExtremes (rightFloatsCB, RIGHT, oofMinWidth, oofMaxWidth); // TODO Absolutely positioned elements } void OutOfFlowMgr::accumExtremes (SortedFloatsVector *list, Side side, int *oofMinWidth, int *oofMaxWidth) { // Idea for a faster implementation: use incremental resizing? for (int i = 0; i < list->size(); i++) { Float *vloat = list->get(i); int minBorderDiff = getMinBorderDiff (vloat, side); int maxBorderDiff = getMaxBorderDiff (vloat, side); Extremes extr; vloat->widget->getExtremes (&extr); *oofMinWidth = max (*oofMinWidth, extr.minWidth + minBorderDiff); *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + maxBorderDiff); } } /** * Minimal difference between generating block and to containing * block, Greater or equal than 0, so dealing with 0 when it cannot * yet be calculated is safe. (No distiction whether it is defined or * not is necessary.) */ int OutOfFlowMgr::getMinBorderDiff (Float *vloat, Side side) { if (vloat->generatingBlock == containingBlock) // Simplest case: the generator is the container. // Since the way, how left and right floats are positioned when // there is not much space, is not symmetric, the *minimal* value // considered for the margin/border/padding of the generating block // is *zero* for floats on the right. return side == LEFT ? vloat->generatingBlock->getStyle()->boxOffsetX() : 0; else { if (wasAllocated (containingBlock)) { if (wasAllocated (vloat->generatingBlock)) { // Simple case: both containing block and generating block // are defined. Allocation *gba = getAllocation(vloat->generatingBlock); Allocation *cba = getAllocation(containingBlock); if (side == LEFT) return gba->x - cba->x + vloat->generatingBlock->getStyle()->boxOffsetX(); else // For margin/border/padding see comment above. Also, // in the worst case, the float can take the whole CB // (not GB!) width. Therefore: return 0; } else // Generating block not yet allocation; the next // allocation will, when necessary, trigger // getExtremes. (TODO: Is this really the case?) return 0; } else // Nothing can be done now, but the next allocation will // trigger getExtremes. (TODO: Is this really the case?) return 0; } } /** * Maximal difference between generating block and to containing * block, i. e. sum on both sides. */ int OutOfFlowMgr::getMaxBorderDiff (Float *vloat, Side side) { if (vloat->generatingBlock == containingBlock) // Simplest case: the generator is the container. return vloat->generatingBlock->getStyle()->boxDiffWidth(); else { if (wasAllocated (containingBlock)) { if (wasAllocated (vloat->generatingBlock)) // Simple case: both containing block and generating block // are defined. return getAllocation(containingBlock)->width - getAllocation(vloat->generatingBlock)->width + vloat->generatingBlock->getStyle()->boxDiffWidth(); else // Generating block not yet allocation; the next // allocation will, when necessary, trigger // getExtremes. (TODO: Is this really the case?) return 0; } else // Nothing can be done now, but the next allocation will // trigger getExtremes. (TODO: Is this really the case?) return 0; } } OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock) { TypedPointer key (textblock); TBInfo *tbInfo = tbInfosByTextblock->get (&key); assert (tbInfo); return tbInfo; } /** * Get the left border for the vertical position of *y*, for a height * of *h", based on floats; relative to the allocation of the calling * textblock. * * The border includes marging/border/padding of the calling textblock * but is 0 if there is no float, so a caller should also consider * other borders. */ int OutOfFlowMgr::getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB, int lastExtIndex) { int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex); //printf ("getLeftBorder (%p, %d, %d) => %d\n", textblock, y, h, b); return b; } /** * Get the right border for the vertical position of *y*, for a height * of *h", based on floats. * * See also getLeftBorder(int, int); */ int OutOfFlowMgr::getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB, int lastExtIndex) { int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex); //printf ("getRightBorder (%p, %d, %d) => %d\n", textblock, y, h, b); return b; } int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, Textblock *lastGB, int lastExtIndex) { //printf ("[%p] GET_BORDER (%p (allocated: %s), %s, %d, %d, %p, %d)\n", // containingBlock, textblock, // wasAllocated (textblock) ? "true" : "false", // side == LEFT ? "LEFT" : "RIGHT", y, h, lastGB, lastExtIndex); SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); //printf (" searching in list:\n"); //for (int i = 0; i < list->size(); i++) { // printf (" %d: %s\n", i, list->get(i)->toString()); // //printf (" (widget at (%d, %d))\n", // // list->get(i)->widget->getAllocation()->x, // // list->get(i)->widget->getAllocation()->y); //} int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex); //printf (" first = %d\n", first); if (first == -1) // No float. return 0; else { // 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. int border = 0; bool covers = true; // TODO Also check against lastGB and lastExtIndex for (int i = first; covers && i < list->size(); i++) { Float *vloat = list->get(i); covers = vloat->covers (textblock, y, h); //printf (" float %d: %s; covers? %s.\n", // i, vloat->toString(), covers ? "yes" : "no"); if (covers) { int borderDiff = getBorderDiff (textblock, vloat, side); int borderIn = side == LEFT ? vloat->generatingBlock->getStyle()->boxOffsetX() : vloat->generatingBlock->getStyle()->boxRestWidth(); border = max (border, vloat->size.width + borderIn + borderDiff); //printf (" => border = %d\n", border); } } return border; } } OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock (Textblock *textblock, Side side) { if (wasAllocated (textblock)) return side == LEFT ? leftFloatsCB : rightFloatsCB; else { TBInfo *tbInfo = getTextblock (textblock); return side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; } } bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB, int lastExtIndex) { return hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); } bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB, int lastExtIndex) { return hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); } bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, Textblock *lastGB, int lastExtIndex) { //printf ("[%p] hasFloat (%p, %s, %d, %d, %p, %d)\n", // containingBlock, textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, // lastGB, lastExtIndex); SortedFloatsVector *list = getFloatsListForTextblock(textblock, side); return list->findFirst (textblock, y, h, lastGB, lastExtIndex) != -1; } /** * Returns position relative to the textblock "tb". */ int OutOfFlowMgr::getClearPosition (Textblock *tb) { if (tb->getStyle()) { bool left = false, right = false; switch (tb->getStyle()->clear) { case core::style::CLEAR_NONE: break; case core::style::CLEAR_LEFT: left = true; break; case core::style::CLEAR_RIGHT: right = true; break; case core::style::CLEAR_BOTH: left = right = true; break; default: assertNotReached (); } return max (left ? getClearPosition (tb, LEFT) : 0, right ? getClearPosition (tb, RIGHT) : 0); } else return 0; } int OutOfFlowMgr::getClearPosition (Textblock *tb, Side side) { if (!wasAllocated (tb)) // There is no relation yet to floats generated by other // textblocks, and this textblocks floats are unimportant for // the "clear" property. return 0; SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; int i = list->findFloatIndex (tb, 0); if (i < 0) return 0; Float *vloat = list->get(i); // We pass this texblock and 0 (first word, or smallest external // index, respectively), but search for the last float before this // position. Therefore this check. if (vloat->generatingBlock== tb) i--; if (i < 0) return 0; vloat = list->get(i); ensureFloatSize (vloat); return vloat->yReal + vloat->size.ascent + vloat->size.descent - getAllocation(tb)->y; } void OutOfFlowMgr::ensureFloatSize (Float *vloat) { if (vloat->dirty || // If the size of the containing block has changed (represented // currently by the available width), a recalculation of a // relative float width may also be necessary. (isPerLength (vloat->widget->getStyle()->width) && vloat->cbAvailWidth != containingBlock->getAvailWidth ())) { // TODO Ugly. Soon to be replaced by cleaner code? See also // comment in Textblock::calcWidgetSize. if (vloat->widget->usesHints ()) { if (isAbsLength (vloat->widget->getStyle()->width)) vloat->widget->setWidth (absLengthVal (vloat->widget->getStyle()->width)); else if (isPerLength (vloat->widget->getStyle()->width)) vloat->widget->setWidth (multiplyWithPerLength (containingBlock->getAvailWidth(), vloat->widget->getStyle()->width)); } // This is a bit hackish: We first request the size, then set // the available width (also considering the one of the // containing block, and the extremes of the float), then // request the size again, which may of course have a different // result. This is a fix for the bug: // // Text in floats, which are wider because of an image, are // broken at a too narrow width. Reproduce: // test/floats2.html. After the image has been loaded, the // text "Some text in a float." should not be broken // anymore. // // If the call of setWidth not is neccessary, the second call // will read the size from the cache, so no redundant // calculation is necessary. // Furthermore, extremes are considered; especially, floats are too // wide, sometimes. Extremes extremes; vloat->widget->getExtremes (&extremes); vloat->widget->sizeRequest (&vloat->size); // Set width ... int width = vloat->size.width; // Consider the available width of the containing block (when set): if (width > containingBlock->getAvailWidth()) width = containingBlock->getAvailWidth(); // Finally, consider extremes (as described above). if (width < extremes.minWidth) width = extremes.minWidth; if (width > extremes.maxWidth) width = extremes.maxWidth; vloat->widget->setWidth (width); vloat->widget->sizeRequest (&vloat->size); //printf (" float %p (%s %p): %d x (%d + %d)\n", // vloat, vloat->widget->getClassName(), vloat->widget, // vloat->size.width, vloat->size.ascent, vloat->size.descent); vloat->cbAvailWidth = containingBlock->getAvailWidth (); vloat->dirty = false; // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() } } /** * Return the difference between generator and calling textblock. Used * in getBorder() to determine the difference between calling * textblock and float (which position is defined relative to the * generator). */ int OutOfFlowMgr::getBorderDiff (Textblock *textblock, Float *vloat, Side side) { if (textblock == vloat->generatingBlock) return 0; else { assert (wasAllocated (textblock) && wasAllocated (vloat->generatingBlock)); switch (side) { case LEFT: return getAllocation(vloat->generatingBlock)->x - getAllocation(textblock)->x; case RIGHT: return getAllocation(textblock)->x + getAllocation(textblock)->width - (getAllocation(vloat->generatingBlock)->x + min (getAllocation(vloat->generatingBlock)->width, vloat->generatingBlock->getAvailWidth())); default: assertNotReached(); return 0; } } } void OutOfFlowMgr::getAbsolutelyPositionedSize (int *oofWidthAbsPos, int *oofHeightAbsPos) { *oofWidthAbsPos = *oofHeightAbsPos = 0; for (int i = 0; i < absolutelyPositioned->size(); i++) { AbsolutelyPositioned *abspos = absolutelyPositioned->get (i); ensureAbsolutelyPositionedSizeAndPosition (abspos); *oofWidthAbsPos = max (*oofWidthAbsPos, abspos->xCB + abspos->width); *oofHeightAbsPos = max (*oofHeightAbsPos, abspos->yCB + abspos->height); } } void OutOfFlowMgr::ensureAbsolutelyPositionedSizeAndPosition (AbsolutelyPositioned *abspos) { // TODO Similar to floats, changes of the available size of the // containing block are not noticed; which is a bit more severe as // for floats. Find a general solution for both floats and // absolutely positioned blocks. // TODO Compare to ensureFloatSize: some parts are // missing. Nevertheless, a simpler approach should be focussed on. if (abspos->dirty) { Style *style = abspos->widget->getStyle(); int availWidth = containingBlock->getAvailWidth(); int availHeight = containingBlock->getAvailAscent() + containingBlock->getAvailDescent(); if (style->left == LENGTH_AUTO) abspos->xCB = 0; else abspos->xCB = calcValueForAbsolutelyPositioned (abspos, style->left, availWidth); if (style->top == LENGTH_AUTO) abspos->yCB = 0; else abspos->yCB = calcValueForAbsolutelyPositioned (abspos, style->top, availHeight); abspos->width = -1; // undefined if (style->width != LENGTH_AUTO) abspos->width = calcValueForAbsolutelyPositioned (abspos, style->width, availWidth); else if (style->right != LENGTH_AUTO) { int right = calcValueForAbsolutelyPositioned (abspos, style->right, availWidth); abspos->width = max (0, availWidth - (abspos->xCB + right)); } abspos->height = -1; // undefined if (style->height != LENGTH_AUTO) abspos->height = calcValueForAbsolutelyPositioned (abspos, style->height, availHeight); else if (style->bottom != LENGTH_AUTO) { int bottom = calcValueForAbsolutelyPositioned (abspos, style->bottom, availHeight); abspos->height = max (0, availHeight - (abspos->yCB + bottom)); } if (abspos->width != -1) abspos->widget->setWidth (abspos->width); if (abspos->height != -1) { abspos->widget->setAscent (abspos->height); abspos->widget->setDescent (0); // TODO } if (abspos->width == -1 || abspos->height == -1) { Requisition req; abspos->widget->sizeRequest (&req); if (abspos->width == -1) abspos->width = req.width; if (abspos->height == -1) abspos->height = req.ascent + req.descent; } abspos->dirty = false; } } int OutOfFlowMgr::calcValueForAbsolutelyPositioned (AbsolutelyPositioned *abspos, Length styleLen, int refLen) { assert (styleLen != LENGTH_AUTO); if (isAbsLength (styleLen)) return absLengthVal (styleLen); else if (isPerLength (styleLen)) return multiplyWithPerLength (refLen, styleLen); else { assertNotReached (); return 0; // compiler happiness } } void OutOfFlowMgr::sizeAllocateAbsolutelyPositioned () { for (int i = 0; i < absolutelyPositioned->size(); i++) { Allocation *cbAllocation = getAllocation(containingBlock); AbsolutelyPositioned *abspos = absolutelyPositioned->get (i); ensureAbsolutelyPositionedSizeAndPosition (abspos); Allocation childAllocation; childAllocation.x = cbAllocation->x + abspos->xCB; childAllocation.y = cbAllocation->y + abspos->yCB; childAllocation.width = abspos->width; childAllocation.ascent = abspos->height; childAllocation.descent = 0; // TODO abspos->widget->sizeAllocate (&childAllocation); printf ("[%p] allocating child %p at: (%d, %d), %d x (%d + %d)\n", containingBlock, abspos->widget, childAllocation.x, childAllocation.y, childAllocation.width, childAllocation.ascent, childAllocation.descent); } } // TODO Latest change: Check also Textblock::borderChanged: looks OK, // but the comment ("... (i) with canvas coordinates ...") looks wrong // (and looks as having always been wrong). // Another issue: does it make sense to call Textblock::borderChanged // for generators, when the respective widgets have not been called // yet? } // namespace dw