diff options
Diffstat (limited to 'dw/outofflowmgr.cc')
-rw-r--r-- | dw/outofflowmgr.cc | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc new file mode 100644 index 00000000..015fd2ac --- /dev/null +++ b/dw/outofflowmgr.cc @@ -0,0 +1,900 @@ +#include "outofflowmgr.hh" +#include "textblock.hh" + +#include <limits.h> + +using namespace lout::object; +using namespace lout::container::typed; +using namespace lout::misc; +using namespace dw::core; +using namespace dw::core::style; + +namespace dw { + +int OutOfFlowMgr::Float::yForContainer (OutOfFlowMgr *oofm, int y) +{ + return y - generatingBlock->getAllocation()->y + + oofm->containingBlock->getAllocation()->y; +} + +OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) +{ + //printf ("OutOfFlowMgr::OutOfFlowMgr\n"); + + this->containingBlock = containingBlock; + leftFloats = new Vector<Float> (1, true); + rightFloats = new Vector<Float> (1, true); + tbInfos = new HashTable <TypedPointer <Textblock>, TBInfo> (true, true); +} + +OutOfFlowMgr::~OutOfFlowMgr () +{ + //printf ("OutOfFlowMgr::~OutOfFlowMgr\n"); + + delete leftFloats; + delete rightFloats; + delete tbInfos; +} + +void OutOfFlowMgr::sizeAllocate (Allocation *containingBlockAllocation) +{ + // 1. Floats have to be allocated + sizeAllocateFloats (leftFloats, false, containingBlockAllocation); + sizeAllocateFloats (rightFloats, true, containingBlockAllocation); + + // 2. Textblocks have already been allocated, but we store some + // information for later use. TODO: Update this comment! + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfos->iterator (); + it.hasNext (); ) { + TypedPointer <Textblock> *key = it.getNext (); + TBInfo *tbInfo = tbInfos->get (key); + Textblock *tb = key->getTypedValue(); + + int xCB = tb->getAllocation()->x - containingBlockAllocation->x; + int yCB = tb->getAllocation()->y - containingBlockAllocation->y; + int width = tb->getAllocation()->width; + int height = tb->getAllocation()->ascent + tb->getAllocation()->descent; + + if ((!tbInfo->wasAllocated || tbInfo->xCB != xCB || tbInfo->yCB != yCB || + tbInfo->width != width || tbInfo->height != height)) { + int oldPos, newPos; + // To calculate the minimum, both allocations, old and new, + // have to be tested. + + // Old allocation: + bool c1 = isTextblockCoveredByFloats (containingBlockAllocation, tb, + tbInfo->xCB + + containingBlockAllocation->x, + tbInfo->yCB + + containingBlockAllocation->y, + tbInfo->width, tbInfo->height, + &oldPos); + // new allocation: + int c2 = isTextblockCoveredByFloats (containingBlockAllocation, tb, + tb->getAllocation()->x, + tb->getAllocation()->y, + width, height, &newPos); + if (c1 || c2) + tb->borderChanged (min (oldPos, newPos)); + } + + tbInfo->wasAllocated = true; + tbInfo->xCB = xCB; + tbInfo->yCB = yCB; + tbInfo->width = width; + tbInfo->height = height; + } +} + +bool OutOfFlowMgr::isTextblockCoveredByFloats (Allocation + *containingBlockAllocation, + Textblock *tb, int tbx, int tby, + int tbWidth, int tbHeight, + int *floatPos) +{ + int leftPos, rightPos; + bool c1 = isTextblockCoveredByFloats (leftFloats, containingBlockAllocation, + tb, tbx, tby, tbWidth, tbHeight, + &leftPos); + bool c2 = isTextblockCoveredByFloats (rightFloats, containingBlockAllocation, + tb, tbx, tby, tbWidth, tbHeight, + &rightPos); + *floatPos = min (leftPos, rightPos); + return c1 || c2; +} + +bool OutOfFlowMgr::isTextblockCoveredByFloats (Vector<Float> *list, + Allocation + *containingBlockAllocation, + Textblock *tb, int tbx, int tby, + int tbWidth, int tbHeight, + int *floatPos) +{ + *floatPos = INT_MAX; + bool covered = false; + + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + + // This method is called within OOFM::sizeAllocate, which is + // called in Textblock::sizeAllocateImpl for the containing + // block, so that the only textblock which is not necessary + // allocates is the containing block. + assert (vloat->generatingBlock->wasAllocated() || + vloat->generatingBlock == containingBlock); + + // In this case we have to refer to the allocation passed to + // Textblock::sizeAllocateImpl. + Allocation *generatingBlockAllocation = + vloat->generatingBlock == containingBlock ? + containingBlockAllocation : vloat->generatingBlock->getAllocation(); + + // Idea: the distinction could be removed, and the code so made + // simpler, by moving this second part of OOFM::sizeAllocate + // into an idle function. This would also make the question + // obsolete whether it is allowed to call queueResize within + // sizeAllocate. Unfortunately, idle funtions currently only + // refer to layouts, not to widgets. + + if (tb != vloat->generatingBlock && vloat->positioned) { + int flh = vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; + int y1 = generatingBlockAllocation->y + vloat->yReal; + int y2 = y1 + flh; + + // TODO: Also regard horizontal dimension (same for tellPositionOrNot). + if (y2 > tby && y1 < tby + tbWidth) { + covered = true; + *floatPos = min (*floatPos, y1 - tby); + } + } + + // All floarts are searched, to find the minimum. TODO: Are + // floats sorted, so this can be shortene? (The first is the + // minimum?) + } + + return covered; +} + +void OutOfFlowMgr::sizeAllocateFloats (Vector<Float> *list, bool right, + Allocation *containingBlockAllocation) +{ + 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); + assert (vloat->positioned); + ensureFloatSize (vloat); + + Allocation childAllocation; + if (right) + childAllocation.x = + vloat->generatingBlock->getAllocation()->x + + min (vloat->generatingBlock->getAllocation()->width, + vloat->generatingBlock->getAvailWidth()) + - vloat->size.width + - vloat->generatingBlock->getStyle()->boxRestWidth(); + else + childAllocation.x = vloat->generatingBlock->getAllocation()->x + + vloat->generatingBlock->getStyle()->boxOffsetX(); + + childAllocation.y = + vloat->generatingBlock->getAllocation()->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 #%d -> (%d, %d), %d x (%d + %d)\n", + // right ? "right" : "left", i, childAllocation.x, + // childAllocation.y, childAllocation.width, + // childAllocation.ascent, childAllocation.descent); + } +} + + + +void OutOfFlowMgr::draw (View *view, Rectangle *area) +{ + draw (leftFloats, view, area); + draw (rightFloats, view, area); +} + +void OutOfFlowMgr::draw (Vector<Float> *list, View *view, Rectangle *area) +{ + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + assert (vloat->positioned); + + core::Rectangle childArea; + if (vloat->widget->intersects (area, &childArea)) + vloat->widget->draw (view, &childArea); + } +} + +void OutOfFlowMgr::queueResize(int ref) +{ + // TODO Is there something to do? +} + +bool OutOfFlowMgr::isWidgetOutOfFlow (core::Widget *widget) +{ + // Will be extended for absolute positions. + return widget->getStyle()->vloat != FLOAT_NONE; +} + +void OutOfFlowMgr::addWidget (Widget *widget, Textblock *generatingBlock) +{ + if (widget->getStyle()->vloat != FLOAT_NONE) { + Float *vloat = new Float (); + vloat->widget = widget; + vloat->generatingBlock = generatingBlock; + vloat->dirty = true; + vloat->yReq = vloat->positioned; + + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + leftFloats->put (vloat); + widget->parentRef = createRefLeftFloat (leftFloats->size() - 1); + break; + + case FLOAT_RIGHT: + rightFloats->put (vloat); + widget->parentRef = createRefRightFloat (rightFloats->size() - 1); + break; + + default: + assertNotReached(); + } + } else + // Will continue here for absolute positions. + assertNotReached(); +} + +OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) +{ + Vector<Float> *list = getFloatList (widget); + + for(int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + if(vloat->widget == widget) + return vloat; + } + + assertNotReached(); + return NULL; +} + +Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getFloatList (Widget *widget) +{ + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + return leftFloats; + + case FLOAT_RIGHT: + return rightFloats; + + default: + assertNotReached(); + return NULL; + } +} + +Vector<OutOfFlowMgr::Float> *OutOfFlowMgr::getOppositeFloatList (Widget *widget) +{ + switch (widget->getStyle()->vloat) { + case FLOAT_LEFT: + return rightFloats; + + case FLOAT_RIGHT: + return leftFloats; + + default: + assertNotReached(); + return NULL; + } +} + +void OutOfFlowMgr::markSizeChange (int ref) +{ + //printf ("[%p] MARK_SIZE_CHANGE (%d)\n", containingBlock, ref); + + if (isRefFloat (ref)) { + Float *vloat; + + if (isRefLeftFloat (ref)) + vloat = leftFloats->get (getFloatIndexFromRef (ref)); + else if (isRefRightFloat (ref)) + vloat = rightFloats->get (getFloatIndexFromRef (ref)); + else { + assertNotReached(); + vloat = NULL; // compiler happiness + } + + vloat->dirty = true; + // In some cases, the vertical position is not yet defined. Nothing + // necessary then. + if (vloat->positioned) + vloat->generatingBlock->borderChanged (vloat->yReal); + } else + // later: absolute positions + assertNotReached(); +} + + +void OutOfFlowMgr::markExtremesChange (int ref) +{ + // Nothing to do here. +} + +Widget *OutOfFlowMgr::getWidgetAtPoint (int x, int y, int level) +{ + Widget *childAtPoint = getWidgetAtPoint (leftFloats, x, y, level); + if (childAtPoint == NULL) + childAtPoint = getWidgetAtPoint (rightFloats, x, y, level); + return childAtPoint; +} + +Widget *OutOfFlowMgr::getWidgetAtPoint (Vector<Float> *list, + int x, int y, int level) +{ + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + Widget *childAtPoint = vloat->widget->getWidgetAtPoint (x, y, level + 1); + if (childAtPoint) + return childAtPoint; + } + + return NULL; +} + + +/** + * This method is called by Textblock::rewrap at the beginning, to + * avoid wrong positions. + */ +void OutOfFlowMgr::tellNoPosition (Widget *widget) +{ + tellPositionOrNot (widget, 0, false); +} + + +void OutOfFlowMgr::tellPosition (Widget *widget, int y) +{ + assert (y >= 0); + tellPositionOrNot (widget, y, true); +} + +void OutOfFlowMgr::tellPositionOrNot (Widget *widget, int y, bool positioned) +{ + Float *vloat = findFloatByWidget(widget); + if ((!positioned && !vloat->positioned) || + (positioned && vloat->positioned && y == vloat->yReq)) + // Nothing happened. + return; + + if (positioned) + ensureFloatSize (vloat); + int oldY = vloat->yReal; + bool oldPositioned = vloat->positioned; + + // "yReal" may change due to collisions (see below). + vloat->yReq = vloat->yReal = y; + vloat->positioned = positioned; + + if (positioned) { + Vector<Float> *listSame = getFloatList (widget); + Vector<Float> *listOpp = getOppositeFloatList (widget); + bool collides; + + // Collisions. TODO Can this be simplified? + do { + collides = false; + + // Test collisions on the same side. + for (int i = 0; i < listSame->size(); i++) { + Float *v = listSame->get(i); + int yWidget; + if (v != vloat && + getYWidget (vloat->generatingBlock, v, &yWidget)) { + ensureFloatSize (v); + if (v->positioned != -1 && vloat->yReal >= yWidget && + vloat->yReal < yWidget + v->size.ascent + v->size.descent) { + collides = true; + vloat->yReal = yWidget + v->size.ascent + v->size.descent; + break; + } + } + } + + // Test collisions on the other side. + for (int i = 0; i < listOpp->size(); i++) { + Float *v = listOpp->get(i); + // Note: Since v is on the opposite side, the condition + // v != vloat (used above) is always true. + int yWidget; + if (getYWidget (vloat->generatingBlock, v, &yWidget)) { + ensureFloatSize (v); + if (v->positioned != -1 && vloat->yReal >= yWidget && + vloat->yReal < yWidget + v->size.ascent + v->size.descent) { + // For the other side, horizontal dimensions have + // to be considered, too. + bool collidesH; + if (vloat->generatingBlock == v->generatingBlock) + collidesH = vloat->size.width + v->size.width + + vloat->generatingBlock->getStyle()->boxDiffWidth() + > vloat->generatingBlock->getAvailWidth(); + else { + // Here (different generating blocks) it can be + // assumed that the allocations are defined + // (because getYWidget() would have returned + // false, otherwise). + Float *left, *right; + if (listSame == leftFloats) { + left = vloat; + right = v; + } else { + left = v; + 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) { + collides = true; + vloat->yReal = yWidget + v->size.ascent + v->size.descent; + break; + } + } + } + } + } while (collides); + + // It is assumed that there are no floats below this float + // within this generator. For this reason, no other floats have + // to be adjusted. + } + + // Only this float has been changed (see above), so only this float + // has to be tested against all textblocks. + if (vloat->generatingBlock->wasAllocated () && + // A change from "no position" to "no position" is uninteresting. + !(oldPositioned && !vloat->positioned)) { + int yChange; + + if (!vloat->positioned) { + // position -> no position + assert (oldPositioned); + yChange = oldY; + } else if (!oldPositioned) { + // no position -> position + assert (vloat->positioned); + yChange = vloat->yReal; + } else { + // position -> position + assert (oldPositioned && vloat->positioned); + yChange = min (oldY, vloat->yReal); + } + + // TODO This (and similar code) is not very efficient. + for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = + tbInfos->iterator (); + it.hasNext (); ) { + TypedPointer <Textblock> *key = it.getNext (); + Textblock *textblock = key->getTypedValue(); + + if (textblock->wasAllocated () && + // If not positioned, there will be soon a rewrap (see + // Textblock::rewrap), or, if positioned , the generating + // block takes care of the possible change (see + // Textblock::wrapWidgetOofRef), respectively; so, only + // the other textblocks must be told about this. + textblock != vloat->generatingBlock) { + int tby1 = textblock->getAllocation()->y; + int tby2 = tby1 + textblock->getAllocation()->ascent + + textblock->getAllocation()->descent; + int flh = + vloat->dirty ? 0 : vloat->size.ascent + vloat->size.descent; + bool covered = false; + + if (oldPositioned) { + int y1 = vloat->generatingBlock->getAllocation()->y + oldY; + int y2 = y1 + flh; + covered = y2 > tby1 && y1 < tby2; + } + + if (!covered && vloat->positioned) { + int y1 = + vloat->generatingBlock->getAllocation()->y + vloat->yReal; + int y2 = y1 + flh; + covered = y2 > tby1 && y1 < tby2; + } + + if (covered) { + int yTextblock = + vloat->generatingBlock->getAllocation()->y + yChange + - textblock->getAllocation()->y; + textblock->borderChanged (yTextblock); + } + } + } + } +} + +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. + + *oofWidth = cbWidth; /* This (or "<=" instead of "=") should be + the case for floats. */ + + int oofHeightLeft = getFloatsSize (leftFloats); + int oofHeightRight = getFloatsSize (rightFloats); + *oofHeight = max (oofHeightLeft, oofHeightRight); +} + +int OutOfFlowMgr::getFloatsSize (Vector<Float> *list) +{ + int height = containingBlock->getStyle()->boxDiffHeight(); + + // Idea for a faster implementation: find the last float; this + // should be the relevant one. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + if (vloat->positioned) { + ensureFloatSize (vloat); + + // 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 (containingBlock->wasAllocated()) { + if (vloat->generatingBlock->wasAllocated()) + // Simple case: both containing block and generating + // block are defined. + yGBinCB = vloat->generatingBlock->getAllocation()->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()); + } + } + + return height; +} + +void OutOfFlowMgr::getExtremes (int cbMinWidth, int cbMaxWidth, + int *oofMinWidth, int *oofMaxWidth) +{ + *oofMinWidth = *oofMaxWidth = 0; + accumExtremes (leftFloats, oofMinWidth, oofMaxWidth); + accumExtremes (rightFloats, oofMinWidth, oofMaxWidth); +} + +void OutOfFlowMgr::accumExtremes (Vector<Float> *list, 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); + + // Difference between generating block and to containing block, + // sum on both sides. 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 borderDiff; + + if (vloat->generatingBlock == containingBlock) + // Simplest case: the generator is the container. + borderDiff = 0; + else { + if (containingBlock->wasAllocated()) { + if (vloat->generatingBlock->wasAllocated()) + // Simple case: both containing block and generating + // block are defined. + borderDiff = containingBlock->getAllocation()->width - + vloat->generatingBlock->getAllocation()->width; + else + // Generating block not yet allocation; the next + // allocation will, when necessary, trigger + // getExtremes. (TODO: Is this really the case?) + borderDiff = 0; + } else + // Nothing can be done now, but the next allocation will + // trigger getExtremes. (TODO: Is this really the case?) + borderDiff = 0; + } + + Extremes extr; + vloat->widget->getExtremes (&extr); + + *oofMinWidth = max (*oofMinWidth, extr.minWidth + borderDiff); + *oofMaxWidth = max (*oofMaxWidth, extr.maxWidth + borderDiff); + } +} + +void OutOfFlowMgr::registerCaller (Textblock *textblock) +{ + TypedPointer<Textblock> key (textblock); + TBInfo *tbInfo = tbInfos->get (&key); + if (tbInfo == NULL) { + tbInfo = new TBInfo; + tbInfo->wasAllocated = false; + tbInfos->put (new TypedPointer<Textblock> (textblock), 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) +{ + return getBorder (textblock, leftFloats, false, y, h); +} + +/** + * 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) +{ + return getBorder (textblock, rightFloats, true, y, h); +} + +int OutOfFlowMgr::getBorder (Textblock *textblock, Vector<Float> *list, + bool right, int y, int h) +{ + registerCaller (textblock); + + int border = 0; + + // To be a bit more efficient, one could use linear search to find + // the first affected float. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + int yWidget; + if (getYWidget (textblock, vloat, &yWidget) + && y + h > yWidget + && y < yWidget + vloat->size.ascent + vloat->size.descent) { + int borderDiff = getBorderDiff (textblock, vloat, right); + int borderIn = right ? + vloat->generatingBlock->getStyle()->boxRestWidth() : + vloat->generatingBlock->getStyle()->boxOffsetX(); + + //printf (" borderDiff = %d, borderIn = %d\n", borderDiff, borderIn); + border = max (border, vloat->size.width + borderIn + borderDiff); + } + + // 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. + + // To be a bit more efficient, the loop could be stopped when + // (i) at least one float has been found, and (ii) the next float is + // below y + h. + } + + //printf ("[%p] %s border (%d, %d) = %d\n", + // textblock, right ? "right" : "left", y, h, border); + + return border; +} + +bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h) +{ + return hasFloat (textblock, leftFloats, false, y, h); +} + +bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h) +{ + return hasFloat (textblock, rightFloats, true, y, h); +} + +bool OutOfFlowMgr::hasFloat (Textblock *textblock, Vector<Float> *list, + bool right, int y, int h) +{ + // Compare to getBorder(). Actually much copy and paste. + + registerCaller (textblock); + + // To be a bit more efficient, one could use linear search. + for (int i = 0; i < list->size(); i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + int yWidget; + if (getYWidget (textblock, vloat, &yWidget) + && y + h > yWidget + && y < yWidget + vloat->size.ascent + vloat->size.descent) { + // As opposed to getBorder, finding the first float is + // sufficient. + + //printf ("[%p] float on %s side (%d, %d): (%d, %d)\n", + // textblock, right ? "right" : "left", y, h, + // yWidget, vloat->size.ascent + vloat->size.descent); + return true; + } + } + + //printf ("[%p] no float on %s side (%d, %d)\n", + // textblock, right ? "right" : "left", y, h); + + return false; +} + +void OutOfFlowMgr::ensureFloatSize (Float *vloat) +{ + if (vloat->dirty) { + // 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 + (containingBlock->getAvailWidth() + * perLengthVal (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 at %d: %d x (%d + %d)\n", + // vloat->y, vloat->width, vloat->ascent, vloat->descent); + + vloat->dirty = false; + } +} + +/** + * Returns true, when position can be determined; in this case, the + * position is tored in *yWidget. + */ +bool OutOfFlowMgr::getYWidget (Textblock *textblock, Float *vloat, int *yWidget) +{ + if (!vloat->positioned) + return false; + else if (textblock == vloat->generatingBlock) { + *yWidget = vloat->yReal; + return true; + } else { + if (textblock->wasAllocated() && vloat->generatingBlock->wasAllocated()) { + *yWidget = vloat->yReal + vloat->generatingBlock->getAllocation()->y + - textblock->getAllocation()->y; + return true; + } else + return false; + } +} + +/** + * Return the between generator and calling textblock. (TODO Exact + * definition. See getBorder(), where it is used.) + * + * Assumes that the position can be determined (getYWidget() returns true). + */ +int OutOfFlowMgr::getBorderDiff (Textblock *textblock, Float *vloat, bool right) +{ + assert (vloat->positioned); + + if (textblock == vloat->generatingBlock) + return 0; + else { + assert (textblock->wasAllocated() && + vloat->generatingBlock->wasAllocated()); + + if (right) + return + textblock->getAllocation()->x + textblock->getAllocation()->width + - (vloat->generatingBlock->getAllocation()->x + + min (vloat->generatingBlock->getAllocation()->width, + vloat->generatingBlock->getAvailWidth())); + else + return vloat->generatingBlock->getAllocation()->x + - textblock->getAllocation()->x; + } +} + +// 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 |