diff options
Diffstat (limited to 'dw')
55 files changed, 7223 insertions, 3773 deletions
diff --git a/dw/Makefile.am b/dw/Makefile.am index 68410805..ee891234 100644 --- a/dw/Makefile.am +++ b/dw/Makefile.am @@ -23,6 +23,8 @@ libDw_core_a_SOURCES = \ platform.hh \ selection.hh \ selection.cc \ + stackingcontextmgr.hh \ + stackingcontextmgr.cc \ style.cc \ style.hh \ types.cc \ @@ -69,6 +71,21 @@ libDw_widgets_a_SOURCES = \ image.hh \ listitem.cc \ listitem.hh \ + oofawarewidget.cc \ + oofawarewidget_iterator.cc \ + oofawarewidget.hh \ + ooffloatsmgr.cc \ + ooffloatsmgr.hh \ + oofposabslikemgr.cc \ + oofposabslikemgr.hh \ + oofposabsmgr.cc \ + oofposabsmgr.hh \ + oofposfixedmgr.cc \ + oofposfixedmgr.hh \ + oofpositionedmgr.cc \ + oofpositionedmgr.hh \ + oofposrelmgr.cc \ + oofposrelmgr.hh \ outofflowmgr.cc \ outofflowmgr.hh \ regardingborder.cc \ diff --git a/dw/alignedtablecell.cc b/dw/alignedtablecell.cc index e65bbf2c..b5e321b7 100644 --- a/dw/alignedtablecell.cc +++ b/dw/alignedtablecell.cc @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - - #include "alignedtablecell.hh" #include "table.hh" #include "tablecell.hh" @@ -65,7 +63,7 @@ bool AlignedTableCell::mustBeWidenedToAvailWidth () int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailWidthOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width = tablecell::correctAvailWidthOfChild @@ -78,7 +76,7 @@ int AlignedTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) int AlignedTableCell::getAvailHeightOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/getAvailHeightOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::getAvailHeightOfChild", "%p, %s", child, forceValue ? "true" : "false"); int height = tablecell::correctAvailHeightOfChild @@ -96,7 +94,7 @@ void AlignedTableCell::correctRequisitionOfChild (Widget *child, int*, int*)) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctRequisitionOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); @@ -112,7 +110,7 @@ void AlignedTableCell::correctExtremesOfChild (Widget *child, core::Extremes *extremes, bool useAdjustmentWidth) { - DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell/correctExtremesOfChild", + DBG_OBJ_ENTER ("resize", 0, "AlignedTableCell::correctExtremesOfChild", "%p, %d (%d) / %d (%d)", child, extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); @@ -137,6 +135,11 @@ int AlignedTableCell::applyPerHeight (int containerHeight, return tablecell::applyPerHeight (this, containerHeight, perHeight); } +bool AlignedTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +} + int AlignedTableCell::wordWrap(int wordIndex, bool wrapAll) { Textblock::Word *word; @@ -199,7 +202,7 @@ int AlignedTableCell::getValue () void AlignedTableCell::setMaxValue (int maxValue, int value) { line1Offset = maxValue - value; - queueResize (OutOfFlowMgr::createRefNormalFlow (0), true); + queueResize (makeParentRefInFlow (0), true); } } // namespace dw diff --git a/dw/alignedtablecell.hh b/dw/alignedtablecell.hh index b4203047..56196e6d 100644 --- a/dw/alignedtablecell.hh +++ b/dw/alignedtablecell.hh @@ -23,6 +23,8 @@ protected: bool getAdjustMinWidth (); + bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); + int wordWrap (int wordIndex, bool wrapAll); int getValue (); diff --git a/dw/bullet.cc b/dw/bullet.cc index 40c197e6..7fea82a1 100644 --- a/dw/bullet.cc +++ b/dw/bullet.cc @@ -35,7 +35,8 @@ Bullet::~Bullet () DBG_OBJ_DELETE (); } -void Bullet::sizeRequestImpl (core::Requisition *requisition) +void Bullet::sizeRequestImpl (core::Requisition *requisition, bool posDefined, + int x, int y) { requisition->width = lout::misc::max (getStyle()->font->xHeight * 4 / 5, 1); requisition->ascent = lout::misc::max (getStyle()->font->xHeight, 1); @@ -56,7 +57,8 @@ void Bullet::containerSizeChangedForChildren () DBG_OBJ_LEAVE (); } -void Bullet::draw (core::View *view, core::Rectangle *area) +void Bullet::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { int x, y, l; bool filled = true; diff --git a/dw/bullet.hh b/dw/bullet.hh index 004187cd..66ca45f6 100644 --- a/dw/bullet.hh +++ b/dw/bullet.hh @@ -14,10 +14,12 @@ namespace dw { class Bullet: public core::Widget { protected: - void sizeRequestImpl (core::Requisition *requisition); + void sizeRequestImpl (core::Requisition *requisition, bool posDefined, int x, + int y); void getExtremesImpl (core::Extremes *extremes); void containerSizeChangedForChildren (); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); core::Iterator *iterator (core::Content::Type mask, bool atEnd); public: @@ -25,6 +25,7 @@ class Layout; class View; class Widget; class Iterator; +class StackingContextMgr; // Nothing yet to free. inline void freeall () { } @@ -53,6 +54,7 @@ class ResourceFactory; #include "selection.hh" #include "layout.hh" #include "widget.hh" +#include "stackingcontextmgr.hh" #include "ui.hh" #undef __INCLUDED_FROM_DW_CORE_HH__ diff --git a/dw/fltkui.cc b/dw/fltkui.cc index 51523b95..09965ea7 100644 --- a/dw/fltkui.cc +++ b/dw/fltkui.cc @@ -476,7 +476,8 @@ void FltkResource::sizeAllocate (core::Allocation *allocation) DBG_OBJ_LEAVE (); } -void FltkResource::draw (core::View *view, core::Rectangle *area) +void FltkResource::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { FltkView *fltkView = (FltkView*)view; if (fltkView->usesFltkWidgets () && this->view == fltkView) { @@ -577,9 +578,11 @@ template <class I> void FltkSpecificResource<I>::sizeAllocate (core::Allocation } template <class I> void FltkSpecificResource<I>::draw (core::View *view, - core::Rectangle *area) + core::Rectangle *area, + core::DrawingContext + *context) { - FltkResource::draw (view, area); + FltkResource::draw (view, area, context); } template <class I> void FltkSpecificResource<I>::setStyle (core::style::Style diff --git a/dw/fltkui.hh b/dw/fltkui.hh index 09cdc978..ff6331d1 100644 --- a/dw/fltkui.hh +++ b/dw/fltkui.hh @@ -211,7 +211,8 @@ public: virtual void detachView (FltkView *view); void sizeAllocate (core::Allocation *allocation); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); void setStyle (core::style::Style *style); @@ -227,7 +228,8 @@ public: ~FltkSpecificResource (); void sizeAllocate (core::Allocation *allocation); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); void setStyle (core::style::Style *style); bool isEnabled (); diff --git a/dw/image.cc b/dw/image.cc index fd959695..2a164c7d 100644 --- a/dw/image.cc +++ b/dw/image.cc @@ -172,7 +172,8 @@ Image::~Image() DBG_OBJ_DELETE (); } -void Image::sizeRequestImpl (core::Requisition *requisition) +void Image::sizeRequestImpl (core::Requisition *requisition, bool posDefined, + int x, int y) { DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); @@ -337,7 +338,7 @@ void Image::leaveNotifyImpl (core::EventCrossing *event) */ int Image::contentX (core::MousePositionEvent *event) { - int ret = event->xWidget - getStyle()->boxOffsetX(); + int ret = event->xWidget - boxOffsetX(); ret = misc::min(getContentWidth(), misc::max(ret, 0)); return ret; @@ -345,7 +346,7 @@ int Image::contentX (core::MousePositionEvent *event) int Image::contentY (core::MousePositionEvent *event) { - int ret = event->yWidget - getStyle()->boxOffsetY(); + int ret = event->yWidget - boxOffsetY(); ret = misc::min(getContentHeight(), misc::max(ret, 0)); return ret; @@ -407,7 +408,8 @@ bool Image::buttonReleaseImpl (core::EventButton *event) return false; } -void Image::draw (core::View *view, core::Rectangle *area) +void Image::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { int dx, dy; core::Rectangle content, intersection; @@ -415,8 +417,8 @@ void Image::draw (core::View *view, core::Rectangle *area) drawWidgetBox (view, area, false); if (buffer) { - dx = getStyle()->boxOffsetX (); - dy = getStyle()->boxOffsetY (); + dx = boxOffsetX (); + dy = boxOffsetY (); content.x = dx; content.y = dy; content.width = getContentWidth (); @@ -443,31 +445,29 @@ void Image::draw (core::View *view, core::Rectangle *area) (getContentHeight() < getStyle()->font->ascent + getStyle()->font->descent)) { clippingView = usedView = - view->getClippingView (allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY (), + view->getClippingView (allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight()); } usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color, core::style::Color::SHADING_NORMAL, - allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY (), + allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight(), altText); if (clippingView) view->mergeClippingView (clippingView); } if (mapKey) { - clippingView = view->getClippingView (allocation.x + - getStyle()->boxOffsetX (), - allocation.y + - getStyle()->boxOffsetY (), + clippingView = view->getClippingView (allocation.x + boxOffsetX (), + allocation.y + boxOffsetY (), getContentWidth(), getContentHeight()); mapList->drawMap(mapKey, clippingView, getStyle(), - allocation.x + getStyle()->boxOffsetX (), - allocation.y + getStyle()->boxOffsetY ()); + allocation.x + boxOffsetX (), + allocation.y + boxOffsetY ()); view->mergeClippingView (clippingView); } } @@ -518,9 +518,8 @@ void Image::drawRow (int row) buffer->getRowArea (row, &area); if (area.width && area.height) - queueDrawArea (area.x + getStyle()->boxOffsetX (), - area.y + getStyle()->boxOffsetY (), - area.width, area.height); + queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width, + area.height); } void Image::finish () diff --git a/dw/image.hh b/dw/image.hh index b94f647d..c8752b22 100644 --- a/dw/image.hh +++ b/dw/image.hh @@ -130,12 +130,14 @@ private: bool isMap; protected: - void sizeRequestImpl (core::Requisition *requisition); + void sizeRequestImpl (core::Requisition *requisition, bool posDefined, int x, + int y); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); void containerSizeChangedForChildren (); - - void draw (core::View *view, core::Rectangle *area); + + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); bool buttonPressImpl (core::EventButton *event); bool buttonReleaseImpl (core::EventButton *event); diff --git a/dw/iterator.cc b/dw/iterator.cc index 0edb580b..dbb779f6 100644 --- a/dw/iterator.cc +++ b/dw/iterator.cc @@ -204,14 +204,6 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end, } } - -void Iterator::print () -{ - misc::StringBuffer sb; - intoStringBuffer (&sb); - printf ("%s", sb.getChars ()); -} - // ------------------- // EmptyIterator // ------------------- diff --git a/dw/iterator.hh b/dw/iterator.hh index abf31d0b..9460adc4 100644 --- a/dw/iterator.hh +++ b/dw/iterator.hh @@ -86,8 +86,6 @@ public: static void scrollTo (Iterator *it1, Iterator *it2, int start, int end, HPosition hpos, VPosition vpos); - - virtual void print (); }; diff --git a/dw/layout.cc b/dw/layout.cc index c2a53d08..d18bc486 100644 --- a/dw/layout.cc +++ b/dw/layout.cc @@ -374,6 +374,14 @@ void Layout::addWidget (Widget *widget) return; } + // The toplevel widget always establishes a stacking context. It could + // already be set in Widget::setStyle(). + if (widget->stackingContextMgr == NULL) { + widget->stackingContextMgr = new StackingContextMgr (widget); + DBG_OBJ_ASSOC (widget, widget->stackingContextMgr); + widget->stackingContextWidget = widget; + } + topLevel = widget; widget->layout = this; widget->container = NULL; @@ -654,6 +662,9 @@ bool Layout::calcScrollInto (int requestedValue, int requestedSize, void Layout::draw (View *view, Rectangle *area) { + DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + Rectangle widgetArea, intersection, widgetDrawArea; // First of all, draw background image. (Unlike background *color*, @@ -693,11 +704,14 @@ void Layout::draw (View *view, Rectangle *area) widgetDrawArea.width = intersection.width; widgetDrawArea.height = intersection.height; - topLevel->draw (view, &widgetDrawArea); + DrawingContext context (&widgetArea); + topLevel->draw (view, &widgetDrawArea, &context); view->finishDrawing (&intersection); } } + + DBG_OBJ_LEAVE (); } int Layout::currHScrollbarThickness() @@ -1093,12 +1107,18 @@ void Layout::leaveNotify (View *view, ButtonState state) */ Widget *Layout::getWidgetAtPoint (int x, int y) { - _MSG ("------------------------------------------------------------\n"); - _MSG ("widget at (%d, %d)\n", x, y); - if (topLevel && topLevel->wasAllocated ()) - return topLevel->getWidgetAtPoint (x, y, 0); - else - return NULL; + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + Widget *widget; + + if (topLevel && topLevel->wasAllocated ()) { + GettingWidgetAtPointContext context; + widget = topLevel->getWidgetAtPoint (x, y, &context); + } else + widget = NULL; + + DBG_OBJ_MSGF ("events", 0, "=> %p", widget); + DBG_OBJ_LEAVE (); + return widget; } @@ -1108,12 +1128,16 @@ Widget *Layout::getWidgetAtPoint (int x, int y) */ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) { + DBG_OBJ_ENTER ("events", 0, "moveToWidget", "%p, %d", + newWidgetAtPoint, state); + Widget *ancestor, *w; Widget **track; int trackLen, i, i_a; EventCrossing crossingEvent; - _MSG("moveToWidget: wap=%p nwap=%p\n",widgetAtPoint,newWidgetAtPoint); + DBG_OBJ_MSGF ("events", 1, "(old) widgetAtPoint = %p", widgetAtPoint); + if (newWidgetAtPoint != widgetAtPoint) { // The mouse pointer has been moved into another widget. if (newWidgetAtPoint && widgetAtPoint) @@ -1183,6 +1207,8 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state) widgetAtPoint = newWidgetAtPoint; updateCursor (); } + + DBG_OBJ_LEAVE (); } /** diff --git a/dw/layout.hh b/dw/layout.hh index 32b9a134..16d19979 100644 --- a/dw/layout.hh +++ b/dw/layout.hh @@ -314,7 +314,12 @@ public: /* View */ - inline void expose (View *view, Rectangle *area) { draw (view, area); } + inline void expose (View *view, Rectangle *area) { + DBG_OBJ_ENTER ("draw", 0, "expose", "%d, %d, %d * %d", + area->x, area->y, area->width, area->height); + draw (view, area); + DBG_OBJ_LEAVE (); + } /** * \brief This function is called by a view, to delegate a button press diff --git a/dw/oofawarewidget.cc b/dw/oofawarewidget.cc new file mode 100644 index 00000000..c1398a64 --- /dev/null +++ b/dw/oofawarewidget.cc @@ -0,0 +1,592 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofawarewidget.hh" +#include "ooffloatsmgr.hh" +#include "oofposabsmgr.hh" +#include "oofposrelmgr.hh" +#include "oofposfixedmgr.hh" + +using namespace dw; +using namespace dw::core; +using namespace dw::core::style; +using namespace lout::object; +using namespace lout::misc; +using namespace lout::container::typed; + +namespace dw { + +namespace oof { + +const char *OOFAwareWidget::OOFM_NAME[NUM_OOFM] = { + "FLOATS", "ABSOLUTE", "RELATIVE", "FIXED" +}; + +int OOFAwareWidget::CLASS_ID = -1; + +OOFAwareWidget::OOFAwareWidget () +{ + DBG_OBJ_CREATE ("dw::oof::OOFAwareWidget"); + registerName ("dw::oof::OOFAwareWidget", &CLASS_ID); + + for (int i = 0; i < NUM_OOFM; i++) { + oofContainer[i] = NULL; + DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]); + outOfFlowMgr[i] = NULL; + } +} + +OOFAwareWidget::~OOFAwareWidget () +{ + for (int i = 0; i < NUM_OOFM; i++) { + if(outOfFlowMgr[i]) { + // I feel more comfortable by letting the OOF aware widget delete + // these widgets, instead of doing this in ~OutOfFlowMgr. + for (int j = 0; j < outOfFlowMgr[i]->getNumWidgets (); j++) + delete outOfFlowMgr[i]->getWidget (j); + + delete outOfFlowMgr[i]; + } + } + + DBG_OBJ_DELETE (); +} + +const char *OOFAwareWidget::stackingLevelText (int level) +{ + switch (level) { + case SL_START: return "START"; + case SL_BACKGROUND: return "BACKGROUND"; + case SL_SC_BOTTOM: return "SC_BOTTOM"; + case SL_IN_FLOW: return "IN_FLOW"; + case SL_OOF_REF: return "OOF_REF"; + case SL_OOF_CONT: return "OOF_CONT"; + case SL_SC_TOP: return "SC_TOP"; + case SL_END: return "END"; + default: return "???"; + } +} + +void OOFAwareWidget::notifySetAsTopLevel () +{ + for (int i = 0; i < NUM_OOFM; i++) { + oofContainer[i] = this; + DBG_OBJ_ARRSET_PTR ("oofContainer", i, oofContainer[i]); + } +} + +int OOFAwareWidget::getOOFMIndex (Widget *widget) +{ + DBG_OBJ_ENTER_O ("construct", 0, NULL, "getOOFMIndex", "%p", widget); + DBG_OBJ_MSGF_O ("construct", 1, NULL, "position = %s, float = %s", + widget->getStyle()->position + == style::POSITION_STATIC ? "static" : + (widget->getStyle()->position + == style::POSITION_RELATIVE ? "relative" : + (widget->getStyle()->position + == style::POSITION_ABSOLUTE ? "absolute" : + (widget->getStyle()->position + == style::POSITION_FIXED ? "fixed" : "???"))), + widget->getStyle()->vloat == style::FLOAT_NONE ? "none" : + (widget->getStyle()->vloat == style::FLOAT_LEFT ? "left" : + (widget->getStyle()->vloat == style::FLOAT_RIGHT ? + "right" : "???"))); + + int index = -1; + if (testWidgetFloat (widget)) + index = OOFM_FLOATS; + else if (testWidgetAbsolutelyPositioned (widget)) + index = OOFM_ABSOLUTE; + else if (testWidgetRelativelyPositioned (widget)) + index = OOFM_RELATIVE; + else if (testWidgetFixedlyPositioned (widget)) + index = OOFM_FIXED; + else + lout::misc::assertNotReached (); + + DBG_OBJ_LEAVE_VAL_O (NULL, "%d (%s)", index, OOFM_NAME[index]); + return index; +} + +bool OOFAwareWidget::isOOFContainer (Widget *widget, int oofmIndex) +{ + // TODO The methods isPossibleContainer() and isPossibleContainerParent() + // are only used in few cases. Does not matter currently, however. + + switch (oofmIndex) { + case OOFM_FLOATS: + return widget->instanceOf (OOFAwareWidget::CLASS_ID) && + (// For floats, only some OOF aware widgets are considered as + // containers. + ((OOFAwareWidget*)widget)->isPossibleContainer (OOFM_FLOATS) && + // The second condition: that this block is "out of flow", in a + // wider sense. + (// The toplevel widget is "out of flow", since there is no + // parent, and so no context. + widget->getParent() == NULL || + // A similar reasoning applies to a widget with an + // unsuitable parent (typical example: a table cell (this + // is also a text block, so possible float container) + // within a table widget, which is not a suitable float + // container parent). + !(widget->getParent()->instanceOf (OOFAwareWidget::CLASS_ID) && + ((OOFAwareWidget*)widget->getParent()) + ->isPossibleContainerParent (OOFM_FLOATS)) || + // Inline blocks are containing blocks, too. + widget->getStyle()->display == DISPLAY_INLINE_BLOCK || + // Same for blocks with 'overview' set to another value than + // (the default value) 'visible'. + widget->getStyle()->overflow != OVERFLOW_VISIBLE || + // Finally, "out of flow" in a narrower sense: floats; + // absolutely and fixedly positioned elements. (No + // relatively positioned elements; since the latters + // constitute a stacking context, drawing of floats gets + // somewhat more complicated; see "interrupting the drawing + // process" in "dw-stacking-context.doc". + testWidgetOutOfFlow (widget))); + + case OOFM_RELATIVE: + case OOFM_ABSOLUTE: + return widget->instanceOf (OOFAwareWidget::CLASS_ID) && + (widget->getParent() == NULL || + OOFAwareWidget::testWidgetPositioned (widget)); + + + case OOFM_FIXED: + // The single container for fixedly positioned elements is the + // toplevel (canvas; actually the viewport). (The toplevel + // widget should always be a textblock; at least this is the + // case in dillo.) + return widget->getParent() == NULL; + + default: + // compiler happiness + lout::misc::assertNotReached (); + return false; + } +} + +void OOFAwareWidget::notifySetParent () +{ + // Search for containing blocks. + for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) { + oofContainer[oofmIndex] = NULL; + + for (Widget *widget = this; + widget != NULL && oofContainer[oofmIndex] == NULL; + widget = widget->getParent ()) + if (isOOFContainer (widget, oofmIndex)) { + assert (widget->instanceOf (OOFAwareWidget::CLASS_ID)); + oofContainer[oofmIndex] = (OOFAwareWidget*)widget; + } + + DBG_OBJ_ARRSET_PTR ("oofContainer", oofmIndex, oofContainer[oofmIndex]); + + assert (oofContainer[oofmIndex] != NULL); + } +} + +void OOFAwareWidget::initOutOfFlowMgrs () +{ + if (oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] == NULL) { + oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS] = + new OOFFloatsMgr (oofContainer[OOFM_FLOATS]); + DBG_OBJ_ASSOC (oofContainer[OOFM_FLOATS], + oofContainer[OOFM_FLOATS]->outOfFlowMgr[OOFM_FLOATS]); + } + + if (oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] == NULL) { + oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE] = + new OOFPosAbsMgr (oofContainer[OOFM_ABSOLUTE]); + DBG_OBJ_ASSOC (oofContainer[OOFM_ABSOLUTE], + oofContainer[OOFM_ABSOLUTE]->outOfFlowMgr[OOFM_ABSOLUTE]); + } + + if (oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] == NULL) { + oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE] = + new OOFPosRelMgr (oofContainer[OOFM_RELATIVE]); + DBG_OBJ_ASSOC (oofContainer[OOFM_RELATIVE], + oofContainer[OOFM_RELATIVE]->outOfFlowMgr[OOFM_RELATIVE]); + } + + if (oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] == NULL) { + oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED] = + new OOFPosFixedMgr (oofContainer[OOFM_FIXED]); + DBG_OBJ_ASSOC (oofContainer[OOFM_FIXED], + oofContainer[OOFM_FIXED]->outOfFlowMgr[OOFM_FIXED]); + } +} + +void OOFAwareWidget::correctRequisitionByOOF (Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionByOOF", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + requisitionWithoutOOF = *requisition; + + for (int i = 0; i < NUM_OOFM; i++) { + if (outOfFlowMgr[i]) { + DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]); + DBG_OBJ_MSG_START (); + + int oofWidth, oofHeight; + + outOfFlowMgr[i]->getSize (requisition, &oofWidth, &oofHeight); + DBG_OBJ_MSGF ("resize", 1, "result: %d * %d", oofWidth, oofHeight); + + if (oofWidth > requisition->width) { + if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () && + adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + extraSpace.right = max (extraSpace.right, + oofWidth - requisition->width); + DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right); + } + + requisition->width = oofWidth; + } + + if (oofHeight > requisition->ascent + requisition->descent) { + if (outOfFlowMgr[i]->containerMustAdjustExtraSpace () && + adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + extraSpace.bottom = max (extraSpace.bottom, + oofHeight - (requisition->ascent + + requisition->descent)); + DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom); + } + + splitHeightFun (oofHeight, + &requisition->ascent, &requisition->descent); + } + + if (!adjustExtraSpaceWhenCorrectingRequisitionByOOF ()) { + requisitionWithoutOOF.width = max (requisitionWithoutOOF.width, + oofWidth); + if (oofHeight > + requisitionWithoutOOF.ascent + requisitionWithoutOOF.descent) + splitHeightFun (oofHeight, &requisitionWithoutOOF.ascent, + &requisitionWithoutOOF.descent); + } + + DBG_OBJ_MSGF ("resize", 1, "after correction: %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_MSG_END (); + } else + DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]); + } + + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.width", requisitionWithoutOOF.width); + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.ascent", + requisitionWithoutOOF.ascent); + DBG_OBJ_SET_NUM ("requisitionWithoutOOF.descent", + requisitionWithoutOOF.descent); + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::correctExtremesByOOF (Extremes *extremes) +{ + DBG_OBJ_ENTER ("resize", 0, "correctExtremesByOOF", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + for (int i = 0; i < NUM_OOFM; i++) { + if (outOfFlowMgr[i]) { + DBG_OBJ_MSGF ("resize", 1, "OOFM for %s", OOFM_NAME[i]); + DBG_OBJ_MSG_START (); + + int oofMinWidth, oofMaxWidth; + outOfFlowMgr[i]->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); + DBG_OBJ_MSGF ("resize", 1, "result: %d / %d", + oofMinWidth, oofMaxWidth); + + extremes->minWidth = max (extremes->minWidth, oofMinWidth); + extremes->minWidthIntrinsic = max (extremes->minWidthIntrinsic, + oofMinWidth); + extremes->maxWidth = max (extremes->maxWidth, oofMaxWidth); + extremes->maxWidthIntrinsic = max (extremes->maxWidthIntrinsic, + oofMinWidth); + extremes->adjustmentWidth = max (extremes->adjustmentWidth, + oofMinWidth); + + DBG_OBJ_MSGF ("resize", 1, "after correction: %d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + DBG_OBJ_MSG_END (); + } else + DBG_OBJ_MSGF ("resize", 1, "no OOFM for %s", OOFM_NAME[i]); + } + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::sizeAllocateStart (Allocation *allocation) +{ + + for (int i = 0; i < NUM_OOFM; i++) + if (oofContainer[i]->outOfFlowMgr[i]) + oofContainer[i]->outOfFlowMgr[i]->sizeAllocateStart (this, allocation); +} + +void OOFAwareWidget::sizeAllocateEnd () +{ + for (int i = 0; i < NUM_OOFM; i++) + if (oofContainer[i]->outOfFlowMgr[i]) + oofContainer[i]->outOfFlowMgr[i]->sizeAllocateEnd (this); +} + +void OOFAwareWidget::containerSizeChangedForChildrenOOF () +{ + for (int i = 0; i < NUM_OOFM; i++) + if (outOfFlowMgr[i]) + outOfFlowMgr[i]->containerSizeChangedForChildren (); +} + +bool OOFAwareWidget::doesWidgetOOFInterruptDrawing (Widget *widget) +{ + DBG_OBJ_ENTER ("draw", 0, "doesWidgetOOFInterruptDrawing", "%p", widget); + + // This is the generator of the widget. + int oofmIndex = getOOFMIndex (widget); + DBG_OBJ_MSGF ("draw", 1, "oofmIndex = %d", oofmIndex); + + int cl = oofContainer[oofmIndex]->stackingContextWidget->getLevel (), + gl = stackingContextWidget->getLevel (); + + DBG_OBJ_MSGF ("draw", 1,"%d < %d => %s", cl, gl, cl < gl ? "true" : "false"); + + DBG_OBJ_LEAVE (); + return cl < gl; +} + +void OOFAwareWidget::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); + + for (int level = SL_START + 1; level < SL_END; level++) + drawLevel (view, area, level, context); + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::drawLevel (View *view, Rectangle *area, int level, + DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "OOFAwareWidget::drawLevel", + "(%d, %d, %d * %d), %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); + + switch (level) { + case SL_START: + break; + + case SL_BACKGROUND: + drawWidgetBox (view, area, false); + break; + + case SL_SC_BOTTOM: + if (stackingContextMgr) + stackingContextMgr->drawBottom (view, area, context); + break; + + case SL_IN_FLOW: + // Should be implemented in the sub class. + break; + + case SL_OOF_REF: + // Should be implemented in the sub class (when references are hold). + break; + + case SL_OOF_CONT: + drawOOF (view, area, context); + break; + + case SL_SC_TOP: + if (stackingContextMgr) + stackingContextMgr->drawTop (view, area, context); + break; + + case SL_END: + break; + } + + DBG_OBJ_LEAVE (); +} + +void OOFAwareWidget::drawOOF (View *view, Rectangle *area, + DrawingContext *context) +{ + for (int i = 0; i < NUM_OOFM; i++) { + if(outOfFlowMgr[i]) + outOfFlowMgr[i]->draw (view, area, context); + } +} + +Widget *OOFAwareWidget::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + Widget *widgetAtPoint = NULL; + + if (inAllocation (x, y)) { + for (int level = SL_END - 1; widgetAtPoint == NULL && level > SL_START; + level--) + widgetAtPoint = getWidgetAtPointLevel (x, y, level, context); + } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +Widget *OOFAwareWidget::getWidgetAtPointLevel (int x, int y, int level, + GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "OOFAwareWidget::getWidgetAtPointLevel", + "%d, %d, %s", x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_BACKGROUND: + if (inAllocation (x, y)) + widgetAtPoint = this; + break; + + case SL_SC_BOTTOM: + if (stackingContextMgr) + widgetAtPoint = + stackingContextMgr->getBottomWidgetAtPoint (x, y, context); + break; + + case SL_IN_FLOW: + // Should be implemented in the sub class. + assertNotReached (); + break; + + case SL_OOF_REF: + // Should be implemented in the sub class (when references are hold). + break; + + case SL_OOF_CONT: + widgetAtPoint = getWidgetOOFAtPoint (x, y, context); + break; + + case SL_SC_TOP: + if (stackingContextMgr) + widgetAtPoint = + stackingContextMgr->getTopWidgetAtPoint (x, y, context); + break; + + default: + assertNotReached (); + } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +Widget *OOFAwareWidget::getWidgetOOFAtPoint (int x, int y, + GettingWidgetAtPointContext + *context) +{ + Widget *widgetAtPoint = NULL; + + for (int i = NUM_OOFM -1; widgetAtPoint == NULL && i >= 0; i--) { + if(outOfFlowMgr[i]) + widgetAtPoint = outOfFlowMgr[i]->getWidgetAtPoint (x, y, context); + } + + return widgetAtPoint; +} + +void OOFAwareWidget::removeChild (Widget *child) +{ + // Sub classes should implement this method (and Textblock and + // Table do so), so this point is only reached from + // ~OOFAwareWidget, which removes widgets out of flow. + assert (isWidgetOOF (child)); +} + +bool OOFAwareWidget::mustBeWidenedToAvailWidth () +{ + // Only used for floats. + assertNotReached (); + return false; +} + +void OOFAwareWidget::borderChanged (int y, Widget *vloat) +{ + assertNotReached (); +} + +void OOFAwareWidget::widgetRefSizeChanged (int externalIndex) +{ + assertNotReached (); +} + +void OOFAwareWidget::clearPositionChanged () +{ + assertNotReached (); +} + +void OOFAwareWidget::oofSizeChanged (bool extremesChanged) +{ + DBG_OBJ_ENTER ("resize", 0, "oofSizeChanged", "%s", + extremesChanged ? "true" : "false"); + queueResize (-1, extremesChanged); + + // Extremes changes may become also relevant for the children. + if (extremesChanged) + containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +int OOFAwareWidget::getLineBreakWidth () +{ + assertNotReached (); + return 0; +} + +bool OOFAwareWidget::isPossibleContainer (int oofmIndex) +{ + return oofmIndex != OOFM_FLOATS; +} + +bool OOFAwareWidget::isPossibleContainerParent (int oofmIndex) +{ + return oofmIndex != OOFM_FLOATS; +} + +bool OOFAwareWidget::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return true; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofawarewidget.hh b/dw/oofawarewidget.hh new file mode 100644 index 00000000..3946c72b --- /dev/null +++ b/dw/oofawarewidget.hh @@ -0,0 +1,264 @@ +#ifndef __DW_OOFAWAREWIDGET_HH__ +#define __DW_OOFAWAREWIDGET_HH__ + +#include "core.hh" +#include "outofflowmgr.hh" + +namespace dw { + +namespace oof { + +/** + * \brief Base class for widgets which can act as container and + * generator for widgets out of flow. + * + * (Perhaps it should be diffenciated between the two roles, container + * and generator, but this would make multiple inheritance necessary.) + * + * See \ref dw-out-of-flow for an overview. + * + * Requirements for sub classes (in most cases refer to dw::Textblock + * as a good example): + * + * - A sub class should at least take care to call these methods at the + * respective points: + * + * - dw::oof::OOFAwareWidget::correctRequisitionByOOF (from + * dw::core::Widget::getExtremesImpl) + * - dw::oof::OOFAwareWidget::correctExtremesByOOF (from + * dw::core::Widget::sizeRequestImpl) + * - dw::oof::OOFAwareWidget::sizeAllocateStart + * - dw::oof::OOFAwareWidget::sizeAllocateEnd (latter two from + * dw::core::Widget::sizeAllocateImpl) + * - dw::oof::OOFAwareWidget::containerSizeChangedForChildrenOOF + * (from dw::core::Widget::containerSizeChangedForChildren) + * - dw::oof::OOFAwareWidget::drawOOF (from dw::core::Widget::draw) + * - dw::oof::OOFAwareWidget::getWidgetOOFAtPoint (from + * dw::core::Widget::getWidgetAtPoint) + * + * - Implementations of dw::core::Widget::getAvailWidthOfChild and + * dw::core::Widget::getAvailHeightOfChild have to distinguish + * between widgets in flow and out of flow; general pattern: + * + * \code + * if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) && + * getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + * width = + * getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue); + * else { + * // ... specific implementation ... + * \endcode + * + * See also implementations of dw::Textblock and dw::Table. (Open + * issue: What about dw::core::Widget::correctRequisitionOfChild and + * dw::core::Widget::correctExtremesOfChild? Currently, all widgets + * are used the default implementation.) + * + * - Iterators have to consider widgets out of flow; + * dw::oof::OOFAwareWidget::OOFAwareWidgetIterator is recommended as + * base class. + * + * - dw::core::Widget::parentRef has to be set for widgets in flow; if + * not used further, a simple *makeParentRefInFlow(0)* is sufficient + * (as dw::Table::addCell does). Widgets which are only containers, + * but not generators, do not have to care about widgets out of + * flow in this regard. + * + * For both generators and containers of floats (which is only + * implemented by dw::Textblock) it gets a bit more complicated. + * + * \todo Currently, on the level of dw::oof::OOFAwareWidget, nothing + * is done about dw::core::Widget::markSizeChange and + * dw::core::Widget::markExtremesChange. This does not matter, though: + * dw::Textblock takes care of these, and dw::Table is only connected + * to subclasses of dw::oof::OOFPositionedMgr, which do care about + * these. However, this should be considered for completeness. + */ +class OOFAwareWidget: public core::Widget +{ +protected: + enum { OOFM_FLOATS, OOFM_ABSOLUTE, OOFM_RELATIVE, OOFM_FIXED, NUM_OOFM }; + static const char *OOFM_NAME[NUM_OOFM]; + enum { PARENT_REF_OOFM_BITS = 3, + PARENT_REF_OOFM_MASK = (1 << PARENT_REF_OOFM_BITS) - 1 }; + + class OOFAwareWidgetIterator: public core::Iterator + { + private: + enum { NUM_SECTIONS = NUM_OOFM + 1 }; + int sectionIndex; // 0 means in flow, otherwise OOFM index + 1 + int index; + + int numParts (int sectionIndex, int numContentsInFlow = -1); + void getPart (int sectionIndex, int index, core::Content *content); + + protected: + virtual int numContentsInFlow () = 0; + virtual void getContentInFlow (int index, core::Content *content) = 0; + + void setValues (int sectionIndex, int index); + inline void cloneValues (OOFAwareWidgetIterator *other) + { other->setValues (sectionIndex, index); } + + inline bool inFlow () { return sectionIndex == 0; } + inline int getInFlowIndex () { assert (inFlow ()); return index; } + void highlightOOF (int start, int end, core::HighlightLayer layer); + void unhighlightOOF (int direction, core::HighlightLayer layer); + void getAllocationOOF (int start, int end, core::Allocation *allocation); + + public: + OOFAwareWidgetIterator (OOFAwareWidget *widget, core::Content::Type mask, + bool atEnd, int numContentsInFlow); + + void intoStringBuffer(lout::misc::StringBuffer *sb); + int compareTo(lout::object::Comparable *other); + + bool next (); + bool prev (); + }; + + inline bool isParentRefOOF (int parentRef) + { return parentRef != -1 && (parentRef & PARENT_REF_OOFM_MASK); } + + inline int makeParentRefInFlow (int inFlowSubRef) + { return (inFlowSubRef << PARENT_REF_OOFM_BITS); } + inline int getParentRefInFlowSubRef (int parentRef) + { assert (!isParentRefOOF (parentRef)); + return parentRef >> PARENT_REF_OOFM_BITS; } + + inline int makeParentRefOOF (int oofmIndex, int oofmSubRef) + { return (oofmSubRef << PARENT_REF_OOFM_BITS) | (oofmIndex + 1); } + inline int getParentRefOOFSubRef (int parentRef) + { assert (isParentRefOOF (parentRef)); + return parentRef >> PARENT_REF_OOFM_BITS; } + inline int getParentRefOOFIndex (int parentRef) + { assert (isParentRefOOF (parentRef)); + return (parentRef & PARENT_REF_OOFM_MASK) - 1; } + inline oof::OutOfFlowMgr *getParentRefOutOfFlowMgr (int parentRef) + { return outOfFlowMgr[getParentRefOOFIndex (parentRef)]; } + + inline bool isWidgetOOF (Widget *widget) + { return isParentRefOOF (widget->parentRef); } + + inline int getWidgetInFlowSubRef (Widget *widget) + { return getParentRefInFlowSubRef (widget->parentRef); } + + inline int getWidgetOOFSubRef (Widget *widget) + { return getParentRefOOFSubRef (widget->parentRef); } + inline int getWidgetOOFIndex (Widget *widget) + { return getParentRefOOFIndex (widget->parentRef); } + inline oof::OutOfFlowMgr *getWidgetOutOfFlowMgr (Widget *widget) + { return getParentRefOutOfFlowMgr (widget->parentRef); } + + OOFAwareWidget *oofContainer[NUM_OOFM]; + OutOfFlowMgr *outOfFlowMgr[NUM_OOFM]; + core::Requisition requisitionWithoutOOF; + + inline OutOfFlowMgr *searchOutOfFlowMgr (int oofmIndex) + { return oofContainer[oofmIndex] ? + oofContainer[oofmIndex]->outOfFlowMgr[oofmIndex] : NULL; } + + static int getOOFMIndex (Widget *widget); + + void initOutOfFlowMgrs (); + void correctRequisitionByOOF (core::Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)); + void correctExtremesByOOF (core::Extremes *extremes); + void sizeAllocateStart (core::Allocation *allocation); + void sizeAllocateEnd (); + void containerSizeChangedForChildrenOOF (); + + virtual void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + void drawOOF (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + virtual Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context); + Widget *getWidgetOOFAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + static bool isOOFContainer (Widget *widget, int oofmIndex); + + void notifySetAsTopLevel(); + void notifySetParent(); + + void removeChild (Widget *child); + +public: + enum { + SL_START, SL_BACKGROUND, SL_SC_BOTTOM, SL_IN_FLOW, SL_OOF_REF, + SL_OOF_CONT, SL_SC_TOP, SL_END }; + + static int CLASS_ID; + + OOFAwareWidget (); + ~OOFAwareWidget (); + + static const char *stackingLevelText (int level); + + static inline bool testStyleFloat (core::style::Style *style) + { return style->vloat != core::style::FLOAT_NONE; } + + static inline bool testStyleAbsolutelyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_ABSOLUTE; } + static inline bool testStyleFixedlyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_FIXED; } + static inline bool testStyleRelativelyPositioned (core::style::Style *style) + { return style->position == core::style::POSITION_RELATIVE; } + + static inline bool testStylePositioned (core::style::Style *style) + { return testStyleAbsolutelyPositioned (style) || + testStyleRelativelyPositioned (style) || + testStyleFixedlyPositioned (style); } + + static inline bool testStyleOutOfFlow (core::style::Style *style) + { // Second part is equivalent to testStylePositioned(), but we still keep + // the two seperately. + return testStyleFloat (style) || testStyleAbsolutelyPositioned (style) + || testStyleRelativelyPositioned (style) + || testStyleFixedlyPositioned (style); } + + static inline bool testWidgetFloat (Widget *widget) + { return testStyleFloat (widget->getStyle ()); } + + static inline bool testWidgetAbsolutelyPositioned (Widget *widget) + { return testStyleAbsolutelyPositioned (widget->getStyle ()); } + static inline bool testWidgetFixedlyPositioned (Widget *widget) + { return testStyleFixedlyPositioned (widget->getStyle ()); } + static inline bool testWidgetRelativelyPositioned (Widget *widget) + { return testStyleRelativelyPositioned (widget->getStyle ()); } + + static inline bool testWidgetPositioned (Widget *widget) + { return testStylePositioned (widget->getStyle ()); } + + static inline bool testWidgetOutOfFlow (Widget *widget) + { return testStyleOutOfFlow (widget->getStyle ()); } + + inline core::Requisition *getRequisitionWithoutOOF () + { return &requisitionWithoutOOF; } + + bool doesWidgetOOFInterruptDrawing (Widget *widget); + + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + virtual bool mustBeWidenedToAvailWidth (); + virtual void borderChanged (int y, core::Widget *vloat); + virtual void widgetRefSizeChanged (int externalIndex); + virtual void clearPositionChanged (); + virtual void oofSizeChanged (bool extremesChanged); + virtual int getLineBreakWidth (); // Should perhaps be renamed. + virtual bool isPossibleContainer (int oofmIndex); + virtual bool isPossibleContainerParent (int oofmIndex); + virtual bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFAWAREWIDGET_HH__ diff --git a/dw/oofawarewidget_iterator.cc b/dw/oofawarewidget_iterator.cc new file mode 100644 index 00000000..afafb5d5 --- /dev/null +++ b/dw/oofawarewidget_iterator.cc @@ -0,0 +1,261 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofawarewidget.hh" +#include "ooffloatsmgr.hh" +#include "oofposabsmgr.hh" +#include "oofposfixedmgr.hh" + +using namespace dw; +using namespace dw::core; +using namespace lout::misc; +using namespace lout::object; + +namespace dw { + +namespace oof { + +// "numContentsInFlow" is passed here to avoid indirectly callin the (virtual) +// method numContentsInFlow() from the constructor. +OOFAwareWidget::OOFAwareWidgetIterator::OOFAwareWidgetIterator + (OOFAwareWidget *widget, Content::Type mask, bool atEnd, + int numContentsInFlow) : + Iterator (widget, mask, atEnd) +{ + if (atEnd) { + sectionIndex = NUM_SECTIONS - 1; + while (sectionIndex >= 0 && + numParts (sectionIndex, numContentsInFlow) == 0) + sectionIndex--; + index = numParts (sectionIndex, numContentsInFlow); + } else { + sectionIndex = 0; + index = -1; + } + + content.type = atEnd ? core::Content::END : core::Content::START; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::setValues (int sectionIndex, + int index) +{ + this->sectionIndex = sectionIndex; + this->index = index; + + if (sectionIndex == 0 && index < 0) + content.type = core::Content::START; + else if (sectionIndex == NUM_SECTIONS - 1 && + index >= numParts (sectionIndex)) + content.type = core::Content::END; + else + getPart (sectionIndex, index, &content); +} + +int OOFAwareWidget::OOFAwareWidgetIterator::numParts (int sectionIndex, + int numContentsInFlow) +{ + DBG_OBJ_ENTER_O ("iterator", 0, getWidget(), "numParts", "%d, %d", + sectionIndex, numContentsInFlow); + + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + int result; + + if (sectionIndex == 0) + result = numContentsInFlow == -1 ? + this->numContentsInFlow () : numContentsInFlow; + else + result = widget->outOfFlowMgr[sectionIndex - 1] ? + widget->outOfFlowMgr[sectionIndex - 1]->getNumWidgets () : 0; + + DBG_OBJ_MSGF_O ("iterator", 1, getWidget(), "=> %d", result); + DBG_OBJ_LEAVE_O (getWidget()); + return result; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::getPart (int sectionIndex, + int index, + Content *content) +{ + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + + if (sectionIndex == 0) + getContentInFlow (index, content); + else { + content->type = Content::WIDGET_OOF_CONT; + content->widget = + widget->outOfFlowMgr[sectionIndex - 1]->getWidget (index); + } +} + +void OOFAwareWidget::OOFAwareWidgetIterator::intoStringBuffer (StringBuffer *sb) +{ + Iterator::intoStringBuffer (sb); + sb->append (", sectionIndex = "); + sb->appendInt (sectionIndex); + sb->append (", index = "); + sb->appendInt (index); +} + +int OOFAwareWidget::OOFAwareWidgetIterator::compareTo (Comparable *other) +{ + OOFAwareWidgetIterator *otherTI = (OOFAwareWidgetIterator*)other; + + if (sectionIndex != otherTI->sectionIndex) + return sectionIndex - otherTI->sectionIndex; + else + return index - otherTI->index; +} + +bool OOFAwareWidget::OOFAwareWidgetIterator::next () +{ + DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (), + "OOFAwareWidgetIterator/next"); + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "initial value: %s", + sb.getChars ()); + } + + bool r; + + if (content.type == Content::END) + r = false; + else { + r = true; + bool cancel = false; + + do { + index++; + + if (index < numParts(sectionIndex)) + getPart (sectionIndex, index, &content); + else { + sectionIndex++; + while (sectionIndex < NUM_SECTIONS && numParts (sectionIndex) == 0) + sectionIndex++; + + if (sectionIndex == NUM_SECTIONS) { + content.type = Content::END; + r = false; + cancel = true; + } else { + index = 0; + getPart (sectionIndex, index, &content); + } + } + } while (!cancel && (content.type & getMask()) == 0); + } + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s", + sb.getChars ()); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "return value: %s", + r ? "true" : "false"); + } + + DBG_OBJ_LEAVE_O (getWidget ()); + return r; +} + +bool OOFAwareWidget::OOFAwareWidgetIterator::prev () +{ + DBG_OBJ_ENTER0_O ("iterator", 0, getWidget (), + "OOFAwareWidgetIterator/prev"); + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "initial value: %s", + sb.getChars ()); + } + + bool r; + + if (content.type == Content::START) + r = false; + else { + r = true; + bool cancel = false; + + do { + index--; + + if (index >= 0) + getPart (sectionIndex, index, &content); + else { + sectionIndex--; + while (sectionIndex >= 0 && numParts (sectionIndex) == 0) + sectionIndex--; + + if (sectionIndex < 0) { + content.type = Content::START; + r = false; + cancel = true; + } else { + index = numParts (sectionIndex) - 1; + getPart (sectionIndex, index, &content); + } + } + } while (!cancel && (content.type & getMask()) == 0); + } + + DBG_IF_RTFL { + StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "final value: %s", + sb.getChars ()); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "return value: %s", + r ? "true" : "false"); + } + + DBG_OBJ_LEAVE_O (getWidget ()); + return r; +} + +void OOFAwareWidget::OOFAwareWidgetIterator::highlightOOF (int start, int end, + HighlightLayer layer) +{ + // TODO What about OOF widgets? +} + +void OOFAwareWidget::OOFAwareWidgetIterator::unhighlightOOF (int direction, + HighlightLayer + layer) +{ + // TODO What about OOF widgets? +} + +void OOFAwareWidget::OOFAwareWidgetIterator::getAllocationOOF (int start, + int end, + Allocation + *allocation) +{ + // TODO Consider start and end? + OOFAwareWidget *widget = (OOFAwareWidget*)getWidget(); + *allocation = *(widget->outOfFlowMgr[sectionIndex - 1] + ->getWidget(index)->getAllocation()); +} + +} // namespace oof + +} // namespace dw diff --git a/dw/ooffloatsmgr.cc b/dw/ooffloatsmgr.cc new file mode 100644 index 00000000..10e72bae --- /dev/null +++ b/dw/ooffloatsmgr.cc @@ -0,0 +1,2357 @@ +/* + * Dillo Widget + * + * Copyright 2013-2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ooffloatsmgr.hh" +#include "oofawarewidget.hh" +#include "../lout/debug.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 { + +namespace oof { + +OOFFloatsMgr::WidgetInfo::WidgetInfo (OOFFloatsMgr *oofm, Widget *widget) +{ + this->oofm = oofm; + this->widget = widget; + wasAllocated = false; + xCB = yCB = width = height = -1; +} + +void OOFFloatsMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, + int width, int height) +{ + DBG_OBJ_ENTER_O ("resize.oofm", 0, widget, "update", "%s, %d, %d, %d, %d", + wasAllocated ? "true" : "false", xCB, yCB, width, height); + + this->wasAllocated = wasAllocated; + this->xCB = xCB; + this->yCB = yCB; + this->width = width; + this->height = height; + + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width); + DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height); + + DBG_OBJ_LEAVE_O (widget); +} + +// ---------------------------------------------------------------------- + +OOFFloatsMgr::Float::Float (OOFFloatsMgr *oofm, Widget *widget, + OOFAwareWidget *generatingBlock, int externalIndex) + : WidgetInfo (oofm, widget) +{ + this->generatingBlock = generatingBlock; + this->externalIndex = externalIndex; + + yReq = yReal = size.width = size.ascent = size.descent = 0; + dirty = sizeChangedSinceLastAllocation = true; + indexGBList = indexCBList = -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, "<Float>.generatingBlock", generatingBlock); + DBG_OBJ_SET_NUM_O (widget, "<Float>.externalIndex", externalIndex); + DBG_OBJ_SET_NUM_O (widget, "<Float>.yReq", yReq); + DBG_OBJ_SET_NUM_O (widget, "<Float>.yReal", yReal); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent); + DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty); + DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation", + sizeChangedSinceLastAllocation); + } +} + +void OOFFloatsMgr::Float::updateAllocation () +{ + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); + + update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), + getNewHeight ()); + + DBG_OBJ_LEAVE_O (getWidget ()); +} + +void OOFFloatsMgr::Float::intoStringBuffer(StringBuffer *sb) +{ + sb->append ("{ widget = "); + sb->appendPointer (getWidget ()); + + if (getWidget ()) { + sb->append (" ("); + sb->append (getWidget()->getClassName ()); + sb->append (")"); + } + + sb->append (", indexGBList = "); + sb->appendInt (indexGBList); + sb->append (", indexCBList = "); + sb->appendInt (indexCBList); + 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 (" }"); +} + +bool OOFFloatsMgr::Float::covers (OOFAwareWidget *textblock, int y, int h) +{ + DBG_OBJ_ENTER_O ("border", 0, getOOFFloatsMgr (), "covers", + "%p, %d, %d [vloat: %p]", + textblock, y, h, getWidget ()); + + bool b; + + if (textblock == generatingBlock) { + int reqyGB = y; + int flyGB = yReal; + getOOFFloatsMgr()->ensureFloatSize (this); + int flh = size.ascent + size.descent; + b = flyGB + flh > reqyGB && flyGB < reqyGB + h; + + DBG_OBJ_MSGF_O ("border", 1, getOOFFloatsMgr (), + "for generator: reqyGB = %d, flyGB = %d, " + "flh = %d + %d = %d => %s", + reqyGB, flyGB, size.ascent, size.descent, flh, + b ? "true" : "false"); + } else { + // (If the textblock were not allocated, the GB list would have + // been choosen instead of the CB list, and so this else-branch + // would not have been not executed.) + assert (getOOFFloatsMgr()->wasAllocated (textblock)); + + if (!getWidget()->wasAllocated ()) { + DBG_OBJ_MSG_O ("border", 1, getOOFFloatsMgr (), + "not generator (not allocated) => false"); + b = false; + } else { + Allocation *tba = getOOFFloatsMgr()->getAllocation(textblock), + *fla = getWidget()->getAllocation (); + int reqyCanv = tba->y + y; + int flyCanv = fla->y; + int flh = fla->ascent + fla->descent; + b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h; + + DBG_OBJ_MSGF_O ("border", 1, getOOFFloatsMgr (), + "not generator (allocated): reqyCanv = %d + %d = %d, " + "flyCanv = %d, flh = %d + %d = %d => %s", + tba->y, y, reqyCanv, flyCanv, + fla->ascent, fla->descent, flh, b ? "true" : "false"); + } + } + + DBG_OBJ_LEAVE_O (getOOFFloatsMgr ()); + + return b; +} + +int OOFFloatsMgr::Float::ComparePosition::compare (Object *o1, Object *o2) +{ + Float *fl1 = (Float*)o1, *fl2 = (Float*)o2; + int r; + + DBG_OBJ_ENTER_O ("border", 1, oofm, + "ComparePosition/compare", "(#%d, #%d) [refTB = %p]", + fl1->getIndex (type), fl2->getIndex (type), refTB); + + if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); + r = fl1->yReal - fl2->yReal; + } else { + DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats"); + DBG_OBJ_MSG_START_O (oofm); + + DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p", + fl1->generatingBlock, fl2->generatingBlock); + + // (i) Floats may not yet been allocated. Non-allocated floats + // do not have an effect yet, they are considered "at the end" + // of the list. + + // (ii) Float::widget is NULL for the key used for binary + // search. In this case, Float::yReal is used instead (which is + // set in SortedFloatsVector::find, too). The generator is the + // textblock, and should be allocated. (If not, the GB list + // would have been choosen instead of the CB list, and so this + // else-branch would not have been not executed.) + + bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true; + bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true; + + DBG_OBJ_MSGF_O ("border", 2, oofm, + "float 1 (%p) allocated: %s; float 2 (%p) allocated: %s", + fl1->getWidget (), a1 ? "yes" : "no", fl2->getWidget (), + a2 ? "yes" : "no"); + + if (a1 && a2) { + int fly1, fly2; + + if (fl1->getWidget()) { + fly1 = fl1->getWidget()->getAllocation()->y; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d", fly1); + } else { + assert (oofm->wasAllocated (fl1->generatingBlock)); + fly1 = oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d + %d = %d", + oofm->getAllocation(fl1->generatingBlock)->y, + fl1->yReal, fly1); + } + + if (fl2->getWidget()) { + fly2 = fl2->getWidget()->getAllocation()->y; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d", fly2); + } else { + assert (oofm->wasAllocated (fl2->generatingBlock)); + fly2 = oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal; + DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d + %d = %d", + oofm->getAllocation(fl2->generatingBlock)->y, + fl2->yReal, fly2); + } + + r = fly1 - fly2; + + DBG_OBJ_MSGF_O ("border", 2, oofm, "r = %d - %d = %d", fly1, fly2, r); + } else if (a1 && !a2) + r = -1; + else if (!a1 && a2) + r = +1; + else // if (!a1 && !a2) + return 0; + + DBG_OBJ_MSG_END_O (oofm); + } + + DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); + DBG_OBJ_LEAVE_O (oofm); + return r; +} + +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->getIndex (type), f1->generatingBlock, f1->externalIndex, + f2->getIndex (type), f2->generatingBlock, + f2->externalIndex); + + if (f1->generatingBlock == f2->generatingBlock) { + 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->generatingBlock), + *t2 = oofm->getOOFAwareWidget (f2->generatingBlock); + 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_MSGF_O ("border", 2, oofm, "result: %d", r); + DBG_OBJ_LEAVE_O (oofm); + 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.setIndex (type, -1); // for debugging + Float::CompareGBAndExtIndex comparator (oofm, type); + 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_MSGF_O ("border", 1, oofm, "=> r = %d", r); + DBG_OBJ_LEAVE_O (oofm); + return r; +} + +int OOFFloatsMgr::SortedFloatsVector::find (OOFAwareWidget *textblock, int y, + int start, int end) +{ + DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d", + textblock, y, start, end); + + Float key (oofm, NULL, NULL, 0); + key.generatingBlock = textblock; + key.yReal = y; + key.setIndex (type, -1); // for debugging + Float::ComparePosition comparator (oofm, textblock, type); + int result = bsearch (&key, false, start, end, &comparator); + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_LEAVE_O (oofm); + return result; +} + +int OOFFloatsMgr::SortedFloatsVector::findFirst (OOFAwareWidget *textblock, + int y, int h, + OOFAwareWidget *lastGB, + int lastExtIndex, + int *lastReturn) +{ + DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d", + textblock, y, h, lastGB, lastExtIndex); + + DBG_IF_RTFL { + DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); + DBG_OBJ_MSG_START_O (oofm); + + for (int i = 0; i < size(); i++) { + DBG_OBJ_MSGF_O ("border", 2, oofm, + "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), " + "%s, %s, ext = %d, GB = %p); widget at (%d, %d)", + i, get(i)->getWidget (), get(i)->getIndex (type), + get(i)->sideSpanningIndex, get(i)->yReq, get(i)->yReal, + get(i)->size.width, get(i)->size.ascent, + get(i)->size.descent, + get(i)->dirty ? "dirty" : "clean", + get(i)->sizeChangedSinceLastAllocation ? "scsla" + : "sNcsla", + get(i)->externalIndex, get(i)->generatingBlock, + get(i)->getWidget()->getAllocation()->x, + get(i)->getWidget()->getAllocation()->y); + } + + DBG_OBJ_MSG_END_O (oofm); + } + + 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 (textblock, 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 (textblock, y, h)) + result = i - 1; + else if (i <= last && get(i)->covers (textblock, y, h)) + result = i; + else + result = -1; + + DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); + DBG_OBJ_LEAVE_O (oofm); + 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<Float>::put (vloat); + vloat->setIndex (type, size() - 1); +} + +OOFFloatsMgr::TBInfo::TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock, + TBInfo *parent, int parentExtIndex) : + WidgetInfo (oofm, textblock) +{ + this->parent = parent; + this->parentExtIndex = parentExtIndex; + + leftFloatsGB = new SortedFloatsVector (oofm, LEFT, GB); + rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, GB); + + wasAllocated = getWidget()->wasAllocated (); + allocation = *(getWidget()->getAllocation ()); + clearPosition = 0; +} + +OOFFloatsMgr::TBInfo::~TBInfo () +{ + delete leftFloatsGB; + delete rightFloatsGB; +} + +void OOFFloatsMgr::TBInfo::updateAllocation () +{ + DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); + + update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), + getNewHeight ()); + + DBG_OBJ_LEAVE_O (getWidget ()); +} + +OOFFloatsMgr::OOFFloatsMgr (OOFAwareWidget *container) +{ + DBG_OBJ_CREATE ("dw::OOFFloatsMgr"); + + this->container = (OOFAwareWidget*)container; + + leftFloatsCB = new SortedFloatsVector (this, LEFT, CB); + rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB); + + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + + leftFloatsAll = new Vector<Float> (1, true); + rightFloatsAll = new Vector<Float> (1, true); + + DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + + floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false); + + tbInfos = new Vector<TBInfo> (1, false); + tbInfosByOOFAwareWidget = + new HashTable <TypedPointer <OOFAwareWidget>, TBInfo> (true, true); + + leftFloatsMark = rightFloatsMark = 0; + lastLeftTBIndex = lastRightTBIndex = 0; + + containerWasAllocated = container->wasAllocated (); + containerAllocation = *(container->getAllocation()); + + addWidgetInFlow (this->container, NULL, 0); +} + +OOFFloatsMgr::~OOFFloatsMgr () +{ + //printf ("OOFFloatsMgr::~OOFFloatsMgr\n"); + + delete leftFloatsCB; + delete rightFloatsCB; + + // Order is important: tbInfosByOOFAwareWidget is owner of the instances + // of TBInfo.tbInfosByOOFAwareWidget + delete tbInfos; + delete tbInfosByOOFAwareWidget; + + 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; + + 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); + + // Some callers are not registered, especially tables. (Where the + // floats manager is actually empty?) + TBInfo *oofAWInfo = getOOFAwareWidgetWhenRegistered (caller); + if (oofAWInfo) { + oofAWInfo->allocation = *allocation; + oofAWInfo->wasAllocated = true; + } + + if (caller == container) { + // In the size allocation process, the *first* OOFM method + // called is sizeAllocateStart, with the containing block as an + // argument. So this is the correct point to initialize size + // allocation. + + containerWasAllocated = true; + containerAllocation = *allocation; + + // Move floats from GB lists to the one CB list. + moveFromGBToCB (LEFT); + moveFromGBToCB (RIGHT); + + // These attributes are used to keep track which floats have + // been allocated (referring to leftFloatsCB and rightFloatsCB). + lastAllocatedLeftFloat = lastAllocatedRightFloat = -1; + } + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::sizeAllocateEnd (OOFAwareWidget *caller) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); + + if (isOOFAwareWidgetRegistered (caller)) { + if (caller != container) { + // Allocate all floats "before" this textblock. + sizeAllocateFloats (LEFT, leftFloatsCB->findFloatIndex (caller, -1)); + sizeAllocateFloats (RIGHT, rightFloatsCB->findFloatIndex (caller, -1)); + } + + // The checks below do not cover "clear position" in all cases, + // so this is done here separately. This position is stored in + // TBInfo and calculated at this points; changes will be noticed + // to the textblock. + TBInfo *tbInfo = getOOFAwareWidget (caller); + int newClearPosition = calcClearPosition (caller); + if (newClearPosition != tbInfo->clearPosition) { + tbInfo->clearPosition = newClearPosition; + caller->clearPositionChanged (); + } + + if (caller == container) { + // In the size allocation process, the *last* OOFM method called + // is sizeAllocateEnd, with the containing block as an + // argument. So this is the correct point to finish size + // allocation. + + // Allocate all remaining floats. + sizeAllocateFloats (LEFT, leftFloatsCB->size () - 1); + sizeAllocateFloats (RIGHT, rightFloatsCB->size () - 1); + + // Check changes of both textblocks and floats allocation. (All + // is checked by hasRelationChanged (...).) + for (lout::container::typed::Iterator<TypedPointer <OOFAwareWidget> > + it = tbInfosByOOFAwareWidget->iterator (); + it.hasNext (); ) { + TypedPointer <OOFAwareWidget> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (key); + OOFAwareWidget *tb = key->getTypedValue(); + + int minFloatPos; + Widget *minFloat; + if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) + tb->borderChanged (minFloatPos, minFloat); + } + + checkAllocatedFloatCollisions (LEFT); + checkAllocatedFloatCollisions (RIGHT); + + // Store some information for later use. + for (lout::container::typed::Iterator<TypedPointer <OOFAwareWidget> > + it = tbInfosByOOFAwareWidget->iterator (); + it.hasNext (); ) { + TypedPointer <OOFAwareWidget> *key = it.getNext (); + TBInfo *tbInfo = tbInfosByOOFAwareWidget->get (key); + OOFAwareWidget *tb = key->getTypedValue(); + + tbInfo->updateAllocation (); + tbInfo->lineBreakWidth = tb->getLineBreakWidth (); + } + + // There are cases where some allocated floats exceed the CB size. + bool sizeChanged = doFloatsExceedCB (LEFT) || doFloatsExceedCB (RIGHT); + + // Similar for extremes. + bool extremesChanged = + haveExtremesChanged (LEFT) || haveExtremesChanged (RIGHT); + + for (int i = 0; i < leftFloatsCB->size(); i++) + leftFloatsCB->get(i)->updateAllocation (); + + for (int i = 0; i < rightFloatsCB->size(); i++) + rightFloatsCB->get(i)->updateAllocation (); + + if (sizeChanged || extremesChanged) + container->oofSizeChanged (extremesChanged); + } + } + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int i = 0; i < leftFloatsAll->size (); i++) + leftFloatsAll->get(i)->getWidget()->containerSizeChanged (); + for (int i = 0; i < rightFloatsAll->size (); i++) + rightFloatsAll->get(i)->getWidget()->containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, + Widget **minFloat) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, ...", tbInfo->getWidget ()); + + 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; + } + } + } + + if (c1 || c2) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + return c1 || c2; +} + +bool OOFFloatsMgr::hasRelationChanged (TBInfo *tbInfo, Side side, + int *minFloatPos, Widget **minFloat) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>widget:</i> %p, %s, ...", + tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool changed = false; + + for (int i = 0; i < list->size(); i++) { + // TODO binary search? + Float *vloat = list->get(i); + int floatPos; + + if (tbInfo->getOOFAwareWidget () == vloat->generatingBlock) + DBG_OBJ_MSGF ("resize.oofm", 1, + "not checking (generating!) textblock %p against float " + "%p", tbInfo->getWidget (), vloat->getWidget ()); + else { + Allocation *gba = getAllocation (vloat->generatingBlock); + + int newFlx = calcFloatX (vloat, side, + gba->x - containerAllocation.x, + getGBWidthForAllocation (vloat)); + int newFly = vloat->generatingBlock->getAllocation()->y + - containerAllocation.y + vloat->yReal; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "checking textblock %p against float %p", + tbInfo->getWidget (), vloat->getWidget ()); + DBG_OBJ_MSG_START (); + + if (hasRelationChanged (tbInfo->wasThenAllocated (), + tbInfo->getOldXCB (), tbInfo->getOldYCB (), + tbInfo->getNewWidth (), + tbInfo->getNewHeight (), + tbInfo->getNewXCB (), tbInfo->getNewYCB (), + tbInfo->getNewWidth (), + tbInfo->getNewHeight (), + vloat->wasThenAllocated (), + // When not allocated before, these values + // are undefined, but this does not matter, + // since they are neither used. + vloat->getOldXCB (), vloat->getOldYCB (), + vloat->getOldWidth (), vloat->getOldHeight (), + newFlx, newFly, vloat->size.width, + vloat->size.ascent + vloat->size.descent, + side, &floatPos)) { + if (!changed || floatPos < *minFloatPos) { + *minFloatPos = floatPos; + *minFloat = vloat->getWidget (); + } + changed = true; + } else + DBG_OBJ_MSG ("resize.oofm", 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?) + } + + if (changed) + DBG_OBJ_MSGF ("resize.oofm", 1, + "has changed: minFloatPos = %d, minFloat = %p", + *minFloatPos, *minFloat); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + return changed; +} + +/** + * \brief ... + * + * All coordinates are given relative to the CB. *floatPos is relative + * to the TB, and may be negative. + */ +bool OOFFloatsMgr::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_ENTER ("resize.oofm", 0, "hasRelationChanged", + "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT"); + + if (oldTBAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", + oldTBx, oldTBy, oldTBw, oldTBh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d", + newTBx, newTBy, newTBw, newTBh); + + if (oldFlAlloc) + DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d", + oldFlx, oldFly, oldFlw, oldFlh); + else + DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined"); + DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d", + newFlx, newFly, newFlw, newFlh); + + bool result; + if (oldTBAlloc && oldFlAlloc) { + bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh; + bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; + + DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.", + oldCov ? "yes" : "no", newCov ? "yes" : "no"); + DBG_OBJ_MSG_START (); + + if (oldCov && newCov) { + int yOld = oldFly - oldTBy, yNew = newFly - newTBy; + if (yOld == yNew) { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d) and new (%d - %d) position equal: %d", + oldFly, oldTBy, newFly, newTBy, yOld); + + // 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; + } + + DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n", + wOld, wNew); + + if (wOld == wNew) { + if (oldFlh == newFlh) + result = false; + else { + // Only heights of floats changed. Relevant only + // from bottoms of float. + *floatPos = min (yOld + oldFlh, yNew + newFlh); + result = true; + } + } else { + *floatPos = yOld; + result = true; + } + } else { + DBG_OBJ_MSGF ("resize.oofm", 2, + "old (%d - %d = %d) and new (%d - %d = %d) position " + "different", + oldFly, oldTBy, yOld, newFly, newTBy, yNew); + *floatPos = min (yOld, yNew); + result = true; + } + } else if (oldCov) { + *floatPos = oldFly - oldTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning old position: %d - %d = %d", oldFly, oldTBy, + *floatPos); + } else if (newCov) { + *floatPos = newFly - newTBy; + result = true; + DBG_OBJ_MSGF ("resize.oofm", 2, + "returning new position: %d - %d = %d", newFly, newTBy, + *floatPos); + } else + result = false; + + DBG_OBJ_MSG_END (); + } 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; + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d", + *floatPos); + else + DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); + + DBG_OBJ_LEAVE (); + + return result; +} + +void OOFFloatsMgr::checkAllocatedFloatCollisions (Side side) +{ + // In some cases, the collision detection in tellPosition() is + // based on the wrong allocations. Here (just after all Floats have + // been allocated), we correct this. + + // TODO In some cases this approach is rather slow, causing a too + // long queueResize() cascade. + + DBG_OBJ_ENTER ("resize.oofm", 0, "checkAllocatedFloatCollisions", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + SortedFloatsVector *oppList = side == LEFT ? rightFloatsCB : leftFloatsCB; + + // While iterating through the list of floats to be checked, we + // iterate equally through the list of the opposite floats, using + // this index: + int oppIndex = 0; + + for (int index = 0; index < list->size (); index++) { + Float *vloat = list->get(index); + bool needsChange = false; + int yRealNew = INT_MAX; + + // Same side. + if (index >= 1) { + Float *other = list->get(index - 1); + DBG_OBJ_MSGF ("resize.oofm", 1, + "same side: checking %p (#%d, GB: %p) against " + "%p (#%d, GB: %p)", + vloat->getWidget (), index, vloat->generatingBlock, + other->getWidget (), index - 1, other->generatingBlock); + + if (vloat->generatingBlock != other->generatingBlock) { + int yRealNewSame; + if (collidesV (vloat, other, CB, &yRealNewSame, true)) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> collides, new yReal = %d (old: %d)", + yRealNewSame, vloat->yReal); + if (vloat->yReal != yRealNewSame) { + needsChange = true; + yRealNew = min (yRealNew, yRealNewSame); + } + } else + DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision"); + } + } + + if (oppList->size () > 0) { + // Other side. Iterate to next float on the other side, + // before this float. + while (oppIndex + 1 < oppList->size () && + oppList->get(oppIndex + 1)->sideSpanningIndex + < vloat->sideSpanningIndex) + oppIndex++; + + if (oppList->get(oppIndex)->sideSpanningIndex + < vloat->sideSpanningIndex) { + int oppIndexTmp = oppIndex, yRealNewOpp; + + // Aproach is similar to tellPosition(); see comments + // there. Again, loop as long as the vertical dimensions test + // is positive (and, of course, there are floats), ... + for (bool foundColl = false; + !foundColl && oppIndexTmp >= 0 && + collidesV (vloat, oppList->get (oppIndexTmp), CB, + &yRealNewOpp, true); + oppIndexTmp--) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "opposite side (after collision (v) test): " + "checking %p (#%d/%d, GB: %p) against " + "%p (#%d/%d, GB: %p)", + vloat->getWidget (), index, + vloat->sideSpanningIndex, + vloat->generatingBlock, + oppList->get(oppIndexTmp)->getWidget (), + oppList->get(oppIndexTmp)->getIndex (CB), + oppList->get(oppIndexTmp)->sideSpanningIndex, + oppList->get(oppIndexTmp)->generatingBlock); + + // ... but stop the loop as soon as the horizontal dimensions + // test is positive. + if (collidesH (vloat, oppList->get (oppIndexTmp), CB)) { + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> collides (h), new yReal = %d (old: %d)", + yRealNewOpp, vloat->yReal); + foundColl = true; + if (vloat->yReal != yRealNewOpp) { + needsChange = true; + yRealNew = min (yRealNew, yRealNewOpp); + } + } else + DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision (h)"); + } + } + } + + if (needsChange) + vloat->generatingBlock->borderChanged (min (vloat->yReal, yRealNew), + vloat->getWidget ()); + } + + DBG_OBJ_LEAVE (); +} + +bool OOFFloatsMgr::doFloatsExceedCB (Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + // This method is called to determine whether the *requisition* of + // the CB must be recalculated. So, we check the float allocations + // against the *requisition* of the CB, which may (e. g. within + // tables) differ from the new allocation. (Generally, a widget may + // allocated at a different size.) + core::Requisition cbReq; + container->sizeRequest (&cbReq); + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool exceeds = false; + + DBG_OBJ_MSG_START (); + + for (int i = 0; i < list->size () && !exceeds; i++) { + Float *vloat = list->get (i); + if (vloat->getWidget()->wasAllocated ()) { + Allocation *fla = vloat->getWidget()->getAllocation (); + DBG_OBJ_MSGF ("resize.oofm", 2, + "Does FlA = (%d, %d, %d * %d) exceed CB req+alloc = " + "(%d, %d, %d * %d)?", + fla->x, fla->y, fla->width, fla->ascent + fla->descent, + containerAllocation.x, containerAllocation.y, + cbReq.width, cbReq.ascent + cbReq.descent); + if (fla->x + fla->width > containerAllocation.x + cbReq.width || + fla->y + fla->ascent + fla->descent + > containerAllocation.y + cbReq.ascent + cbReq.descent) { + exceeds = true; + DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); + } else + DBG_OBJ_MSG ("resize.oofm", 2, "No."); + } + } + + DBG_OBJ_MSG_END (); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return exceeds; +} + +bool OOFFloatsMgr::haveExtremesChanged (Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + // This is quite different from doFloatsExceedCB, since there is no + // counterpart to getExtremes, as sizeAllocate is a counterpart to + // sizeRequest. So we have to determine whether the allocation has + // changed the extremes, which is done by examining the part of the + // allocation which is part of the extremes calculation (see + // getFloatsExtremes). Changes of the extremes are handled by the + // normal queueResize mechanism. + + // (This may refer to the old implementation of doFloatsExceedCB, + // which checked the container *allocation*, not the *requisition*. + // + // TODO: (i) Correct the comment. (ii) Is this implementation still + // correct, considering the reasoning behind the change of + // doFloatsExceedCB? + // + // See also OOFPositionedMgr::haveExtremesChanged.) + + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + bool changed = false; + + for (int i = 0; i < list->size () && !changed; i++) { + Float *vloat = list->get (i); + // When the GB is the CB, an allocation change does not play a + // role here. + if (vloat->generatingBlock != container) { + if (!vloat->wasThenAllocated () && vloat->isNowAllocated ()) + changed = true; + else { + // This method is called within sizeAllocateEnd, where + // containinBlock->getAllocation() (old value) and + // containinBlockAllocation (new value) are different. + + Allocation *oldCBA = container->getAllocation (); + Allocation *newCBA = &containerAllocation; + + // Compare also to getFloatsExtremes. The box difference + // of the GB (from style) has not changed in this context, + // so it is ignored. + + int oldDiffLeft = vloat->getOldXCB (); + int newDiffLeft = vloat->getNewXCB (); + int oldDiffRight = + oldCBA->width - (vloat->getOldXCB () + vloat->getOldWidth ()); + int newDiffRight = + newCBA->width - (vloat->getNewXCB () + vloat->getNewWidth ()); + + if (// regarding minimum + (side == LEFT && oldDiffLeft != newDiffLeft) || + (side == RIGHT && oldDiffRight != newDiffRight) || + // regarding maximum + oldDiffLeft + oldDiffRight != newDiffLeft + newDiffRight) + changed = true; + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return changed; +} + +void OOFFloatsMgr::moveFromGBToCB (Side side) +{ + DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s", + side == LEFT ? "LEFT" : "RIGHT"); + + SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; + int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; + + for (int mark = 0; mark <= *floatsMark; mark++) + for (lout::container::typed::Iterator<TBInfo> 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); + // "vloat->indexCBList == -1": prevent copying the vloat twice. + if (vloat->indexCBList == -1 && vloat->mark == mark) { + dest->put (vloat); + DBG_OBJ_MSGF ("oofm.resize", 1, + "moving float %p (mark %d) to CB list\n", + vloat->getWidget (), vloat->mark); + DBG_OBJ_SET_NUM (side == LEFT ? + "leftFloatsCB.size" : "rightFloatsCB.size", + dest->size()); + DBG_OBJ_ARRATTRSET_PTR (side == LEFT ? + "leftFloatsCB" : "rightFloatsCB", + dest->size() - 1, "widget", + vloat->getWidget ()); + + } + } + } + + *floatsMark = 0; + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) +{ + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + int *lastAllocatedFloat = + side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat; + + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", + "%s, [%d ->] %d [size = %d]", + side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, + newLastAllocatedFloat, list->size ()); + + Allocation *cba = &containerAllocation; + + for (int i = *lastAllocatedFloat + 1; i <= newLastAllocatedFloat; i++) { + Float *vloat = list->get(i); + ensureFloatSize (vloat); + + Allocation *gba = getAllocation (vloat->generatingBlock); + + Allocation childAllocation; + childAllocation.x = cba->x + calcFloatX (vloat, side, gba->x - cba->x, + getGBWidthForAllocation (vloat)); + childAllocation.y = gba->y + vloat->yReal; + childAllocation.width = vloat->size.width; + childAllocation.ascent = vloat->size.ascent; + childAllocation.descent = vloat->size.descent; + + vloat->getWidget()->sizeAllocate (&childAllocation); + } + + *lastAllocatedFloat = newLastAllocatedFloat; + + DBG_OBJ_LEAVE (); +} + +// Used as argument "gbWidth" for calcFloatX(), in the context of allocation. +int OOFFloatsMgr::getGBWidthForAllocation (Float *vloat) +{ + // See comments in getFloatsSize() for a detailed rationale ... + if (container->mustBeWidenedToAvailWidth ()) + return vloat->generatingBlock->getLineBreakWidth (); + else + // ... but notice this difference: not GB width + float width is + // used, but only GB width, since the float width has already + // been included in getFloatsSize(). + return min (getAllocation(vloat->generatingBlock)->width, + vloat->generatingBlock->getLineBreakWidth ()); +} + +/** + * \brief ... + * + * gbX is given relative to the CB, as is the return value. + */ +int OOFFloatsMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth) +{ + DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d", + vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, + gbWidth); + int x; + + switch (side) { + case LEFT: + // Left floats are always aligned on the left side of the + // generator (content, not allocation) ... + x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); + DBG_OBJ_MSGF ("resize.common", 1, "left: x = %d + %d = %d", + gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x); + // ... 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 (wasAllocated (container) && + x + vloat->size.width > container->getLineBreakWidth ()) { + x = max (0, container->getLineBreakWidth () - vloat->size.width); + DBG_OBJ_MSGF ("resize.common", 1, + "corrected to: max (0, %d - %d) = %d", + container->getLineBreakWidth (), vloat->size.width, x); + } + break; + + case 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). + + x = max (gbX + gbWidth - vloat->size.width + - vloat->generatingBlock->getStyle()->boxRestWidth(), + // Do not exceed CB allocation: + 0); + DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d", + gbX, gbWidth, vloat->size.width, + vloat->generatingBlock->getStyle()->boxRestWidth(), x); + break; + + default: + assertNotReached (); + x = 0; + break; + } + + DBG_OBJ_LEAVE (); + 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 (leftFloatsCB, view, area, context); + drawFloats (rightFloatsCB, 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<OOFAwareWidget> (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: + leftFloatsAll->put (vloat); + DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + + subRef = createSubRefLeftFloat (leftFloatsAll->size() - 1); + tbInfo->leftFloatsGB->put (vloat); + + if (wasAllocated (generatingBlock)) { + leftFloatsCB->put (vloat); + DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1, + "widget", vloat->getWidget ()); + } else { + if (tbInfo->index < lastLeftTBIndex) + leftFloatsMark++; + + vloat->mark = leftFloatsMark; + lastLeftTBIndex = tbInfo->index; + } + break; + + case FLOAT_RIGHT: + rightFloatsAll->put (vloat); + DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, + "widget", vloat->getWidget ()); + + subRef = createSubRefRightFloat (rightFloatsAll->size() - 1); + tbInfo->rightFloatsGB->put (vloat); + + if (wasAllocated (generatingBlock)) { + rightFloatsCB->put (vloat); + DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); + DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1, + "widget", vloat->getWidget ()); + } else { + if (tbInfo->index < lastRightTBIndex) + rightFloatsMark++; + + vloat->mark = rightFloatsMark; + 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> (widget), vloat); + + DBG_OBJ_MSGF ("construct.oofm", 1, "=> %d", subRef); + DBG_OBJ_LEAVE (); + 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->leftFloatsGB, oldStartIndex, diff); + moveExternalIndices (tbInfo->rightFloatsGB, oldStartIndex, diff); +} + +void OOFFloatsMgr::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; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.externalIndex", + vloat->externalIndex); + } + } +} + +OOFFloatsMgr::Float *OOFFloatsMgr::findFloatByWidget (Widget *widget) +{ + TypedPointer <Widget> key (widget); + Float *vloat = floatsByWidget->get (&key); + assert (vloat != NULL); + return vloat; +} + +void OOFFloatsMgr::markSizeChange (int ref) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); + + Float *vloat; + + if (isSubRefLeftFloat (ref)) + vloat = leftFloatsAll->get (getFloatIndexFromSubRef (ref)); + else if (isSubRefRightFloat (ref)) + vloat = rightFloatsAll->get (getFloatIndexFromSubRef (ref)); + else { + assertNotReached(); + vloat = NULL; // compiler happiness + } + + vloat->dirty = vloat->sizeChangedSinceLastAllocation = true; + + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), + "<Float>.sizeChangedSinceLastAllocation", + vloat->sizeChangedSinceLastAllocation); + + // The generating block is told directly about this. (Others later, in + // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which + // differentiates many special cases), but the size is not known yet, + vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ()); + + DBG_OBJ_LEAVE (); +} + + +void OOFFloatsMgr::markExtremesChange (int ref) +{ + // Nothing to do here. +} + +Widget *OOFFloatsMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + Widget *widgetAtPoint = NULL; + + widgetAtPoint = getFloatWidgetAtPoint (rightFloatsCB, x, y, context); + if (widgetAtPoint == NULL) + widgetAtPoint = getFloatWidgetAtPoint (leftFloatsCB, 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); + + // "yReal" may change due to collisions (see below). + vloat->yReq = vloat->yReal = y; + + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReq", vloat->yReq); + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.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 index = vloat->getIndex (listSame->type), yRealNew; + if (index >= 1 && + collidesV (vloat, listSame->get (index - 1), listSame->type, + &yRealNew, false)) { + vloat->yReal = yRealNew; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.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): + // + // <div style="float:left" id="f1"> + // Left left left left left left left left left left. + // </div> + // <div style="float:left" id="f2">Also left.</div> + // <div style="float:right" id="f3">Right.</div> + // + // 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), listSame->type, + &yRealNew, false); + oppFloatIndex--) { + // ... but stop the loop as soon as the horizontal dimensions + // test is positive. + if (collidesH (vloat, listOpp->get (oppFloatIndex), listSame->type)) { + vloat->yReal = yRealNew; + DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); + foundColl = true; + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", + vloat->yReq, vloat->yReal); + + DBG_OBJ_LEAVE (); +} + +void OOFFloatsMgr::tellPosition2 (Widget *widget, int x, int y) +{ +} + +bool OOFFloatsMgr::collidesV (Float *vloat, Float *other, SFVType type, + int *yReal, bool useAllocation) +{ + // Only checks vertical (possible) collisions, and only refers to + // vloat->yReal; never to vloat->allocation->y, even when the GBs are + // different. Used only in tellPosition. + + DBG_OBJ_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...", + vloat->getIndex (type), vloat->getWidget (), + other->getIndex (type), other->getWidget ()); + + bool result; + + DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); + + if (vloat->generatingBlock == other->generatingBlock) { + ensureFloatSize (other); + int otherBottomGB = + other->yReal + other->size.ascent + other->size.descent; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "same generators: 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; + } else { + // If the other float is not allocated, there is no collision. The + // allocation of this float (vloat) is not used at all. + if (!other->getWidget()->wasAllocated ()) + result = false; + else { + assert (wasAllocated (vloat->generatingBlock)); + Allocation *gba = getAllocation (vloat->generatingBlock), + *flaOther = other->getWidget()->getAllocation (); + + // We distinguish two cases (by different values of useAllocation): + // (i) within tellPosition, GB allocation + yReal is used for the + // y position of the other float, while (ii) in checkAllocatedFloat- + // Collisions, the float allocation is used. The latter is necessary + // by the definition of this method, the former increases performance, + // as compared to using the float allocation, in some cases, as in + // this: + // + // When '<div><div style="float:left">[Some text]</div></div>' is + // repeated n times, the resize idle function (Layout::resizeIdle) + // would be repeated roughly n times, when also in case (i) the float + // allocation is used, since for the collision test of float n with + // float n - 1, the allocation of float n - 1 does not yet reflect the + // collision test between n - 1 and n - 2, but yReal does for n - 1. + // + // On the other hand, the GB allocations will most likely more stable + // than the float allocations. + // + // Cases where this is incorrect will hopefully be rare, and, in any + // case, corrected in sizeAllocateEnd, either because hasRelation- + // Changed returns true, or in checkAllocatedFloatCollisions. + + int otherFloatY = useAllocation ? flaOther->y : + getAllocation(other->generatingBlock)->y + other->yReal; + int otherBottomGB = + otherFloatY + flaOther->ascent + flaOther->descent - gba->y; + + DBG_OBJ_MSGF ("resize.oofm", 1, + "different generators: " + "otherBottomGB = %d + (%d + %d) - %d = %d", + otherFloatY, flaOther->ascent, flaOther->descent, gba->y, + otherBottomGB); + + if (vloat->yReal < otherBottomGB) { + *yReal = otherBottomGB; + result = true; + } else + result = false; + } + } + + if (result) + DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal); + else + DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); + + DBG_OBJ_LEAVE (); + return result; +} + + +bool OOFFloatsMgr::collidesH (Float *vloat, Float *other, SFVType type) +{ + // Only checks horizontal collision. For a complete test, use + // collidesV (...) && collidesH (...). + bool collidesH; + + if (vloat->generatingBlock == other->generatingBlock) + collidesH = vloat->size.width + other->size.width + + vloat->generatingBlock->boxDiffWidth() + > vloat->generatingBlock->getLineBreakWidth(); + else { + // Again, if the other float is not allocated, there is no + // collision. Compare to collidesV. (But vloat->size is used + // here.) + if (!other->getWidget()->wasAllocated ()) + collidesH = false; + else { + assert (wasAllocated (vloat->generatingBlock)); + Allocation *gba = getAllocation (vloat->generatingBlock); + int vloatX = + calcFloatX (vloat, + vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? + LEFT : RIGHT, + gba->x, getGBWidthForAllocation (vloat)); + + // 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 + collidesH = vloatX + vloat->size.width + > other->getWidget()->getAllocation()->x; + else + // "other" is left, "vloat" is right + collidesH = other->getWidget()->getAllocation()->x + + other->getWidget()->getAllocation()->width + > vloatX; + } + } + + return collidesH; +} + +void OOFFloatsMgr::getFloatsListsAndSide (Float *vloat, + SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, + Side *side) +{ + TBInfo *tbInfo = getOOFAwareWidget (vloat->generatingBlock); + + switch (vloat->getWidget()->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; + } + if (side) *side = LEFT; + 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; + } + 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 (); + + 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 = getFloatsListForOOFAwareWidget (container, side); + + *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->generatingBlock, container); + + if (vloat->generatingBlock == container || + wasAllocated (vloat->generatingBlock)) { + ensureFloatSize (vloat); + int x, y; + + int effWidth; + if (container->mustBeWidenedToAvailWidth ()) + // 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.) + effWidth = vloat->generatingBlock->getLineBreakWidth (); + 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. + effWidth = min (cbReq->width + vloat->size.width, + vloat->generatingBlock->getLineBreakWidth ()); + + if (vloat->generatingBlock == container) { + x = calcFloatX (vloat, side, 0, effWidth); + y = vloat->yReal; + } else { + Allocation *gba = getAllocation(vloat->generatingBlock); + x = calcFloatX (vloat, side, gba->x - containerAllocation.x, + effWidth); + y = gba->y - containerAllocation.y + vloat->yReal; + } + + *width = max (*width, x + vloat->size.width); + *height = max (*height, y + vloat->size.ascent + vloat->size.descent); + + DBG_OBJ_MSGF ("resize.oofm", 1, + "considering float %p generated by %p: (%d + %d) * " + "(%d + (%d + %d)) => %d * %d", + vloat->getWidget (), vloat->generatingBlock, + x, vloat->size.width, + y, vloat->size.ascent, vloat->size.descent, + *width, *height); + } else + DBG_OBJ_MSGF ("resize.oofm", 1, + "considering float %p generated by %p: not allocated", + vloat->getWidget (), vloat->generatingBlock); + } + + 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 = getFloatsListForOOFAwareWidget (container, side); + 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->generatingBlock, + container); + + if (vloat->generatingBlock == container || + wasAllocated (vloat->generatingBlock)) { + 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. + + if (vloat->generatingBlock == container) + *maxWidth = + max (*maxWidth, + extr.maxWidth + + vloat->generatingBlock->getStyle()->boxDiffWidth()); + else { + Allocation *gba = getAllocation (vloat->generatingBlock); + *maxWidth = + max (*maxWidth, + extr.maxWidth + + vloat->generatingBlock->getStyle()->boxDiffWidth() + + max (containerAllocation.width - gba->width, 0)); + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "%d / %d => %d / %d", + extr.minWidth, extr.maxWidth, *minWidth, *maxWidth); + } else + DBG_OBJ_MSG ("resize.oofm", 1, "not allocated"); + } + + DBG_OBJ_LEAVE (); +} + +OOFFloatsMgr::TBInfo *OOFFloatsMgr::getOOFAwareWidgetWhenRegistered + (OOFAwareWidget *widget) +{ + DBG_OBJ_ENTER ("oofm.common", 0, "getOOFAwareWidgetWhenRegistered", "%p", + widget); + TypedPointer<OOFAwareWidget> 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 (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +int OOFFloatsMgr::getRightBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + int b = getBorder (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d", + textblock, y, h, lastGB, lastExtIndex, b); + return b; +} + +int OOFFloatsMgr::getBorder (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int last; + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, &last); + + DBG_OBJ_MSGF ("border", 1, "first = %d", first); + + if (first == -1) { + // No float. + DBG_OBJ_LEAVE (); + 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; + + // 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 (textblock, y, h); + DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", + i, vloat->getWidget(), covers ? "<b>yes</b>" : "no"); + + if (covers) { + int thisBorder; + if (vloat->generatingBlock == textblock) { + int borderIn = side == LEFT ? + vloat->generatingBlock->boxOffsetX() : + vloat->generatingBlock->boxRestWidth(); + thisBorder = vloat->size.width + borderIn; + DBG_OBJ_MSGF ("border", 1, "GB: thisBorder = %d + %d = %d", + vloat->size.width, borderIn, thisBorder); + } else { + assert (wasAllocated (vloat->generatingBlock)); + assert (vloat->getWidget()->wasAllocated ()); + + Allocation *tba = getAllocation(textblock), + *fla = vloat->getWidget()->getAllocation (); + if (side == LEFT) { + thisBorder = fla->x + fla->width - tba->x; + DBG_OBJ_MSGF ("border", 1, + "not GB: thisBorder = %d + %d - %d = %d", + fla->x, fla->width, tba->x, thisBorder); + } else { + // See also calcFloatX. + thisBorder = + tba->x + textblock->getLineBreakWidth () - fla->x; + DBG_OBJ_MSGF ("border", 1, + "not GB: thisBorder = %d + %d - %d " + "= %d", + tba->x, textblock->getLineBreakWidth (), fla->x, + thisBorder); + } + } + + border = max (border, thisBorder); + DBG_OBJ_MSGF ("border", 1, "=> border = %d", border); + } + } + + DBG_OBJ_LEAVE (); + return border; + } +} + + +OOFFloatsMgr::SortedFloatsVector *OOFFloatsMgr::getFloatsListForOOFAwareWidget + (OOFAwareWidget *textblock, Side side) +{ + DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForOOFAwareWidget", "%p, %s", + textblock, side == LEFT ? "LEFT" : "RIGHT"); + + OOFFloatsMgr::SortedFloatsVector *list; + + if (wasAllocated (textblock)) { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list"); + list = side == LEFT ? leftFloatsCB : rightFloatsCB; + } else { + DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list"); + TBInfo *tbInfo = getOOFAwareWidget (textblock); + list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; + } + + DBG_OBJ_LEAVE (); + return list; +} + + +bool OOFFloatsMgr::hasFloatLeft (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +bool OOFFloatsMgr::hasFloatRight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); + DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s", + textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); + return b; +} + +bool OOFFloatsMgr::hasFloat (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); + + DBG_OBJ_MSGF ("border", 1, "first = %d", first); + DBG_OBJ_LEAVE (); + return first != -1; +} + +int OOFFloatsMgr::getLeftFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + return getFloatHeight (textblock, LEFT, y, h, lastGB, lastExtIndex); +} + +int OOFFloatsMgr::getRightFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex) +{ + return getFloatHeight (textblock, RIGHT, y, h, lastGB, lastExtIndex); +} + +int OOFFloatsMgr::getFloatHeight (OOFAwareWidget *textblock, Side side, int y, + int h, OOFAwareWidget *lastGB, + int lastExtIndex) +{ + DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%p, %s, %d, %d, %p, %d", + textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, + lastGB, lastExtIndex); + + SortedFloatsVector *list = getFloatsListForOOFAwareWidget (textblock, side); + int first = list->findFirst (textblock, 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; + + if (vloat->generatingBlock == textblock) { + yRelToFloat = y - vloat->yReal; + DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d", + y, vloat->yReal, yRelToFloat); + } else { + // The respective widgets are allocated; otherwise, hasFloat() would have + // returned false. + assert (wasAllocated (textblock)); + assert (vloat->getWidget()->wasAllocated ()); + + Allocation *tba = getAllocation(textblock), + *fla = vloat->getWidget()->getAllocation (); + yRelToFloat = tba->y + y - fla->y; + + DBG_OBJ_MSGF ("border", 1, + "caller is not CB: yRelToFloat = %d + %d - %d = %d", + tba->y, y, fla->y, 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; +} + +/** + * Returns position relative to the textblock "tb". + */ +int OOFFloatsMgr::getClearPosition (OOFAwareWidget *textblock) +{ + return getOOFAwareWidget(textblock)->clearPosition; +} + +int OOFFloatsMgr::calcClearPosition (OOFAwareWidget *textblock) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", textblock); + + int pos; + + if (textblock->getStyle()) { + bool left = false, right = false; + switch (textblock->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 ? calcClearPosition (textblock, LEFT) : 0, + right ? calcClearPosition (textblock, RIGHT) : 0); + } else + pos = 0; + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); + DBG_OBJ_LEAVE (); + + 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::calcClearPosition (OOFAwareWidget *textblock, Side side) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", + textblock, side == LEFT ? "LEFT" : "RIGHT"); + + int pos; + + if (!wasAllocated (textblock)) + // There is no relation yet to floats generated by other + // textblocks, and this textblocks floats are unimportant for + // the "clear" property. + pos = 0; + else { + SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; + + // Search the last float before (therfore -1) this textblock. + int i = list->findFloatIndex (textblock, -1); + if (i < 0) { + pos = 0; + DBG_OBJ_MSG ("resize.oofm", 1, "no float"); + } else { + Float *vloat = list->get(i); + assert (vloat->generatingBlock != textblock); + if (!wasAllocated (vloat->generatingBlock)) + pos = 0; // See above. + else { + ensureFloatSize (vloat); + pos = max (getAllocation(vloat->generatingBlock)->y + vloat->yReal + + vloat->size.ascent + vloat->size.descent + - getAllocation(textblock)->y, + 0); + DBG_OBJ_MSGF ("resize.oofm", 1, + "float %p => max (%d + %d + (%d + %d) - %d, 0)", + vloat->getWidget (), + getAllocation(vloat->generatingBlock)->y, + vloat->yReal, vloat->size.ascent, vloat->size.descent, + getAllocation(textblock)->y); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); + DBG_OBJ_LEAVE (); + + 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->cbLineBreakWidth = container->getLineBreakWidth (); + vloat->dirty = false; + DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.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(), "<Float>.size.width", + vloat->size.width); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.ascent", + vloat->size.ascent); + DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.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) +{ + assertNotReached (); + return 0; +} + +int OOFFloatsMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +int OOFFloatsMgr::getNumWidgets () +{ + return leftFloatsAll->size() + rightFloatsAll->size(); +} + +Widget *OOFFloatsMgr::getWidget (int i) +{ + if (i < leftFloatsAll->size()) + return leftFloatsAll->get(i)->getWidget (); + else + return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget (); +} + +} // namespace oof + +} // namespace dw diff --git a/dw/ooffloatsmgr.hh b/dw/ooffloatsmgr.hh new file mode 100644 index 00000000..1a66e2ff --- /dev/null +++ b/dw/ooffloatsmgr.hh @@ -0,0 +1,402 @@ +#ifndef __DW_OOFFLOATSMGR_HH__ +#define __DW_OOFFLOATSMGR_HH__ + +#include "outofflowmgr.hh" + +namespace dw { + +namespace oof { + +/** + * \brief OutOfFlowMgr implementation dealing with floats. + * + * Note: The identifiers and comments of this class still refer to + * "Textblock" instead of "OOFAwareWidget"; should be cleaned up some + * day. (OTOH, these widgets are always textblocks.) + */ +class OOFFloatsMgr: public OutOfFlowMgr +{ + friend class WidgetInfo; + +private: + enum Side { LEFT, RIGHT }; + enum SFVType { GB, CB }; + + OOFAwareWidget *container; + + // These two values are redundant to TBInfo::wasAllocated and + // TBInfo::allocation, for some special cases. + bool containerWasAllocated; + core::Allocation containerAllocation; + + class WidgetInfo: public lout::object::Object + { + private: + bool wasAllocated; + int xCB, yCB; // relative to the containing block + int width, height; + + OOFFloatsMgr *oofm; + core::Widget *widget; + + protected: + OOFFloatsMgr *getOOFFloatsMgr () { return oofm; } + + public: + WidgetInfo (OOFFloatsMgr *oofm, core::Widget *widget); + + inline bool wasThenAllocated () { return wasAllocated; } + inline int getOldXCB () { return xCB; } + inline int getOldYCB () { return yCB; } + inline int getOldWidth () { return width; } + inline int getOldHeight () { return height; } + + + void update (bool wasAllocated, int xCB, int yCB, int width, int height); + + inline core::Widget *getWidget () { return widget; } + }; + + class Float: public WidgetInfo + { + public: + class ComparePosition: public lout::object::Comparator + { + private: + OOFFloatsMgr *oofm; + OOFAwareWidget *refTB; + SFVType type; // actually only used for debugging + + public: + ComparePosition (OOFFloatsMgr *oofm, OOFAwareWidget *refTB, + SFVType type) + { this->oofm = oofm; this->refTB = refTB; this->type = type; } + int compare(Object *o1, Object *o2); + }; + + class CompareSideSpanningIndex: public lout::object::Comparator + { + public: + int compare(Object *o1, Object *o2); + }; + + class CompareGBAndExtIndex: public lout::object::Comparator + { + private: + OOFFloatsMgr *oofm; + SFVType type; // actually only used for debugging + + public: + CompareGBAndExtIndex (OOFFloatsMgr *oofm, SFVType type) + { this->oofm = oofm; this->type = type; } + int compare(Object *o1, Object *o2); + }; + + OOFAwareWidget *generatingBlock; + int externalIndex; + int yReq, yReal; // relative to generator, not container + int indexGBList; /* Refers to TBInfo::leftFloatsGB or + TBInfo::rightFloatsGB, respectively. -1 + initially. */ + int indexCBList; /* Refers to leftFloatsCB or rightFloatsCB, + respectively. -1 initially. */ + int sideSpanningIndex, mark; + core::Requisition size; + int cbLineBreakWidth; /* On which the calculation of relative sizes + is based. Height not yet used, and probably + not added before size redesign. */ + bool dirty, sizeChangedSinceLastAllocation; + + Float (OOFFloatsMgr *oofm, core::Widget *widget, + OOFAwareWidget *generatingBlock, int externalIndex); + + inline bool isNowAllocated () { return getWidget()->wasAllocated (); } + inline int getNewXCB () { return getWidget()->getAllocation()->x - + getOOFFloatsMgr()->containerAllocation.x; } + inline int getNewYCB () { return getWidget()->getAllocation()->y - + getOOFFloatsMgr()->containerAllocation.y; } + inline int getNewWidth () { return getWidget()->getAllocation()->width; } + inline int getNewHeight () { return getWidget()->getAllocation()->ascent + + getWidget()->getAllocation()->descent; } + void updateAllocation (); + + inline int *getIndexRef (SFVType type) { + return type == GB ? &indexGBList : &indexCBList; } + inline int getIndex (SFVType type) { return *(getIndexRef (type)); } + inline void setIndex (SFVType type, int value) { + *(getIndexRef (type)) = value; } + + void intoStringBuffer(lout::misc::StringBuffer *sb); + + bool covers (OOFAwareWidget *textblock, int y, int h); + }; + + /** + * This list is kept sorted. + * + * To prevent accessing methods of the base class in an + * uncontrolled way, the inheritance is private, not public; this + * means that all methods must be delegated (see iterator(), size() + * etc. below.) + * + * TODO Update comment: still sorted, but ... + * + * More: add() and change() may check order again. + */ + class SortedFloatsVector: private lout::container::typed::Vector<Float> + { + public: + SFVType type; + + private: + OOFFloatsMgr *oofm; + Side side; + + public: + inline SortedFloatsVector (OOFFloatsMgr *oofm, Side side, SFVType type) : + lout::container::typed::Vector<Float> (1, false) + { this->oofm = oofm; this->side = side; this->type = type; } + + int findFloatIndex (OOFAwareWidget *lastGB, int lastExtIndex); + int find (OOFAwareWidget *textblock, int y, int start, int end); + int findFirst (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex, int *lastReturn); + int findLastBeforeSideSpanningIndex (int sideSpanningIndex); + void put (Float *vloat); + + inline lout::container::typed::Iterator<Float> iterator() + { return lout::container::typed::Vector<Float>::iterator (); } + inline int size () + { return lout::container::typed::Vector<Float>::size (); } + inline Float *get (int pos) + { return lout::container::typed::Vector<Float>::get (pos); } + inline void clear () + { lout::container::typed::Vector<Float>::clear (); } + }; + + class TBInfo: public WidgetInfo + { + public: + int lineBreakWidth; + int index; // position within "tbInfos" + + TBInfo *parent; + int parentExtIndex; + + // These two values are set by sizeAllocateStart(), and they are + // accessable also within sizeAllocateEnd() for the same + // textblock, for which allocation and WAS_ALLOCATED is set + // *after* sizeAllocateEnd(). See the two functions + // wasAllocated(Widget*) and getAllocation(Widget*) (further + // down) for usage. + bool wasAllocated; + core::Allocation allocation; + int clearPosition; + + // These two lists store all floats generated by this textblock, + // as long as this textblock is not allocates. + SortedFloatsVector *leftFloatsGB, *rightFloatsGB; + + TBInfo (OOFFloatsMgr *oofm, OOFAwareWidget *textblock, + TBInfo *parent, int parentExtIndex); + ~TBInfo (); + + inline bool isNowAllocated () { + return getOOFFloatsMgr()->wasAllocated (getOOFAwareWidget ()); } + inline int getNewXCB () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->x - + getOOFFloatsMgr()->containerAllocation.x; } + inline int getNewYCB () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->y - + getOOFFloatsMgr()->containerAllocation.y; } + inline int getNewWidth () { + return getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ())->width; } + inline int getNewHeight () { + core::Allocation *allocation = + getOOFFloatsMgr()->getAllocation (getOOFAwareWidget ()); + return allocation->ascent + allocation->descent; } + void updateAllocation (); + + inline OOFAwareWidget *getOOFAwareWidget () + { return (OOFAwareWidget*)getWidget (); } + }; + + // These two lists store all floats, in the order in which they are + // defined. Only used for iterators. + lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll; + + // These two lists store all floats whose generators are already + // allocated. + SortedFloatsVector *leftFloatsCB, *rightFloatsCB; + + // These two attributes are used in the size allocation process; + // see sizeAllocateStart and sizeAllocateEnd. + int lastAllocatedLeftFloat, lastAllocatedRightFloat; + + lout::container::typed::HashTable<lout::object::TypedPointer + <dw::core::Widget>, Float> *floatsByWidget; + + lout::container::typed::Vector<TBInfo> *tbInfos; + lout::container::typed::HashTable<lout::object::TypedPointer<OOFAwareWidget>, + TBInfo> *tbInfosByOOFAwareWidget; + + int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark; + + /** + * Variant of Widget::wasAllocated(), which can also be used within + * OOFM::sizeAllocateEnd(). + */ + inline bool wasAllocated (OOFAwareWidget *textblock) { + return getOOFAwareWidget(textblock)->wasAllocated; + } + + /** + * Variant of Widget::getAllocation(), which can also be used + * within OOFM::sizeAllocateEnd(). + */ + inline core::Allocation *getAllocation (OOFAwareWidget *textblock) { + return &(getOOFAwareWidget(textblock)->allocation); + } + + void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, + int diff); + Float *findFloatByWidget (core::Widget *widget); + + void moveFromGBToCB (Side side); + void sizeAllocateFloats (Side side, int newLastAllocatedFloat); + int getGBWidthForAllocation (Float *vloat); + int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth); + + bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, + core::Widget **minFloat); + bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, + core::Widget **minFloat); + bool 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); + + void checkAllocatedFloatCollisions (Side side); + + bool doFloatsExceedCB (Side side); + bool haveExtremesChanged (Side side); + + void drawFloats (SortedFloatsVector *list, core::View *view, + core::Rectangle *area, core::DrawingContext *context); + core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, + core::GettingWidgetAtPointContext + *context); + + bool collidesV (Float *vloat, Float *other, SFVType type, int *yReal, + bool useAllocation); + bool collidesH (Float *vloat, Float *other, SFVType type); + + void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, + SortedFloatsVector **listOpp, Side *side); + + void getFloatsSize (core::Requisition *cbReq, Side side, int *width, + int *height); + void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth, + int *maxWidth); + + TBInfo *getOOFAwareWidget (OOFAwareWidget *widget); + TBInfo *getOOFAwareWidgetWhenRegistered (OOFAwareWidget *widget); + inline bool isOOFAwareWidgetRegistered (OOFAwareWidget *widget) + { return getOOFAwareWidgetWhenRegistered (widget) != NULL; } + int getBorder (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + SortedFloatsVector *getFloatsListForOOFAwareWidget (OOFAwareWidget + *textblock, + Side side); + bool hasFloat (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int getFloatHeight (OOFAwareWidget *textblock, Side side, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int calcClearPosition (OOFAwareWidget *textblock); + int calcClearPosition (OOFAwareWidget *textblock, Side side); + + void ensureFloatSize (Float *vloat); + + inline static int createSubRefLeftFloat (int index) { return index << 1; } + inline static int createSubRefRightFloat (int index) + { return (index << 1) | 1; } + + inline static bool isSubRefLeftFloat (int ref) + { return ref != -1 && (ref & 1) == 0; } + inline static bool isSubRefRightFloat (int ref) + { return ref != -1 && (ref & 1) == 1; } + + inline static int getFloatIndexFromSubRef (int ref) + { return ref == -1 ? ref : (ref >> 1); } + +public: + OOFFloatsMgr (OOFAwareWidget *container); + ~OOFFloatsMgr (); + + void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation); + void sizeAllocateEnd (OOFAwareWidget *caller); + void containerSizeChangedForChildren (); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + void addWidgetInFlow (OOFAwareWidget *textblock, OOFAwareWidget *parentBlock, + int externalIndex); + int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generatingBlock, + int externalIndex); + void calcWidgetRefSize (core::Widget *widget,core::Requisition *size); + void moveExternalIndices (OOFAwareWidget *generatingBlock, int oldStartIndex, + int diff); + + void tellPosition1 (core::Widget *widget, int x, int y); + void tellPosition2 (core::Widget *widget, int x, int y); + + void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight); + bool containerMustAdjustExtraSpace (); + void getExtremes (core::Extremes *cbExtr, + int *oofMinWidth, int *oofMaxWidth); + + int getLeftBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + int getRightBorder (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + bool hasFloatLeft (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + bool hasFloatRight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + int getLeftFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + int getRightFloatHeight (OOFAwareWidget *textblock, int y, int h, + OOFAwareWidget *lastGB, int lastExtIndex); + + bool affectsLeftBorder (core::Widget *widget); + bool affectsRightBorder (core::Widget *widget); + bool mayAffectBordersAtAll (); + + int getClearPosition (OOFAwareWidget *textblock); + + bool dealingWithSizeOfChild (core::Widget *child); + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); + + int getNumWidgets (); + core::Widget *getWidget (int i); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFFLOATSMGR_HH__ diff --git a/dw/oofposabslikemgr.cc b/dw/oofposabslikemgr.cc new file mode 100644 index 00000000..bde9d71b --- /dev/null +++ b/dw/oofposabslikemgr.cc @@ -0,0 +1,427 @@ +/* + * Dillo Widget + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposabslikemgr.hh" + +using namespace dw::core; +using namespace lout::misc; + +namespace dw { + +namespace oof { + +OOFPosAbsLikeMgr::OOFPosAbsLikeMgr (OOFAwareWidget *container) : + OOFPositionedMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosAbsLikeMgr"); +} + +OOFPosAbsLikeMgr::~OOFPosAbsLikeMgr () +{ + DBG_OBJ_DELETE (); +} + +void OOFPosAbsLikeMgr::calcWidgetRefSize (Widget *widget, Requisition *size) +{ + size->width = size->ascent = size->descent = 0; +} + +void OOFPosAbsLikeMgr::sizeAllocateChildren () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren"); + + int refWidth = container->getAvailWidth (true) - containerBoxDiffWidth (); + int refHeight = container->getAvailHeight (true) - containerBoxDiffHeight (); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get (i); + + int x, y, width, ascent, descent; + calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width, + &ascent, &descent); + + Allocation childAllocation; + childAllocation.x = containerAllocation.x + x + containerBoxOffsetX (); + childAllocation.y = containerAllocation.y + y + containerBoxOffsetY (); + childAllocation.width = width; + childAllocation.ascent = ascent; + childAllocation.descent = descent; + + child->widget->sizeAllocate (&childAllocation); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPosAbsLikeMgr::getSize (Requisition *containerReq, int *oofWidth, + int *oofHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)", + containerReq->width, containerReq->ascent, + containerReq->descent); + + *oofWidth = *oofHeight = 0; + + int refWidth = container->getAvailWidth (true); + int refHeight = container->getAvailHeight (true); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child) && posYDefined (child)) { + int x, y, width, ascent, descent; + calcPosAndSizeChildOfChild (child, refWidth, refHeight, &x, &y, &width, + &ascent, &descent); + *oofWidth = max (*oofWidth, x + width) + containerBoxDiffWidth (); + *oofHeight = + max (*oofHeight, y + ascent + descent) + containerBoxDiffHeight (); + + child->consideredForSize = true; + } else + child->consideredForSize = false; + } + + DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight); +} + +void OOFPosAbsLikeMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getExtremes", "(%d / %d), ...", + containerExtr->minWidth, containerExtr->maxWidth); + + *oofMinWidth = *oofMaxWidth = 0; + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child)) { + int x, width; + Extremes childExtr; + child->widget->getExtremes (&childExtr); + + // Here, we put the extremes of the container in relation to + // the extremes of the child, as sizes are put in relation + // for calculating the size. In one case, the allocation is + // used: when neither "left" nor "right" is set, and so the + // position told by the generator is used. + // + // If you look at the Textblock widget, you'll find that this + // is always boxOffsetX(), and the horizontal position of a + // textblock within its parent is also constant; so this is + // not a problem. + // + // (TODO What about a table cell within a table?) + + calcHPosAndSizeChildOfChild (child, containerExtr->minWidth, + childExtr.minWidth, &x, &width); + *oofMinWidth = max (*oofMinWidth, x + width); + + calcHPosAndSizeChildOfChild (child, containerExtr->maxWidth, + childExtr.maxWidth, &x, &width); + *oofMaxWidth = max (*oofMaxWidth, x + width); + + child->consideredForExtremes = true; + } else + child->consideredForExtremes = false; + } + + *oofMinWidth += containerBoxDiffWidth (); + *oofMaxWidth += containerBoxDiffWidth (); + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> %d / %d", *oofMinWidth, *oofMaxWidth); + DBG_OBJ_LEAVE (); +} + +int OOFPosAbsLikeMgr::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, + "OOFPositionedMgr/getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO && + child->getStyle()->minWidth == style::LENGTH_AUTO && + child->getStyle()->maxWidth == style::LENGTH_AUTO) { + // TODO This should (perhaps?) only used when 'width' is undefined. + // TODO Is "boxDiffWidth()" correct here? + DBG_OBJ_MSG ("resize.oofm", 1, "no specification"); + if (forceValue) { + int availWidth = container->getAvailWidth (true), left, right; + + // Regard undefined values as 0: + if (!getPosLeft (child, availWidth, &left)) left = 0; + if (!getPosRight (child, availWidth, &right)) right = 0; + + width = max (availWidth - containerBoxDiffWidth () - left - right, 0); + } else + width = -1; + } else { + if (forceValue) { + int availWidth = container->getAvailWidth (true); + child->calcFinalWidth (child->getStyle(), + availWidth - containerBoxDiffWidth (), NULL, + 0, true, &width); + } else + width = -1; + } + + if (width != -1) + width = max (width, child->getMinWidth (NULL, forceValue)); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int OOFPosAbsLikeMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, + "OOFPositionedMgr/getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO && + child->getStyle()->minHeight == style::LENGTH_AUTO && + child->getStyle()->maxHeight == style::LENGTH_AUTO) { + // TODO This should (perhaps?) only used when 'height' is undefined. + // TODO Is "boxDiffHeight()" correct here? + DBG_OBJ_MSG ("resize.oofm", 1, "no specification"); + if (forceValue) { + int availHeight = container->getAvailHeight (true), top, bottom; + + // Regard undefined values as 0: + if (!getPosTop (child, availHeight, &top)) top = 0; + if (!getPosBottom (child, availHeight, &bottom)) bottom = 0; + + height = + max (availHeight - containerBoxDiffHeight () - top - bottom, 0); + } else + height = -1; + } else { + if (forceValue) { + int availHeight = container->getAvailHeight (true); + height = child->calcHeight (child->getStyle()->height, true, + availHeight - containerBoxDiffHeight (), + NULL, true); + } else + height = -1; + } + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +bool OOFPosAbsLikeMgr::posXAbsolute (Child *child) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "posXAbsolute", "[%p]", child->widget); + bool b = + (style::isAbsLength (child->widget->getStyle()->left) || + style::isPerLength (child->widget->getStyle()->left)) && + (style::isAbsLength (child->widget->getStyle()->right) || + style::isPerLength (child->widget->getStyle()->right)); + DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b)); + return b; +} + +bool OOFPosAbsLikeMgr::posYAbsolute (Child *child) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "posYAbsolute", "[%p]", child->widget); + bool b = + (style::isAbsLength (child->widget->getStyle()->top) || + style::isPerLength (child->widget->getStyle()->top)) && + (style::isAbsLength (child->widget->getStyle()->bottom) || + style::isPerLength (child->widget->getStyle()->bottom)); + DBG_OBJ_LEAVE_VAL ("%s", boolToStr (b)); + return b; +} + +void OOFPosAbsLikeMgr::calcPosAndSizeChildOfChild (Child *child, int refWidth, + int refHeight, int *xPtr, + int *yPtr, int *widthPtr, + int *ascentPtr, + int *descentPtr) +{ + // *xPtr and *yPtr refer to reference area; caller must adjust them. + + DBG_OBJ_ENTER ("resize.oofm", 0, "calcPosAndSizeChildOfChild", + "[%p], %d, %d, ...", child->widget, refWidth, refHeight); + + // TODO (i) Consider {min|max}-{width|heigt}. (ii) Height is always + // apportioned to descent (ascent is preserved), which makes sense + // when the children are textblocks. (iii) Consider minimal width + // (getMinWidth)? + + Requisition childRequisition; + child->widget->sizeRequest (&childRequisition); + + calcHPosAndSizeChildOfChild (child, refWidth, childRequisition.width, + xPtr, widthPtr); + calcVPosAndSizeChildOfChild (child, refHeight, childRequisition.ascent, + childRequisition.descent, yPtr, ascentPtr, + descentPtr); + + DBG_OBJ_LEAVE (); +} + +void OOFPosAbsLikeMgr::calcHPosAndSizeChildOfChild (Child *child, int refWidth, + int origChildWidth, + int *xPtr, int *widthPtr) +{ + assert (refWidth != -1 || (xPtr == NULL && widthPtr == NULL)); + + int width; + bool widthDefined; + if (style::isAbsLength (child->widget->getStyle()->width)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "absolute width: %dpx", + style::absLengthVal (child->widget->getStyle()->width)); + width = style::absLengthVal (child->widget->getStyle()->width) + + child->widget->boxDiffWidth (); + widthDefined = true; + } else if (style::isPerLength (child->widget->getStyle()->width)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->widget->getStyle()->width)); + width = style::multiplyWithPerLength (refWidth, + child->widget->getStyle()->width) + + child->widget->boxDiffWidth (); + widthDefined = true; + } else { + DBG_OBJ_MSG ("resize.oofm", 1, "width not specified"); + width = origChildWidth; + widthDefined = false; + } + + int left, right; + bool leftDefined = getPosLeft (child->widget, refWidth, &left), + rightDefined = getPosRight (child->widget, refWidth, &right); + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> left = %d, right = %d, width = %d (defined: %s)", + left, right, width, widthDefined ? "true" : "false"); + + if (xPtr) { + if (!leftDefined && !rightDefined) + *xPtr = generatorPosX (child) + child->x; + else { + if (!leftDefined && rightDefined) + *xPtr = refWidth - width - right - containerBoxRestWidth (); + else if (leftDefined && !rightDefined) + *xPtr = left + containerBoxOffsetX (); + else { + *xPtr = left; + if (!widthDefined) { + width = refWidth - (left + right + containerBoxDiffWidth ()); + DBG_OBJ_MSGF ("resize.oofm", 0, "=> width (corrected) = %d", + width); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> x = %d", *xPtr); + } + + if (widthPtr) + *widthPtr = width; +} + +void OOFPosAbsLikeMgr::calcVPosAndSizeChildOfChild (Child *child, int refHeight, + int origChildAscent, + int origChildDescent, + int *yPtr, int *ascentPtr, + int *descentPtr) +{ + assert (refHeight != -1 || + (yPtr == NULL && ascentPtr == NULL && descentPtr == NULL)); + + int ascent = origChildAscent, descent = origChildDescent; + bool heightDefined; + + if (style::isAbsLength (child->widget->getStyle()->height)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "absolute height: %dpx", + style::absLengthVal (child->widget->getStyle()->height)); + int height = style::absLengthVal (child->widget->getStyle()->height) + + child->widget->boxDiffHeight (); + splitHeightPreserveAscent (height, &ascent, &descent); + heightDefined = true; + } else if (style::isPerLength (child->widget->getStyle()->height)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->widget->getStyle()->height)); + int height = + style::multiplyWithPerLength (refHeight, + child->widget->getStyle()->height) + + child->widget->boxDiffHeight (); + splitHeightPreserveAscent (height, &ascent, &descent); + heightDefined = true; + } else { + DBG_OBJ_MSG ("resize.oofm", 1, "height not specified"); + heightDefined = false; + } + + int top, bottom; + bool topDefined = getPosTop (child->widget, refHeight, &top), + bottomDefined = getPosBottom (child->widget, refHeight, &bottom); + DBG_OBJ_MSGF ("resize.oofm", 1, + "=> top = %d, bottom = %d, height = %d + %d (defined: %s)", + top, bottom, ascent, descent, + heightDefined ? "true" : "false"); + + if (yPtr) { + if (!topDefined && !bottomDefined) + *yPtr = generatorPosY (child) + child->y; + else { + if (!topDefined && bottomDefined) + *yPtr = refHeight - (ascent + descent) - bottom + - containerBoxDiffHeight (); + else if (topDefined && !bottomDefined) + *yPtr = top + containerBoxOffsetY (); + else { + *yPtr = top; + if (!heightDefined) { + int height = + refHeight - (top + bottom + containerBoxDiffHeight ()); + splitHeightPreserveAscent (height, &ascent, &descent); + DBG_OBJ_MSGF ("resize.oofm", 0, + "=> ascent + descent (corrected) = %d + %d", + ascent, descent); + } + } + } + + DBG_OBJ_MSGF ("resize.oofm", 0, "=> y = %d", *yPtr); + } + + if (ascentPtr) + *ascentPtr = ascent; + if (descentPtr) + *descentPtr = descent; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposabslikemgr.hh b/dw/oofposabslikemgr.hh new file mode 100644 index 00000000..f9e94a5d --- /dev/null +++ b/dw/oofposabslikemgr.hh @@ -0,0 +1,61 @@ +#ifndef __DW_OOFPOSABSLIKEMGR_HH__ +#define __DW_OOFPOSABSLIKEMGR_HH__ + +#include "oofpositionedmgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosAbsLikeMgr: public OOFPositionedMgr +{ +protected: + virtual int containerBoxOffsetX () = 0; + virtual int containerBoxOffsetY () = 0; + virtual int containerBoxRestWidth () = 0; + virtual int containerBoxRestHeight () = 0; + + inline int containerBoxDiffWidth () + { return containerBoxOffsetX () + containerBoxRestWidth (); } + inline int containerBoxDiffHeight () + { return containerBoxOffsetY () + containerBoxRestHeight (); } + + bool haveExtremesChanged (); + + void sizeAllocateChildren (); + + bool posXAbsolute (Child *child); + bool posYAbsolute (Child *child); + + void calcPosAndSizeChildOfChild (Child *child, int refWidth, int refHeight, + int *xPtr, int *yPtr, int *widthPtr, + int *ascentPtr, int *descentPtr); + void calcHPosAndSizeChildOfChild (Child *child, int refWidth, + int origChildWidth, int *xPtr, + int *widthPtr); + void calcVPosAndSizeChildOfChild (Child *child, int refHeight, + int origChildAscent, int origChildDescent, + int *yPtr, int *ascentPtr, + int *descentPtr); + + +public: + OOFPosAbsLikeMgr (OOFAwareWidget *container); + ~OOFPosAbsLikeMgr (); + + void calcWidgetRefSize (core::Widget *widget, core::Requisition *size); + + void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight); + void getExtremes (core::Extremes *containerExtr, + int *oofMinWidth, int *oofMaxWidth); + + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSABSLIKEMGR_HH__ diff --git a/dw/oofposabsmgr.cc b/dw/oofposabsmgr.cc new file mode 100644 index 00000000..c8bc64b1 --- /dev/null +++ b/dw/oofposabsmgr.cc @@ -0,0 +1,68 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposabsmgr.hh" + +namespace dw { + +namespace oof { + +OOFPosAbsMgr::OOFPosAbsMgr (OOFAwareWidget *container) : + OOFPosAbsLikeMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosAbsMgr"); +} + +OOFPosAbsMgr::~OOFPosAbsMgr () +{ + DBG_OBJ_DELETE (); +} + +// Comment for all containerBox* implementations: for the toplevel +// widget, assume margin = border = 0 (should perhaps set so when +// widgets are constructed), so that the padding area is actually the +// allocation. + +int OOFPosAbsMgr::containerBoxOffsetX () +{ + return container->getParent () ? + container->boxOffsetX () - container->getStyle()->padding.left : 0; +} + +int OOFPosAbsMgr::containerBoxOffsetY () +{ + return container->getParent () ? + container->boxOffsetY () - container->getStyle()->padding.top : 0; +} + +int OOFPosAbsMgr::containerBoxRestWidth () +{ + return container->getParent () ? + container->boxRestWidth () - container->getStyle()->padding.right : 0; +} + +int OOFPosAbsMgr::containerBoxRestHeight () +{ + return container->getParent () ? + container->boxRestHeight () - container->getStyle()->padding.bottom : 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposabsmgr.hh b/dw/oofposabsmgr.hh new file mode 100644 index 00000000..153f8ff2 --- /dev/null +++ b/dw/oofposabsmgr.hh @@ -0,0 +1,27 @@ +#ifndef __DW_OOFPOSABSMGR_HH__ +#define __DW_OOFPOSABSMGR_HH__ + +#include "oofposabslikemgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosAbsMgr: public OOFPosAbsLikeMgr +{ +protected: + int containerBoxOffsetX (); + int containerBoxOffsetY (); + int containerBoxRestWidth (); + int containerBoxRestHeight (); + +public: + OOFPosAbsMgr (OOFAwareWidget *container); + ~OOFPosAbsMgr (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSABSMGR_HH__ diff --git a/dw/oofposfixedmgr.cc b/dw/oofposfixedmgr.cc new file mode 100644 index 00000000..1b36cd5c --- /dev/null +++ b/dw/oofposfixedmgr.cc @@ -0,0 +1,59 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposfixedmgr.hh" + +namespace dw { + +namespace oof { + +OOFPosFixedMgr::OOFPosFixedMgr (OOFAwareWidget *container) : + OOFPosAbsLikeMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosFixedMgr"); +} + +OOFPosFixedMgr::~OOFPosFixedMgr () +{ + DBG_OBJ_DELETE (); +} + +int OOFPosFixedMgr::containerBoxOffsetX () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxOffsetY () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxRestWidth () +{ + return 0; +} + +int OOFPosFixedMgr::containerBoxRestHeight () +{ + return 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposfixedmgr.hh b/dw/oofposfixedmgr.hh new file mode 100644 index 00000000..3f7b9683 --- /dev/null +++ b/dw/oofposfixedmgr.hh @@ -0,0 +1,27 @@ +#ifndef __DW_OOFPOSFIXEDMGR_HH__ +#define __DW_OOFPOSFIXEDMGR_HH__ + +#include "oofposabslikemgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosFixedMgr: public OOFPosAbsLikeMgr +{ +protected: + int containerBoxOffsetX (); + int containerBoxOffsetY (); + int containerBoxRestWidth (); + int containerBoxRestHeight (); + +public: + OOFPosFixedMgr (OOFAwareWidget *container); + ~OOFPosFixedMgr (); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSFIXEDMGR_HH__ diff --git a/dw/oofpositionedmgr.cc b/dw/oofpositionedmgr.cc new file mode 100644 index 00000000..28852258 --- /dev/null +++ b/dw/oofpositionedmgr.cc @@ -0,0 +1,384 @@ +/* + * Dillo Widget + * + * Copyright 2013-2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofpositionedmgr.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 { + +namespace oof { + +OOFPositionedMgr::Child::Child (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex) +{ + this->widget = widget; + this->generator = generator; + this->externalIndex = externalIndex; + + x = y = 0; + + // Initially, this child does not actually have been considered, + // but since adding a new element will force a size/extremes + // calculation, this is equivalent. + consideredForSize = consideredForExtremes = true; +} + +OOFPositionedMgr::OOFPositionedMgr (OOFAwareWidget *container) +{ + DBG_OBJ_CREATE ("dw::OOFPositionedMgr"); + + this->container = (OOFAwareWidget*)container; + children = new Vector<Child> (1, false); + childrenByWidget = new HashTable<TypedPointer<Widget>, Child> (true, true); + + containerAllocation = *(container->getAllocation()); + + DBG_OBJ_SET_NUM ("children.size", children->size()); +} + +OOFPositionedMgr::~OOFPositionedMgr () +{ + delete children; + delete childrenByWidget; + + DBG_OBJ_DELETE (); +} + +void OOFPositionedMgr::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) { + if (containerAllocationState == NOT_ALLOCATED) + containerAllocationState = IN_ALLOCATION; + containerAllocation = *allocation; + } + + DBG_OBJ_LEAVE (); +} + + +void OOFPositionedMgr::sizeAllocateEnd (OOFAwareWidget *caller) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); + + if (caller == container) { + sizeAllocateChildren (); + + bool extremesChanged = !allChildrenConsideredForExtremes (); + if (extremesChanged || doChildrenExceedContainer () || + !allChildrenConsideredForSize ()) + container->oofSizeChanged (extremesChanged); + + containerAllocationState = WAS_ALLOCATED; + } + + DBG_OBJ_LEAVE (); +} + +bool OOFPositionedMgr::doChildrenExceedContainer () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "doChildrenExceedContainer"); + + // This method is called to determine whether the *requisition* of + // the container must be recalculated. So, we check the allocations + // of the children against the *requisition* of the container, + // which may (e. g. within tables) differ from the new allocation. + // (Generally, a widget may allocated at a different size.) + + Requisition containerReq; + container->sizeRequest (&containerReq); + bool exceeds = false; + + DBG_OBJ_MSG_START (); + + for (int i = 0; i < children->size () && !exceeds; i++) { + Child *child = children->get (i); + Allocation *childAlloc = child->widget->getAllocation (); + DBG_OBJ_MSGF ("resize.oofm", 2, + "Does childAlloc = (%d, %d, %d * %d) exceed container " + "alloc+req = (%d, %d, %d * %d)?", + childAlloc->x, childAlloc->y, childAlloc->width, + childAlloc->ascent + childAlloc->descent, + containerAllocation.x, containerAllocation.y, + containerReq.width, + containerReq.ascent + containerReq.descent); + if (childAlloc->x + childAlloc->width + > containerAllocation.x + containerReq.width || + childAlloc->y + childAlloc->ascent + childAlloc->descent + > containerAllocation.y + + containerReq.ascent + containerReq.descent) { + exceeds = true; + DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); + } else + DBG_OBJ_MSG ("resize.oofm", 2, "No."); + } + + DBG_OBJ_MSG_END (); + + DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); + DBG_OBJ_LEAVE (); + + return exceeds; +} + +void OOFPositionedMgr::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + for (int i = 0; i < children->size(); i++) + children->get(i)->widget->containerSizeChanged (); + + DBG_OBJ_LEAVE (); +} + +void OOFPositionedMgr::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); + + for (int i = 0; i < children->size(); i++) { + Child *child = children->get(i); + + Rectangle childArea; + if (!context->hasWidgetBeenProcessedAsInterruption (child->widget) && + !StackingContextMgr::handledByStackingContextMgr (child->widget) && + child->widget->intersects (container, area, &childArea)) + child->widget->draw (view, &childArea, context); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPositionedMgr::addWidgetInFlow (OOFAwareWidget *widget, + OOFAwareWidget *parent, + int externalIndex) +{ +} + +int OOFPositionedMgr::addWidgetOOF (Widget *widget, OOFAwareWidget *generator, + int externalIndex) +{ + DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", + widget, generator, externalIndex); + + Child *child = new Child (widget, generator, externalIndex); + children->put (child); + childrenByWidget->put (new TypedPointer<Widget> (widget), child); + + int subRef = children->size() - 1; + DBG_OBJ_SET_NUM ("children.size", children->size()); + DBG_OBJ_ARRSET_PTR ("children", children->size() - 1, widget); + + DBG_OBJ_SET_PTR_O (widget, "<Positioned>.generator", generator); + DBG_OBJ_SET_NUM_O (widget, "<Positioned>.externalIndex", externalIndex); + + DBG_OBJ_MSGF ("construct.oofm", 1, "=> %d", subRef); + DBG_OBJ_LEAVE (); + return subRef; +} + +void OOFPositionedMgr::moveExternalIndices (OOFAwareWidget *generator, + int oldStartIndex, int diff) +{ + for (int i = 0; i < children->size (); i++) { + Child *child = children->get (i); + if (child->externalIndex >= oldStartIndex) { + child->externalIndex += diff; + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.externalIndex", + child->externalIndex); + } + } +} + +void OOFPositionedMgr::markSizeChange (int ref) +{ +} + + +void OOFPositionedMgr::markExtremesChange (int ref) +{ +} + +Widget *OOFPositionedMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + + Widget *widgetAtPoint = NULL; + + for (int i = children->size() - 1; widgetAtPoint == NULL && i >= 0; i--) { + Widget *childWidget = children->get(i)->widget; + if (!context->hasWidgetBeenProcessedAsInterruption (childWidget) && + !StackingContextMgr::handledByStackingContextMgr (childWidget)) + widgetAtPoint = childWidget->getWidgetAtPoint (x, y, context); + } + + DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + + return widgetAtPoint; +} + +void OOFPositionedMgr::tellPosition1 (Widget *widget, int x, int y) +{ +} + +void OOFPositionedMgr::tellPosition2 (Widget *widget, int x, int y) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "tellPosition2", "%p, %d, %d", + widget, x, y); + + TypedPointer<Widget> key (widget); + Child *child = childrenByWidget->get (&key); + assert (child); + + child->x = x; + child->y = y; + + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.x", x); + DBG_OBJ_SET_NUM_O (child->widget, "<Positioned>.y", y); + + DBG_OBJ_LEAVE (); +} + +bool OOFPositionedMgr::containerMustAdjustExtraSpace () +{ + return true; +} + +int OOFPositionedMgr::getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return 0; +} + +bool OOFPositionedMgr::hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return false; +} + +bool OOFPositionedMgr::hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) +{ + return false; +} + + +int OOFPositionedMgr::getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, + int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, + int lastExtIndex) +{ + return 0; +} + +int OOFPositionedMgr::getClearPosition (OOFAwareWidget *widget) +{ + return 0; +} + +bool OOFPositionedMgr::affectsLeftBorder (Widget *widget) +{ + return false; +} + +bool OOFPositionedMgr::affectsRightBorder (Widget *widget) +{ + return false; +} + +bool OOFPositionedMgr::mayAffectBordersAtAll () +{ + return false; +} + +bool OOFPositionedMgr::dealingWithSizeOfChild (Widget *child) +{ + return true; +} + +int OOFPositionedMgr::getNumWidgets () +{ + return children->size(); +} + +Widget *OOFPositionedMgr::getWidget (int i) +{ + return children->get(i)->widget; +} + +bool OOFPositionedMgr::getPosBorder (style::Length cssValue, int refLength, + int *result) +{ + if (style::isAbsLength (cssValue)) { + *result = style::absLengthVal (cssValue); + return true; + } else if (style::isPerLength (cssValue)) { + *result = style::multiplyWithPerLength (refLength, cssValue); + return true; + } else + // "false" means "undefined": + return false; +} + +bool OOFPositionedMgr::allChildrenConsideredForSize () +{ + for (int i = 0; i < children->size(); i++) + if (!children->get(i)->consideredForSize) + return false; + return true; +} + +bool OOFPositionedMgr::allChildrenConsideredForExtremes () +{ + for (int i = 0; i < children->size(); i++) + if (!children->get(i)->consideredForExtremes) + return false; + return true; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofpositionedmgr.hh b/dw/oofpositionedmgr.hh new file mode 100644 index 00000000..4edf11e8 --- /dev/null +++ b/dw/oofpositionedmgr.hh @@ -0,0 +1,138 @@ + #ifndef __DW_OOFPOSITIONEDMGR_HH__ +#define __DW_OOFPOSITIONEDMGR_HH__ + +#include "outofflowmgr.hh" +#include "oofawarewidget.hh" + +namespace dw { + +namespace oof { + +class OOFPositionedMgr: public OutOfFlowMgr +{ +protected: + class Child: public lout::object::Object + { + public: + core::Widget *widget; + OOFAwareWidget *generator; + int externalIndex, x, y; + bool consideredForSize, consideredForExtremes; + + Child (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex); + }; + + OOFAwareWidget *container; + core::Allocation containerAllocation; + enum { NOT_ALLOCATED, IN_ALLOCATION, WAS_ALLOCATED } + containerAllocationState; + + lout::container::typed::Vector<Child> *children; + lout::container::typed::HashTable<lout::object::TypedPointer + <dw::core::Widget>, + Child> *childrenByWidget; + + inline bool getPosLeft (core::Widget *child, int availWidth, int *result) + { return getPosBorder (child->getStyle()->left, availWidth, result); } + inline bool getPosRight (core::Widget *child, int availWidth, int *result) + { return getPosBorder (child->getStyle()->right, availWidth, result); } + inline bool getPosTop (core::Widget *child, int availHeight, int *result) + { return getPosBorder (child->getStyle()->top, availHeight, result); } + inline bool getPosBottom (core::Widget *child, int availHeight, int *result) + { return getPosBorder (child->getStyle()->bottom, availHeight, result); } + + bool getPosBorder (core::style::Length cssValue, int refLength, int *result); + + bool allChildrenConsideredForSize (); + bool allChildrenConsideredForExtremes (); + + bool doChildrenExceedContainer (); + + virtual void sizeAllocateChildren () = 0; + virtual bool posXAbsolute (Child *child) = 0; + virtual bool posYAbsolute (Child *child) = 0; + + inline bool generatorPosDefined (Child *child) { + return child->generator == container || + (containerAllocationState != NOT_ALLOCATED + && child->generator->wasAllocated ()); + } + inline int generatorPosX (Child *child) { + assert (generatorPosDefined (child)); + return child->generator == container ? 0 : + child->generator->getAllocation()->x - containerAllocation.x; + } + inline int generatorPosY (Child *child) { + assert (generatorPosDefined (child)); + return child->generator == container ? 0 : + child->generator->getAllocation()->y - containerAllocation.y; + } + + inline bool posXDefined (Child *child) + { return posXAbsolute (child) || generatorPosDefined (child); } + + inline bool posYDefined (Child *child) + { return posYAbsolute (child) || generatorPosDefined (child); } + +public: + OOFPositionedMgr (OOFAwareWidget *container); + ~OOFPositionedMgr (); + + void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation); + void sizeAllocateEnd (OOFAwareWidget *caller); + + void containerSizeChangedForChildren (); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context); + + void addWidgetInFlow (OOFAwareWidget *widget, OOFAwareWidget *parent, + int externalIndex); + int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex); + void moveExternalIndices (OOFAwareWidget *generator, int oldStartIndex, + int diff); + + void tellPosition1 (core::Widget *widget, int x, int y); + void tellPosition2 (core::Widget *widget, int x, int y); + + bool containerMustAdjustExtraSpace (); + + int getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + int getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + bool hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + bool hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + int getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + int getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex); + + int getClearPosition (OOFAwareWidget *widget); + + bool affectsLeftBorder (core::Widget *widget); + bool affectsRightBorder (core::Widget *widget); + bool mayAffectBordersAtAll (); + + bool dealingWithSizeOfChild (core::Widget *child); + + int getNumWidgets (); + core::Widget *getWidget (int i); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSITIONEDMGR_HH__ diff --git a/dw/oofposrelmgr.cc b/dw/oofposrelmgr.cc new file mode 100644 index 00000000..c19eb2e7 --- /dev/null +++ b/dw/oofposrelmgr.cc @@ -0,0 +1,249 @@ +/* + * Dillo Widget + * + * Copyright 2015 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "oofposrelmgr.hh" + +using namespace dw::core; +using namespace lout::object; +using namespace lout::misc; + +namespace dw { + +namespace oof { + +OOFPosRelMgr::OOFPosRelMgr (OOFAwareWidget *container) : + OOFPositionedMgr (container) +{ + DBG_OBJ_CREATE ("dw::OOFPosRelMgr"); +} + +OOFPosRelMgr::~OOFPosRelMgr () +{ + DBG_OBJ_DELETE (); +} + + +void OOFPosRelMgr::markSizeChange (int ref) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", ref); + Child *child = children->get(ref); + DBG_OBJ_MSGF ("resize.oofm", 1, "generator = %p, externalIndex = %d", + child->generator, child->externalIndex); + child->generator->widgetRefSizeChanged (child->externalIndex); + DBG_OBJ_LEAVE (); +} + +void OOFPosRelMgr::markExtremesChange (int ref) +{ +} + +void OOFPosRelMgr::calcWidgetRefSize (Widget *widget, Requisition *size) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "calcWidgetRefSize", "%p", widget); + + widget->sizeRequest (size); + + // In some cases, the widget has been enlarged for widgets out of + // flow. Partly, this is done by adding "extra space"; however, at + // this point, the extra space is not relevant here. See + // "oofawarewidget.cc" for a calculation of RequisitionWithoutOOF. + // (Notice also that Widget::sizeRequest has to be called in all + // cases.) + + if (widget->instanceOf (OOFAwareWidget::CLASS_ID)) + *size = *((OOFAwareWidget*)widget)->getRequisitionWithoutOOF (); + + + DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", + size->width, size->ascent, size->descent); +} + + +void OOFPosRelMgr::sizeAllocateChildren () +{ + DBG_OBJ_ENTER0 ("resize.oofm", 0, "sizeAllocateChildren"); + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + Requisition childReq; + child->widget->sizeRequest (&childReq); + + Allocation childAlloc; + childAlloc.x = containerAllocation.x + getChildPosX (child); + childAlloc.y = containerAllocation.y + getChildPosY (child); + childAlloc.width = childReq.width; + childAlloc.ascent = childReq.ascent; + childAlloc.descent = childReq.descent; + child->widget->sizeAllocate (&childAlloc); + } + + DBG_OBJ_LEAVE (); +} + +void OOFPosRelMgr::getSize (Requisition *containerReq, int *oofWidth, + int *oofHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getSize", "%d * (%d + %d)", + containerReq->width, containerReq->ascent, + containerReq->descent); + + *oofWidth = *oofHeight = 0; + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child) && posYDefined (child)) { + Requisition childReq; + child->widget->sizeRequest (&childReq); + *oofWidth = max (*oofWidth, getChildPosX (child) + childReq.width); + *oofHeight = max (*oofHeight, + getChildPosY (child) + childReq.ascent + + childReq.descent); + + child->consideredForSize = true; + } else + child->consideredForSize = false; + } + + DBG_OBJ_LEAVE_VAL ("%d * %d", *oofWidth, *oofHeight); +} + +void OOFPosRelMgr::getExtremes (Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) +{ + *oofMinWidth = *oofMaxWidth = 0; + + for (int i = 0; i < children->size (); i++) { + Child *child = children->get(i); + + // Children whose position cannot be determined will be + // considered later in sizeAllocateEnd. + if (posXDefined (child)) { + Extremes childExtr; + child->widget->getExtremes (&childExtr); + + // Put the extremes of the container in relation to the extremes + // of the child, as in OOFPosAbsLikeMgr::getExtremes (see + // comment there). + *oofMinWidth = max (*oofMinWidth, + getChildPosX (child, containerExtr->minWidth) + + childExtr.minWidth); + *oofMaxWidth = max (*oofMaxWidth, + getChildPosX (child, containerExtr->maxWidth) + + childExtr.maxWidth); + + child->consideredForExtremes = true; + } else + child->consideredForExtremes = false; + } +} + +bool OOFPosRelMgr::posXAbsolute (Child *child) +{ + return false; +} + +bool OOFPosRelMgr::posYAbsolute (Child *child) +{ + return false; +} + +int OOFPosRelMgr::getChildPosX (Child *child, int refWidth) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosX", "[%p], %d", + child->widget, refWidth); + + int gx = generatorPosX (child); + int dim = getChildPosDim (child->widget->getStyle()->left, + child->widget->getStyle()->right, + child->x, + refWidth + - child->widget->getStyle()->boxDiffWidth ()); + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gx, dim, gx + dim); + return gx + dim; +} + + +int OOFPosRelMgr::getChildPosY (Child *child, int refHeight) +{ + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosY", "[%p], %d", + child->widget, refHeight); + + int gy = generatorPosY (child); + int dim = getChildPosDim (child->widget->getStyle()->top, + child->widget->getStyle()->bottom, + child->y, + refHeight + - child->widget->getStyle()->boxDiffHeight ()); + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", gy, dim, gy + dim); + return gy + dim; +} + +int OOFPosRelMgr::getChildPosDim (style::Length posCssValue, + style::Length negCssValue, int refPos, + int refLength) +{ + // posCssValue refers to "left" or "top", negCssValue refers to "right" or + // "bottom". The former values are preferred ("left" over "right" etc.), + // which should later depend on the CSS value "direction". + + DBG_OBJ_ENTER ("resize.oofm", 0, "getChildPosDim", + "<i>%d</i>, <i>%d</i>, %d, %d", + posCssValue, negCssValue, refPos, refLength); + + int diff; + if (getPosBorder (posCssValue, refLength, &diff)) + DBG_OBJ_MSGF ("resize.oofm", 1, "posCssValue: diff = %d", diff); + else { + if (getPosBorder (negCssValue, refLength, &diff)) { + DBG_OBJ_MSGF ("resize.oofm", 1, "negCssValue: diff = %d", diff); + diff *= -1; + } else + diff = 0; + } + + DBG_OBJ_LEAVE_VAL ("%d + %d = %d", refPos, diff, refPos + diff); + return refPos + diff; +} + +bool OOFPosRelMgr::dealingWithSizeOfChild (Widget *child) +{ + return false; +} + +int OOFPosRelMgr::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +int OOFPosRelMgr::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + assertNotReached (); + return 0; +} + +} // namespace oof + +} // namespace dw diff --git a/dw/oofposrelmgr.hh b/dw/oofposrelmgr.hh new file mode 100644 index 00000000..d61c15d9 --- /dev/null +++ b/dw/oofposrelmgr.hh @@ -0,0 +1,50 @@ +#ifndef __DW_OOFPOSRELMGR_HH__ +#define __DW_OOFPOSRELMGR_HH__ + +#include "oofpositionedmgr.hh" + +namespace dw { + +namespace oof { + +class OOFPosRelMgr: public OOFPositionedMgr +{ +protected: + void sizeAllocateChildren (); + bool posXAbsolute (Child *child); + bool posYAbsolute (Child *child); + + int getChildPosX (Child *child, int refWidth); + int getChildPosY (Child *child, int refHeight); + int getChildPosDim (core::style::Length posCssValue, + core::style::Length negCssValue, int refPos, + int refLength); + + inline int getChildPosX (Child *child) + { return getChildPosX (child, container->getAvailWidth (true)); } + inline int getChildPosY (Child *child) + { return getChildPosY (child, container->getAvailHeight (true)); } + +public: + OOFPosRelMgr (OOFAwareWidget *container); + ~OOFPosRelMgr (); + + void markSizeChange (int ref); + void markExtremesChange (int ref); + void calcWidgetRefSize (core::Widget *widget, core::Requisition *size); + + void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight); + void getExtremes (core::Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth); + + bool dealingWithSizeOfChild (core::Widget *child); + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); +}; + +} // namespace oof + +} // namespace dw + +#endif // __DW_OOFPOSRELMGR_HH__ diff --git a/dw/outofflowmgr.cc b/dw/outofflowmgr.cc index 5081a2cb..405c5e62 100644 --- a/dw/outofflowmgr.cc +++ b/dw/outofflowmgr.cc @@ -19,2275 +19,22 @@ #include "outofflowmgr.hh" -#include "textblock.hh" +#include "oofawarewidget.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::WidgetInfo::WidgetInfo (OutOfFlowMgr *oofm, Widget *widget) -{ - this->oofm = oofm; - this->widget = widget; - wasAllocated = false; - xCB = yCB = width = height = -1; -} - -void OutOfFlowMgr::WidgetInfo::update (bool wasAllocated, int xCB, int yCB, - int width, int height) -{ - DBG_OBJ_ENTER_O ("resize.oofm", 0, widget, "update", "%s, %d, %d, %d, %d", - wasAllocated ? "true" : "false", xCB, yCB, width, height); - - this->wasAllocated = wasAllocated; - this->xCB = xCB; - this->yCB = yCB; - this->width = width; - this->height = height; - - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.xCB", xCB); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.yCB", yCB); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.width", width); - DBG_OBJ_SET_NUM_O (widget, "<WidgetInfo>.height", height); - - DBG_OBJ_LEAVE_O (widget); -} - -// ---------------------------------------------------------------------- - -OutOfFlowMgr::Float::Float (OutOfFlowMgr *oofm, Widget *widget, - Textblock *generatingBlock, int externalIndex) : - WidgetInfo (oofm, widget) -{ - this->generatingBlock = generatingBlock; - this->externalIndex = externalIndex; - - yReq = yReal = size.width = size.ascent = size.descent = 0; - dirty = sizeChangedSinceLastAllocation = true; - indexGBList = indexCBList = -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, "<Float>.generatingBlock", generatingBlock); - DBG_OBJ_SET_NUM_O (widget, "<Float>.externalIndex", externalIndex); - DBG_OBJ_SET_NUM_O (widget, "<Float>.yReq", yReq); - DBG_OBJ_SET_NUM_O (widget, "<Float>.yReal", yReal); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.width", size.width); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.ascent", size.ascent); - DBG_OBJ_SET_NUM_O (widget, "<Float>.size.descent", size.descent); - DBG_OBJ_SET_BOOL_O (widget, "<Float>.dirty", dirty); - DBG_OBJ_SET_BOOL_O (widget, "<Float>.sizeChangedSinceLastAllocation", - sizeChangedSinceLastAllocation); - } -} - -void OutOfFlowMgr::Float::updateAllocation () -{ - DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); - - update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), - getNewHeight ()); - - DBG_OBJ_LEAVE_O (getWidget ()); -} - -void OutOfFlowMgr::Float::intoStringBuffer(StringBuffer *sb) -{ - sb->append ("{ widget = "); - sb->appendPointer (getWidget ()); - - if (getWidget ()) { - sb->append (" ("); - sb->append (getWidget()->getClassName ()); - sb->append (")"); - } - - sb->append (", indexGBList = "); - sb->appendInt (indexGBList); - sb->append (", indexCBList = "); - sb->appendInt (indexCBList); - 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 (" }"); -} - -bool OutOfFlowMgr::Float::covers (Textblock *textblock, int y, int h) -{ - DBG_OBJ_ENTER_O ("border", 0, getOutOfFlowMgr (), "covers", - "%p, %d, %d [vloat: %p]", - textblock, y, h, getWidget ()); - - bool b; - - if (textblock == generatingBlock) { - int reqyGB = y; - int flyGB = yReal; - getOutOfFlowMgr()->ensureFloatSize (this); - int flh = size.ascent + size.descent; - b = flyGB + flh > reqyGB && flyGB < reqyGB + h; - - DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), - "for generator: reqyGB = %d, flyGB = %d, " - "flh = %d + %d = %d => %s", - reqyGB, flyGB, size.ascent, size.descent, flh, - b ? "true" : "false"); - } else { - // (If the textblock were not allocated, the GB list would have - // been choosen instead of the CB list, and so this else-branch - // would not have been not executed.) - assert (getOutOfFlowMgr()->wasAllocated (textblock)); - - if (!getWidget()->wasAllocated ()) { - DBG_OBJ_MSG_O ("border", 1, getOutOfFlowMgr (), - "not generator (not allocated) => false"); - b = false; - } else { - Allocation *tba = getOutOfFlowMgr()->getAllocation(textblock), - *fla = getWidget()->getAllocation (); - int reqyCanv = tba->y + y; - int flyCanv = fla->y; - int flh = fla->ascent + fla->descent; - b = flyCanv + flh > reqyCanv && flyCanv < reqyCanv + h; - - DBG_OBJ_MSGF_O ("border", 1, getOutOfFlowMgr (), - "not generator (allocated): reqyCanv = %d + %d = %d, " - "flyCanv = %d, flh = %d + %d = %d => %s", - tba->y, y, reqyCanv, flyCanv, - fla->ascent, fla->descent, flh, b ? "true" : "false"); - } - } - - DBG_OBJ_LEAVE_O (getOutOfFlowMgr ()); - - return b; -} - -int OutOfFlowMgr::Float::ComparePosition::compare (Object *o1, Object *o2) -{ - Float *fl1 = (Float*)o1, *fl2 = (Float*)o2; - int r; - - DBG_OBJ_ENTER_O ("border", 1, oofm, - "ComparePosition/compare", "(#%d, #%d) [refTB = %p]", - fl1->getIndex (type), fl2->getIndex (type), refTB); - - if (refTB == fl1->generatingBlock && refTB == fl2->generatingBlock) { - DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is generating both floats"); - r = fl1->yReal - fl2->yReal; - } else { - DBG_OBJ_MSG_O ("border", 2, oofm, "refTB is not generating both floats"); - DBG_OBJ_MSG_START_O (oofm); - - DBG_OBJ_MSGF_O ("border", 2, oofm, "generators are %p and %p", - fl1->generatingBlock, fl2->generatingBlock); - - // (i) Floats may not yet been allocated. Non-allocated floats - // do not have an effect yet, they are considered "at the end" - // of the list. - - // (ii) Float::widget is NULL for the key used for binary - // search. In this case, Float::yReal is used instead (which is - // set in SortedFloatsVector::find, too). The generator is the - // textblock, and should be allocated. (If not, the GB list - // would have been choosen instead of the CB list, and so this - // else-branch would not have been not executed.) - - bool a1 = fl1->getWidget () ? fl1->getWidget()->wasAllocated () : true; - bool a2 = fl2->getWidget () ? fl2->getWidget()->wasAllocated () : true; - - DBG_OBJ_MSGF_O ("border", 2, oofm, - "float 1 (%p) allocated: %s; float 2 (%p) allocated: %s", - fl1->getWidget (), a1 ? "yes" : "no", fl2->getWidget (), - a2 ? "yes" : "no"); - - if (a1 && a2) { - int fly1, fly2; - - if (fl1->getWidget()) { - fly1 = fl1->getWidget()->getAllocation()->y; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d", fly1); - } else { - assert (oofm->wasAllocated (fl1->generatingBlock)); - fly1 = oofm->getAllocation(fl1->generatingBlock)->y + fl1->yReal; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly1 = %d + %d = %d", - oofm->getAllocation(fl1->generatingBlock)->y, - fl1->yReal, fly1); - } - - if (fl2->getWidget()) { - fly2 = fl2->getWidget()->getAllocation()->y; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d", fly2); - } else { - assert (oofm->wasAllocated (fl2->generatingBlock)); - fly2 = oofm->getAllocation(fl2->generatingBlock)->y + fl2->yReal; - DBG_OBJ_MSGF_O ("border", 2, oofm, "fly2 = %d + %d = %d", - oofm->getAllocation(fl2->generatingBlock)->y, - fl2->yReal, fly2); - } - - r = fly1 - fly2; - - DBG_OBJ_MSGF_O ("border", 2, oofm, "r = %d - %d = %d", fly1, fly2, r); - } else if (a1 && !a2) - r = -1; - else if (!a1 && a2) - r = +1; - else // if (!a1 && !a2) - return 0; - - DBG_OBJ_MSG_END_O (oofm); - } - - DBG_OBJ_MSGF_O ("border", 1, oofm, "result: %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} - -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; - 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->getIndex (type), f1->generatingBlock, f1->externalIndex, - f2->getIndex (type), f2->generatingBlock, - f2->externalIndex); - - if (f1->generatingBlock == f2->generatingBlock) { - 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->getTextblock (f1->generatingBlock), - *t2 = oofm->getTextblock (f2->generatingBlock); - 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->getTextblock (), t1->getTextblock (), - t->getTextblock (), 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->getTextblock (), t2->getTextblock (), - t->getTextblock (), 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_MSGF_O ("border", 2, oofm, "result: %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} - -int OutOfFlowMgr::SortedFloatsVector::findFloatIndex (Textblock *lastGB, - int lastExtIndex) -{ - DBG_OBJ_ENTER_O ("border", 0, oofm, "findFloatIndex", "%p, %d", - lastGB, lastExtIndex); - - Float key (oofm, NULL, lastGB, lastExtIndex); - key.setIndex (type, -1); // for debugging - Float::CompareGBAndExtIndex comparator (oofm, type); - 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->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); - //} - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> r = %d", r); - DBG_OBJ_LEAVE_O (oofm); - return r; -} +namespace oof { -int OutOfFlowMgr::SortedFloatsVector::find (Textblock *textblock, int y, - int start, int end) +OutOfFlowMgr::OutOfFlowMgr () { - DBG_OBJ_ENTER_O ("border", 0, oofm, "find", "%p, %d, %d, %d", - textblock, y, start, end); - - Float key (oofm, NULL, NULL, 0); - key.generatingBlock = textblock; - key.yReal = y; - key.setIndex (type, -1); // for debugging - Float::ComparePosition comparator (oofm, textblock, type); - int result = bsearch (&key, false, start, end, &comparator); - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_LEAVE_O (oofm); - return result; -} - -int OutOfFlowMgr::SortedFloatsVector::findFirst (Textblock *textblock, - int y, int h, - Textblock *lastGB, - int lastExtIndex, - int *lastReturn) -{ - DBG_OBJ_ENTER_O ("border", 0, oofm, "findFirst", "%p, %d, %d, %p, %d", - textblock, y, h, lastGB, lastExtIndex); - - DBG_IF_RTFL { - DBG_OBJ_MSG_O ("border", 2, oofm, "searching in list:"); - DBG_OBJ_MSG_START_O (oofm); - - for (int i = 0; i < size(); i++) { - DBG_OBJ_MSGF_O ("border", 2, oofm, - "%d: (%p, i = %d/%d, y = %d/%d, s = (%d * (%d + %d)), " - "%s, %s, ext = %d, GB = %p); widget at (%d, %d)", - i, get(i)->getWidget (), get(i)->getIndex (type), - get(i)->sideSpanningIndex, get(i)->yReq, get(i)->yReal, - get(i)->size.width, get(i)->size.ascent, - get(i)->size.descent, - get(i)->dirty ? "dirty" : "clean", - get(i)->sizeChangedSinceLastAllocation ? "scsla" - : "sNcsla", - get(i)->externalIndex, get(i)->generatingBlock, - get(i)->getWidget()->getAllocation()->x, - get(i)->getWidget()->getAllocation()->y); - } - - DBG_OBJ_MSG_END_O (oofm); - } - - 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 (textblock, 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 (textblock, y, h)) - result = i - 1; - else if (i <= last && get(i)->covers (textblock, y, h)) - result = i; - else - result = -1; - - DBG_OBJ_MSGF_O ("border", 1, oofm, "=> result = %d", result); - DBG_OBJ_LEAVE_O (oofm); - return result; -} - -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<Float>::put (vloat); - vloat->setIndex (type, size() - 1); -} - -OutOfFlowMgr::TBInfo::TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, - TBInfo *parent, int parentExtIndex) : - WidgetInfo (oofm, textblock) -{ - this->parent = parent; - this->parentExtIndex = parentExtIndex; - - leftFloatsGB = new SortedFloatsVector (oofm, LEFT, GB); - rightFloatsGB = new SortedFloatsVector (oofm, RIGHT, GB); - - wasAllocated = getWidget()->wasAllocated (); - allocation = *(getWidget()->getAllocation ()); - clearPosition = 0; -} - -OutOfFlowMgr::TBInfo::~TBInfo () -{ - delete leftFloatsGB; - delete rightFloatsGB; -} - -void OutOfFlowMgr::TBInfo::updateAllocation () -{ - DBG_OBJ_ENTER0_O ("resize.oofm", 0, getWidget (), "updateAllocation"); - - update (isNowAllocated (), getNewXCB (), getNewYCB (), getNewWidth (), - getNewHeight ()); - - DBG_OBJ_LEAVE_O (getWidget ()); -} - -OutOfFlowMgr::OutOfFlowMgr (Textblock *containingBlock) -{ - DBG_OBJ_CREATE ("dw::OutOfFlowMgr"); - - this->containingBlock = containingBlock; - - leftFloatsCB = new SortedFloatsVector (this, LEFT, CB); - rightFloatsCB = new SortedFloatsVector (this, RIGHT, CB); - - DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); - DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); - - leftFloatsAll = new Vector<Float> (1, true); - rightFloatsAll = new Vector<Float> (1, true); - - DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); - DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); - - floatsByWidget = new HashTable <TypedPointer <Widget>, Float> (true, false); - - tbInfos = new Vector<TBInfo> (1, false); - tbInfosByTextblock = - new HashTable <TypedPointer <Textblock>, TBInfo> (true, true); - - leftFloatsMark = rightFloatsMark = 0; - lastLeftTBIndex = lastRightTBIndex = 0; - - containingBlockWasAllocated = containingBlock->wasAllocated (); - 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; - - DBG_OBJ_DELETE (); -} - -void OutOfFlowMgr::sizeAllocateStart (Textblock *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); - - getTextblock(caller)->allocation = *allocation; - getTextblock(caller)->wasAllocated = true; - - if (caller == containingBlock) { - // In the size allocation process, the *first* OOFM method - // called is sizeAllocateStart, with the containing block as an - // argument. So this is the correct point to initialize size - // allocation. - - containingBlockAllocation = *allocation; - containingBlockWasAllocated = true; - - // Move floats from GB lists to the one CB list. - moveFromGBToCB (LEFT); - moveFromGBToCB (RIGHT); - - // These attributes are used to keep track which floats have - // been allocated (referring to leftFloatsCB and rightFloatsCB). - lastAllocatedLeftFloat = lastAllocatedRightFloat = -1; - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::sizeAllocateEnd (Textblock *caller) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateEnd", "%p", caller); - - // (Later, absolutely positioned blocks have to be allocated.) - - if (caller != containingBlock) { - // Allocate all floats "before" this textblock. - sizeAllocateFloats (LEFT, leftFloatsCB->findFloatIndex (caller, -1)); - sizeAllocateFloats (RIGHT, rightFloatsCB->findFloatIndex (caller, -1)); - } - - // The checks below do not cover "clear position" in all cases, so - // this is done here separately. This position is stored in TBInfo - // and calculated at this points; changes will be noticed to the - // textblock. - TBInfo *tbInfo = getTextblock (caller); - int newClearPosition = calcClearPosition (caller); - if (newClearPosition != tbInfo->clearPosition) { - tbInfo->clearPosition = newClearPosition; - caller->clearPositionChanged (); - } - - if (caller == containingBlock) { - // In the size allocation process, the *last* OOFM method called - // is sizeAllocateEnd, with the containing block as an - // argument. So this is the correct point to finish size - // allocation. - - // Allocate all remaining floats. - sizeAllocateFloats (LEFT, leftFloatsCB->size () - 1); - sizeAllocateFloats (RIGHT, rightFloatsCB->size () - 1); - - // Check changes of both textblocks and floats allocation. (All - // is checked by hasRelationChanged (...).) - for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = - tbInfosByTextblock->iterator (); - it.hasNext (); ) { - TypedPointer <Textblock> *key = it.getNext (); - TBInfo *tbInfo = tbInfosByTextblock->get (key); - Textblock *tb = key->getTypedValue(); - - int minFloatPos; - Widget *minFloat; - if (hasRelationChanged (tbInfo, &minFloatPos, &minFloat)) - tb->borderChanged (minFloatPos, minFloat); - } - - checkAllocatedFloatCollisions (LEFT); - checkAllocatedFloatCollisions (RIGHT); - - // Store some information for later use. - for (lout::container::typed::Iterator<TypedPointer <Textblock> > it = - tbInfosByTextblock->iterator (); - it.hasNext (); ) { - TypedPointer <Textblock> *key = it.getNext (); - TBInfo *tbInfo = tbInfosByTextblock->get (key); - Textblock *tb = key->getTypedValue(); - - tbInfo->updateAllocation (); - tbInfo->lineBreakWidth = tb->getLineBreakWidth (); - } - - // There are cases where some allocated floats (TODO: later also - // absolutely positioned elements?) exceed the CB allocation. - bool sizeChanged = doFloatsExceedCB (LEFT) || doFloatsExceedCB (RIGHT); - - // Similar for extremes. (TODO: here also absolutely positioned - // elements?) - bool extremesChanged = - haveExtremesChanged (LEFT) || haveExtremesChanged (RIGHT); - - for (int i = 0; i < leftFloatsCB->size(); i++) - leftFloatsCB->get(i)->updateAllocation (); - - for (int i = 0; i < rightFloatsCB->size(); i++) - rightFloatsCB->get(i)->updateAllocation (); - - if (sizeChanged || extremesChanged) - containingBlock->oofSizeChanged (extremesChanged); - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::containerSizeChangedForChildren () -{ - DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); - - DBG_OBJ_MSGF ("resize", 0, "%d left floats, %d right floats", - leftFloatsAll->size (), rightFloatsAll->size ()); - - for (int i = 0; i < leftFloatsAll->size (); i++) - leftFloatsAll->get(i)->getWidget()->containerSizeChanged (); - for (int i = 0; i < rightFloatsAll->size (); i++) - rightFloatsAll->get(i)->getWidget()->containerSizeChanged (); - - DBG_OBJ_LEAVE (); -} - -bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, int *minFloatPos, - Widget **minFloat) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>widget:</i> %p, ...", tbInfo->getWidget ()); - - 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; - } - } - } - - if (c1 || c2) - DBG_OBJ_MSGF ("resize.oofm", 1, - "has changed: minFloatPos = %d, minFloat = %p", - *minFloatPos, *minFloat); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - return c1 || c2; -} - -bool OutOfFlowMgr::hasRelationChanged (TBInfo *tbInfo, Side side, - int *minFloatPos, Widget **minFloat) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>widget:</i> %p, %s, ...", - tbInfo->getWidget (), side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool changed = false; - - for (int i = 0; i < list->size(); i++) { - // TODO binary search? - Float *vloat = list->get(i); - int floatPos; - - if (tbInfo->getTextblock () == vloat->generatingBlock) - DBG_OBJ_MSGF ("resize.oofm", 1, - "not checking (generating!) textblock %p against float " - "%p", tbInfo->getWidget (), vloat->getWidget ()); - else { - Allocation *gba = getAllocation (vloat->generatingBlock); - - int newFlx = calcFloatX (vloat, side, - gba->x - containingBlockAllocation.x, - getGBWidthForAllocation (vloat)); - int newFly = vloat->generatingBlock->getAllocation()->y - - containingBlockAllocation.y + vloat->yReal; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "checking textblock %p against float %p", - tbInfo->getWidget (), vloat->getWidget ()); - DBG_OBJ_MSG_START (); - - if (hasRelationChanged (tbInfo->wasThenAllocated (), - tbInfo->getOldXCB (), tbInfo->getOldYCB (), - tbInfo->getNewWidth (), - tbInfo->getNewHeight (), - tbInfo->getNewXCB (), tbInfo->getNewYCB (), - tbInfo->getNewWidth (), - tbInfo->getNewHeight (), - vloat->wasThenAllocated (), - // When not allocated before, these values - // are undefined, but this does not matter, - // since they are neither used. - vloat->getOldXCB (), vloat->getOldYCB (), - vloat->getOldWidth (), vloat->getOldHeight (), - newFlx, newFly, vloat->size.width, - vloat->size.ascent + vloat->size.descent, - side, &floatPos)) { - if (!changed || floatPos < *minFloatPos) { - *minFloatPos = floatPos; - *minFloat = vloat->getWidget (); - } - changed = true; - } else - DBG_OBJ_MSG ("resize.oofm", 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?) - } - - if (changed) - DBG_OBJ_MSGF ("resize.oofm", 1, - "has changed: minFloatPos = %d, minFloat = %p", - *minFloatPos, *minFloat); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - return changed; -} - -/** - * \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_ENTER ("resize.oofm", 0, "hasRelationChanged", - "<i>see below</i>, %s, ...", side == LEFT ? "LEFT" : "RIGHT"); - - if (oldTBAlloc) - DBG_OBJ_MSGF ("resize.oofm", 1, "old TB: %d, %d; %d * %d", - oldTBx, oldTBy, oldTBw, oldTBh); - else - DBG_OBJ_MSG ("resize.oofm", 1, "old TB: undefined"); - DBG_OBJ_MSGF ("resize.oofm", 1, "new TB: %d, %d; %d * %d", - newTBx, newTBy, newTBw, newTBh); - - if (oldFlAlloc) - DBG_OBJ_MSGF ("resize.oofm", 1, "old Fl: %d, %d; %d * %d", - oldFlx, oldFly, oldFlw, oldFlh); - else - DBG_OBJ_MSG ("resize.oofm", 1, "old Fl: undefined"); - DBG_OBJ_MSGF ("resize.oofm", 1, "new Fl: %d, %d; %d * %d", - newFlx, newFly, newFlw, newFlh); - - bool result; - if (oldTBAlloc && oldFlAlloc) { - bool oldCov = oldFly + oldFlh > oldTBy && oldFly < oldTBy + oldTBh; - bool newCov = newFly + newFlh > newTBy && newFly < newTBy + newTBh; - - DBG_OBJ_MSGF ("resize.oofm", 1, "covered? then: %s, now: %s.", - oldCov ? "yes" : "no", newCov ? "yes" : "no"); - DBG_OBJ_MSG_START (); - - if (oldCov && newCov) { - int yOld = oldFly - oldTBy, yNew = newFly - newTBy; - if (yOld == yNew) { - DBG_OBJ_MSGF ("resize.oofm", 2, - "old (%d - %d) and new (%d - %d) position equal: %d", - oldFly, oldTBy, newFly, newTBy, yOld); - - // 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; - } - - DBG_OBJ_MSGF ("resize.oofm", 2, "wOld = %d, wNew = %d\n", - wOld, wNew); - - if (wOld == wNew) { - if (oldFlh == newFlh) - result = false; - else { - // Only heights of floats changed. Relevant only - // from bottoms of float. - *floatPos = min (yOld + oldFlh, yNew + newFlh); - result = true; - } - } else { - *floatPos = yOld; - result = true; - } - } else { - DBG_OBJ_MSGF ("resize.oofm", 2, - "old (%d - %d = %d) and new (%d - %d = %d) position " - "different", - oldFly, oldTBy, yOld, newFly, newTBy, yNew); - *floatPos = min (yOld, yNew); - result = true; - } - } else if (oldCov) { - *floatPos = oldFly - oldTBy; - result = true; - DBG_OBJ_MSGF ("resize.oofm", 2, - "returning old position: %d - %d = %d", oldFly, oldTBy, - *floatPos); - } else if (newCov) { - *floatPos = newFly - newTBy; - result = true; - DBG_OBJ_MSGF ("resize.oofm", 2, - "returning new position: %d - %d = %d", newFly, newTBy, - *floatPos); - } else - result = false; - - DBG_OBJ_MSG_END (); - } 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; - } - - if (result) - DBG_OBJ_MSGF ("resize.oofm", 1, "has changed: floatPos = %d", - *floatPos); - else - DBG_OBJ_MSG ("resize.oofm", 1, "has not changed"); - - DBG_OBJ_LEAVE (); - - return result; -} - -void OutOfFlowMgr::checkAllocatedFloatCollisions (Side side) -{ - // In some cases, the collision detection in tellPosition() is - // based on the wrong allocations. Here (just after all Floats have - // been allocated), we correct this. - - // TODO In some cases this approach is rather slow, causing a too - // long queueResize() cascade. - - DBG_OBJ_ENTER ("resize.oofm", 0, "checkAllocatedFloatCollisions", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - SortedFloatsVector *oppList = side == LEFT ? rightFloatsCB : leftFloatsCB; - - // While iterating through the list of floats to be checked, we - // iterate equally through the list of the opposite floats, using - // this index: - int oppIndex = 0; - - for (int index = 0; index < list->size (); index++) { - Float *vloat = list->get(index); - bool needsChange = false; - int yRealNew = INT_MAX; - - // Same side. - if (index >= 1) { - Float *other = list->get(index - 1); - DBG_OBJ_MSGF ("resize.oofm", 1, - "same side: checking %p (#%d, GB: %p) against " - "%p (#%d, GB: %p)", - vloat->getWidget (), index, vloat->generatingBlock, - other->getWidget (), index - 1, other->generatingBlock); - - if (vloat->generatingBlock != other->generatingBlock) { - int yRealNewSame; - if (collidesV (vloat, other, CB, &yRealNewSame, true)) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> collides, new yReal = %d (old: %d)", - yRealNewSame, vloat->yReal); - if (vloat->yReal != yRealNewSame) { - needsChange = true; - yRealNew = min (yRealNew, yRealNewSame); - } - } else - DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision"); - } - } - - if (oppList->size () > 0) { - // Other side. Iterate to next float on the other side, - // before this float. - while (oppIndex + 1 < oppList->size () && - oppList->get(oppIndex + 1)->sideSpanningIndex - < vloat->sideSpanningIndex) - oppIndex++; - - if (oppList->get(oppIndex)->sideSpanningIndex - < vloat->sideSpanningIndex) { - int oppIndexTmp = oppIndex, yRealNewOpp; - - // Aproach is similar to tellPosition(); see comments - // there. Again, loop as long as the vertical dimensions test - // is positive (and, of course, there are floats), ... - for (bool foundColl = false; - !foundColl && oppIndexTmp >= 0 && - collidesV (vloat, oppList->get (oppIndexTmp), CB, - &yRealNewOpp, true); - oppIndexTmp--) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "opposite side (after collision (v) test): " - "checking %p (#%d/%d, GB: %p) against " - "%p (#%d/%d, GB: %p)", - vloat->getWidget (), index, - vloat->sideSpanningIndex, - vloat->generatingBlock, - oppList->get(oppIndexTmp)->getWidget (), - oppList->get(oppIndexTmp)->getIndex (CB), - oppList->get(oppIndexTmp)->sideSpanningIndex, - oppList->get(oppIndexTmp)->generatingBlock); - - // ... but stop the loop as soon as the horizontal dimensions - // test is positive. - if (collidesH (vloat, oppList->get (oppIndexTmp), CB)) { - DBG_OBJ_MSGF ("resize.oofm", 1, - "=> collides (h), new yReal = %d (old: %d)", - yRealNewOpp, vloat->yReal); - foundColl = true; - if (vloat->yReal != yRealNewOpp) { - needsChange = true; - yRealNew = min (yRealNew, yRealNewOpp); - } - } else - DBG_OBJ_MSG ("resize.oofm", 1, "=> no collision (h)"); - } - } - } - - if (needsChange) - vloat->generatingBlock->borderChanged (min (vloat->yReal, yRealNew), - vloat->getWidget ()); - } - - DBG_OBJ_LEAVE (); -} - -bool OutOfFlowMgr::doFloatsExceedCB (Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "doFloatsExceedCB", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - // This method is called to determine whether the *requisition* of - // the CB must be recalculated. So, we check the float allocations - // against the *requisition* of the CB, which may (e. g. within - // tables) differ from the new allocation. (Generally, a widget may - // allocated at a different size.) - core::Requisition cbReq; - containingBlock->sizeRequest (&cbReq); - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool exceeds = false; - - DBG_OBJ_MSG_START (); - - for (int i = 0; i < list->size () && !exceeds; i++) { - Float *vloat = list->get (i); - if (vloat->getWidget()->wasAllocated ()) { - Allocation *fla = vloat->getWidget()->getAllocation (); - DBG_OBJ_MSGF ("resize.oofm", 2, - "Does FlA = (%d, %d, %d * %d) exceed CBA = " - "(%d, %d, %d * %d)?", - fla->x, fla->y, fla->width, fla->ascent + fla->descent, - containingBlockAllocation.x, containingBlockAllocation.y, - cbReq.width, cbReq.ascent + cbReq.descent); - if (fla->x + fla->width > containingBlockAllocation.x + cbReq.width || - fla->y + fla->ascent + fla->descent - > containingBlockAllocation.y + cbReq.ascent + cbReq.descent) { - exceeds = true; - DBG_OBJ_MSG ("resize.oofm", 2, "Yes."); - } else - DBG_OBJ_MSG ("resize.oofm", 2, "No."); - } - } - - DBG_OBJ_MSG_END (); - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", exceeds ? "true" : "false"); - DBG_OBJ_LEAVE (); - - return exceeds; -} - -bool OutOfFlowMgr::haveExtremesChanged (Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "haveExtremesChanged", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - // This is quite different from doFloatsExceedCB, since there is no - // counterpart to getExtremes, as sizeAllocate is a counterpart to - // sizeRequest. So we have to determine whether the allocation has - // changed the extremes, which is done by examining the part of the - // allocation which is part of the extremes calculation (see - // getFloatsExtremes). Changes of the extremes are handled by the - // normal queueResize mechanism. - - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - bool changed = false; - - for (int i = 0; i < list->size () && !changed; i++) { - Float *vloat = list->get (i); - // When the GB is the CB, an allocation change does not play a - // role here. - if (vloat->generatingBlock != containingBlock) { - if (!vloat->wasThenAllocated () && vloat->isNowAllocated ()) - changed = true; - else { - // This method is called within sizeAllocateEnd, where - // containinBlock->getAllocation() (old value) and - // containinBlockAllocation (new value) are different. - - Allocation *oldCBA = containingBlock->getAllocation (); - Allocation *newCBA = &containingBlockAllocation; - - // Compare also to getFloatsExtremes. The box difference - // of the GB (from style) has not changed in this context, - // so it is ignored. - - int oldDiffLeft = vloat->getOldXCB (); - int newDiffLeft = vloat->getNewXCB (); - int oldDiffRight = - oldCBA->width - (vloat->getOldXCB () + vloat->getOldWidth ()); - int newDiffRight = - newCBA->width - (vloat->getNewXCB () + vloat->getNewWidth ()); - - if (// regarding minimum - (side == LEFT && oldDiffLeft != newDiffLeft) || - (side == RIGHT && oldDiffRight != newDiffRight) || - // regarding maximum - oldDiffLeft + oldDiffRight != newDiffLeft + newDiffRight) - changed = true; - } - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %s", changed ? "true" : "false"); - DBG_OBJ_LEAVE (); - - return changed; -} - -void OutOfFlowMgr::moveFromGBToCB (Side side) -{ - DBG_OBJ_ENTER ("oofm.resize", 0, "moveFromGBToCB", "%s", - side == LEFT ? "LEFT" : "RIGHT"); - - SortedFloatsVector *dest = side == LEFT ? leftFloatsCB : rightFloatsCB; - int *floatsMark = side == LEFT ? &leftFloatsMark : &rightFloatsMark; - - for (int mark = 0; mark <= *floatsMark; mark++) - for (lout::container::typed::Iterator<TBInfo> 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); - // "vloat->indexCBList == -1": prevent copying the vloat twice. - if (vloat->indexCBList == -1 && vloat->mark == mark) { - dest->put (vloat); - DBG_OBJ_MSGF ("oofm.resize", 1, - "moving float %p (mark %d) to CB list\n", - vloat->getWidget (), vloat->mark); - DBG_OBJ_SET_NUM (side == LEFT ? - "leftFloatsCB.size" : "rightFloatsCB.size", - dest->size()); - DBG_OBJ_ARRATTRSET_PTR (side == LEFT ? - "leftFloatsCB" : "rightFloatsCB", - dest->size() - 1, "widget", - vloat->getWidget ()); - - } - } - } - - *floatsMark = 0; - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::sizeAllocateFloats (Side side, int newLastAllocatedFloat) -{ - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - int *lastAllocatedFloat = - side == LEFT ? &lastAllocatedLeftFloat : &lastAllocatedRightFloat; - - DBG_OBJ_ENTER ("resize.oofm", 0, "sizeAllocateFloats", - "%s, [%d ->] %d [size = %d]", - side == LEFT ? "LEFT" : "RIGHT", *lastAllocatedFloat, - newLastAllocatedFloat, list->size ()); - - Allocation *cba = &containingBlockAllocation; - - for (int i = *lastAllocatedFloat + 1; i <= newLastAllocatedFloat; i++) { - Float *vloat = list->get(i); - ensureFloatSize (vloat); - - Allocation *gba = getAllocation (vloat->generatingBlock); - - Allocation childAllocation; - childAllocation.x = cba->x + calcFloatX (vloat, side, gba->x - cba->x, - getGBWidthForAllocation (vloat)); - childAllocation.y = gba->y + vloat->yReal; - childAllocation.width = vloat->size.width; - childAllocation.ascent = vloat->size.ascent; - childAllocation.descent = vloat->size.descent; - - vloat->getWidget()->sizeAllocate (&childAllocation); - } - - *lastAllocatedFloat = newLastAllocatedFloat; - - DBG_OBJ_LEAVE (); -} - -// Used as argument "gbWidth" for calcFloatX(), in the context of allocation. -int OutOfFlowMgr::getGBWidthForAllocation (Float *vloat) -{ - // See comments in getFloatsSize() for a detailed rationale ... - if (containingBlock->mustBeWidenedToAvailWidth ()) - return vloat->generatingBlock->getLineBreakWidth (); - else - // ... but notice this difference: not GB width + float width is - // used, but only GB width, since the float width has already - // been included in getFloatsSize(). - return min (getAllocation(vloat->generatingBlock)->width, - vloat->generatingBlock->getLineBreakWidth ()); -} - -/** - * \brief ... - * - * gbX is given relative to the CB, as is the return value. - */ -int OutOfFlowMgr::calcFloatX (Float *vloat, Side side, int gbX, int gbWidth) -{ - DBG_OBJ_ENTER ("resize.common", 0, "calcFloatX", "%p, %s, %d, %d", - vloat->getWidget (), side == LEFT ? "LEFT" : "RIGHT", gbX, - gbWidth); - int x; - - switch (side) { - case LEFT: - // Left floats are always aligned on the left side of the - // generator (content, not allocation) ... - x = gbX + vloat->generatingBlock->getStyle()->boxOffsetX(); - DBG_OBJ_MSGF ("resize.common", 1, "left: x = %d + %d = %d", - gbX, vloat->generatingBlock->getStyle()->boxOffsetX(), x); - // ... 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 (wasAllocated (containingBlock) && - x + vloat->size.width > containingBlock->getLineBreakWidth ()) { - x = max (0, containingBlock->getLineBreakWidth () - vloat->size.width); - DBG_OBJ_MSGF ("resize.common", 1, - "corrected to: max (0, %d - %d) = %d", - containingBlock->getLineBreakWidth (), vloat->size.width, - x); - } - break; - - case 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). - - x = max (gbX + gbWidth - vloat->size.width - - vloat->generatingBlock->getStyle()->boxRestWidth(), - // Do not exceed CB allocation: - 0); - DBG_OBJ_MSGF ("resize.common", 1, "x = max (%d + %d - %d - %d, 0) = %d", - gbX, gbWidth, vloat->size.width, - vloat->generatingBlock->getStyle()->boxRestWidth(), x); - break; - - default: - assertNotReached (); - x = 0; - break; - } - - DBG_OBJ_LEAVE (); - return x; -} - -void OutOfFlowMgr::draw (View *view, Rectangle *area) -{ - drawFloats (leftFloatsCB, view, area); - drawFloats (rightFloatsCB, 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); - Rectangle childArea; - if (vloat->getWidget()->intersects (area, &childArea)) - vloat->getWidget()->draw (view, &childArea); - } -} - -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->index = tbInfos->size(); - - tbInfos->put (tbInfo); - tbInfosByTextblock->put (new TypedPointer<Textblock> (textblock), tbInfo); -} - -void OutOfFlowMgr::addWidgetOOF (Widget *widget, Textblock *generatingBlock, - int externalIndex) -{ - DBG_OBJ_ENTER ("construct.oofm", 0, "addWidgetOOF", "%p, %p, %d", - widget, generatingBlock, 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); - DBG_OBJ_SET_NUM ("leftFloatsAll.size", leftFloatsAll->size()); - DBG_OBJ_ARRATTRSET_PTR ("leftFloatsAll", leftFloatsAll->size() - 1, - "widget", vloat->getWidget ()); - - widget->parentRef = createRefLeftFloat (leftFloatsAll->size() - 1); - DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); - tbInfo->leftFloatsGB->put (vloat); - - if (wasAllocated (generatingBlock)) { - leftFloatsCB->put (vloat); - DBG_OBJ_SET_NUM ("leftFloatsCB.size", leftFloatsCB->size()); - DBG_OBJ_ARRATTRSET_PTR ("leftFloatsCB", leftFloatsCB->size() - 1, - "widget", vloat->getWidget ()); - } 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); - DBG_OBJ_SET_NUM ("rightFloatsAll.size", rightFloatsAll->size()); - DBG_OBJ_ARRATTRSET_PTR ("rightFloatsAll", rightFloatsAll->size() - 1, - "widget", vloat->getWidget ()); - - widget->parentRef = createRefRightFloat (rightFloatsAll->size() - 1); - DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); - tbInfo->rightFloatsGB->put (vloat); - - if (wasAllocated (generatingBlock)) { - rightFloatsCB->put (vloat); - DBG_OBJ_SET_NUM ("rightFloatsCB.size", rightFloatsCB->size()); - DBG_OBJ_ARRATTRSET_PTR ("rightFloatsCB", rightFloatsCB->size() - 1, - "widget", vloat->getWidget ()); - } 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> (widget), vloat); - } else - // May be extended. - assertNotReached(); - - DBG_OBJ_LEAVE (); -} - -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; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.externalIndex", - vloat->externalIndex); - } - } -} - -OutOfFlowMgr::Float *OutOfFlowMgr::findFloatByWidget (Widget *widget) -{ - TypedPointer <Widget> key (widget); - Float *vloat = floatsByWidget->get (&key); - assert (vloat != NULL); - return vloat; -} - -void OutOfFlowMgr::markSizeChange (int ref) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "markSizeChange", "%d", 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; - - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.dirty", vloat->dirty); - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), - "<Float>.sizeChangedSinceLastAllocation", - vloat->sizeChangedSinceLastAllocation); - - // The generating block is told directly about this. (Others later, in - // sizeAllocateEnd.) Could be faster (cf. hasRelationChanged, which - // differentiates many special cases), but the size is not known yet, - vloat->generatingBlock->borderChanged (vloat->yReal, vloat->getWidget ()); - } else - assertNotReached(); - - DBG_OBJ_LEAVE (); -} - - -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); - 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); - if (vloat->getWidget()->wasAllocated ()) { - Widget *childAtPoint = - vloat->getWidget()->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_ENTER ("resize.oofm", 0, "tellFloatPosition", "%p, %d", - widget, yReq); - - assert (yReq >= 0); - - Float *vloat = findFloatByWidget(widget); - - SortedFloatsVector *listSame, *listOpp; - Side side; - getFloatsListsAndSide (vloat, &listSame, &listOpp, &side); - ensureFloatSize (vloat); - - // "yReal" may change due to collisions (see below). - vloat->yReq = vloat->yReal = yReq; - - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReq", vloat->yReq); - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.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 index = vloat->getIndex (listSame->type), yRealNew; - if (index >= 1 && - collidesV (vloat, listSame->get (index - 1), listSame->type, - &yRealNew, false)) { - vloat->yReal = yRealNew; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.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): - // - // <div style="float:left" id="f1"> - // Left left left left left left left left left left. - // </div> - // <div style="float:left" id="f2">Also left.</div> - // <div style="float:right" id="f3">Right.</div> - // - // 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), listSame->type, - &yRealNew, false); - oppFloatIndex--) { - // ... but stop the loop as soon as the horizontal dimensions - // test is positive. - if (collidesH (vloat, listOpp->get (oppFloatIndex), listSame->type)) { - vloat->yReal = yRealNew; - DBG_OBJ_SET_NUM_O (vloat->getWidget (), "<Float>.yReal", vloat->yReal); - foundColl = true; - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "vloat->yReq = %d, vloat->yReal = %d", - vloat->yReq, vloat->yReal); - - DBG_OBJ_LEAVE (); } -bool OutOfFlowMgr::collidesV (Float *vloat, Float *other, SFVType type, - int *yReal, bool useAllocation) -{ - // Only checks vertical (possible) collisions, and only refers to - // vloat->yReal; never to vloat->allocation->y, even when the GBs are - // different. Used only in tellPosition. - - DBG_OBJ_ENTER ("resize.oofm", 0, "collidesV", "#%d [%p], #%d [%p], ...", - vloat->getIndex (type), vloat->getWidget (), - other->getIndex (type), other->getWidget ()); - - bool result; - - DBG_OBJ_MSGF ("resize.oofm", 1, "initial yReal = %d", vloat->yReal); - - if (vloat->generatingBlock == other->generatingBlock) { - ensureFloatSize (other); - int otherBottomGB = - other->yReal + other->size.ascent + other->size.descent; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "same generators: 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; - } else { - // If the other float is not allocated, there is no collision. The - // allocation of this float (vloat) is not used at all. - if (!other->getWidget()->wasAllocated ()) - result = false; - else { - assert (wasAllocated (vloat->generatingBlock)); - Allocation *gba = getAllocation (vloat->generatingBlock), - *flaOther = other->getWidget()->getAllocation (); - - // We distinguish two cases (by different values of useAllocation): - // (i) within tellPosition, GB allocation + yReal is used for the - // y position of the other float, while (ii) in checkAllocatedFloat- - // Collisions, the float allocation is used. The latter is necessary - // by the definition of this method, the former increases performance, - // as compared to using the float allocation, in some cases, as in - // this: - // - // When '<div><div style="float:left">[Some text]</div></div>' is - // repeated n times, the resize idle function (Layout::resizeIdle) - // would be repeated roughly n times, when also in case (i) the float - // allocation is used, since for the collision test of float n with - // float n - 1, the allocation of float n - 1 does not yet reflect the - // collision test between n - 1 and n - 2, but yReal does for n - 1. - // - // On the other hand, the GB allocations will most likely more stable - // than the float allocations. - // - // Cases where this is incorrect will hopefully be rare, and, in any - // case, corrected in sizeAllocateEnd, either because hasRelation- - // Changed returns true, or in checkAllocatedFloatCollisions. - - int otherFloatY = useAllocation ? flaOther->y : - getAllocation(other->generatingBlock)->y + other->yReal; - int otherBottomGB = - otherFloatY + flaOther->ascent + flaOther->descent - gba->y; - - DBG_OBJ_MSGF ("resize.oofm", 1, - "different generators: " - "otherBottomGB = %d + (%d + %d) - %d = %d", - otherFloatY, flaOther->ascent, flaOther->descent, gba->y, - otherBottomGB); - - if (vloat->yReal < otherBottomGB) { - *yReal = otherBottomGB; - result = true; - } else - result = false; - } - } - - if (result) - DBG_OBJ_MSGF ("resize.oofm", 1, "collides: new yReal = %d", *yReal); - else - DBG_OBJ_MSG ("resize.oofm", 1, "does not collide"); - - DBG_OBJ_LEAVE (); - return result; -} - - -bool OutOfFlowMgr::collidesH (Float *vloat, Float *other, SFVType type) -{ - // Only checks horizontal collision. For a complete test, use - // collidesV (...) && collidesH (...). - bool collidesH; - - if (vloat->generatingBlock == other->generatingBlock) - collidesH = vloat->size.width + other->size.width - + vloat->generatingBlock->getStyle()->boxDiffWidth() - > vloat->generatingBlock->getLineBreakWidth(); - else { - // Again, if the other float is not allocated, there is no - // collision. Compare to collidesV. (But vloat->size is used - // here.) - if (!other->getWidget()->wasAllocated ()) - collidesH = false; - else { - assert (wasAllocated (vloat->generatingBlock)); - Allocation *gba = getAllocation (vloat->generatingBlock); - int vloatX = - calcFloatX (vloat, - vloat->getWidget()->getStyle()->vloat == FLOAT_LEFT ? - LEFT : RIGHT, - gba->x, getGBWidthForAllocation (vloat)); - - // 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 - collidesH = vloatX + vloat->size.width - > other->getWidget()->getAllocation()->x; - else - // "other" is left, "vloat" is right - collidesH = other->getWidget()->getAllocation()->x - + other->getWidget()->getAllocation()->width - > vloatX; - } - } - - return collidesH; -} - -void OutOfFlowMgr::getFloatsListsAndSide (Float *vloat, - SortedFloatsVector **listSame, - SortedFloatsVector **listOpp, - Side *side) -{ - TBInfo *tbInfo = getTextblock (vloat->generatingBlock); - - switch (vloat->getWidget()->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; - } - if (side) *side = LEFT; - 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; - } - if (side) *side = RIGHT; - break; - - default: - assertNotReached(); - } -} - -void OutOfFlowMgr::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); - - *oofWidth = max (oofWidthtLeft, oofWidthRight); - *oofHeight = max (oofHeightLeft, oofHeightRight); - - 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 OutOfFlowMgr::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 = getFloatsListForTextblock (containingBlock, side); - - *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->generatingBlock, - containingBlock); - - if (vloat->generatingBlock == containingBlock || - wasAllocated (vloat->generatingBlock)) { - ensureFloatSize (vloat); - int x, y; - - int effWidth; - if (containingBlock->mustBeWidenedToAvailWidth ()) - // 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.) - effWidth = vloat->generatingBlock->getLineBreakWidth (); - 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. - effWidth = min (cbReq->width + vloat->size.width, - vloat->generatingBlock->getLineBreakWidth ()); - - if (vloat->generatingBlock == containingBlock) { - x = calcFloatX (vloat, side, 0, effWidth); - y = vloat->yReal; - } else { - Allocation *gba = getAllocation(vloat->generatingBlock); - x = calcFloatX (vloat, side, gba->x - containingBlockAllocation.x, - effWidth); - y = gba->y - containingBlockAllocation.y + vloat->yReal; - } - - *width = max (*width, x + vloat->size.width); - *height = max (*height, y + vloat->size.ascent + vloat->size.descent); - - DBG_OBJ_MSGF ("resize.oofm", 1, - "considering float %p generated by %p: (%d + %d) * " - "(%d + (%d + %d)) => %d * %d", - vloat->getWidget (), vloat->generatingBlock, - x, vloat->size.width, - y, vloat->size.ascent, vloat->size.descent, - *width, *height); - } else - DBG_OBJ_MSGF ("resize.oofm", 1, - "considering float %p generated by %p: not allocated", - vloat->getWidget (), vloat->generatingBlock); - } - - DBG_OBJ_LEAVE (); -} - -void OutOfFlowMgr::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 OutOfFlowMgr::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 = getFloatsListForTextblock (containingBlock, side); - 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->generatingBlock, - containingBlock); - - if (vloat->generatingBlock == containingBlock || - wasAllocated (vloat->generatingBlock)) { - 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. - - if (vloat->generatingBlock == containingBlock) - *maxWidth = - max (*maxWidth, - extr.maxWidth - + vloat->generatingBlock->getStyle()->boxDiffWidth()); - else { - Allocation *gba = getAllocation (vloat->generatingBlock); - *maxWidth = - max (*maxWidth, - extr.maxWidth - + vloat->generatingBlock->getStyle()->boxDiffWidth() - + max (containingBlockAllocation.width - gba->width, 0)); - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "%d / %d => %d / %d", - extr.minWidth, extr.maxWidth, *minWidth, *maxWidth); - } else - DBG_OBJ_MSG ("resize.oofm", 1, "not allocated"); - } - - DBG_OBJ_LEAVE (); -} - -OutOfFlowMgr::TBInfo *OutOfFlowMgr::getTextblock (Textblock *textblock) -{ - TypedPointer<Textblock> 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); - DBG_OBJ_MSGF ("border", 0, "left border (%p, %d, %d, %p, %d) => %d", - textblock, y, h, lastGB, lastExtIndex, 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); - DBG_OBJ_MSGF ("border", 0, "right border (%p, %d, %d, %p, %d) => %d", - textblock, y, h, lastGB, lastExtIndex, b); - return b; -} - -int OutOfFlowMgr::getBorder (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "getBorder", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int last; - int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, &last); - - DBG_OBJ_MSGF ("border", 1, "first = %d", first); - - if (first == -1) { - // No float. - DBG_OBJ_LEAVE (); - 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; - - // 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 (textblock, y, h); - DBG_OBJ_MSGF ("border", 1, "float %d (%p) covers? %s.", - i, vloat->getWidget(), covers ? "<b>yes</b>" : "no"); - - if (covers) { - int thisBorder; - if (vloat->generatingBlock == textblock) { - int borderIn = side == LEFT ? - vloat->generatingBlock->getStyle()->boxOffsetX() : - vloat->generatingBlock->getStyle()->boxRestWidth(); - thisBorder = vloat->size.width + borderIn; - DBG_OBJ_MSGF ("border", 1, "GB: thisBorder = %d + %d = %d", - vloat->size.width, borderIn, thisBorder); - } else { - assert (wasAllocated (vloat->generatingBlock)); - assert (vloat->getWidget()->wasAllocated ()); - - Allocation *tba = getAllocation(textblock), - *fla = vloat->getWidget()->getAllocation (); - if (side == LEFT) { - thisBorder = fla->x + fla->width - tba->x; - DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + %d - %d = %d", - fla->x, fla->width, tba->x, thisBorder); - } else { - // See also calcFloatX. - thisBorder = - tba->x + textblock->getLineBreakWidth () - fla->x; - DBG_OBJ_MSGF ("border", 1, - "not GB: thisBorder = %d + %d - %d " - "= %d", - tba->x, textblock->getLineBreakWidth (), fla->x, - thisBorder); - } - } - - border = max (border, thisBorder); - DBG_OBJ_MSGF ("border", 1, "=> border = %d", border); - } - } - - DBG_OBJ_LEAVE (); - return border; - } -} - - -OutOfFlowMgr::SortedFloatsVector *OutOfFlowMgr::getFloatsListForTextblock - (Textblock *textblock, Side side) -{ - DBG_OBJ_ENTER ("oofm.common", 1, "getFloatsListForTextblock", "%p, %s", - textblock, side == LEFT ? "LEFT" : "RIGHT"); - - OutOfFlowMgr::SortedFloatsVector *list; - - if (wasAllocated (textblock)) { - DBG_OBJ_MSG ("oofm.common", 2, "returning <b>CB</b> list"); - list = side == LEFT ? leftFloatsCB : rightFloatsCB; - } else { - DBG_OBJ_MSG ("oofm.common", 2, "returning <b>GB</b> list"); - TBInfo *tbInfo = getTextblock (textblock); - list = side == LEFT ? tbInfo->leftFloatsGB : tbInfo->rightFloatsGB; - } - - DBG_OBJ_LEAVE (); - return list; -} - - -bool OutOfFlowMgr::hasFloatLeft (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - bool b = hasFloat (textblock, LEFT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "has float left (%p, %d, %d, %p, %d) => %s", - textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); - return b; -} - -bool OutOfFlowMgr::hasFloatRight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - bool b = hasFloat (textblock, RIGHT, y, h, lastGB, lastExtIndex); - DBG_OBJ_MSGF ("border", 0, "has float right (%p, %d, %d, %p, %d) => %s", - textblock, y, h, lastGB, lastExtIndex, b ? "true" : "false"); - return b; -} - -bool OutOfFlowMgr::hasFloat (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "hasFloat", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int first = list->findFirst (textblock, y, h, lastGB, lastExtIndex, NULL); - - DBG_OBJ_MSGF ("border", 1, "first = %d", first); - DBG_OBJ_LEAVE (); - return first != -1; -} - -int OutOfFlowMgr::getLeftFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - return getFloatHeight (textblock, LEFT, y, h, lastGB, lastExtIndex); -} - -int OutOfFlowMgr::getRightFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - return getFloatHeight (textblock, RIGHT, y, h, lastGB, lastExtIndex); -} - -// Calculate height from the position *y*. -int OutOfFlowMgr::getFloatHeight (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex) -{ - DBG_OBJ_ENTER ("border", 0, "getFloatHeight", "%p, %s, %d, %d, %p, %d", - textblock, side == LEFT ? "LEFT" : "RIGHT", y, h, - lastGB, lastExtIndex); - - SortedFloatsVector *list = getFloatsListForTextblock (textblock, side); - int first = list->findFirst (textblock, 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; - - if (vloat->generatingBlock == textblock) { - yRelToFloat = y - vloat->yReal; - DBG_OBJ_MSGF ("border", 1, "caller is CB: yRelToFloat = %d - %d = %d", - y, vloat->yReal, yRelToFloat); - } else { - // The respective widgets are allocated; otherwise, hasFloat() would have - // returned false. - assert (wasAllocated (textblock)); - assert (vloat->getWidget()->wasAllocated ()); - - Allocation *tba = getAllocation(textblock), - *fla = vloat->getWidget()->getAllocation (); - yRelToFloat = tba->y + y - fla->y; - - DBG_OBJ_MSGF ("border", 1, - "caller is not CB: yRelToFloat = %d + %d - %d = %d", - tba->y, y, fla->y, 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; -} - -/** - * Returns position relative to the textblock "tb". - */ -int OutOfFlowMgr::getClearPosition (Textblock *tb) -{ - return getTextblock(tb)->clearPosition; -} - -int OutOfFlowMgr::calcClearPosition (Textblock *tb) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p", tb); - - int pos; - - if (tb->getStyle()) { - bool left = false, right = false; - switch (tb->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 ? calcClearPosition (tb, LEFT) : 0, - right ? calcClearPosition (tb, RIGHT) : 0); - } else - pos = 0; - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_LEAVE (); - - return pos; -} - -int OutOfFlowMgr::calcClearPosition (Textblock *tb, Side side) -{ - DBG_OBJ_ENTER ("resize.oofm", 0, "getClearPosition", "%p, %s", - tb, side == LEFT ? "LEFT" : "RIGHT"); - - int pos; - - if (!wasAllocated (tb)) - // There is no relation yet to floats generated by other - // textblocks, and this textblocks floats are unimportant for - // the "clear" property. - pos = 0; - else { - SortedFloatsVector *list = side == LEFT ? leftFloatsCB : rightFloatsCB; - - // Search the last float before (therfore -1) this textblock. - int i = list->findFloatIndex (tb, -1); - if (i < 0) { - pos = 0; - DBG_OBJ_MSG ("resize.oofm", 1, "no float"); - } else { - Float *vloat = list->get(i); - assert (vloat->generatingBlock != tb); - if (!wasAllocated (vloat->generatingBlock)) - pos = 0; // See above. - else { - ensureFloatSize (vloat); - pos = max (getAllocation(vloat->generatingBlock)->y + vloat->yReal - + vloat->size.ascent + vloat->size.descent - - getAllocation(tb)->y, - 0); - DBG_OBJ_MSGF ("resize.oofm", 1, - "float %p => max (%d + %d + (%d + %d) - %d, 0)", - vloat->getWidget (), - getAllocation(vloat->generatingBlock)->y, - vloat->yReal, vloat->size.ascent, vloat->size.descent, - getAllocation(tb)->y); - } - } - } - - DBG_OBJ_MSGF ("resize.oofm", 1, "=> %d", pos); - DBG_OBJ_LEAVE (); - - return pos; -} - -void OutOfFlowMgr::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->cbLineBreakWidth = containingBlock->getLineBreakWidth (); - vloat->dirty = false; - DBG_OBJ_SET_BOOL_O (vloat->getWidget (), "<Float>.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(), "<Float>.size.width", - vloat->size.width); - DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.ascent", - vloat->size.ascent); - DBG_OBJ_SET_NUM_O (vloat->getWidget(), "<Float>.size.descent", - vloat->size.descent); - - // "sizeChangedSinceLastAllocation" is reset in sizeAllocateEnd() - } - - DBG_OBJ_LEAVE (); -} +} // namespace oof } // namespace dw diff --git a/dw/outofflowmgr.hh b/dw/outofflowmgr.hh index 78146d08..6dff2055 100644 --- a/dw/outofflowmgr.hh +++ b/dw/outofflowmgr.hh @@ -5,431 +5,138 @@ namespace dw { -class Textblock; +/** + * \brief Out Of Flow. See \ref dw-out-of-flow. + */ +namespace oof { + +class OOFAwareWidget; /** - * \brief Represents additional data for containing blocks. + * \brief Represents additional data for OOF containers. */ class OutOfFlowMgr { - friend class WidgetInfo; - -private: - enum Side { LEFT, RIGHT }; - enum SFVType { GB, CB }; - - Textblock *containingBlock; - - // These two values are redundant to TBInfo::wasAllocated and - // TBInfo::allocation, for some special cases. - bool containingBlockWasAllocated; - core::Allocation containingBlockAllocation; - - class WidgetInfo: public lout::object::Object - { - private: - bool wasAllocated; - int xCB, yCB; // relative to the containing block - int width, height; - - OutOfFlowMgr *oofm; - core::Widget *widget; - - protected: - OutOfFlowMgr *getOutOfFlowMgr () { return oofm; } - - public: - WidgetInfo (OutOfFlowMgr *oofm, core::Widget *widget); - - inline bool wasThenAllocated () { return wasAllocated; } - inline int getOldXCB () { return xCB; } - inline int getOldYCB () { return yCB; } - inline int getOldWidth () { return width; } - inline int getOldHeight () { return height; } - - - void update (bool wasAllocated, int xCB, int yCB, int width, int height); - - inline core::Widget *getWidget () { return widget; } - }; - - class Float: public WidgetInfo - { - public: - class ComparePosition: public lout::object::Comparator - { - private: - OutOfFlowMgr *oofm; - Textblock *refTB; - SFVType type; // actually only used for debugging - - public: - ComparePosition (OutOfFlowMgr *oofm, Textblock *refTB, SFVType type) - { this->oofm = oofm; this->refTB = refTB; this->type = type; } - int compare(Object *o1, Object *o2); - }; - - class CompareSideSpanningIndex: public lout::object::Comparator - { - public: - int compare(Object *o1, Object *o2); - }; - - class CompareGBAndExtIndex: public lout::object::Comparator - { - private: - OutOfFlowMgr *oofm; - SFVType type; // actually only used for debugging - - public: - CompareGBAndExtIndex (OutOfFlowMgr *oofm, SFVType type) - { this->oofm = oofm; this->type = type; } - int compare(Object *o1, Object *o2); - }; - - Textblock *generatingBlock; - int externalIndex; - int yReq, yReal; // relative to generator, not container - int indexGBList; /* Refers to TBInfo::leftFloatsGB or - TBInfo::rightFloatsGB, respectively. -1 - initially. */ - int indexCBList; /* Refers to leftFloatsCB or rightFloatsCB, - respectively. -1 initially. */ - int sideSpanningIndex, mark; - core::Requisition size; - int cbLineBreakWidth; /* On which the calculation of relative sizes - is based. Height not yet used, and probably - not added before size redesign. */ - bool dirty, sizeChangedSinceLastAllocation; - - Float (OutOfFlowMgr *oofm, core::Widget *widget, - Textblock *generatingBlock, int externalIndex); - - inline bool isNowAllocated () { return getWidget()->wasAllocated (); } - inline int getNewXCB () { return getWidget()->getAllocation()->x - - getOutOfFlowMgr()->containingBlockAllocation.x; } - inline int getNewYCB () { return getWidget()->getAllocation()->y - - getOutOfFlowMgr()->containingBlockAllocation.y; } - inline int getNewWidth () { return getWidget()->getAllocation()->width; } - inline int getNewHeight () { return getWidget()->getAllocation()->ascent + - getWidget()->getAllocation()->descent; } - void updateAllocation (); - - inline int *getIndexRef (SFVType type) { - return type == GB ? &indexGBList : &indexCBList; } - inline int getIndex (SFVType type) { return *(getIndexRef (type)); } - inline void setIndex (SFVType type, int value) { - *(getIndexRef (type)) = value; } - - void intoStringBuffer(lout::misc::StringBuffer *sb); - - bool covers (Textblock *textblock, int y, int h); - }; +public: + OutOfFlowMgr (); + virtual ~OutOfFlowMgr (); + + virtual void sizeAllocateStart (OOFAwareWidget *caller, + core::Allocation *allocation) = 0; + virtual void sizeAllocateEnd (OOFAwareWidget *caller) = 0; + virtual void containerSizeChangedForChildren () = 0; + virtual void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) = 0; + + virtual void markSizeChange (int ref) = 0; + virtual void markExtremesChange (int ref) = 0; + virtual core::Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) = 0; + + virtual void addWidgetInFlow (OOFAwareWidget *widget, + OOFAwareWidget *parent, int externalIndex) = 0; + virtual int addWidgetOOF (core::Widget *widget, OOFAwareWidget *generator, + int externalIndex) = 0; + virtual void calcWidgetRefSize (core::Widget *widget, + core::Requisition *size) = 0; + virtual void moveExternalIndices (OOFAwareWidget *generator, + int oldStartIndex, int diff) = 0; + + /** + * \brief Called before tellPosition2, see there for more. + */ + virtual void tellPosition1 (core::Widget *widget, int x, int y) = 0; /** - * This list is kept sorted. + * \brief Called after tellPosition1. * - * To prevent accessing methods of the base class in an - * uncontrolled way, the inheritance is private, not public; this - * means that all methods must be delegated (see iterator(), size() - * etc. below.) - * - * TODO Update comment: still sorted, but ... - * - * More: add() and change() may check order again. + * An implementation should only implement either tellPosition1 or + * tellPosition2. Coordinates are relative to the *container*. */ - class SortedFloatsVector: private lout::container::typed::Vector<Float> - { - public: - SFVType type; - - private: - OutOfFlowMgr *oofm; - Side side; - - public: - inline SortedFloatsVector (OutOfFlowMgr *oofm, Side side, SFVType type) : - lout::container::typed::Vector<Float> (1, false) - { this->oofm = oofm; this->side = side; this->type = type; } - - int findFloatIndex (Textblock *lastGB, int lastExtIndex); - int find (Textblock *textblock, int y, int start, int end); - int findFirst (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex, int *lastReturn); - int findLastBeforeSideSpanningIndex (int sideSpanningIndex); - void put (Float *vloat); - - inline lout::container::typed::Iterator<Float> iterator() - { return lout::container::typed::Vector<Float>::iterator (); } - inline int size () - { return lout::container::typed::Vector<Float>::size (); } - inline Float *get (int pos) - { return lout::container::typed::Vector<Float>::get (pos); } - inline void clear () - { lout::container::typed::Vector<Float>::clear (); } - }; - - class TBInfo: public WidgetInfo - { - public: - int lineBreakWidth; - int index; // position within "tbInfos" - - TBInfo *parent; - int parentExtIndex; - - // These two values are set by sizeAllocateStart(), and they are - // accessable also within sizeAllocateEnd() for the same - // textblock, for which allocation and WAS_ALLOCATED is set - // *after* sizeAllocateEnd(). See the two functions - // wasAllocated(Widget*) and getAllocation(Widget*) (further - // down) for usage. - bool wasAllocated; - core::Allocation allocation; - int clearPosition; - - // These two lists store all floats generated by this textblock, - // as long as this textblock is not allocates. - SortedFloatsVector *leftFloatsGB, *rightFloatsGB; - - TBInfo (OutOfFlowMgr *oofm, Textblock *textblock, - TBInfo *parent, int parentExtIndex); - ~TBInfo (); - - inline bool isNowAllocated () { - return getOutOfFlowMgr()->wasAllocated (getTextblock ()); } - inline int getNewXCB () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->x - - getOutOfFlowMgr()->containingBlockAllocation.x; } - inline int getNewYCB () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->y - - getOutOfFlowMgr()->containingBlockAllocation.y; } - inline int getNewWidth () { - return getOutOfFlowMgr()->getAllocation (getTextblock ())->width; } - inline int getNewHeight () { - core::Allocation *allocation = - getOutOfFlowMgr()->getAllocation (getTextblock ()); - return allocation->ascent + allocation->descent; } - void updateAllocation (); - - inline Textblock *getTextblock () { return (Textblock*)getWidget (); } - }; - - // These two lists store all floats, in the order in which they are - // defined. Only used for iterators. - lout::container::typed::Vector<Float> *leftFloatsAll, *rightFloatsAll; - - // These two lists store all floats whose generators are already - // allocated. - SortedFloatsVector *leftFloatsCB, *rightFloatsCB; - - // These two attributes are used in the size allocation process; - // see sizeAllocateStart and sizeAllocateEnd. - int lastAllocatedLeftFloat, lastAllocatedRightFloat; - - lout::container::typed::HashTable<lout::object::TypedPointer - <dw::core::Widget>, Float> *floatsByWidget; - - lout::container::typed::Vector<TBInfo> *tbInfos; - lout::container::typed::HashTable<lout::object::TypedPointer <Textblock>, - TBInfo> *tbInfosByTextblock; - - int lastLeftTBIndex, lastRightTBIndex, leftFloatsMark, rightFloatsMark; + virtual void tellPosition2 (core::Widget *widget, int x, int y) = 0; + + virtual void getSize (core::Requisition *containerReq, int *oofWidth, + int *oofHeight) = 0; + virtual bool containerMustAdjustExtraSpace ()= 0; + virtual void getExtremes (core::Extremes *containerExtr, int *oofMinWidth, + int *oofMaxWidth) = 0; /** - * Variant of Widget::wasAllocated(), which can also be used within - * OOFM::sizeAllocateEnd(). + * Get the left border for the vertical position of *y*, for a height + * of *h", based on floats; relative to the *container*. + * + * 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. */ - inline bool wasAllocated (Textblock *textblock) { - return getTextblock(textblock)->wasAllocated; - } + virtual int getLeftBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; /** - * Variant of Widget::getAllocation(), which can also be used - * within OOFM::sizeAllocateEnd(). + * Get the right border for the vertical position of *y*, for a height + * of *h*, based on floats; relative to the *container*. + * + * See also getLeftBorder(). */ - inline core::Allocation *getAllocation (Textblock *textblock) { - return &(getTextblock(textblock)->allocation); - } + virtual int getRightBorder (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; - void moveExternalIndices (SortedFloatsVector *list, int oldStartIndex, - int diff); - Float *findFloatByWidget (core::Widget *widget); - - void moveFromGBToCB (Side side); - void sizeAllocateFloats (Side side, int newLastAllocatedFloat); - int getGBWidthForAllocation (Float *vloat); - int calcFloatX (Float *vloat, Side side, int gbX, int gbWidth); - - bool hasRelationChanged (TBInfo *tbInfo,int *minFloatPos, - core::Widget **minFloat); - bool hasRelationChanged (TBInfo *tbInfo, Side side, int *minFloatPos, - core::Widget **minFloat); - bool 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); - - void checkAllocatedFloatCollisions (Side side); - - bool doFloatsExceedCB (Side side); - bool haveExtremesChanged (Side side); - - void drawFloats (SortedFloatsVector *list, core::View *view, - core::Rectangle *area); - core::Widget *getFloatWidgetAtPoint (SortedFloatsVector *list, int x, int y, - int level); - - bool collidesV (Float *vloat, Float *other, SFVType type, int *yReal, - bool useAllocation); - bool collidesH (Float *vloat, Float *other, SFVType type); - - void getFloatsListsAndSide (Float *vloat, SortedFloatsVector **listSame, - SortedFloatsVector **listOpp, Side *side); - - void getFloatsSize (core::Requisition *cbReq, Side side, int *width, - int *height); - void getFloatsExtremes (core::Extremes *cbExtr, Side side, int *minWidth, - int *maxWidth); - - TBInfo *getTextblock (Textblock *textblock); - int getBorder (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - SortedFloatsVector *getFloatsListForTextblock (Textblock *textblock, - Side side); - bool hasFloat (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int getFloatHeight (Textblock *textblock, Side side, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int calcClearPosition (Textblock *tb, Side side); - int calcClearPosition (Textblock *tb); - - void ensureFloatSize (Float *vloat); - - void tellFloatPosition (core::Widget *widget, int yReq); - - static inline bool isStyleFloat (core::style::Style *style) - { return style->vloat != core::style::FLOAT_NONE; } - static inline bool isWidgetFloat (core::Widget *widget) - { return isStyleFloat (widget->getStyle()); } - - /* - * Format for parent ref (see also below for isRefOutOfFlow, - * createRefNormalFlow, and getLineNoFromRef. - * - * Widget in flow: - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | line number | 0 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * - * So, anything with the least signifant bit set to 1 is out of flow. - * - * Floats: - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | left float index | 0 | 0 | 1 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ - * | right float index | 1 | 0 | 1 | - * +---+ - - - +---+---+- - - - - -+---+---+---+---+ + /** + * Return whether there is a float on the left side. *y* is + * relative to the *container*. * - * Absolutely positioned blocks: solved differently in the - * "dillo_grows" repository. + * See also getLeftBorder(). */ + virtual bool hasFloatLeft (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; - inline static bool isRefFloat (int ref) - { return ref != -1 && (ref & 3) == 1; } - inline static bool isRefLeftFloat (int ref) - { return ref != -1 && (ref & 7) == 1; } - inline static bool isRefRightFloat (int ref) - { return ref != -1 && (ref & 7) == 5; } - - inline static int createRefLeftFloat (int index) - { return (index << 3) | 1; } - inline static int createRefRightFloat (int index) - { return (index << 3) | 5; } - - inline static int getFloatIndexFromRef (int ref) - { return ref == -1 ? ref : (ref >> 3); } - -public: - OutOfFlowMgr (Textblock *containingBlock); - ~OutOfFlowMgr (); - - void sizeAllocateStart (Textblock *caller, core::Allocation *allocation); - void sizeAllocateEnd (Textblock *caller); - void containerSizeChangedForChildren (); - void draw (core::View *view, core::Rectangle *area); - - void markSizeChange (int ref); - void markExtremesChange (int ref); - core::Widget *getWidgetAtPoint (int x, int y, int level); - - static bool isStyleOutOfFlow (core::style::Style *style) - { return isStyleFloat (style); } - static inline bool isWidgetOutOfFlow (core::Widget *widget) - { return isStyleOutOfFlow (widget->getStyle()); } - - void addWidgetInFlow (Textblock *textblock, Textblock *parentBlock, - int externalIndex); - void addWidgetOOF (core::Widget *widget, Textblock *generatingBlock, - int externalIndex); - void moveExternalIndices (Textblock *generatingBlock, int oldStartIndex, - int diff); - - void tellPosition (core::Widget *widget, int yReq); - - void getSize (core::Requisition *cbReq, int *oofWidth, int *oofHeight); - void getExtremes (core::Extremes *cbExtr, - int *oofMinWidth, int *oofMaxWidth); - - int getLeftBorder (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - int getRightBorder (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - - bool hasFloatLeft (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - bool hasFloatRight (Textblock *textblock, int y, int h, Textblock *lastGB, - int lastExtIndex); - - int getLeftFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex); - int getRightFloatHeight (Textblock *textblock, int y, int h, - Textblock *lastGB, int lastExtIndex); - - int getClearPosition (Textblock *tb); + /** + * Return whether there is a float on the right side. *y* is + * relative to the *container*. + * + * See also hasFloatLeft(), getLeftBorder(); + */ + virtual bool hasFloatRight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) = 0; - inline static bool isRefOutOfFlow (int ref) - { return ref != -1 && (ref & 1) != 0; } - inline static int createRefNormalFlow (int lineNo) { return lineNo << 1; } - inline static int getLineNoFromRef (int ref) - { return ref == -1 ? ref : (ref >> 1); } + /** + * Assuming there is a float on the left side, return the rest + * height of it. *y* is relative to the *container*. + * + * See also getLeftBorder(). + */ + virtual int getLeftFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) + = 0; + /** + * Assuming there is a float on the right side, return the rest + * height of it. *y* is relative to the *container*. + * + * See also getLeftFloatHeight(), getLeftBorder(). + */ + virtual int getRightFloatHeight (OOFAwareWidget *widget, int y, int h, + OOFAwareWidget *lastGen, int lastExtIndex) + = 0; + + virtual bool affectsLeftBorder (core::Widget *widget) = 0; + virtual bool affectsRightBorder (core::Widget *widget) = 0; + virtual bool mayAffectBordersAtAll () = 0; + + virtual int getClearPosition (OOFAwareWidget *widget) = 0; + + virtual bool dealingWithSizeOfChild (core::Widget *child) = 0; + virtual int getAvailWidthOfChild (core::Widget *child, bool forceValue) = 0; + virtual int getAvailHeightOfChild (core::Widget *child, bool forceValue) = 0; + // for iterators - inline int getNumWidgets () - { return leftFloatsAll->size() + rightFloatsAll->size(); } - - inline core::Widget *getWidget (int i) { - if (i < leftFloatsAll->size()) - return leftFloatsAll->get(i)->getWidget (); - else - return rightFloatsAll->get(i - leftFloatsAll->size())->getWidget (); - } - - inline bool affectsLeftBorder (core::Widget *widget) { - return widget->getStyle()->vloat == core::style::FLOAT_LEFT; } - inline bool affectsRightBorder (core::Widget *widget) { - return widget->getStyle()->vloat == core::style::FLOAT_RIGHT; } + virtual int getNumWidgets () = 0; + virtual core::Widget *getWidget (int i) = 0; }; +} // namespace oof + } // namespace dw #endif // __DW_OUTOFFLOWMGR_HH__ diff --git a/dw/regardingborder.hh b/dw/regardingborder.hh index 4c8951ad..1cb6c25f 100644 --- a/dw/regardingborder.hh +++ b/dw/regardingborder.hh @@ -1,7 +1,7 @@ #ifndef __DW_REGARDINGBORDER_HH__ #define __DW_REGARDINGBORDER_HH__ -#include "core.hh" +#include "oofawarewidget.hh" namespace dw { @@ -9,11 +9,8 @@ namespace dw { * \brief Base class (rather a tag interface) for those widgets * regarding borders defined by floats, and so allocated on the * full width. - * - * Will, when integrated to the "dillo_grows" repository, become a sub - * class of OOFAwareWidget. */ -class RegardingBorder: public core::Widget +class RegardingBorder: public oof::OOFAwareWidget { public: static int CLASS_ID; diff --git a/dw/ruler.cc b/dw/ruler.cc index ccf58baa..ac5644bb 100644 --- a/dw/ruler.cc +++ b/dw/ruler.cc @@ -39,17 +39,17 @@ Ruler::~Ruler () DBG_OBJ_DELETE (); } -void Ruler::sizeRequestImpl (core::Requisition *requisition) +void Ruler::sizeRequestImpl (core::Requisition *requisition, bool posDefined, + int x, int y) { - requisition->width = - lout::misc::max (getAvailWidth (true), getStyle()->boxDiffWidth ()); - requisition->ascent = getStyle()->boxOffsetY (); - requisition->descent = getStyle()->boxRestHeight (); + requisition->width = lout::misc::max (getAvailWidth (true), boxDiffWidth ()); + requisition->ascent = boxOffsetY (); + requisition->descent = boxRestHeight (); } void Ruler::getExtremesImpl (core::Extremes *extremes) { - extremes->minWidth = extremes->maxWidth = getStyle()->boxDiffWidth (); + extremes->minWidth = extremes->maxWidth = boxDiffWidth (); extremes->minWidthIntrinsic = extremes->minWidth; extremes->maxWidthIntrinsic = extremes->maxWidth; correctExtremes (extremes, false); @@ -74,7 +74,8 @@ bool Ruler::usesAvailWidth () return true; } -void Ruler::draw (core::View *view, core::Rectangle *area) +void Ruler::draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context) { drawWidgetBox (view, area, false); } diff --git a/dw/ruler.hh b/dw/ruler.hh index cd4f63f4..6b28ce8e 100644 --- a/dw/ruler.hh +++ b/dw/ruler.hh @@ -14,18 +14,19 @@ namespace dw { * Ruler implements RegardingBorder; this way, it is simpler to fit * the ruler exactly within the space between floats. Currently, the * drawn area of the ruler is too large (but most of the superfluous - * part is hidden by the floats); this problem will not solved but in - * "dillo_grows", where RegardingBorder is a sub class of - * OOFAwareWidget. + * part is hidden by the floats); this problem will soon solved here + * in the "dillo_grows" repository. */ class Ruler: public RegardingBorder { protected: - void sizeRequestImpl (core::Requisition *requisition); + void sizeRequestImpl (core::Requisition *requisition, bool posDefined, int x, + int y); void getExtremesImpl (core::Extremes *extremes); void containerSizeChangedForChildren (); bool usesAvailWidth (); - void draw (core::View *view, core::Rectangle *area); + void draw (core::View *view, core::Rectangle *area, + core::DrawingContext *context); public: static int CLASS_ID; diff --git a/dw/selection.cc b/dw/selection.cc index b67f4a6d..a69eb82a 100644 --- a/dw/selection.cc +++ b/dw/selection.cc @@ -93,63 +93,79 @@ void SelectionState::resetLink () bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo, EventButton *event) { + DBG_IF_RTFL { + misc::StringBuffer sb; + it->intoStringBuffer (&sb); + DBG_OBJ_ENTER ("events", 0, "buttonPress", "[%s], %d, %d, ...", + sb.getChars (), charPos, linkNo); + } + Widget *itWidget = it->getWidget (); bool ret = false; - if (!event) return ret; - - if (event->button == 3) { - // menu popup - layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); - ret = true; - } else if (linkNo != -1) { - // link handling - (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); - resetLink (); - linkState = LINK_PRESSED; - linkButton = event->button; - DeepIterator *newLink = new DeepIterator (it); - if (newLink->isEmpty ()) { - delete newLink; + if (event) { + if (event->button == 3) { + // menu popup + layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); + ret = true; + } else if (linkNo != -1) { + // link handling + (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event); resetLink (); - } else { - link = newLink; - // It may be that the user has pressed on something activatable - // (linkNo != -1), but there is no contents, e.g. with images - // without ALTernative text. - if (link) { - linkChar = correctCharPos (link, charPos); - linkNumber = linkNo; + linkState = LINK_PRESSED; + linkButton = event->button; + DeepIterator *newLink = new DeepIterator (it); + if (newLink->isEmpty ()) { + delete newLink; + resetLink (); + } else { + link = newLink; + // It may be that the user has pressed on something activatable + // (linkNo != -1), but there is no contents, e.g. with images + // without ALTernative text. + if (link) { + linkChar = correctCharPos (link, charPos); + linkNumber = linkNo; + } } - } - // We do not return the value of the signal method, - // but we do actually process this event. - ret = true; - } else if (event->button == 1) { - // normal selection handling - highlight (false, 0); - resetSelection (); - - selectionState = SELECTING; - DeepIterator *newFrom = new DeepIterator (it); - if (newFrom->isEmpty ()) { - delete newFrom; + // We do not return the value of the signal method, + // but we do actually process this event. + ret = true; + } else if (event->button == 1) { + // normal selection handling + highlight (false, 0); resetSelection (); - } else { - from = newFrom; - fromChar = correctCharPos (from, charPos); - to = from->cloneDeepIterator (); - toChar = correctCharPos (to, charPos); + + selectionState = SELECTING; + DeepIterator *newFrom = new DeepIterator (it); + if (newFrom->isEmpty ()) { + delete newFrom; + resetSelection (); + } else { + from = newFrom; + fromChar = correctCharPos (from, charPos); + to = from->cloneDeepIterator (); + toChar = correctCharPos (to, charPos); + } + ret = true; } - ret = true; } + DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); return ret; } bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, EventButton *event) { + DBG_IF_RTFL { + misc::StringBuffer sb; + it->intoStringBuffer (&sb); + DBG_OBJ_ENTER ("events", 0, "buttonRelease", "[%s], %d, %d, ...", + sb.getChars (), charPos, linkNo); + } + Widget *itWidget = it->getWidget (); bool ret = false; @@ -186,6 +202,8 @@ bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo, } } + DBG_OBJ_MSGF ("events", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); return ret; } diff --git a/dw/simpletablecell.cc b/dw/simpletablecell.cc index feec46c8..f39d94b5 100644 --- a/dw/simpletablecell.cc +++ b/dw/simpletablecell.cc @@ -57,7 +57,7 @@ bool SimpleTableCell::mustBeWidenedToAvailWidth () int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailWidthOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width = tablecell::correctAvailWidthOfChild @@ -70,7 +70,7 @@ int SimpleTableCell::getAvailWidthOfChild (Widget *child, bool forceValue) int SimpleTableCell::getAvailHeightOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/getAvailHeightOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::getAvailHeightOfChild", "%p, %s", child, forceValue ? "true" : "false"); int height = tablecell::correctAvailHeightOfChild @@ -87,7 +87,7 @@ void SimpleTableCell::correctRequisitionOfChild (Widget *child, int*, int*)) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctRequisitionOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); @@ -102,7 +102,7 @@ void SimpleTableCell::correctExtremesOfChild (Widget *child, core::Extremes *extremes, bool useAdjustmentWidth) { - DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell/correctExtremesOfChild", + DBG_OBJ_ENTER ("resize", 0, "SimpleTableCell::correctExtremesOfChild", "%p, %d (%d) / %d (%d)", child, extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); @@ -126,4 +126,9 @@ int SimpleTableCell::applyPerHeight (int containerHeight, return tablecell::applyPerHeight (this, containerHeight, perHeight); } +bool SimpleTableCell::adjustExtraSpaceWhenCorrectingRequisitionByOOF () +{ + return tablecell::adjustExtraSpaceWhenCorrectingRequisitionByOOF (); +} + } // namespace dw diff --git a/dw/simpletablecell.hh b/dw/simpletablecell.hh index 60715d90..90f24ae4 100644 --- a/dw/simpletablecell.hh +++ b/dw/simpletablecell.hh @@ -19,6 +19,8 @@ protected: bool getAdjustMinWidth (); + bool adjustExtraSpaceWhenCorrectingRequisitionByOOF (); + public: static int CLASS_ID; diff --git a/dw/stackingcontextmgr.cc b/dw/stackingcontextmgr.cc new file mode 100644 index 00000000..98cef164 --- /dev/null +++ b/dw/stackingcontextmgr.cc @@ -0,0 +1,195 @@ +/* + * Dillo Widget + * + * Copyright 2014 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "core.hh" +#include "../lout/debug.hh" + +using namespace lout::misc; +using namespace lout::container::typed; + +namespace dw { + +namespace core { + +StackingContextMgr::StackingContextMgr (Widget *widget) +{ + DBG_OBJ_CREATE ("dw::core::StackingContextMgr"); + + this->widget = widget; + + childSCWidgets = new Vector<Widget> (1, false); + DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size()); + + numZIndices = 0; + zIndices = NULL; + DBG_OBJ_SET_NUM ("numZIndices", numZIndices); +} + +StackingContextMgr::~StackingContextMgr () +{ + delete childSCWidgets; + if (zIndices) + free (zIndices); + DBG_OBJ_DELETE (); +} + +void StackingContextMgr::addChildSCWidget (Widget *widget) +{ + DBG_OBJ_ENTER ("common.scm", 0, "addChildSCWidget", "%p [z-index = %d]", + widget, widget->getStyle()->zIndex); + + int pos = findZIndex (widget->getStyle()->zIndex, true); + DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos); + if (pos == -1) { + pos = findZIndex (widget->getStyle()->zIndex, false); + DBG_OBJ_MSGF ("common.scm", 1, "pos = %d", pos); + + numZIndices++; + DBG_OBJ_SET_NUM ("numZIndices", numZIndices); + zIndices = (int*)(zIndices ? + realloc (zIndices, numZIndices * sizeof (int)) : + malloc (numZIndices * sizeof (int))); + + for (int i = numZIndices - 1; i >= pos + 1; i--) { + zIndices[i] = zIndices[i - 1]; + DBG_OBJ_ARRSET_NUM ("zIndex", i, zIndices[i]); + } + + zIndices[pos] = widget->getStyle()->zIndex; + DBG_OBJ_ARRSET_NUM ("zIndex", pos, zIndices[pos]); + } + + childSCWidgets->put (widget); + DBG_OBJ_SET_NUM ("childSCWidgets.size", childSCWidgets->size()); + DBG_OBJ_ARRSET_PTR ("childSCWidgets", childSCWidgets->size() - 1, widget); + + DBG_OBJ_LEAVE (); +} + +int StackingContextMgr::findZIndex (int zIndex, bool mustExist) +{ + int result = -123; // Compiler happiness: GCC 4.7 does not handle this? + + if (numZIndices == 0) + result = mustExist ? -1 : 0; + else { + int low = 0, high = numZIndices - 1; + bool found = false; + + while (!found) { + int index = (low + high) / 2; + if (zIndex == zIndices[index]) { + found = true; + result = index; + } else { + if (low >= high) { + if (mustExist) { + found = true; + result = -1; + } else { + found = true; + result = zIndex > zIndices[index] ? index + 1 : index; + } + } + + if (zIndex < zIndices[index]) + high = index - 1; + else + low = index + 1; + } + } + } + + return result; +} + +void StackingContextMgr::draw (View *view, Rectangle *area, int startZIndex, + int endZIndex, DrawingContext *context) +{ + DBG_OBJ_ENTER ("draw", 0, "draw", "[%d, %d, %d * %d], %d, %d", + area->x, area->y, area->width, area->height, startZIndex, + endZIndex); + + for (int zIndexIndex = 0; zIndexIndex < numZIndices; zIndexIndex++) { + // Wrong region of z-indices (top or bottom) is simply ignored + // (as well as non-defined zIndices). + if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex && + zIndices[zIndexIndex] <= endZIndex) { + DBG_OBJ_MSGF ("draw", 1, "drawing zIndex = %d", zIndices[zIndexIndex]); + DBG_OBJ_MSG_START (); + + for (int i = 0; i < childSCWidgets->size (); i++) { + Widget *child = childSCWidgets->get (i); + DBG_OBJ_MSGF ("draw", 2, "widget %p has zIndex = %d", + child, child->getStyle()->zIndex); + + Rectangle childArea; + if (child->getStyle()->zIndex == zIndices[zIndexIndex] && + child->intersects (widget, area, &childArea)) + child->draw (view, &childArea, context); + } + + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_LEAVE (); +} + +Widget *StackingContextMgr::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext + *context, + int startZIndex, int endZIndex) +{ + DBG_OBJ_ENTER ("events", 0, "getWidgetAtPoint", "%d, %d", x, y); + + Widget *widgetAtPoint = NULL; + + for (int zIndexIndex = numZIndices - 1; + widgetAtPoint == NULL && zIndexIndex >= 0; zIndexIndex--) { + // Wrong region of z-indices (top or bottom) is simply ignored + // (as well as non-defined zIndices). + if (zIndices != NULL && zIndices[zIndexIndex] >= startZIndex && + zIndices[zIndexIndex] <= endZIndex) { + DBG_OBJ_MSGF ("events", 1, "searching zIndex = %d", + zIndices[zIndexIndex]); + DBG_OBJ_MSG_START (); + + for (int i = childSCWidgets->size () - 1; + widgetAtPoint == NULL && i >= 0; i--) { + Widget *child = childSCWidgets->get (i); + DBG_OBJ_MSGF ("events", 2, "widget %p has zIndex = %d", + child, child->getStyle()->zIndex); + if (child->getStyle()->zIndex == zIndices[zIndexIndex]) + widgetAtPoint = child->getWidgetAtPoint (x, y, context); + } + + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("events", 0, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; +} + +} // namespace core + +} // namespace dw diff --git a/dw/stackingcontextmgr.hh b/dw/stackingcontextmgr.hh new file mode 100644 index 00000000..3a877031 --- /dev/null +++ b/dw/stackingcontextmgr.hh @@ -0,0 +1,75 @@ +#ifndef __DW_STACKINGCONTEXTMGR_HH__ +#define __DW_STACKINGCONTEXTMGR_HH__ + +#ifndef __INCLUDED_FROM_DW_CORE_HH__ +# error Do not include this file directly, use "core.hh" instead. +#endif + +#include "../lout/container.hh" + +#include <limits.h> + +namespace dw { + +namespace core { + +/** + * \brief See \ref dw-stacking-context. + */ +class StackingContextMgr +{ +private: + Widget *widget; + lout::container::typed::Vector<Widget> *childSCWidgets; + int *zIndices, numZIndices; + + int findZIndex (int zIndex, bool mustExist); + void draw (View *view, Rectangle *area, int startZIndex, int endZIndex, + DrawingContext *context); + Widget *getWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext *context, + int startZIndex, int endZIndex); + +public: + StackingContextMgr (Widget *widget); + ~StackingContextMgr (); + + inline static bool isEstablishingStackingContext (Widget *widget) { + return widget->getStyle()->position != style::POSITION_STATIC && + widget->getStyle()->zIndex != style::Z_INDEX_AUTO; + } + + inline static bool handledByStackingContextMgr (Widget *widget) { + // Each widget establishing a stacking context is child of another + // stacking context, so drawn by StackingContextMgr::drawTop or + // StackingContextMgr::drawBottom etc. + return widget->getParent () != NULL + && isEstablishingStackingContext (widget); + } + + void addChildSCWidget (Widget *widget); + + inline int getNumZIndices () { return numZIndices; } + inline int getNumChildSCWidgets () { return childSCWidgets->size (); } + + inline void drawBottom (View *view, Rectangle *area, DrawingContext *context) + { draw (view, area, INT_MIN, -1, context); } + void drawTop (View *view, Rectangle *area, DrawingContext *context) + { draw (view, area, 0, INT_MAX, context); } + + inline Widget *getTopWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) + { return getWidgetAtPoint (x, y, context, 0, INT_MAX); } + + inline Widget *getBottomWidgetAtPoint (int x, int y, + core::GettingWidgetAtPointContext + *context) + { return getWidgetAtPoint (x, y, context, INT_MIN, -1); } +}; + +} // namespace core + +} // namespace dw + +#endif // __DW_STACKINGCONTEXTMGR_HH__ diff --git a/dw/style.cc b/dw/style.cc index 54d9af4b..32a33a9e 100644 --- a/dw/style.cc +++ b/dw/style.cc @@ -93,6 +93,7 @@ void StyleAttrs::initValues () display = DISPLAY_INLINE; whiteSpace = WHITE_SPACE_NORMAL; cursor = CURSOR_DEFAULT; + zIndex = Z_INDEX_AUTO; } /** @@ -203,6 +204,7 @@ bool StyleAttrs::equals (object::Object *other) { listStylePosition == otherAttrs->listStylePosition && listStyleType == otherAttrs->listStyleType && cursor == otherAttrs->cursor && + zIndex == otherAttrs->zIndex && x_link == otherAttrs->x_link && x_lang[0] == otherAttrs->x_lang[0] && x_lang[1] == otherAttrs->x_lang[1] && @@ -260,6 +262,7 @@ int StyleAttrs::hashValue () { listStylePosition + listStyleType + cursor + + zIndex + x_link + x_lang[0] + x_lang[1] + x_img + @@ -381,6 +384,7 @@ void Style::copyAttrs (StyleAttrs *attrs) listStylePosition = attrs->listStylePosition; listStyleType = attrs->listStyleType; cursor = attrs->cursor; + zIndex = attrs->zIndex; x_link = attrs->x_link; x_lang[0] = attrs->x_lang[0]; x_lang[1] = attrs->x_lang[1]; diff --git a/dw/style.hh b/dw/style.hh index 230baa24..12ca1664 100644 --- a/dw/style.hh +++ b/dw/style.hh @@ -7,6 +7,8 @@ # error Do not include this file directly, use "core.hh" instead. #endif +#include <limits.h> + #include "../lout/signal.hh" #include "../lout/debug.hh" @@ -374,6 +376,17 @@ enum ClearType { CLEAR_NONE }; +enum { + /** + * \brief 'z-index' is stored as int; use this for the value 'auto'. + * + * Only some random value, which has to be checked explicitly; do + * not compare this (less or greater) to integer values of + * 'z-index'. + */ + Z_INDEX_AUTO = INT_MAX +}; + /** * \brief Type for representing all lengths within dw::core::style. * @@ -553,6 +566,7 @@ public: ListStylePosition listStylePosition; ListStyleType listStyleType; Cursor cursor; + int zIndex; int x_link; int x_img; diff --git a/dw/table.cc b/dw/table.cc index d55fd72f..8b8fe853 100644 --- a/dw/table.cc +++ b/dw/table.cc @@ -103,7 +103,8 @@ Table::~Table() DBG_OBJ_DELETE (); } -void Table::sizeRequestImpl (core::Requisition *requisition) +void Table::sizeRequestImpl (core::Requisition *requisition, bool posDefined, + int x, int y) { DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); @@ -112,18 +113,20 @@ void Table::sizeRequestImpl (core::Requisition *requisition) /** * \bug Baselines are not regarded here. */ - requisition->width = getStyle()->boxDiffWidth () - + (numCols + 1) * getStyle()->hBorderSpacing; + requisition->width = + boxDiffWidth () + (numCols + 1) * getStyle()->hBorderSpacing; for (int col = 0; col < numCols; col++) requisition->width += colWidths->get (col); requisition->ascent = - getStyle()->boxDiffHeight () + cumHeight->get (numRows) - + getStyle()->vBorderSpacing; + boxDiffHeight () + cumHeight->get (numRows) + getStyle()->vBorderSpacing; requisition->descent = 0; correctRequisition (requisition, core::splitHeightPreserveDescent); + // For the order, see similar reasoning for dw::Textblock. + correctRequisitionByOOF (requisition, core::splitHeightPreserveDescent); + DBG_OBJ_LEAVE (); } @@ -154,6 +157,9 @@ void Table::getExtremesImpl (core::Extremes *extremes) correctExtremes (extremes, true); + // For the order, see similar reasoning for dw::Textblock. + correctExtremesByOOF (extremes); + DBG_OBJ_LEAVE (); } @@ -163,16 +169,16 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) allocation->x, allocation->y, allocation->width, allocation->ascent, allocation->descent); + sizeAllocateStart (allocation); + calcCellSizes (true); /** * \bug Baselines are not regarded here. */ - int offy = - allocation->y + getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; - int x = - allocation->x + getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; + int offy = allocation->y + boxOffsetY () + getStyle()->vBorderSpacing; + int x = allocation->x + boxOffsetX () + getStyle()->hBorderSpacing; for (int col = 0; col < numCols; col++) { for (int row = 0; row < numRows; row++) { @@ -203,6 +209,8 @@ void Table::sizeAllocateImpl (core::Allocation *allocation) x += colWidths->get (col) + getStyle()->hBorderSpacing; } + sizeAllocateEnd (); + DBG_OBJ_LEAVE (); } @@ -220,34 +228,40 @@ int Table::getAvailWidthOfChild (Widget *child, bool forceValue) child, forceValue ? "true" : "false"); int width; + oof::OutOfFlowMgr *oofm; - // We do not calculate the column widths at this point, because - // this tends to be rather inefficient for tables with many - // cells: - // - // For each of the n cells, some text is added (say, only one word - // per cell). Textblock::addText will eventually (via addText0 - // etc.) call this method, Table::getAvailWidthOfChild. If - // calcCellSizes() is called here, this will call - // forceCalcCellSizes(), since the last call, sizes have to be - // re-calculated (because cells have been added). This will - // calculate the extremes for each existing cell, so - // Widget::getExtremes is called n * (n + 1) / 2 times. Even if the - // extremes are cached (so that getExtremesImpl does not have to be - // called in each case), this would make rendering tables with more - // than a few hundred cells unacceptably slow. - // - // Instead, column widths are calculated in Table::sizeRequestImpl. - // - // An alternative would be incremental resizing for tables; this - // approach resembles the behaviour before GROWS. - - // TODO Does it still make sence to return -1 when forceValue is - // set? - if (forceValue) - width = calcAvailWidthForDescendant (child); - else - width = -1; + if (isWidgetOOF(child) && (oofm = getWidgetOutOfFlowMgr(child)) && + oofm->dealingWithSizeOfChild (child)) + width = oofm->getAvailWidthOfChild (child, forceValue); + else { + // We do not calculate the column widths at this point, because + // this tends to be rather inefficient for tables with many + // cells: + // + // For each of the n cells, some text is added (say, only one word + // per cell). Textblock::addText will eventually (via addText0 + // etc.) call this method, Table::getAvailWidthOfChild. If + // calcCellSizes() is called here, this will call + // forceCalcCellSizes(), since the last call, sizes have to be + // re-calculated (because cells have been added). This will + // calculate the extremes for each existing cell, so + // Widget::getExtremes is called n * (n + 1) / 2 times. Even if the + // extremes are cached (so that getExtremesImpl does not have to be + // called in each case), this would make rendering tables with more + // than a few hundred cells unacceptably slow. + // + // Instead, column widths are calculated in Table::sizeRequestImpl. + // + // An alternative would be incremental resizing for tables; this + // approach resembles the behaviour before GROWS. + + // TODO Does it still make sence to return -1 when forceValue is + // set? + if (forceValue) + width = calcAvailWidthForDescendant (child); + else + width = -1; + } DBG_OBJ_MSGF ("resize", 1, "=> %d", width); DBG_OBJ_LEAVE (); @@ -266,10 +280,17 @@ int Table::calcAvailWidthForDescendant (Widget *child) assert (actualChild != NULL); - // ActualChild->parentRef contains the position in the children - // array (see addCell()), so the column can be easily determined. - int col = actualChild->parentRef % numCols; - int colspanEff = children->get(actualChild->parentRef)->cell.colspanEff; + // ActualChild->parentRef contains (indirectly) the position in the + // children array (see addCell()), so the column can be easily + // determined. + int childNo = getParentRefInFlowSubRef (actualChild->parentRef); + int col = childNo % numCols; + DBG_OBJ_MSGF ("resize", 1, "actualChild = %p, " + "childNo = getParentRefInFlowSubRef (%d) = %d, " + "column = %d %% %d = %d", + actualChild, actualChild->parentRef, childNo, childNo, + numCols, col); + int colspanEff = children->get(childNo)->cell.colspanEff; DBG_OBJ_MSGF ("resize", 1, "calculated from column %d, colspanEff = %d", col, colspanEff); @@ -318,6 +339,8 @@ void Table::containerSizeChangedForChildren () } } + containerSizeChangedForChildrenOOF (); + DBG_OBJ_LEAVE (); } @@ -353,18 +376,21 @@ bool Table::isBlockLevel () return true; } -void Table::draw (core::View *view, core::Rectangle *area) +void Table::drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context) { - // Can be optimized, by iterating on the lines in area. - drawWidgetBox (view, area, false); + DBG_OBJ_ENTER ("draw", 0, "Table::drawLevel", "[%d, %d, %d * %d], %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); #if 0 + // This old code belongs perhaps to the background. Check when reactivated. int offx = getStyle()->boxOffsetX () + getStyle()->hBorderSpacing; int offy = getStyle()->boxOffsetY () + getStyle()->vBorderSpacing; int width = getContentWidth (); - + // This part seems unnecessary. It also segfaulted sometimes when - // cumHeight size was less than numRows. --jcid + // cumHeight size was less than numRows. --jcid for (int row = 0; row < numRows; row++) { if (rowStyle->get (row)) drawBox (view, rowStyle->get (row), area, @@ -375,14 +401,58 @@ void Table::draw (core::View *view, core::Rectangle *area) } #endif - for (int i = 0; i < children->size (); i++) { - if (childDefined (i)) { - Widget *child = children->get(i)->cell.widget; - core::Rectangle childArea; - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); + switch (level) { + case SL_IN_FLOW: + for (int i = 0; i < children->size (); i++) { + if (childDefined (i)) { + Widget *child = children->get(i)->cell.widget; + core::Rectangle childArea; + if (!core::StackingContextMgr::handledByStackingContextMgr (child) + && child->intersects (this, area, &childArea)) + child->draw (view, &childArea, context); + } + } + break; + + default: + OOFAwareWidget::drawLevel (view, area, level, context); + break; + } + + DBG_OBJ_LEAVE (); +} + +core::Widget *Table::getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "Table::getWidgetAtPointLevel", "%d, %d, %s", + x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_IN_FLOW: + for (int i = children->size () - 1; widgetAtPoint == NULL && i >= 0; + i--) { + if (childDefined (i)) { + Widget *child = children->get(i)->cell.widget; + if (!core::StackingContextMgr::handledByStackingContextMgr (child)) + widgetAtPoint = child->getWidgetAtPoint (x, y, context); + } } + break; + + default: + widgetAtPoint = + OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context); + break; } + + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + + return widgetAtPoint; } void Table::removeChild (Widget *child) @@ -475,14 +545,14 @@ void Table::addCell (Widget *widget, int colspan, int rowspan) child->cell.rowspan = rowspan; children->set (curRow * numCols + curCol, child); - // The position in the children array is assigned to parentRef, - // although incremental resizing is not implemented. Useful, e. g., - // in calcAvailWidthForDescendant(). See also reallocChildren(). - widget->parentRef = curRow * numCols + curCol; + // The position in the children array is (indirectly) assigned to parentRef, + // although incremental resizing is not implemented. Useful, e. g., in + // calcAvailWidthForDescendant(). See also reallocChildren(). + widget->parentRef = makeParentRefInFlow (curRow * numCols + curCol); DBG_OBJ_SET_NUM_O (widget, "parentRef", widget->parentRef); curCol += colspanEff; - + widget->setParent (this); if (rowStyle->get (curRow)) widget->setBgColor (rowStyle->get(curRow)->backgroundColor); @@ -722,7 +792,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows) int n = row * newNumCols + col; Child *child = children->get (n); if (child != NULL && child->type == Child::CELL) { - child->cell.widget->parentRef = n; + child->cell.widget->parentRef = makeParentRefInFlow (n); DBG_OBJ_SET_NUM_O (child->cell.widget, "parentRef", child->cell.widget->parentRef); } @@ -768,6 +838,9 @@ void Table::calcCellSizes (bool calcHeights) void Table::forceCalcCellSizes (bool calcHeights) { + DBG_OBJ_ENTER ("resize", 0, "forceCalcCellSizes", "%s", + calcHeights ? "true" : "false"); + // Since Table::getAvailWidthOfChild does not calculate the column // widths, and so initially a random value (100) is returned, a // correction is necessary. The old values are temporary preserved @@ -822,11 +895,14 @@ void Table::forceCalcCellSizes (bool calcHeights) } } } + + DBG_OBJ_LEAVE (); } void Table::actuallyCalcCellSizes (bool calcHeights) { - DBG_OBJ_ENTER0 ("resize", 0, "forceCalcCellSizes"); + DBG_OBJ_ENTER ("resize", 0, "actuallyCalcCellSizes", "%s", + calcHeights ? "true" : "false"); int childHeight; core::Extremes extremes; diff --git a/dw/table.hh b/dw/table.hh index 9b868642..b4f6a7bc 100644 --- a/dw/table.hh +++ b/dw/table.hh @@ -1,7 +1,7 @@ #ifndef __DW_TABLE_HH__ #define __DW_TABLE_HH__ -#include "core.hh" +#include "oofawarewidget.hh" #include "alignedtablecell.hh" #include "../lout/misc.hh" @@ -322,7 +322,7 @@ namespace dw { * Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo. * Look at the HTML parser for more details. */ -class Table: public core::Widget +class Table: public oof::OOFAwareWidget { private: struct Child @@ -342,20 +342,17 @@ private: }; }; - class TableIterator: public core::Iterator + class TableIterator: public OOFAwareWidgetIterator { - private: - int index; + protected: + int numContentsInFlow (); + void getContentInFlow (int index, core::Content *content); public: TableIterator (Table *table, core::Content::Type mask, bool atEnd); - TableIterator (Table *table, core::Content::Type mask, int index); lout::object::Object *clone(); - int compareTo(lout::object::Comparable *other); - bool next (); - bool prev (); void highlight (int start, int end, core::HighlightLayer layer); void unhighlight (int direction, core::HighlightLayer layer); void getAllocation (int start, int end, core::Allocation *allocation); @@ -464,7 +461,8 @@ private: } protected: - void sizeRequestImpl (core::Requisition *requisition); + void sizeRequestImpl (core::Requisition *requisition, bool posDefined, int x, + int y); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); void resizeDrawImpl (); @@ -479,7 +477,11 @@ protected: bool isBlockLevel (); - void draw (core::View *view, core::Rectangle *area); + void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + + Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext *context); //bool buttonPressImpl (core::EventButton *event); //bool buttonReleaseImpl (core::EventButton *event); diff --git a/dw/table_iterator.cc b/dw/table_iterator.cc index 4da0ef4f..9ba61619 100644 --- a/dw/table_iterator.cc +++ b/dw/table_iterator.cc @@ -28,107 +28,62 @@ namespace dw { Table::TableIterator::TableIterator (Table *table, core::Content::Type mask, bool atEnd): - core::Iterator (table, mask, atEnd) + OOFAwareWidgetIterator (table, mask, atEnd, table->children->size ()) { - index = atEnd ? table->children->size () : -1; - content.type = atEnd ? core::Content::END : core::Content::START; -} - -Table::TableIterator::TableIterator (Table *table, - core::Content::Type mask, int index): - core::Iterator (table, mask, false) -{ - this->index = index; - - if (index < 0) - content.type = core::Content::START; - else if (index >= table->children->size ()) - content.type = core::Content::END; - else { - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - } } object::Object *Table::TableIterator::clone() { - return new TableIterator ((Table*)getWidget(), getMask(), index); -} - -int Table::TableIterator::compareTo(object::Comparable *other) -{ - return index - ((TableIterator*)other)->index; + TableIterator *tIt = + new TableIterator ((Table*)getWidget(), getMask(), false); + cloneValues (tIt); + return tIt; } -bool Table::TableIterator::next () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::END) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::END; - return false; - } - - do { - index++; - if (index >= table->children->size ()) { - content.type = core::Content::END; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} - -bool Table::TableIterator::prev () -{ - Table *table = (Table*)getWidget(); - - if (content.type == core::Content::START) - return false; - - // tables only contain widgets (in flow): - if ((getMask() & core::Content::WIDGET_IN_FLOW) == 0) { - content.type = core::Content::START; - return false; - } - - do { - index--; - if (index < 0) { - content.type = core::Content::START; - return false; - } - } while (table->children->get(index) == NULL || - table->children->get(index)->type != Child::CELL); - - content.type = core::Content::WIDGET_IN_FLOW; - content.widget = table->children->get(index)->cell.widget; - return true; -} void Table::TableIterator::highlight (int start, int end, core::HighlightLayer layer) { - /** todo Needs this an implementation? */ + if (inFlow ()) { + /** todo Needs this an implementation? */ + } else + highlightOOF (start, end, layer); } void Table::TableIterator::unhighlight (int direction, core::HighlightLayer layer) { + if (inFlow ()) { + // ??? + } else + unhighlightOOF (direction, layer); } void Table::TableIterator::getAllocation (int start, int end, core::Allocation *allocation) { - /** \bug Not implemented. */ + if (inFlow ()) { + /** \bug Not implemented. */ + } else + getAllocationOOF (start, end, allocation); +} + +int Table::TableIterator::numContentsInFlow () +{ + return ((Table*)getWidget())->children->size (); +} + +void Table::TableIterator::getContentInFlow (int index, + core::Content *content) +{ + Table *table = (Table*)getWidget(); + + if (table->children->get(index) != NULL && + table->children->get(index)->type == Child::CELL) { + content->type = core::Content::WIDGET_IN_FLOW; + content->widget = table->children->get(index)->cell.widget; + } else + content->type = core::Content::INVALID; } } // namespace dw diff --git a/dw/tablecell.cc b/dw/tablecell.cc index 3e143c96..a7b16776 100644 --- a/dw/tablecell.cc +++ b/dw/tablecell.cc @@ -45,7 +45,7 @@ bool isBlockLevel () int correctAvailWidthOfChild (core::Widget *widget, core::Widget *child, int width, bool forceValue) { - DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctAvailWidthOfChild", + DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctAvailWidthOfChild", "%p, %d, %s", child, width, forceValue ? "true" : "false"); // Make sure that this width does not exceed the width of the table @@ -80,7 +80,7 @@ void correctCorrectedRequisitionOfChild (core::Widget *widget, void (*splitHeightFun) (int, int*, int*)) { - DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell/correctRequisitionOfChild", + DBG_OBJ_ENTER_O ("resize", 0, widget, "tablecell::correctRequisitionOfChild", "%p, %d * (%d + %d), ...", child, requisition->width, requisition->ascent, requisition->descent); diff --git a/dw/tablecell.hh b/dw/tablecell.hh index 257f87c7..1300a944 100644 --- a/dw/tablecell.hh +++ b/dw/tablecell.hh @@ -31,6 +31,8 @@ int applyPerWidth (core::Widget *widget, int containerWidth, int applyPerHeight (core::Widget *widget, int containerHeight, core::style::Length perHeight); +inline bool adjustExtraSpaceWhenCorrectingRequisitionByOOF () { return false; } + } // namespace dw } // namespace dw diff --git a/dw/textblock.cc b/dw/textblock.cc index 21de1991..788360b2 100644 --- a/dw/textblock.cc +++ b/dw/textblock.cc @@ -37,6 +37,7 @@ static dw::core::style::Tooltip *hoverTooltip = NULL; using namespace lout; +using namespace lout::misc; using namespace lout::unicode; namespace dw { @@ -105,16 +106,6 @@ void Textblock::WordImgRenderer::draw (int x, int y, int width, int height) } -void Textblock::WordImgRenderer::print () -{ - printf ("%p: word #%d, ", this, wordNo); - if (wordNo < textblock->words->size()) - textblock->printWordShort (textblock->words->getRef(wordNo)); - else - printf ("<word %d does not exist>", wordNo); - printf (", data set: %s", dataSet ? "yes" : "no"); -} - void Textblock::SpaceImgRenderer::getBgArea (int *x, int *y, int *width, int *height) { @@ -128,16 +119,6 @@ core::style::Style *Textblock::SpaceImgRenderer::getStyle () return textblock->words->getRef(wordNo)->spaceStyle; } -void Textblock::SpaceImgRenderer::print () -{ - printf ("%p: word FOR SPACE #%d, ", this, wordNo); - if (wordNo < textblock->words->size()) - textblock->printWordShort (textblock->words->getRef(wordNo)); - else - printf ("<word %d does not exist>", wordNo); - printf (", data set: %s", dataSet ? "yes" : "no"); -} - // ---------------------------------------------------------------------- Textblock::DivChar Textblock::divChars[NUM_DIV_CHARS] = { @@ -227,7 +208,6 @@ Textblock::Textblock (bool limitTextWidth) registerName ("dw::Textblock", &CLASS_ID); setButtonSensitive(true); - containingBlock = NULL; hasListitemValue = false; leftInnerPadding = 0; line1Offset = 0; @@ -237,7 +217,9 @@ Textblock::Textblock (bool limitTextWidth) DBG_OBJ_SET_NUM ("redrawY", redrawY); lastWordDrawn = -1; DBG_OBJ_SET_NUM ("lastWordDrawn", lastWordDrawn); - + sizeRequestPosDefined = false; + DBG_OBJ_SET_BOOL ("sizeRequestPosDefined", sizeRequestPosDefined); + /* * The initial sizes of lines and words should not be * too high, since this will waste much memory with tables @@ -252,7 +234,6 @@ Textblock::Textblock (bool limitTextWidth) nonTemporaryLines = 0; words = new misc::NotSoSimpleVector <Word> (1); anchors = new misc::SimpleVector <Anchor> (1); - outOfFlowMgr = NULL; wrapRefLines = wrapRefParagraphs = -1; @@ -267,9 +248,6 @@ Textblock::Textblock (bool limitTextWidth) lineBreakWidth = -1; DBG_OBJ_SET_NUM ("lineBreakWidth", lineBreakWidth); - verticalOffset = 0; - DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); - this->limitTextWidth = limitTextWidth; for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) { @@ -278,6 +256,11 @@ Textblock::Textblock (bool limitTextWidth) hlStart[layer].nChar = 0; hlEnd[layer].index = 0; hlEnd[layer].nChar = 0; + + DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "index", hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM ("hlStart", layer, "nChar", hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "index", hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM ("hlEnd", layer, "nChar", hlEnd[layer].nChar); } initNewLine (); @@ -285,8 +268,6 @@ Textblock::Textblock (bool limitTextWidth) Textblock::~Textblock () { - _MSG("Textblock::~Textblock\n"); - /* make sure not to call a free'd tooltip (very fast overkill) */ hoverTooltip = NULL; @@ -304,16 +285,6 @@ Textblock::~Textblock () delete words; delete anchors; - if(outOfFlowMgr) { - // I feel more comfortable by letting the textblock delete these - // widgets, instead of doing this in ~OutOfFlowMgr. - - for (int i = 0; i < outOfFlowMgr->getNumWidgets (); i++) - delete outOfFlowMgr->getWidget (i); - - delete outOfFlowMgr; - } - /* Make sure we don't own widgets anymore. Necessary before call of parent class destructor. (???) */ words = NULL; @@ -326,10 +297,25 @@ Textblock::~Textblock () * padding/border/margin. This can be used to align the first lines * of several textblocks in a horizontal line. */ -void Textblock::sizeRequestImpl (core::Requisition *requisition) +void Textblock::sizeRequestImpl (core::Requisition *requisition, + bool posDefined, int x, int y) { DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl"); + assert (posDefined || !needsPositionForSize ()); + + sizeRequestPosDefined = true; + if (posDefined) { + sizeRequestX = x; + sizeRequestY = y; + } else { + sizeRequestX = 0; + sizeRequestY = 0; + } + DBG_OBJ_SET_BOOL ("sizeRequestPosDefined", sizeRequestPosDefined); + DBG_OBJ_SET_NUM ("sizeRequestX", sizeRequestX); + DBG_OBJ_SET_NUM ("sizeRequestY", sizeRequestY); + int newLineBreakWidth = getAvailWidth (true); if (newLineBreakWidth != lineBreakWidth) { lineBreakWidth = newLineBreakWidth; @@ -347,14 +333,15 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // Note: the breakSpace of the last line is ignored, so breaks // at the end of a textblock are not visible. - requisition->width = lastLine->maxLineWidth + leftInnerPadding - + getStyle()->boxDiffWidth (); + requisition->width = + lastLine->maxLineWidth + leftInnerPadding + boxDiffWidth (); // Also regard collapsing of this widget top margin and the top // margin of the first line box: requisition->ascent = calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + + extraSpace.top, firstLine->borderAscent, firstLine->marginAscent); @@ -367,16 +354,14 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // for this case is not necessary.) calcVerticalBorder (getStyle()->padding.bottom, getStyle()->borderWidth.bottom, - getStyle()->margin.bottom, + getStyle()->margin.bottom + extraSpace.bottom, lastLine->borderDescent, lastLine->marginDescent); } else { - requisition->width = leftInnerPadding + getStyle()->boxDiffWidth (); - requisition->ascent = getStyle()->boxOffsetY (); - requisition->descent = getStyle()->boxRestHeight ();; + requisition->width = leftInnerPadding + boxDiffWidth (); + requisition->ascent = boxOffsetY (); + requisition->descent = boxRestHeight (); } - requisition->ascent += verticalOffset; - if (mustBeWidenedToAvailWidth ()) { DBG_OBJ_MSGF ("resize", 1, "before considering lineBreakWidth (= %d): %d * (%d + %d)", @@ -407,31 +392,23 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition) // Is this really what we want? An alternative could be that // OutOfFlowMgr::getSize honours CSS attributes an corrected sizes. - DBG_OBJ_MSGF ("resize", 1, "before considering OOF widgets: %d * (%d + %d)", - requisition->width, requisition->ascent, requisition->descent); - - if (outOfFlowMgr) { - int oofWidth, oofHeight; - outOfFlowMgr->getSize (requisition, &oofWidth, &oofHeight); - - // Floats must be within the *content* area, not the *margin* - // area (which is equivalent to the requisition / - // allocation). For this reason, boxRestWidth() and - // boxRestHeight() must be considered. - - if (oofWidth + boxRestWidth () > requisition->width) - requisition->width = oofWidth + boxRestWidth (); - if (oofHeight + boxRestHeight () - > requisition->ascent + requisition->descent) - requisition->descent = - oofHeight + boxRestHeight () - requisition->ascent; - } + correctRequisitionByOOF (requisition, core::splitHeightPreserveAscent); DBG_OBJ_MSGF ("resize", 1, "final: %d * (%d + %d)", requisition->width, requisition->ascent, requisition->descent); DBG_OBJ_LEAVE (); } +core::Widget *Textblock::sizeRequestReference () +{ + return needsPositionForSize() ? oofContainer[OOFM_FLOATS] : NULL; +} + +bool Textblock::needsPositionForSize () +{ + return oofContainer[OOFM_FLOATS] != this; +} + int Textblock::calcVerticalBorder (int widgetPadding, int widgetBorder, int widgetMargin, int lineBorderTotal, int lineMarginTotal) @@ -515,7 +492,7 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); - int diff = leftInnerPadding + getStyle()->boxDiffWidth (); + int diff = leftInnerPadding + boxDiffWidth (); extremes->minWidth += diff; extremes->minWidthIntrinsic += diff; extremes->maxWidth += diff; @@ -534,22 +511,7 @@ void Textblock::getExtremesImpl (core::Extremes *extremes) extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic); - if (outOfFlowMgr) { - int oofMinWidth, oofMaxWidth; - outOfFlowMgr->getExtremes (extremes, &oofMinWidth, &oofMaxWidth); - - DBG_OBJ_MSGF ("resize", 1, "OOFM correction: %d / %d", - oofMinWidth, oofMaxWidth); - - extremes->minWidth = misc::max (extremes->minWidth, oofMinWidth); - extremes->minWidthIntrinsic = - misc::max (extremes->minWidthIntrinsic, oofMinWidth); - extremes->maxWidth = misc::max (extremes->maxWidth, oofMaxWidth); - extremes->maxWidthIntrinsic = - misc::max (extremes->maxWidthIntrinsic, oofMinWidth); - extremes->adjustmentWidth = - misc::max (extremes->adjustmentWidth, oofMinWidth); - } + correctExtremesByOOF (extremes); DBG_OBJ_MSGF ("resize", 0, "finally, after considering OOFM: %d (%d) / %d (%d)", @@ -601,10 +563,10 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) (lines->size () > 0 ? calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + extraSpace.top, lines->getRef(0)->borderAscent, lines->getRef(0)->marginAscent) : - getStyle()->boxOffsetY ()) + verticalOffset); + boxOffsetY ())); childBaseAllocation.descent = allocation->ascent + allocation->descent - childBaseAllocation.ascent; @@ -614,8 +576,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_SET_NUM ("childBaseAllocation.ascent", childBaseAllocation.ascent); DBG_OBJ_SET_NUM ("childBaseAllocation.descent", childBaseAllocation.descent); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->sizeAllocateStart (this, allocation); + sizeAllocateStart (allocation); int lineIndex, wordIndex; Line *line; @@ -665,29 +626,21 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) "allocating widget in flow: line %d, word %d", lineIndex, wordIndex); - childAllocation.x = xCursor + childBaseAllocation.x; - - DBG_OBJ_MSGF ("resize", 1, "childAllocation.x = %d + %d = %d", - xCursor, childBaseAllocation.x, childAllocation.x); + // TODO For word->flags & Word::TOPLEFT_OF_LINE, make + // allocation consistent with calcSizeOfWidgetInFlow(): + childAllocation.x = xCursor + childBaseAllocation.x; + /** \todo Justification within the line is done here. */ - /* align=top: childAllocation.y = line->top + allocation->y; */ - /* align=bottom (base line) */ /* Commented lines break the n2 and n3 test cases at * http://www.dillo.org/test/img/ */ childAllocation.y = lineYOffsetCanvas (line) + (line->borderAscent - word->size.ascent); - DBG_OBJ_MSGF ("resize", 1, - "childAllocation.y = %d + (%d - %d) = %d", - lineYOffsetCanvas (line), - line->borderAscent, word->size.ascent, - childAllocation.y); - childAllocation.width = word->size.width; childAllocation.ascent = word->size.ascent; childAllocation.descent = word->size.descent; @@ -755,9 +708,8 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_MSG_END (); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->sizeAllocateEnd (this); - + sizeAllocateEnd (); + for (int i = 0; i < anchors->size(); i++) { Anchor *anchor = anchors->getRef(i); int y; @@ -777,32 +729,52 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation) DBG_OBJ_LEAVE (); } +void Textblock::calcExtraSpaceImpl () +{ + OOFAwareWidget::calcExtraSpaceImpl (); + + int clearPosition = 0; + for (int i = 0; i < NUM_OOFM; i++) + if (searchOutOfFlowMgr (i)) + clearPosition = + misc::max (clearPosition, + searchOutOfFlowMgr(i)->getClearPosition (this)); + + extraSpace.top = misc::max (extraSpace.top, clearPosition); +} + int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue) { - DBG_OBJ_ENTER ("resize", 0, "Textblock/getAvailWidthOfChild", "%p, %s", + DBG_OBJ_ENTER ("resize", 0, "Textblock::getAvailWidthOfChild", "%p, %s", child, forceValue ? "true" : "false"); int width; - if (child->getStyle()->width == core::style::LENGTH_AUTO) { - // No width specified: similar to standard implementation (see - // there), but "leftInnerPadding" has to be considered, too. - DBG_OBJ_MSG ("resize", 1, "no specification"); - if (forceValue) - width = misc::max (getAvailWidth (true) - boxDiffWidth () - - leftInnerPadding, - 0); - else - width = -1; - } else - width = Widget::getAvailWidthOfChild (child, forceValue); - - if (forceValue && this == child->getContainer () && - !mustBeWidenedToAvailWidth ()) { - core::Extremes extremes; - getExtremes (&extremes); - if (width > extremes.maxWidth - boxDiffWidth () - leftInnerPadding) - width = extremes.maxWidth - boxDiffWidth () - leftInnerPadding; + if (isWidgetOOF (child) && getWidgetOutOfFlowMgr(child) && + getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + width = + getWidgetOutOfFlowMgr(child)->getAvailWidthOfChild (child,forceValue); + else { + if (child->getStyle()->width == core::style::LENGTH_AUTO) { + // No width specified: similar to standard implementation (see + // there), but "leftInnerPadding" has to be considered, too. + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + width = misc::max (getAvailWidth (true) - boxDiffWidth () + - leftInnerPadding, + 0); + else + width = -1; + } else + width = Widget::getAvailWidthOfChild (child, forceValue); + + if (forceValue && this == child->getContainer () && + !mustBeWidenedToAvailWidth ()) { + core::Extremes extremes; + getExtremes (&extremes); + if (width > extremes.maxWidth - boxDiffWidth () - leftInnerPadding) + width = extremes.maxWidth - boxDiffWidth () - leftInnerPadding; + } } DBG_OBJ_MSGF ("resize", 1, "=> %d", width); @@ -810,7 +782,15 @@ int Textblock::getAvailWidthOfChild (Widget *child, bool forceValue) return width; } - +int Textblock::getAvailHeightOfChild (core::Widget *child, bool forceValue) +{ + if (isWidgetOOF(child) && getWidgetOutOfFlowMgr(child) && + getWidgetOutOfFlowMgr(child)->dealingWithSizeOfChild (child)) + return getWidgetOutOfFlowMgr(child)->getAvailHeightOfChild (child, + forceValue); + else + return Widget::getAvailHeightOfChild (child, forceValue); +} void Textblock::containerSizeChangedForChildren () { @@ -822,9 +802,8 @@ void Textblock::containerSizeChangedForChildren () word->content.widget->containerSizeChanged (); } - if (outOfFlowMgr) - outOfFlowMgr->containerSizeChangedForChildren (); - + containerSizeChangedForChildrenOOF (); + DBG_OBJ_LEAVE (); } @@ -833,7 +812,7 @@ bool Textblock::affectsSizeChangeContainerChild (Widget *child) DBG_OBJ_ENTER ("resize", 0, "Textblock/affectsSizeChangeContainerChild", "%p", child); - // See Textblock::getAvailWidthForChild() and Textblock::oofSizeChanged(): + // See Textblock::getAvailWidthOfChild() and Textblock::oofSizeChanged(): // Extremes changes affect the size of the child, too: bool ret; if (!mustBeWidenedToAvailWidth () && @@ -876,13 +855,10 @@ void Textblock::markSizeChange (int ref) { DBG_OBJ_ENTER ("resize", 0, "markSizeChange", "%d", ref); - if (OutOfFlowMgr::isRefOutOfFlow (ref)) { - assert (outOfFlowMgr != NULL); - outOfFlowMgr->markSizeChange (ref); - } else { - PRINTF ("[%p] MARK_SIZE_CHANGE (%d): %d => ...\n", - this, ref, wrapRefLines); - + if (isParentRefOOF (ref)) + getParentRefOutOfFlowMgr(ref) + ->markSizeChange (getParentRefOOFSubRef (ref)); + else { /* By the way: ref == -1 may have two different causes: (i) flush() calls "queueResize (-1, true)", when no rewrapping is necessary; and (ii) a word may have parentRef == -1 , when it is not yet @@ -890,10 +866,10 @@ void Textblock::markSizeChange (int ref) now, but addLine(...) will do everything necessary. */ if (ref != -1) { if (wrapRefLines == -1) - wrapRefLines = OutOfFlowMgr::getLineNoFromRef (ref); + wrapRefLines = getParentRefInFlowSubRef (ref); else wrapRefLines = misc::min (wrapRefLines, - OutOfFlowMgr::getLineNoFromRef (ref)); + getParentRefInFlowSubRef (ref)); } DBG_OBJ_SET_NUM ("wrapRefLines", wrapRefLines); @@ -913,13 +889,10 @@ void Textblock::markExtremesChange (int ref) { DBG_OBJ_ENTER ("resize", 1, "markExtremesChange", "%d", ref); - if (OutOfFlowMgr::isRefOutOfFlow (ref)) { - assert (outOfFlowMgr != NULL); - outOfFlowMgr->markExtremesChange (ref); - } else { - PRINTF ("[%p] MARK_EXTREMES_CHANGE (%d): %d => ...\n", - this, ref, wrapRefParagraphs); - + if (isParentRefOOF (ref)) + getParentRefOutOfFlowMgr(ref) + ->markExtremesChange (getParentRefOOFSubRef (ref)); + else { /* By the way: ref == -1 may have two different causes: (i) flush() calls "queueResize (-1, true)", when no rewrapping is necessary; and (ii) a word may have parentRef == -1 , when it is not yet @@ -927,11 +900,10 @@ void Textblock::markExtremesChange (int ref) now, but addLine(...) will do everything necessary. */ if (ref != -1) { if (wrapRefParagraphs == -1) - wrapRefParagraphs = OutOfFlowMgr::getLineNoFromRef (ref); + wrapRefParagraphs = getParentRefInFlowSubRef (ref); else wrapRefParagraphs = - misc::min (wrapRefParagraphs, - OutOfFlowMgr::getLineNoFromRef (ref)); + misc::min (wrapRefParagraphs, getParentRefInFlowSubRef (ref)); } DBG_OBJ_SET_NUM ("wrapRefParagraphs", wrapRefParagraphs); @@ -940,61 +912,6 @@ void Textblock::markExtremesChange (int ref) DBG_OBJ_LEAVE (); } -void Textblock::notifySetAsTopLevel() -{ - PRINTF ("%p becomes toplevel\n", this); - containingBlock = this; - PRINTF ("-> %p is its own containing block\n", this); -} - -bool Textblock::isContainingBlock (Widget *widget) -{ - return - // Of course, only textblocks are considered as containing - // blocks. - widget->instanceOf (Textblock::CLASS_ID) && - // The second condition: that this block is "out of flow", in a - // wider sense. - (// The toplevel widget is "out of flow", since there is no - // parent, and so no context. - widget->getParent() == NULL || - // A similar reasoning applies to a widget with another parent - // than a textblock (typical example: a table cell (this is - // also a text block) within a table widget). - !widget->getParent()->instanceOf (Textblock::CLASS_ID) || - // Inline blocks are containing blocks, too. - widget->getStyle()->display == core::style::DISPLAY_INLINE_BLOCK || - // Same for blocks with 'overview' set to another value than - // (the default value) 'visible'. - widget->getStyle()->overflow != core::style::OVERFLOW_VISIBLE || - // Finally, "out of flow" in a narrower sense: floats and - // absolute positions. - OutOfFlowMgr::isWidgetOutOfFlow (widget)); -} - -void Textblock::notifySetParent () -{ - PRINTF ("%p becomes a child of %p\n", this, getParent()); - - // Search for containing Box. - containingBlock = NULL; - - for (Widget *widget = this; widget != NULL && containingBlock == NULL; - widget = widget->getParent()) - if (isContainingBlock (widget)) { - containingBlock = (Textblock*)widget; - - if (containingBlock == this) { - PRINTF ("-> %p is its own containing block\n", this); - } else { - PRINTF ("-> %p becomes containing block of %p\n", - containingBlock, this); - } - } - - assert (containingBlock != NULL); -} - bool Textblock::isBlockLevel () { return true; @@ -1084,6 +1001,8 @@ void Textblock::leaveNotifyImpl (core::EventCrossing *event) bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event) { + DBG_OBJ_ENTER0 ("events", 0, "sendSelectionEvent"); + core::Iterator *it; int wordIndex; int charPos = 0, link = -1; @@ -1213,11 +1132,17 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType, } } } - - it = new TextblockIterator (this, core::Content::maskForSelection (true), - false, wordIndex); + + DBG_OBJ_MSGF ("events", 1, "wordIndex = %d", wordIndex); + DBG_MSG_WORD ("events", 1, "<i>this is:</i> ", wordIndex, ""); + + it = TextblockIterator::createWordIndexIterator + (this, core::Content::maskForSelection (true), wordIndex); r = selectionHandleEvent (eventType, it, charPos, link, event); it->unref (); + + DBG_OBJ_MSGF ("events", 1, "=> %s", r ? "true" : "false"); + DBG_OBJ_LEAVE (); return r; } @@ -1538,24 +1463,29 @@ void Textblock::drawSpace(int wordIndex, core::View *view, * - area is used always (ev. set it to event->area) * - event is only used when is_expose */ -void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) +void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area, + core::DrawingContext *context) { - DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., %d, %d, %d * %d", + DBG_OBJ_ENTER ("draw", 0, "drawLine", "..., [%d, %d, %d * %d]", area->x, area->y, area->width, area->height); - + int xWidget = line->textOffset; int yWidgetBase = lineYOffsetWidget (line) + line->borderAscent; DBG_OBJ_MSGF ("draw", 1, "line from %d to %d (%d words), at (%d, %d)", line->firstWord, line->lastWord, words->size (), xWidget, yWidgetBase); - DBG_MSG_WORD ("draw", 0, "<i>line starts with: </i>", line->firstWord, ""); - DBG_MSG_WORD ("draw", 0, "<i>line ends with: </i>", line->lastWord, ""); + DBG_MSG_WORD ("draw", 1, "<i>line starts with: </i>", line->firstWord, ""); + DBG_MSG_WORD ("draw", 1, "<i>line ends with: </i>", line->lastWord, ""); + + DBG_OBJ_MSG_START (); for (int wordIndex = line->firstWord; wordIndex <= line->lastWord && xWidget < area->x + area->width; wordIndex++) { - Word *word = words->getRef(wordIndex); + DBG_MSG_WORD ("draw", 2, "<i>drawing: </i>", wordIndex, ""); + + Word *word = words->getRef (wordIndex); int wordSize = word->size.width; if (xWidget + wordSize + word->hyphenWidth + word->effSpace >= area->x) { @@ -1566,9 +1496,10 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) if (word->content.type == core::Content::WIDGET_IN_FLOW) { core::Widget *child = word->content.widget; core::Rectangle childArea; - - if (child->intersects (area, &childArea)) - child->draw (view, &childArea); + if (!core::StackingContextMgr::handledByStackingContextMgr + (child) && + child->intersects (this, area, &childArea)) + child->draw (view, &childArea, context); } else { int wordIndex2 = wordIndex; while (wordIndex2 < line->lastWord && @@ -1596,8 +1527,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) xWidget + wordSize, yWidgetBase - line->borderAscent, word->effSpace, line->borderAscent + line->borderDescent, false); - drawSpace(wordIndex, view, area, xWidget + wordSize, - yWidgetBase); + drawSpace (wordIndex, view, area, xWidget + wordSize, + yWidgetBase); } } @@ -1605,6 +1536,8 @@ void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area) xWidget += wordSize + word->effSpace; } + DBG_OBJ_MSG_END (); + DBG_OBJ_LEAVE (); } @@ -1626,7 +1559,8 @@ int Textblock::findLineIndexWhenNotAllocated (int y) return findLineIndex (y, calcVerticalBorder (getStyle()->padding.top, getStyle()->borderWidth.top, - getStyle()->margin.top, + getStyle()->margin.top + + extraSpace.top, lines->getRef(0)->borderAscent, lines->getRef(0)->marginAscent)); } @@ -1772,38 +1706,45 @@ Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace) return NULL; } -void Textblock::draw (core::View *view, core::Rectangle *area) +void Textblock::drawLevel (core::View *view, core::Rectangle *area, + int level, core::DrawingContext *context) { - DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d", - area->x, area->y, area->width, area->height); - - int lineIndex; - Line *line; + DBG_OBJ_ENTER ("draw", 0, "Textblock::drawLevel", "(%d, %d, %d * %d), %s", + area->x, area->y, area->width, area->height, + stackingLevelText (level)); - // Instead of drawWidgetBox, use drawBox to include verticalOffset. - if (getParent() == NULL) { - // The toplevel (parent == NULL) widget is a special case, which - // we leave to drawWidgetBox; verticalOffset will here always 0. - assert (verticalOffset == 0); - drawWidgetBox (view, area, false); - } else - drawBox (view, getStyle(), area, 0, verticalOffset, allocation.width, - getHeight() - verticalOffset, false); + switch (level) { + case SL_IN_FLOW: + for (int lineIndex = findLineIndexWhenAllocated (area->y); + lineIndex < lines->size (); lineIndex++) { + Line *line = lines->getRef (lineIndex); + if (lineYOffsetWidget (line) >= area->y + area->height) + break; - lineIndex = findLineIndexWhenAllocated (area->y); + DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ()); + drawLine (line, view, area, context); + } + break; - for (; lineIndex < lines->size (); lineIndex++) { - line = lines->getRef (lineIndex); - if (lineYOffsetWidget (line) >= area->y + area->height) - break; + case SL_OOF_REF: + // TODO Inefficient. Perhaps store OOF references in seperate + // (much smaller!) list. + for (int oofmIndex = 0; oofmIndex < NUM_OOFM; oofmIndex++) { + for (int wordIndex = 0; wordIndex < words->size (); wordIndex++) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + getOOFMIndex (word->content.widget) == oofmIndex && + doesWidgetOOFInterruptDrawing (word->content.widget)) + word->content.widget->drawInterruption (view, area, context); + } + } + break; - DBG_OBJ_MSGF ("draw", 0, "line %d (of %d)", lineIndex, lines->size ()); - drawLine (line, view, area); + default: + OOFAwareWidget::drawLevel (view, area, level, context); + break; } - if(outOfFlowMgr) - outOfFlowMgr->draw(view, area); - DBG_OBJ_LEAVE (); } @@ -1914,6 +1855,7 @@ void Textblock::fillWord (int wordNo, int width, int ascent, int descent, word->size.width = width; word->size.ascent = ascent; word->size.descent = descent; + DBG_SET_WORD_SIZE (wordNo); word->origSpace = word->effSpace = 0; word->hyphenWidth = 0; word->badnessAndPenalty.setPenalty (PENALTY_PROHIBIT_BREAK); @@ -2304,15 +2246,70 @@ void Textblock::calcTextSizes (const char *text, size_t textLen, } /** + * Calculate the size of a widget, and return whether it has to be + * positioned at the top of the line. + */ +bool Textblock::calcSizeOfWidgetInFlow (int wordIndex, Widget *widget, + core::Requisition *size) +{ + DBG_OBJ_ENTER ("resize", 0, "calcSizeOfWidgetInFlow", "%d, %p, ...", + wordIndex, widget); + + int lastWord = lines->empty () ? -1 : lines->getLastRef()->lastWord; + assert (wordIndex > lastWord); + bool result; + + Widget *reference = widget->sizeRequestReference (); + + if (reference == NULL) { + widget->sizeRequest (size); + result = false; + } else { + assert (getParent () == NULL || sizeRequestPosDefined); + assert + (wordIndex == 0 || + words->getRef(wordIndex - 1)->content.type == core::Content::BREAK); + assert (reference == oofContainer[OOFM_FLOATS]); + + // The following assertion is always the case in dillo, and + // plausible (since floats are the reason why the size depends + // on the position); and it simplifies the calculation of x + // below. + assert (widget->instanceOf (RegardingBorder::CLASS_ID) && + !isOOFContainer (widget, OOFM_FLOATS)); + + int xRef, yRef; + if (getParent () == NULL) + xRef = yRef = 0; + else { + xRef = sizeRequestX; + yRef = sizeRequestY; + } + + int x = xRef + boxOffsetX () + leftInnerPadding + + (lines->size () == 0 ? line1OffsetEff : 0); + int lastMargin, yLine = yOffsetOfLineToBeCreated (&lastMargin); + int y = yRef + yLine - lastMargin + + max (lastMargin, widget->getStyle()->margin.top); + + widget->sizeRequest (size, true, x, y); + + result = true; + } + + DBG_OBJ_LEAVE_VAL ("%s", boolToStr (result)); + return result; +} + +/** * Add a word (without hyphens) to the page structure. */ void Textblock::addText0 (const char *text, size_t len, short flags, core::style::Style *style, core::Requisition *size) { DBG_OBJ_ENTER ("construct.word", 0, "addText0", - "..., %d, %s:%s:%s:%s:%s:%s:%s, %p, %d * (%d + %d)", + "..., %d, %s:%s:%s:%s:%s:%s:%s:%s, %p, %d * (%d + %d)", (int)len, - // Ugly copy&paste from printWordFlags: (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--", (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--", (flags & Word::PERM_DIV_CHAR) ? "dp" : "--", @@ -2320,6 +2317,7 @@ void Textblock::addText0 (const char *text, size_t len, short flags, (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--", (flags & Word::WORD_START) ? "st" : "--", (flags & Word::WORD_END) ? "en" : "--", + (flags & Word::TOPLEFT_OF_LINE) ? "00" : "--", style, size->width, size->ascent, size->descent); //printf("[%p] addText0 ('", this); @@ -2364,22 +2362,26 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) widget->setStyle (style); - PRINTF ("adding the %s %p to %p (word %d) ...\n", - widget->getClassName(), widget, this, words->size()); - - if (containingBlock->outOfFlowMgr == NULL) { - containingBlock->outOfFlowMgr = new OutOfFlowMgr (containingBlock); - DBG_OBJ_ASSOC (containingBlock, containingBlock->outOfFlowMgr); - } + initOutOfFlowMgrs (); - if (OutOfFlowMgr::isWidgetOutOfFlow (widget)) { - PRINTF (" -> out of flow.\n"); + if (testWidgetOutOfFlow (widget)) { + int oofmIndex = getOOFMIndex (widget); + DBG_OBJ_MSGF ("construct.word", 1, "ouf of flow: oofmIndex = %d (%s)", + oofmIndex, OOFM_NAME[oofmIndex]); - widget->setParent (containingBlock); + widget->setParent (oofContainer[oofmIndex]); widget->setGenerator (this); - containingBlock->outOfFlowMgr->addWidgetOOF (widget, this, - words->size ()); - Word *word = addWord (0, 0, 0, 0, style); + + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex); + int oofmSubRef = oofm->addWidgetOOF (widget, this, words->size ()); + widget->parentRef = makeParentRefOOF (oofmIndex, oofmSubRef); + + DBG_OBJ_MSGF ("construct.word", 1, "oofmSubRef = %d => parentRef = %d", + oofmSubRef, widget->parentRef); + + core::Requisition size; + oofm->calcWidgetRefSize (widget, &size); + Word *word = addWord (size.width, size.ascent, size.descent, 0, style); word->content.type = core::Content::WIDGET_OOF_REF; word->content.widget = widget; @@ -2387,18 +2389,22 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style) // problems with breaking near float definitions.) setBreakOption (word, style, 0, 0, false); } else { - PRINTF (" -> within flow.\n"); + DBG_OBJ_MSG ("construct.word", 1, "in flow"); widget->setParent (this); // TODO Replace (perhaps) later "textblock" by "OOF aware widget". - if (widget->instanceOf (Textblock::CLASS_ID)) - containingBlock->outOfFlowMgr->addWidgetInFlow ((Textblock*)widget, - this, words->size ()); + if (widget->instanceOf (Textblock::CLASS_ID)) { + for (int i = 0; i < NUM_OOFM; i++) + searchOutOfFlowMgr(i)->addWidgetInFlow ((Textblock*)widget, this, + words->size ()); + } core::Requisition size; - widget->sizeRequest (&size); - Word *word = addWord (size.width, size.ascent, size.descent, 0, style); + short flags = calcSizeOfWidgetInFlow (words->size (), widget, &size) ? + Word::TOPLEFT_OF_LINE : 0; + Word *word = + addWord (size.width, size.ascent, size.descent, flags, style); word->content.type = core::Content::WIDGET_IN_FLOW; word->content.widget = widget; } @@ -2632,7 +2638,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) for (Widget *widget = this; widget->getParent() != NULL && widget->getParent()->instanceOf (Textblock::CLASS_ID) && - !OutOfFlowMgr::isRefOutOfFlow (widget->parentRef); + !isWidgetOOF (widget); widget = widget->getParent ()) { Textblock *textblock2 = (Textblock*)widget->getParent (); int index = textblock2->hasListitemValue ? 1 : 0; @@ -2643,7 +2649,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) if (!isfirst) { /* The text block we searched for has been found. */ Word *word2; - int lineno = OutOfFlowMgr::getLineNoFromRef (widget->parentRef); + int lineno = getWidgetInFlowSubRef (widget); if (lineno > 0 && (word2 = @@ -2652,8 +2658,7 @@ void Textblock::addParbreak (int space, core::style::Style *style) word2->content.type == core::Content::BREAK) { if (word2->content.breakSpace < space) { word2->content.breakSpace = space; - textblock2->queueResize - (OutOfFlowMgr::createRefNormalFlow (lineno), false); + textblock2->queueResize (makeParentRefInFlow (lineno), false); textblock2->mustQueueResize = false; } } @@ -2752,63 +2757,66 @@ void Textblock::breakAdded () words->getRef(words->size () - 2)->effSpace = 0; } -/** - * \brief Search recursively through widget. - * - * This is an optimized version of the general - * dw::core::Widget::getWidgetAtPoint method. - */ -core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level) -{ - //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", - // 3 * level, "", getClassName (), this, allocation.x, allocation.y, - // allocation.width, allocation.ascent, allocation.descent); - - int lineIndex, wordIndex; - Line *line; - - if (x < allocation.x || - y < allocation.y || - x > allocation.x + allocation.width || - y > allocation.y + getHeight ()) { - return NULL; - } - - // First, search for widgets out of flow, notably floats, since - // there are cases where they overlap child textblocks. Should - // later be refined using z-index. - Widget *oofWidget = - outOfFlowMgr ? outOfFlowMgr->getWidgetAtPoint (x, y, level) : NULL; - if (oofWidget) - return oofWidget; - - lineIndex = findLineIndexWhenAllocated (y - allocation.y); - - if (lineIndex < 0 || lineIndex >= lines->size ()) { - return this; - } - - line = lines->getRef (lineIndex); - - for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) { - Word *word = words->getRef (wordIndex); - - if (word->content.type == core::Content::WIDGET_IN_FLOW) { - core::Widget * childAtPoint; - if (word->content.widget->wasAllocated ()) { - childAtPoint = word->content.widget->getWidgetAtPoint (x, y, - level + 1); - if (childAtPoint) { - return childAtPoint; +core::Widget *Textblock::getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext + *context) +{ + DBG_OBJ_ENTER ("events", 0, "Textblock::getWidgetAtPointLevel", "%d, %d, %s", + x, y, stackingLevelText (level)); + + Widget *widgetAtPoint = NULL; + + switch (level) { + case SL_IN_FLOW: + { + int lineIndex = findLineIndexWhenAllocated (y - allocation.y); + + if (lineIndex >= 0 && lineIndex < lines->size ()) { + Line *line = lines->getRef (lineIndex); + + for (int wordIndex = line->lastWord; + widgetAtPoint == NULL && wordIndex >= line->firstWord; + wordIndex--) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_IN_FLOW && + !core::StackingContextMgr::handledByStackingContextMgr + (word->content.widget)) + widgetAtPoint = + word->content.widget->getWidgetAtPoint (x, y, context); } } } + break; + + case SL_OOF_REF: + // TODO Inefficient. Perhaps store OOF references in seperate + // (much smaller!) list. + for (int oofmIndex = NUM_OOFM; widgetAtPoint == NULL && oofmIndex >= 0; + oofmIndex--) { + for (int wordIndex = words->size () - 1; + widgetAtPoint == NULL && wordIndex >= 0; wordIndex--) { + Word *word = words->getRef (wordIndex); + if (word->content.type == core::Content::WIDGET_OOF_REF && + getOOFMIndex (word->content.widget) == oofmIndex && + doesWidgetOOFInterruptDrawing (word->content.widget)) + widgetAtPoint = + word->content.widget->getWidgetAtPointInterrupted (x, y, + context); + } + } + break; + + default: + widgetAtPoint = + OOFAwareWidget::getWidgetAtPointLevel (x, y, level, context); + break; } - return this; + DBG_OBJ_MSGF ("events", 1, "=> %p", widgetAtPoint); + DBG_OBJ_LEAVE (); + return widgetAtPoint; } - /** * This function "hands" the last break of a page "over" to a parent * page. This is used for "collapsing spaces". @@ -2909,6 +2917,8 @@ void Textblock::changeWordStyle (int from, int to, core::style::Style *style, void Textblock::queueDrawRange (int index1, int index2) { + DBG_OBJ_ENTER ("draw", 0, "queueDrawRange", "%d, %d", index1, index2); + int from = misc::min (index1, index2); int to = misc::max (index1, index2); @@ -2930,18 +2940,6 @@ void Textblock::queueDrawRange (int index1, int index2) queueDrawArea (0, y, allocation.width, h); } -} - -void Textblock::setVerticalOffset (int verticalOffset) -{ - DBG_OBJ_ENTER ("resize", 0, "setVerticalOffset", "%d", verticalOffset); - - if (this->verticalOffset != verticalOffset) { - this->verticalOffset = verticalOffset; - DBG_OBJ_SET_NUM ("verticalOffset", verticalOffset); - mustQueueResize = true; - queueDraw (); // Could perhaps be optimized. - } DBG_OBJ_LEAVE (); } @@ -2951,18 +2949,24 @@ bool Textblock::mustBeWidenedToAvailWidth () DBG_OBJ_ENTER0 ("resize", 0, "mustBeWidenedToAvailWidth"); bool toplevel = getParent () == NULL, block = getStyle()->display == core::style::DISPLAY_BLOCK, - vloat = getStyle()->vloat != core::style::FLOAT_NONE, - result = toplevel || (block && !vloat); + vloat = testWidgetFloat (this), + abspos = testWidgetAbsolutelyPositioned (this), + fixpos = testWidgetFixedlyPositioned (this), + // In detail, this depends on what the respective OOFM does + // with the child widget: + result = toplevel || (block && !(vloat || abspos || fixpos)); DBG_OBJ_MSGF ("resize", 0, - "=> %s (toplevel: %s, block: %s, float: %s)", + "=> %s (toplevel: %s, block: %s, float: %s, abspos: %s, " + "fixpos: %s)", result ? "true" : "false", toplevel ? "true" : "false", - block ? "true" : "false", vloat ? "true" : "false"); + block ? "true" : "false", vloat ? "true" : "false", + abspos ? "true" : "false", fixpos ? "true" : "false"); DBG_OBJ_LEAVE (); return result; } /** - * Called by dw::OutOfFlowMgr when the border has changed due to a + * Called by dw::OOFFloatsMgr when the border has changed due to a * float (or some floats). * * "y", which given in widget coordinates, denotes the minimal @@ -3091,7 +3095,7 @@ void Textblock::borderChanged (int y, Widget *vloat) minWrapLineIndex, maxWrapLineIndex, vloat->getGenerator() == this ? "yes" : "no"); - queueResize (OutOfFlowMgr::createRefNormalFlow (realWrapLineIndex), true); + queueResize (makeParentRefInFlow (realWrapLineIndex), true); // Notice that the line no. realWrapLineIndex may not exist yet. if (realWrapLineIndex == 0) @@ -3112,6 +3116,13 @@ void Textblock::borderChanged (int y, Widget *vloat) DBG_OBJ_LEAVE (); } +void Textblock::widgetRefSizeChanged (int externalIndex) +{ + int lineNo = findLineOfWord (externalIndex); + if (lineNo >= 0 && lineNo < lines->size ()) + queueResize (makeParentRefInFlow (lineNo), true); +} + void Textblock::clearPositionChanged () { DBG_OBJ_ENTER0 ("resize", 0, "clearPositionChanged"); @@ -3127,7 +3138,7 @@ void Textblock::oofSizeChanged (bool extremesChanged) extremesChanged ? "true" : "false"); queueResize (-1, extremesChanged); - // See Textblock::getAvailWidthForChild(): Extremes changes may become also + // See Textblock::getAvailWidthOfChild(): Extremes changes may become also // relevant for the children, under certain conditions: if (extremesChanged && !mustBeWidenedToAvailWidth ()) containerSizeChanged (); @@ -3135,6 +3146,21 @@ void Textblock::oofSizeChanged (bool extremesChanged) DBG_OBJ_LEAVE (); } +int Textblock::getLineBreakWidth () +{ + return lineBreakWidth; +} + +bool Textblock::isPossibleContainer (int oofmIndex) +{ + return true; +} + +bool Textblock::isPossibleContainerParent (int oofmIndex) +{ + return true; +} + RegardingBorder *Textblock::getWidgetRegardingBorderForLine (Line *line) { return getWidgetRegardingBorderForLine (line->firstWord, line->lastWord); @@ -3168,16 +3194,10 @@ RegardingBorder *Textblock::getWidgetRegardingBorderForLine (int firstWord, if (word->content.type == core::Content::WIDGET_IN_FLOW) { Widget *widget = word->content.widget; if (widget->instanceOf (RegardingBorder::CLASS_ID) && - // Exclude some cases where a widget constitutes a new - // container (see definition of float container in - // Textblock::isContainingBlock). - widget->getStyle()->display != core::style::DISPLAY_INLINE_BLOCK && - widget->getStyle()->overflow == core::style::OVERFLOW_VISIBLE) + // Exclude cases where a textblock constitutes a new floats + // container. + !isOOFContainer (widget, OOFM_FLOATS)) widgetRegardingBorder = (RegardingBorder*)widget; - - // (TODO: It would look nicer if there is one common place - // for such definitions. Will be fixed in "dillo_grows", not - // here.) } } @@ -3189,7 +3209,7 @@ RegardingBorder *Textblock::getWidgetRegardingBorderForLine (int firstWord, /** * Includes margin, border, and padding. */ -int Textblock::yOffsetOfLineToBeCreated () +int Textblock::yOffsetOfLineToBeCreated (int *lastMargin) { // This method does not return an exact result: the position of the // new line, which does not yet exist, cannot be calculated, since @@ -3209,24 +3229,51 @@ int Textblock::yOffsetOfLineToBeCreated () int result; if (lines->size () == 0) { - result = verticalOffset + calcVerticalBorder (getStyle()->padding.top, - getStyle()->borderWidth.top, - getStyle()->margin.top, - 0, 0); - DBG_OBJ_MSGF ("line.yoffset", 1, "first line: ... = %d", result); + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top + extraSpace.top, + getStyle()->margin.top, 0, 0); + if (lastMargin) + *lastMargin = getStyle()->margin.top; } else { Line *firstLine = lines->getRef (0), *lastLine = lines->getLastRef (); - result = verticalOffset + calcVerticalBorder (getStyle()->padding.top, - getStyle()->borderWidth.top, - getStyle()->margin.top, - firstLine->borderAscent, - firstLine->marginAscent) + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top, + getStyle()->margin.top + extraSpace.top, + firstLine->borderAscent, + firstLine->marginAscent) - firstLine->borderAscent + lastLine->top + lastLine->totalHeight (0); - DBG_OBJ_MSGF ("line.yoffset", 1, "other line: ... = %d", result); + if (lastMargin) + *lastMargin = lastLine->marginDescent - lastLine->borderDescent; } - DBG_OBJ_LEAVE (); + if (lastMargin) + DBG_OBJ_LEAVE_VAL ("%d, %d", result, *lastMargin); + else + DBG_OBJ_LEAVE_VAL ("%d", result); + + return result; +} + +/** + * Includes margin, border, and padding. Can be used without allocation. + */ +int Textblock::yOffsetOfLineCreated (Line *line) +{ + // Similar applies (regarding exactness) as to yOffsetOfLineToBeCreated. + + DBG_OBJ_ENTER0 ("line.yoffset", 0, "yOffsetOfLineToBeCreated"); + + int result; + + Line *firstLine = lines->getRef (0); + result = calcVerticalBorder (getStyle()->padding.top, + getStyle()->borderWidth.top, + getStyle()->margin.top + extraSpace.top, + firstLine->borderAscent, + firstLine->marginAscent) + - firstLine->borderAscent + line->top; + DBG_OBJ_LEAVE_VAL ("%d", result); return result; } diff --git a/dw/textblock.hh b/dw/textblock.hh index 162088dc..6030ff63 100644 --- a/dw/textblock.hh +++ b/dw/textblock.hh @@ -4,7 +4,6 @@ #include <limits.h> #include "regardingborder.hh" -#include "outofflowmgr.hh" #include "../lout/misc.hh" // These were used when improved line breaking and hyphenation were implemented. @@ -24,10 +23,9 @@ namespace dw { * * <div style="border: 2px solid #ffff00; margin-top: 0.5em; * margin-bottom: 0.5em; padding: 0.5em 1em; background-color: - * #ffffe0"><b>Info:</b> The recent changes (line breaking and - * hyphenation on one hand, floats on the other hand) have not yet - * been incorporated into this documentation. See \ref - * dw-line-breaking and \ref dw-out-of-flow.</div> + * #ffffe0"><b>Info:</b> Some (not so) recent changes, line breaking + * and hyphenation, have not yet been incorporated into this + * documentation. See \ref dw-line-breaking.</div> * * <h3>Signals</h3> * @@ -164,11 +162,11 @@ namespace dw { * Anchors associate the anchor name with the index of the next word at * the point of the anchor. * - * <h4>Incremental Resizing</h4> + * <h3>Incremental Resizing</h3> * * dw::Textblock makes use of incremental resizing as described in \ref * dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply - * the number of the line. + * the number of the line. [<b>Update:</b> Incorrect; see \ref dw-out-of-flow.] * * Generally, there are three cases which may change the size of the * widget: @@ -195,6 +193,12 @@ namespace dw { * dw::Textblock, which has the value -1 if no rewrapping of lines * necessary, or otherwise the line from which a rewrap is necessary. * + * <h3>Widgets Ouf Of Flow</h3> + * + * See + * + * - dw::oof::OOFAwareWidget (base class) and + * - \ref dw-out-of-flow. */ class Textblock: public RegardingBorder { @@ -272,7 +276,6 @@ private: int compareTo (int penaltyIndex, BadnessAndPenalty *other); void intoStringBuffer(lout::misc::StringBuffer *sb); - void print (); }; enum { PENALTY_HYPHEN, PENALTY_EM_DASH_LEFT, PENALTY_EM_DASH_RIGHT, @@ -290,10 +293,8 @@ private: static const char *hyphenDrawChar; - Textblock *containingBlock; - OutOfFlowMgr *outOfFlowMgr; - protected: + /** * \brief Implementation used for words. */ @@ -316,8 +317,6 @@ protected: void getRefArea (int *xRef, int *yRef, int *widthRef, int *heightRef); core::style::Style *getStyle (); void draw (int x, int y, int width, int height); - - virtual void print (); }; class SpaceImgRenderer: public WordImgRenderer @@ -328,8 +327,6 @@ protected: void getBgArea (int *x, int *y, int *width, int *height); core::style::Style *getStyle (); - - void print (); }; struct Paragraph @@ -452,8 +449,13 @@ protected: WORD_START = 1 << 5, /* If a word represents a "real" text word, or (after * hyphenation) the last part of a "real" text word, this - * flag is set. Analogue to WORD_START. */ - WORD_END = 1 << 6 + * flag is set. Analogue to WORD_START. */ + WORD_END = 1 << 6, + /* This word is put at the top of the line, and at the + * left. This is necessary if the size of a child widget + * depends on the position, which, on the other hand, cannot + * be determined before the whole line is broken. */ + TOPLEFT_OF_LINE = 1 << 7 }; /* TODO: perhaps add a xLeft? */ @@ -496,38 +498,30 @@ protected: SpaceImgRenderer *spaceImgRenderer; }; - void printWordShort (Word *word); - void printWordFlags (short flags); - void printWordWithFlags (Word *word); - void printWord (Word *word); - struct Anchor { char *name; int wordIndex; }; - class TextblockIterator: public core::Iterator + class TextblockIterator: public OOFAwareWidgetIterator { - private: - bool oofm; - int index; + protected: + int numContentsInFlow (); + void getContentInFlow (int index, core::Content *content); public: TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd); - TextblockIterator (Textblock *textblock, core::Content::Type mask, - bool oofm, int index); + + static TextblockIterator *createWordIndexIterator + (Textblock *textblock, core::Content::Type mask, int wordIndex); lout::object::Object *clone(); - int compareTo(lout::object::Comparable *other); - bool next (); - bool prev (); void highlight (int start, int end, core::HighlightLayer layer); void unhighlight (int direction, core::HighlightLayer layer); void getAllocation (int start, int end, core::Allocation *allocation); - void print (); }; friend class TextblockIterator; @@ -582,12 +576,12 @@ protected: int redrawY; int lastWordDrawn; + bool sizeRequestPosDefined; + int sizeRequestX, sizeRequestY; + /* This value is (currently) set by setAscent(). */ int lineBreakWidth; - // Additional vertical offset, used for the "clear" attribute. - int verticalOffset; - int wrapRefLines, wrapRefParagraphs; /* 0-based. Important: Both are the line numbers, not the value stored in @@ -624,9 +618,10 @@ protected: int hoverLink; /* The link under the mouse pointer */ void queueDrawRange (int index1, int index2); + bool needsPositionForSize (); int calcVerticalBorder (int widgetPadding, int widgetBorder, int widgetMargin, int lineBorderTotal, - int lineMarginTotal); + int lineMarginTotal); void getWordExtremes (Word *word, core::Extremes *extremes); void justifyLine (Line *line, int diff); Line *addLine (int firstWord, int lastWord, int newLastOofPos, @@ -637,7 +632,6 @@ protected: void calcBorders (int lastOofRef, int height); void showMissingLines (); void removeTemporaryLines (); - void setVerticalOffset (int verticalOffset); void decorateText (core::View *view, core::style::Style *style, core::style::Color::Shading shading, @@ -654,7 +648,9 @@ protected: core::Rectangle *area, int xWidget, int yWidgetBase); void drawSpace (int wordIndex, core::View *view, core::Rectangle *area, int xWidget, int yWidgetBase); - void drawLine (Line *line, core::View *view, core::Rectangle *area); + void drawLine (Line *line, core::View *view, core::Rectangle *area, + core::DrawingContext *context); + int findLineIndex (int y); int findLineIndexWhenNotAllocated (int y); int findLineIndexWhenAllocated (int y); @@ -684,6 +680,8 @@ protected: core::style::Style *style, bool isStart, bool isEnd); void calcTextSize (const char *text, size_t len, core::style::Style *style, core::Requisition *size, bool isStart, bool isEnd); + bool calcSizeOfWidgetInFlow (int wordIndex, Widget *widget, + core::Requisition *size); /** * Of nested text blocks, only the most inner one must regard the @@ -767,7 +765,8 @@ protected: int lastWord); void printBorderChangedErrorAndAbort (int y, Widget *vloat, int wrapLineIndex); - int yOffsetOfLineToBeCreated (); + int yOffsetOfLineToBeCreated (int *lastMargin = NULL); + int yOffsetOfLineCreated (Line *line); bool sendSelectionEvent (core::SelectionState::EventType eventType, core::MousePositionEvent *event); @@ -775,6 +774,7 @@ protected: void processWord (int wordIndex); virtual int wordWrap (int wordIndex, bool wrapAll); int wrapWordInFlow (int wordIndex, bool wrapAll); + int wrapWordOofRef (int wordIndex, bool wrapAll); void balanceBreakPosAndHeight (int wordIndex, int firstIndex, int *searchUntil, bool tempNewLine, int penaltyIndex, bool borderIsCalculated, @@ -809,10 +809,23 @@ protected: void alignLine (int lineIndex); void calcTextOffset (int lineIndex, int totalWidth); - void sizeRequestImpl (core::Requisition *requisition); + void drawLevel (core::View *view, core::Rectangle *area, int level, + core::DrawingContext *context); + + Widget *getWidgetAtPointLevel (int x, int y, int level, + core::GettingWidgetAtPointContext *context); + + void sizeRequestImpl (core::Requisition *requisition, bool posDefined, int x, + int y); + Widget *sizeRequestReference (); void getExtremesImpl (core::Extremes *extremes); void sizeAllocateImpl (core::Allocation *allocation); - int getAvailWidthOfChild (Widget *child, bool forceValue); + + void calcExtraSpaceImpl (); + + int getAvailWidthOfChild (core::Widget *child, bool forceValue); + int getAvailHeightOfChild (core::Widget *child, bool forceValue); + void containerSizeChangedForChildren (); bool affectsSizeChangeContainerChild (Widget *child); bool usesAvailWidth (); @@ -821,13 +834,8 @@ protected: void markSizeChange (int ref); void markExtremesChange (int ref); - void notifySetAsTopLevel(); - void notifySetParent(); - bool isBlockLevel (); - void draw (core::View *view, core::Rectangle *area); - bool buttonPressImpl (core::EventButton *event); bool buttonReleaseImpl (core::EventButton *event); bool motionNotifyImpl (core::EventMotion *event); @@ -842,7 +850,6 @@ protected: core::style::Style *style, int numBreaks, int *breakPos, core::Requisition *wordSize); - static bool isContainingBlock (Widget *widget); public: static int CLASS_ID; @@ -854,8 +861,12 @@ public: static void setPenaltyEmDashRight2 (int penaltyRightEmDash2); static void setStretchabilityFactor (int stretchabilityFactor); - Textblock(bool limitTextWidth); - ~Textblock(); + static inline bool mustAddBreaks (core::style::Style *style) + { return !testStyleOutOfFlow (style) || + testStyleRelativelyPositioned (style); } + + Textblock (bool limitTextWidth); + ~Textblock (); core::Iterator *iterator (core::Content::Type mask, bool atEnd); @@ -864,8 +875,6 @@ public: void addText (const char *text, size_t len, core::style::Style *style); inline void addText (const char *text, core::style::Style *style) { addText (text, strlen(text), style); } - static bool isStyleOutOfFlow (core::style::Style *style) - { return OutOfFlowMgr::isStyleOutOfFlow (style); } void addWidget (core::Widget *widget, core::style::Style *style); bool addAnchor (const char *name, core::style::Style *style); void addSpace (core::style::Style *style); @@ -873,21 +882,22 @@ public: void addParbreak (int space, core::style::Style *style); void addLinebreak (core::style::Style *style); - core::Widget *getWidgetAtPoint (int x, int y, int level); void handOverBreak (core::style::Style *style); void changeLinkColor (int link, int newColor); void changeWordStyle (int from, int to, core::style::Style *style, bool includeFirstSpace, bool includeLastSpace); - virtual bool mustBeWidenedToAvailWidth (); - + bool mustBeWidenedToAvailWidth (); void borderChanged (int y, core::Widget *vloat); + void widgetRefSizeChanged (int externalIndex); void clearPositionChanged (); void oofSizeChanged (bool extremesChanged); - inline int getLineBreakWidth () { return lineBreakWidth; } + int getLineBreakWidth (); + bool isPossibleContainer (int oofmIndex); + bool isPossibleContainerParent (int oofmIndex); }; -#define DBG_SET_WORD_PENALTY(n, i, is) \ +#define DBG_SET_WORD_PENALTY(n, i, is) \ D_STMT_START { \ if (words->getRef(n)->badnessAndPenalty.getPenalty (i) == INT_MIN) \ DBG_OBJ_ARRATTRSET_SYM ("words", n, "penalty." is, "-inf"); \ @@ -896,7 +906,7 @@ public: else \ DBG_OBJ_ARRATTRSET_NUM ("words", n, "penalty." is, \ words->getRef(n)->badnessAndPenalty \ - .getPenalty (i)); \ + .getPenalty (i)); \ } D_STMT_END #define DBG_SET_WORD(n) \ @@ -930,6 +940,16 @@ public: DBG_SET_WORD_PENALTY (n, 1, "1"); \ } D_STMT_END +#define DBG_SET_WORD_SIZE(n) \ + D_STMT_START { \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.width", \ + words->getRef(n)->size.width); \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.ascent", \ + words->getRef(n)->size.ascent); \ + DBG_OBJ_ARRATTRSET_NUM ("words", n, "size.descent", \ + words->getRef(n)->size.descent); \ + } D_STMT_END + #define DBG_MSG_WORD(aspect, prio, prefix, n, suffix) \ D_STMT_START { \ if ((n) < 0 || (n) >= words->size ()) \ diff --git a/dw/textblock_iterator.cc b/dw/textblock_iterator.cc index b6423a29..14631eba 100644 --- a/dw/textblock_iterator.cc +++ b/dw/textblock_iterator.cc @@ -33,173 +33,48 @@ namespace dw { Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, core::Content::Type mask, bool atEnd): - core::Iterator (textblock, mask, atEnd) + OOFAwareWidgetIterator (textblock, mask, atEnd, textblock->words->size ()) { - if (atEnd) { - if (textblock->outOfFlowMgr) { - oofm = true; - index = textblock->outOfFlowMgr->getNumWidgets(); - } else { - oofm = false; - index = textblock->words->size(); - } - } else { - oofm = false; - index = -1; - } - - content.type = atEnd ? core::Content::END : core::Content::START; } -Textblock::TextblockIterator::TextblockIterator (Textblock *textblock, - core::Content::Type mask, - bool oofm, int index): - core::Iterator (textblock, mask, false) +Textblock::TextblockIterator + *Textblock::TextblockIterator::createWordIndexIterator (Textblock *textblock, + core::Content::Type + mask, + int wordIndex) { - this->oofm = oofm; - this->index = index; - - // TODO To be completely exact, oofm should be considered here. - if (index < 0) - content.type = core::Content::START; - else if (index >= textblock->words->size()) - content.type = core::Content::END; - else - content = textblock->words->getRef(index)->content; + TextblockIterator *tbIt = new TextblockIterator (textblock, mask, false); + tbIt->setValues (0, wordIndex); + return tbIt; } object::Object *Textblock::TextblockIterator::clone() { - return - new TextblockIterator ((Textblock*)getWidget(), getMask(), oofm, index); -} - -int Textblock::TextblockIterator::compareTo(object::Comparable *other) -{ - TextblockIterator *otherTI = (TextblockIterator*)other; - - if (oofm && !otherTI->oofm) - return +1; - else if (!oofm && otherTI->oofm) - return -1; - else - return index - otherTI->index; -} - -bool Textblock::TextblockIterator::next () -{ - Textblock *textblock = (Textblock*)getWidget(); - - if (content.type == core::Content::END) - return false; - - short type; - - do { - index++; - - if (oofm) { - // Iterating over OOFM. - if (index >= textblock->outOfFlowMgr->getNumWidgets()) { - // End of OOFM list reached. - content.type = core::Content::END; - return false; - } - type = core::Content::WIDGET_OOF_CONT; - } else { - // Iterating over words list. - if (index < textblock->words->size ()) - // Still words left. - type = textblock->words->getRef(index)->content.type; - else { - // End of words list reached. - if (textblock->outOfFlowMgr) { - oofm = true; - index = 0; - if (textblock->outOfFlowMgr->getNumWidgets() > 0) - // Start with OOFM widgets. - type = core::Content::WIDGET_OOF_CONT; - else { - // No OOFM widgets (number is 0). - content.type = core::Content::END; - return false; - } - } else { - // No OOFM widgets (no OOFM agt all). - content.type = core::Content::END; - return false; - } - } - } - } while ((type & getMask()) == 0); - - if (oofm) { - content.type = core::Content::WIDGET_OOF_CONT; - content.widget = textblock->outOfFlowMgr->getWidget (index); - } else - content = textblock->words->getRef(index)->content; - - return true; -} - -bool Textblock::TextblockIterator::prev () -{ - Textblock *textblock = (Textblock*)getWidget(); - - if (content.type == core::Content::START) - return false; - - short type; - - do { - index--; - - if (oofm) { - // Iterating over OOFM. - if (index >= 0) - // Still widgets left. - type = core::Content::WIDGET_OOF_CONT; - else { - // Beginning of OOFM list reached. Continue with words. - oofm = false; - index = textblock->words->size() - 1; - if (index < 0) { - // There are no words. (Actually, this case should not - // happen: When there are OOF widgets, ther must be OOF - // references, or widgets in flow, which contain - // references. - content.type = core::Content::END; - return false; - } - type = textblock->words->getRef(index)->content.type; - } - } else { - // Iterating over words list. - if (index < 0) { - // Beginning of words list reached. - content.type = core::Content::START; - return false; - } - type = textblock->words->getRef(index)->content.type; - } - } while ((type & getMask()) == 0); - - if (oofm) { - content.type = core::Content::WIDGET_OOF_CONT; - content.type = false; - content.widget = textblock->outOfFlowMgr->getWidget (index); - } else - content = textblock->words->getRef(index)->content; - - return true; + TextblockIterator *tbIt = + new TextblockIterator ((Textblock*)getWidget(), getMask(), false); + cloneValues (tbIt); + return tbIt; } void Textblock::TextblockIterator::highlight (int start, int end, core::HighlightLayer layer) { - if (!oofm) { + DBG_OBJ_ENTER_O ("iterator", 0, getWidget (), "TextblockIterator::highlight", + "..., %d, %d, %d", start, end, layer); + + DBG_IF_RTFL { + misc::StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s", + sb.getChars ()); + } + + if (inFlow ()) { + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d", + getInFlowIndex ()); + Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; + int index = getInFlowIndex (), index1 = index, index2 = index; int oldStartIndex = textblock->hlStart[layer].index; int oldStartChar = textblock->hlStart[layer].nChar; @@ -224,22 +99,46 @@ void Textblock::TextblockIterator::highlight (int start, int end, textblock->hlEnd[layer].nChar = end; } + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index", + textblock->hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar", + textblock->hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index", + textblock->hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar", + textblock->hlEnd[layer].nChar); + if (oldStartIndex != textblock->hlStart[layer].index || oldStartChar != textblock->hlStart[layer].nChar || oldEndIndex != textblock->hlEnd[layer].index || oldEndChar != textblock->hlEnd[layer].nChar) textblock->queueDrawRange (index1, index2); - } + } else + highlightOOF (start, end, layer); - // TODO What about OOF widgets? + DBG_OBJ_LEAVE_O (getWidget ()); } void Textblock::TextblockIterator::unhighlight (int direction, core::HighlightLayer layer) { - if (!oofm) { + DBG_OBJ_ENTER_O ("iterator", 0, getWidget (), + "TextblockIterator/unhighlight", "..., %d, %d", + direction, layer); + + DBG_IF_RTFL { + misc::StringBuffer sb; + intoStringBuffer (&sb); + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "iterator: %s", + sb.getChars ()); + } + + if (inFlow ()) { + DBG_OBJ_MSGF_O ("iterator", 1, getWidget (), "in-flow index: %d", + getInFlowIndex ()); + Textblock *textblock = (Textblock*)getWidget(); - int index1 = index, index2 = index; + int index = getInFlowIndex (), index1 = index, index2 = index; if (textblock->hlStart[layer].index > textblock->hlEnd[layer].index) return; @@ -264,26 +163,33 @@ void Textblock::TextblockIterator::unhighlight (int direction, textblock->hlEnd[layer].nChar = INT_MAX; } + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "index", + textblock->hlStart[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlStart", layer, "nChar", + textblock->hlStart[layer].nChar); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "index", + textblock->hlEnd[layer].index); + DBG_OBJ_ARRATTRSET_NUM_O (textblock, "hlEnd", layer, "nChar", + textblock->hlEnd[layer].nChar); + if (oldStartIndex != textblock->hlStart[layer].index || oldStartChar != textblock->hlStart[layer].nChar || oldEndIndex != textblock->hlEnd[layer].index || oldEndChar != textblock->hlEnd[layer].nChar) textblock->queueDrawRange (index1, index2); - } + } else + unhighlightOOF (direction, layer); - // TODO What about OOF widgets? + DBG_OBJ_LEAVE_O (getWidget ()); } void Textblock::TextblockIterator::getAllocation (int start, int end, core::Allocation *allocation) { - Textblock *textblock = (Textblock*)getWidget(); + if (inFlow ()) { + Textblock *textblock = (Textblock*)getWidget(); - if (oofm) { - // TODO Consider start and end? - *allocation = - *(textblock->outOfFlowMgr->getWidget(index)->getAllocation()); - } else { + int index = getInFlowIndex (); Word *word = textblock->words->getRef (index); int firstWordOfLine, textOffset, lineYOffsetCanvas, lineBorderAscent; @@ -360,14 +266,19 @@ void Textblock::TextblockIterator::getAllocation (int start, int end, } allocation->ascent = word->size.ascent; allocation->descent = word->size.descent; - } + } else + getAllocationOOF (start, end, allocation); } -void Textblock::TextblockIterator::print () +int Textblock::TextblockIterator::numContentsInFlow () { - Iterator::print (); - printf (", oofm = %s, index = %d", oofm ? "true" : "false", index); + return ((Textblock*)getWidget())->words->size (); +} +void Textblock::TextblockIterator::getContentInFlow (int index, + core::Content *content) +{ + *content = ((Textblock*)getWidget())->words->getRef(index)->content; } } // namespace dw diff --git a/dw/textblock_linebreaking.cc b/dw/textblock_linebreaking.cc index 3c181388..31fcb833 100644 --- a/dw/textblock_linebreaking.cc +++ b/dw/textblock_linebreaking.cc @@ -75,7 +75,7 @@ void Textblock::BadnessAndPenalty::calcBadness (int totalWidth, int idealWidth, this->totalShrinkability = totalShrinkability; #endif - ratio = 0; // because this is used in print() + ratio = 0; if (totalWidth == idealWidth) { badnessState = BADNESS_VALUE; @@ -191,13 +191,6 @@ int Textblock::BadnessAndPenalty::compareTo (int penaltyIndex, return 0; } -void Textblock::BadnessAndPenalty::print () -{ - misc::StringBuffer sb; - intoStringBuffer(&sb); - printf ("%s", sb.getChars ()); -} - void Textblock::BadnessAndPenalty::intoStringBuffer(misc::StringBuffer *sb) { switch (badnessState) { @@ -247,42 +240,6 @@ void Textblock::BadnessAndPenalty::intoStringBuffer(misc::StringBuffer *sb) sb->append (")"); } -void Textblock::printWordShort (Word *word) -{ - core::Content::print (&(word->content)); -} - -void Textblock::printWordFlags (short flags) -{ - printf ("%s:%s:%s:%s:%s:%s:%s", - (flags & Word::CAN_BE_HYPHENATED) ? "h?" : "--", - (flags & Word::DIV_CHAR_AT_EOL) ? "de" : "--", - (flags & Word::PERM_DIV_CHAR) ? "dp" : "--", - (flags & Word::DRAW_AS_ONE_TEXT) ? "t1" : "--", - (flags & Word::UNBREAKABLE_FOR_MIN_WIDTH) ? "um" : "--", - (flags & Word::WORD_START) ? "st" : "--", - (flags & Word::WORD_END) ? "en" : "--"); -} - -void Textblock::printWordWithFlags (Word *word) -{ - printWordShort (word); - printf (" (flags = "); - printWordFlags (word->flags); - printf (")"); -} - -void Textblock::printWord (Word *word) -{ - printWordWithFlags (word); - - printf (" [%d / %d + %d - %d => %d + %d - %d] => ", - word->size.width, word->origSpace, getSpaceStretchability(word), - getSpaceShrinkability(word), word->totalWidth, - word->totalSpaceStretchability, word->totalSpaceShrinkability); - word->badnessAndPenalty.print (); -} - /* * ... * @@ -495,20 +452,30 @@ Textblock::Line *Textblock::addLine (int firstWord, int lastWord, mustQueueResize = true; - //printWordShort (words->getRef (line->firstWord)); - //printf (" ... "); - //printWordShort (words->getRef (line->lastWord)); - //printf (": "); - //words->getRef(line->lastWord)->badnessAndPenalty.print (); - //printf ("\n"); - int xWidget = line->textOffset; + int yLine = yOffsetOfLineCreated (line); for (int i = firstWord; i <= lastWord; i++) { Word *word = words->getRef (i); if (word->wordImgRenderer) word->wordImgRenderer->setData (xWidget, lines->size () - 1); if (word->spaceImgRenderer) word->spaceImgRenderer->setData (xWidget, lines->size () - 1); + + if (word->content.type == core::Content::WIDGET_OOF_REF) { + Widget *widget = word->content.widget; + oof::OutOfFlowMgr *oofm = + searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + // See also Textblock::sizeAllocate, and notes there about + // vertical alignment. Calculating the vertical position + // should probably be centralized. + if (oofm) { + assert (sizeRequestPosDefined); + oofm->tellPosition2 (widget, sizeRequestX + xWidget, + sizeRequestY + yLine + (line->borderAscent + - word->size.ascent)); + } + } + xWidget += word->size.width + word->effSpace; } @@ -601,7 +568,7 @@ int Textblock::wordWrap (int wordIndex, bool wrapAll) int n; if (word->content.type == core::Content::WIDGET_OOF_REF) - n = 0; + n = wrapWordOofRef (wordIndex, wrapAll); else n = wrapWordInFlow (wordIndex, wrapAll); @@ -764,14 +731,15 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) startSearch, breakPos); for (int i = startSearch; newFloatPos == -1 && i <= breakPos; i++) { core::Content *content = &(words->getRef(i)->content); - if (content->type == core::Content::WIDGET_OOF_REF && - // Later, absolutepositioned elements (which do not affect - // borders) can be ignored at this point. - (containingBlock->outOfFlowMgr->affectsLeftBorder - (content->widget) || - containingBlock->outOfFlowMgr->affectsRightBorder - (content->widget))) - newFloatPos = i; + if (content->type == core::Content::WIDGET_OOF_REF) { + for (int j = 0; newFloatPos == -1 && j < NUM_OOFM; j++) { + if ((searchOutOfFlowMgr(j)->affectsLeftBorder(content + ->widget) || + searchOutOfFlowMgr(j)->affectsRightBorder (content + ->widget))) + newFloatPos = i; + } + } } DBG_OBJ_MSGF ("construct.word", 2, "newFloatPos = %d", newFloatPos); @@ -782,10 +750,20 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) floatHandled = true; // Step 2: position the float and re-calculate the line. - lastFloatPos = newFloatPos; - containingBlock->outOfFlowMgr->tellPosition - (words->getRef(lastFloatPos)->content.widget, yNewLine); + // TODO "x" is not quite correct, but this does not matter + // (currently?). + + lastFloatPos = newFloatPos; + + Widget *widget = words->getRef(lastFloatPos)->content.widget; + oof::OutOfFlowMgr *oofm = + searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + if (oofm && oofm->mayAffectBordersAtAll ()) { + assert (sizeRequestPosDefined); + oofm->tellPosition1 (widget, sizeRequestX + boxOffsetX (), + sizeRequestY + yNewLine); + } balanceBreakPosAndHeight (wordIndex, firstIndex, &searchUntil, tempNewLine, penaltyIndex, false, @@ -851,8 +829,7 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) firstWordWithoutLine = lines->getLastRef()->lastWord + 1; if (wordIndex >= firstWordWithoutLine) { - word->content.widget->parentRef = - OutOfFlowMgr::createRefNormalFlow (lines->size ()); + word->content.widget->parentRef = makeParentRefInFlow (lines->size ()); DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef", word->content.widget->parentRef); } @@ -863,6 +840,33 @@ int Textblock::wrapWordInFlow (int wordIndex, bool wrapAll) return diffWords; } +int Textblock::wrapWordOofRef (int wordIndex, bool wrapAll) +{ + DBG_OBJ_ENTER ("construct.word", 0, "wrapWordOofRef", "%d, %s", + wordIndex, wrapAll ? "true" : "false"); + + Word *word = words->getRef (wordIndex); + Widget *widget = word->content.widget; + int yNewLine = yOffsetOfLineToBeCreated (); + + // Floats, which affect either border, are handled in wrapWordInFlow; this + // is rather for positioned elements (but only for completeness: + // tellPosition1 is not implemented for positioned elements). + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (getWidgetOOFIndex (widget)); + DBG_OBJ_MSGF ("construct.word", 1, "parentRef = %d, oofm = %p", + widget->parentRef, oofm); + if (oofm && !oofm->mayAffectBordersAtAll ()) { + // TODO Again, "x" is not correct (see above). + assert (sizeRequestPosDefined); + oofm->tellPosition1 (widget, sizeRequestX + boxOffsetX (), + sizeRequestY + yNewLine); + } + + DBG_OBJ_LEAVE (); + + return 0; // Words list not changed. +} + // *height must be initialized, but not *breakPos. // *wordIndexEnd must be initialized (initially to wordIndex) void Textblock::balanceBreakPosAndHeight (int wordIndex, int firstIndex, @@ -1469,8 +1473,9 @@ void Textblock::moveWordIndices (int wordIndex, int num, int *addIndex1) DBG_OBJ_ENTER ("construct.word", 0, "moveWordIndices", "%d, %d", wordIndex, num); - if (containingBlock->outOfFlowMgr) - containingBlock->outOfFlowMgr->moveExternalIndices (this, wordIndex, num); + for (int i = 0; i < NUM_OOFM; i++) + if (searchOutOfFlowMgr(i)) + searchOutOfFlowMgr(i)->moveExternalIndices (this, wordIndex, num); for (int i = lines->size () - 1; i >= 0; i--) { Line *line = lines->getRef (i); @@ -1542,8 +1547,7 @@ void Textblock::accumulateWordForLine (int lineIndex, int wordIndex) borderDescent = marginDescent - word->content.widget->getStyle()->margin.bottom; - word->content.widget->parentRef = - OutOfFlowMgr::createRefNormalFlow (lineIndex); + word->content.widget->parentRef = makeParentRefInFlow (lineIndex); DBG_OBJ_SET_NUM_O (word->content.widget, "parentRef", word->content.widget->parentRef); } else { @@ -1669,8 +1673,7 @@ int Textblock::calcLineBreakWidth (int lineIndex) if (limitTextWidth && layout->getUsesViewport () && // margin/border/padding will be subtracted later, via OOFM. - lineBreakWidth - getStyle()->boxDiffWidth() - > layout->getWidthViewport () - 10) + lineBreakWidth - boxDiffWidth() > layout->getWidthViewport () - 10) lineBreakWidth = layout->getWidthViewport () - 10; if (lineIndex == 0) lineBreakWidth -= line1OffsetEff; @@ -1682,8 +1685,8 @@ int Textblock::calcLineBreakWidth (int lineIndex) } else leftBorder = rightBorder = 0; - leftBorder = misc::max (leftBorder, getStyle()->boxOffsetX()); - rightBorder = misc::max (rightBorder, getStyle()->boxRestWidth()); + leftBorder = misc::max (leftBorder, boxOffsetX()); + rightBorder = misc::max (rightBorder, boxRestWidth()); lineBreakWidth -= (leftBorder + rightBorder); @@ -1885,8 +1888,24 @@ void Textblock::rewrap () for (int i = firstWord; i < words->size (); i++) { Word *word = words->getRef (i); - if (word->content.type == core::Content::WIDGET_IN_FLOW) - word->content.widget->sizeRequest (&word->size); + switch (word->content.type) { + case core::Content::WIDGET_IN_FLOW: + calcSizeOfWidgetInFlow (i, word->content.widget, &word->size); + DBG_SET_WORD_SIZE (i); + break; + + case core::Content::WIDGET_OOF_REF: + { + int oofmIndex = getOOFMIndex (word->content.widget); + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr (oofmIndex); + oofm->calcWidgetRefSize (word->content.widget, &(word->size)); + DBG_SET_WORD_SIZE (i); + } + break; + + default: + break; + } wordWrap (i, false); @@ -1972,17 +1991,6 @@ void Textblock::initNewLine () { DBG_OBJ_ENTER0 ("construct.line", 0, "initNewLine"); - // At the very beginning, in Textblock::Textblock, where this - // method is called, containingBlock is not yet defined. - - if (containingBlock && containingBlock->outOfFlowMgr) { - if (lines->size () == 0) { - int clearPosition = - containingBlock->outOfFlowMgr->getClearPosition (this); - setVerticalOffset (misc::max (clearPosition, 0)); - } - } - calcBorders (lines->size() > 0 ? lines->getLastRef()->lastOofRefPositionedBeforeThisLine : -1, 1); @@ -2000,81 +2008,98 @@ void Textblock::calcBorders (int lastOofRef, int height) DBG_OBJ_ENTER ("construct.line", 0, "calcBorders", "%d, %d", lastOofRef, height); - if (containingBlock && containingBlock->outOfFlowMgr) { - // Consider the example: - // - // <div> - // Some text A ... - // <p> Some text B ... <img style="float:right" ...> </p> - // Some more text C ... - // </div> - // - // If the image is large enough, it should float around the last - // paragraph, "Some more text C ...": - // - // Some more text A ... - // - // Some more ,---------. - // text B ... | | - // | <img> | - // Some more | | <---- Consider this line! - // text C ... '---------' - // - // Since this float is generated in the <p> element, not in the- - // <div> element, and since they are represented by different - // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine, - // and so lastOofRef, is -1 for the line marked with an arrow; - // this would result in ignoring the float, because -1 is - // equivalent to the very beginning of the <div> element ("Some - // more text A ..."), which is not affected by the float. - // - // On the other hand, the only relevant values of - // Line::lastOofRefPositionedBeforeThisLine are those greater - // than the first word of the new line, so a solution is to use - // the maximum of both. + newLineHasFloatLeft = newLineHasFloatRight = false; + newLineLeftBorder = newLineRightBorder = 0; + newLineLeftFloatHeight = newLineRightFloatHeight = 0; + bool oofmDefined = false; + for (int i = 0; i < NUM_OOFM && !oofmDefined; i++) + if (searchOutOfFlowMgr(i)) + oofmDefined = true; - int firstWordOfLine = lines->size() > 0 ? - lines->getLastRef()->lastWord + 1 : 0; + if (oofmDefined) { + int firstWordOfLine = + lines->size() > 0 ? lines->getLastRef()->lastWord + 1 : 0; int effOofRef = misc::max (lastOofRef, firstWordOfLine - 1); - - int y = yOffsetOfLineToBeCreated (); - - newLineHasFloatLeft = - containingBlock->outOfFlowMgr->hasFloatLeft (this, y, height, this, - effOofRef); - newLineHasFloatRight = - containingBlock->outOfFlowMgr->hasFloatRight (this, y, height, this, - effOofRef); - newLineLeftBorder = - containingBlock->outOfFlowMgr->getLeftBorder (this, y, height, this, - effOofRef); - newLineRightBorder = - containingBlock->outOfFlowMgr->getRightBorder (this, y, height, this, - effOofRef); - newLineLeftFloatHeight = newLineHasFloatLeft ? - containingBlock->outOfFlowMgr->getLeftFloatHeight (this, y, height, - this, effOofRef) : - 0; - newLineRightFloatHeight = newLineHasFloatRight ? - containingBlock->outOfFlowMgr->getRightFloatHeight (this, y, height, - this, effOofRef) : - 0; - - DBG_OBJ_MSGF ("construct.line", 1, - "%d * %d (%s) / %d * %d (%s), at %d (%d), until %d = " - "max (%d, %d - 1)", - newLineLeftBorder, newLineLeftFloatHeight, - newLineHasFloatLeft ? "true" : "false", - newLineRightBorder, newLineRightFloatHeight, - newLineHasFloatRight ? "true" : "false", - y, height, effOofRef, lastOofRef, firstWordOfLine); - } else { - newLineHasFloatLeft = newLineHasFloatRight = false; - newLineLeftBorder = newLineRightBorder = 0; - newLineLeftFloatHeight = newLineRightFloatHeight = 0; - - DBG_OBJ_MSG ("construct.line", 0, "<i>no CB of OOFM</i>"); + assert (sizeRequestPosDefined); + int y = sizeRequestY + yOffsetOfLineToBeCreated (); + + for (int i = 0; i < NUM_OOFM; i++) { + oof::OutOfFlowMgr *oofm = searchOutOfFlowMgr(i); + if (oofm) { + // Consider the example: + // + // <div> + // Some text A ... + // <p> Some text B ... <img style="float:right" ...> </p> + // Some more text C ... + // </div> + // + // If the image is large enough, it should float around the last + // paragraph, "Some more text C ...": + // + // Some more text A ... + // + // Some more ,---------. + // text B ... | | + // | <img> | + // Some more | | <---- Consider this line! + // text C ... '---------' + // + // Since this float is generated in the <p> element, not in the- + // <div> element, and since they are represented by different + // instances of dw::Textblock, lastOofRefPositionedBeforeThisLine, + // and so lastOofRef, is -1 for the line marked with an arrow; + // this would result in ignoring the float, because -1 is + // equivalent to the very beginning of the <div> element ("Some + // more text A ..."), which is not affected by the float. + // + // On the other hand, the only relevant values of + // Line::lastOofRefPositionedBeforeThisLine are those greater + // than the first word of the new line, so a solution is to use + // the maximum of both. + + bool thisHasLeft, thisHasRight; + + thisHasLeft = oofm->hasFloatLeft (this, y, height, this, effOofRef); + newLineHasFloatLeft = newLineHasFloatLeft || thisHasLeft; + thisHasRight = oofm->hasFloatRight (this, y, height, this, + effOofRef); + newLineHasFloatRight = newLineHasFloatRight || thisHasRight; + + newLineLeftBorder = + misc::max (newLineLeftBorder, + oofm->getLeftBorder (this, y, height, this, + effOofRef)); + newLineRightBorder = + misc::max (newLineRightBorder, + oofm->getRightBorder (this, y, height, this, + effOofRef)); + + // TODO "max" is not really correct for the heights. (Does + // not matter, since only one, the float manager, returns + // meaningful values.) + if (thisHasLeft) + newLineLeftFloatHeight = + misc::max (newLineLeftFloatHeight, + oofm->getLeftFloatHeight (this, y, height, this, + effOofRef)); + if (thisHasRight) + newLineRightFloatHeight = + misc::max (newLineRightFloatHeight, + oofm->getRightFloatHeight (this, y, height, this, + effOofRef)); + + DBG_OBJ_MSGF ("construct.line", 1, + "OOFM #%d: %d * %d (%s) / %d * %d (%s), at %d (%d), " + "until %d = max (%d, %d - 1)", + i, newLineLeftBorder, newLineLeftFloatHeight, + newLineHasFloatLeft ? "true" : "false", + newLineRightBorder, newLineRightFloatHeight, + newLineHasFloatRight ? "true" : "false", + y, height, effOofRef, lastOofRef, firstWordOfLine); + } + } } DBG_OBJ_SET_BOOL ("newLineHasFloatLeft", newLineHasFloatLeft); diff --git a/dw/types.hh b/dw/types.hh index b6b4ca0b..481f4804 100644 --- a/dw/types.hh +++ b/dw/types.hh @@ -208,6 +208,10 @@ struct Content WIDGET_OOF_REF = 1 << 5, BREAK = 1 << 6, + /** \brief can be used internally, but should never be exposed, + e. g. by iterators */ + INVALID = 1 << 7, + ALL = 0xff, REAL_CONTENT = 0xff ^ (START | END), SELECTION_CONTENT = TEXT | BREAK, // WIDGET_* must be set additionally @@ -233,6 +237,67 @@ struct Content static void printMask (Type mask); }; +/** + * \brief Base class for dw::core::DrawingContext and + * dw::core::GettingWidgetAtPointContext. + * + * Not to be confused with the *stacking context* as defined by CSS. + */ +class StackingProcessingContext +{ +private: + lout::container::typed::HashSet<lout::object::TypedPointer<Widget> > + *widgetsProcessedAsInterruption; + +public: + inline StackingProcessingContext () { + widgetsProcessedAsInterruption = + new lout::container::typed::HashSet<lout::object:: + TypedPointer<Widget> > (true); + } + + inline ~StackingProcessingContext () + { delete widgetsProcessedAsInterruption; } + + inline bool hasWidgetBeenProcessedAsInterruption (Widget *widget) { + lout::object::TypedPointer<Widget> key (widget); + return widgetsProcessedAsInterruption->contains (&key); + } + + inline void addWidgetProcessedAsInterruption (Widget *widget) { + lout::object::TypedPointer<Widget> *key = + new lout::object::TypedPointer<Widget> (widget); + return widgetsProcessedAsInterruption->put (key); + } +}; + +/** + * \brief Set at the top when drawing. + * + * See \ref dw-interrupted-drawing for details. + */ +class DrawingContext: public StackingProcessingContext +{ +private: + Rectangle toplevelArea; + +public: + inline DrawingContext (Rectangle *toplevelArea) { + this->toplevelArea = *toplevelArea; + } + + inline Rectangle *getToplevelArea () { return &toplevelArea; } +}; + +/** + * \brief Set at the top when getting the widget at the point. + * + * Similar to dw::core::DrawingContext. + */ +class GettingWidgetAtPointContext: public StackingProcessingContext +{ +}; + } // namespace core } // namespace dw @@ -48,7 +48,8 @@ Embed::~Embed() DBG_OBJ_DELETE (); } -void Embed::sizeRequestImpl (Requisition *requisition) +void Embed::sizeRequestImpl (Requisition *requisition, bool posDefined, int x, + int y) { resource->sizeRequest (requisition); } @@ -131,10 +132,10 @@ void Embed::setEnabled (bool enabled) resource->setEnabled (enabled); } -void Embed::draw (View *view, Rectangle *area) +void Embed::draw (View *view, Rectangle *area, DrawingContext *context) { drawWidgetBox (view, area, false); - resource->draw (view, area); + resource->draw (view, area, context); } Iterator *Embed::iterator (Content::Type mask, bool atEnd) @@ -266,7 +267,7 @@ void Resource::setDisplayed (bool displayed) { } -void Resource::draw (View *view, Rectangle *area) +void Resource::draw (View *view, Rectangle *area, DrawingContext *context) { } @@ -317,7 +318,7 @@ Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd) void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged) { - DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver/resizeQueued", "%s", + DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver::resizeQueued", "%s", extremesChanged ? "true" : "false"); resource->queueResize (extremesChanged); DBG_OBJ_LEAVE (); @@ -228,7 +228,8 @@ private: Resource *resource; protected: - void sizeRequestImpl (Requisition *requisition); + void sizeRequestImpl (Requisition *requisition, bool posDefined, int x, + int y); void getExtremesImpl (Extremes *extremes); void sizeAllocateImpl (Allocation *allocation); @@ -254,7 +255,7 @@ public: void setDisplayed (bool displayed); void setEnabled (bool enabled); - void draw (View *view, Rectangle *area); + void draw (View *view, Rectangle *area, DrawingContext *context); Iterator *iterator (Content::Type mask, bool atEnd); void setStyle (style::Style *style); @@ -369,7 +370,7 @@ public: virtual void containerSizeChangedForChildren (); virtual void setDisplayed (bool displayed); - virtual void draw (View *view, Rectangle *area); + virtual void draw (View *view, Rectangle *area, DrawingContext *context); virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; virtual void setStyle (style::Style *style); diff --git a/dw/widget.cc b/dw/widget.cc index 464459ba..252ff8c8 100644 --- a/dw/widget.cc +++ b/dw/widget.cc @@ -91,6 +91,8 @@ Widget::Widget () deleteCallbackFunc = NULL; widgetImgRenderer = NULL; + + stackingContextMgr = NULL; } Widget::~Widget () @@ -104,6 +106,9 @@ Widget::~Widget () delete widgetImgRenderer; } + if (stackingContextMgr) + delete stackingContextMgr; + if (style) style->unref (); @@ -117,35 +122,111 @@ Widget::~Widget () /** - * \brief Calculates the intersection of widget->allocation and area, returned - * in intersection (in widget coordinates!). + * \brief Calculates the intersection of the visible allocation + * (i. e. the intersection with the visible parent allocation) and + * "area" (in widget coordinates referring to "refWidget"), + * returned in intersection (in widget coordinates). * - * Typically used by containers when - * drawing their children. Returns whether intersection is not empty. + * Typically used by containers when drawing their children (passing + * "this" as "refWidget"). Returns whether intersection is not empty. */ -bool Widget::intersects (Rectangle *area, Rectangle *intersection) +bool Widget::intersects (Widget *refWidget, Rectangle *area, + Rectangle *intersection) { - Rectangle parentArea, childArea; + DBG_OBJ_ENTER ("draw", 0, "intersects", "%p, [%d, %d, %d * %d]", + refWidget, area->x, area->y, area->width, area->height); + bool r; - parentArea = *area; - parentArea.x += parent->allocation.x; - parentArea.y += parent->allocation.y; + if (wasAllocated ()) { + *intersection = *area; + intersection->x += refWidget->allocation.x; + intersection->y += refWidget->allocation.y; + + r = true; + // "RefWidget" is excluded; it is assumed that "area" its already within + // its allocation. + for (Widget *widget = this; r && widget != refWidget; + widget = widget->parent) { + assert (widget != NULL); // refWidget must be ancestor. + + Rectangle widgetArea, newIntersection; + widgetArea.x = widget->allocation.x; + widgetArea.y = widget->allocation.y; + widgetArea.width = widget->allocation.width; + widgetArea.height = widget->getHeight (); + + if (intersection->intersectsWith (&widgetArea, &newIntersection)) { + DBG_OBJ_MSGF ("draw", 1, "new intersection: %d, %d, %d * %d", + newIntersection.x, newIntersection.y, + newIntersection.width, newIntersection.height); + *intersection = newIntersection; + } else { + DBG_OBJ_MSG ("draw", 1, "no new intersection"); + r = false; + } + } - childArea.x = allocation.x; - childArea.y = allocation.y; - childArea.width = allocation.width; - childArea.height = getHeight (); + if (r) { + intersection->x -= allocation.x; + intersection->y -= allocation.y; - if (parentArea.intersectsWith (&childArea, intersection)) { - intersection->x -= allocation.x; - intersection->y -= allocation.y; - return true; - } else - return false; + DBG_OBJ_MSGF ("draw", 1, "final intersection: %d, %d, %d * %d", + intersection->x, intersection->y, + intersection->width, intersection->height); + } + } else { + r = false; + DBG_OBJ_MSG ("draw", 1, "not allocated"); + } + + if (r) + DBG_OBJ_MSGF ("draw", 1, "=> true: %d, %d, %d * %d", + intersection->x, intersection->y, + intersection->width, intersection->height); + else + DBG_OBJ_MSG ("draw", 1, "=> false"); + + DBG_OBJ_LEAVE (); + return r; +} + +/** + * See \ref dw-interrupted-drawing for details. + */ +void Widget::drawInterruption (View *view, Rectangle *area, + DrawingContext *context) +{ + Rectangle thisArea; + if (intersects (layout->topLevel, context->getToplevelArea (), &thisArea)) + draw (view, &thisArea, context); + + context->addWidgetProcessedAsInterruption (this); +} + +Widget *Widget::getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context) +{ + // Suitable for simple widgets, without children. + + if (inAllocation (x, y)) + return this; + else + return NULL; +} + +Widget *Widget::getWidgetAtPointInterrupted (int x, int y, + GettingWidgetAtPointContext + *context) +{ + Widget *widgetAtPoint = getWidgetAtPoint (x, y, context); + context->addWidgetProcessedAsInterruption (this); + return widgetAtPoint; } void Widget::setParent (Widget *parent) { + DBG_OBJ_ENTER ("construct", 0, "setParent", "%p", parent); + this->parent = parent; layout = parent->layout; @@ -170,7 +251,21 @@ void Widget::setParent (Widget *parent) // Textblock. DBG_OBJ_SET_PTR ("container", container); + // If at all, stackingContextMgr should have set *before*, see also + // Widget::setStyle() and Layout::addWidget(). + if (stackingContextMgr) { + Widget *stackingContextWidget = parent; + while (stackingContextWidget && + stackingContextWidget->stackingContextMgr == NULL) + stackingContextWidget = stackingContextWidget->parent; + assert (stackingContextWidget); + stackingContextWidget->stackingContextMgr->addChildSCWidget (this); + } else + stackingContextWidget = parent->stackingContextWidget; + notifySetParent(); + + DBG_OBJ_LEAVE (); } void Widget::setQuasiParent (Widget *quasiParent) @@ -472,7 +567,8 @@ bool Widget::usesAvailHeight () * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls * the latter only when needed. */ -void Widget::sizeRequest (Requisition *requisition) +void Widget::sizeRequest (Requisition *requisition, bool posDefined, int x, + int y) { assert (!queueResizeEntered ()); @@ -490,8 +586,9 @@ void Widget::sizeRequest (Requisition *requisition) } if (needsResize ()) { + calcExtraSpace (); /** \todo Check requisition == &(this->requisition) and do what? */ - sizeRequestImpl (requisition); + sizeRequestImpl (requisition, posDefined, x, y); this->requisition = *requisition; unsetFlags (NEEDS_RESIZE); @@ -519,7 +616,7 @@ int Widget::getMinWidth (Extremes *extremes, bool forceValue) { DBG_IF_RTFL { if (extremes) - DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "[%d (%d) / %d (%d), %s", + DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "[%d (%d) / %d (%d)], %s", extremes->minWidth, extremes->minWidthIntrinsic, extremes->maxWidth, extremes->maxWidthIntrinsic, forceValue ? "true" : "false"); @@ -627,7 +724,7 @@ int Widget::getAvailHeight (bool forceValue) } else if (style::isPerLength (getStyle()->height)) { DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", 100 * style::perLengthVal_useThisOnlyForDebugging - (getStyle()->height)); + (getStyle()->height)); // Notice that here -- unlike getAvailWidth() -- // layout->hScrollbarThickness is not considered here; // something like canvasWidthGreater (analogue to @@ -909,6 +1006,8 @@ void Widget::getExtremes (Extremes *extremes) } if (extremesChanged ()) { + calcExtraSpace (); + // For backward compatibility (part 1/2): extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; @@ -939,6 +1038,28 @@ void Widget::getExtremes (Extremes *extremes) } /** + * \brief Calculates dw::core::Widget::extraSpace. + * + * Delegated to dw::core::Widget::calcExtraSpaceImpl. Called both from + * dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes. + */ +void Widget::calcExtraSpace () +{ + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + calcExtraSpaceImpl (); + + DBG_OBJ_SET_NUM ("extraSpace.top", extraSpace.top); + DBG_OBJ_SET_NUM ("extraSpace.bottom", extraSpace.bottom); + DBG_OBJ_SET_NUM ("extraSpace.left", extraSpace.left); + DBG_OBJ_SET_NUM ("extraSpace.right", extraSpace.right); +} + +Widget *Widget::sizeRequestReference () +{ + return NULL; +} + +/** * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when * needed. */ @@ -1082,6 +1203,16 @@ void Widget::setStyle (style::Style *style) layout->updateCursor (); } + // After Layout::addWidget() (as toplevel widget) or Widget::setParent() + // (which also sets layout), changes of the style cannot be considered + // anymore. (Should print a warning?) + if (layout == NULL && + StackingContextMgr::isEstablishingStackingContext (this)) { + stackingContextMgr = new StackingContextMgr (this); + DBG_OBJ_ASSOC_CHILD (stackingContextMgr); + stackingContextWidget = this; + } + if (sizeChanged) queueResize (0, true); else @@ -1196,7 +1327,7 @@ void Widget::drawBox (View *view, style::Style *style, Rectangle *area, // TODO Handle inverse drawing the same way as in drawWidgetBox? // Maybe this method (drawBox) is anyway obsolete when extraSpace - // is fully supported (as in the "dillo_grows" repository). + // is fully supported (as here, in the "dillo_grows" repository). int xPad, yPad, widthPad, heightPad; getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); @@ -1226,8 +1357,10 @@ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) canvasArea.width = area->width; canvasArea.height = area->height; - style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y, - allocation.width, getHeight (), style, inverse); + int xMar, yMar, widthMar, heightMar; + getMarginArea (&xMar, &yMar, &widthMar, &heightMar); + style::drawBorder (view, layout, &canvasArea, xMar, yMar, widthMar, + heightMar, style, inverse); int xPad, yPad, widthPad, heightPad; getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); @@ -1363,53 +1496,6 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) return widget1; } - -/** - * \brief Search recursively through widget. - * - * Used by dw::core::Layout:getWidgetAtPoint. - */ -Widget *Widget::getWidgetAtPoint (int x, int y, int level) -{ - Iterator *it; - Widget *childAtPoint; - - //printf ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", - // 3 * level, "", getClassName (), this, allocation.x, allocation.y, - // allocation.width, allocation.ascent, allocation.descent); - - if (x >= allocation.x && - y >= allocation.y && - x <= allocation.x + allocation.width && - y <= allocation.y + getHeight ()) { - //_MSG ("%*s -> inside\n", 3 * level, ""); - /* - * Iterate over the children of this widget. Test recursively, whether - * the point is within the child (or one of its children...). If there - * is such a child, it is returned. Otherwise, this widget is returned. - */ - childAtPoint = NULL; - it = iterator ((Content::Type) - (Content::WIDGET_IN_FLOW | Content::WIDGET_OOF_CONT), - false); - - while (childAtPoint == NULL && it->next ()) { - Widget *child = it->getContent()->widget; - if (child->wasAllocated ()) - childAtPoint = child->getWidgetAtPoint (x, y, level + 1); - } - - it->unref (); - - if (childAtPoint) - return childAtPoint; - else - return this; - } else - return NULL; -} - - void Widget::scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height) { @@ -1417,6 +1503,24 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos, x + allocation.x, y + allocation.y, width, height); } +void Widget::getMarginArea (int *xMar, int *yMar, int *widthMar, int *heightMar) +{ + *xMar = allocation.x + extraSpace.left; + *yMar = allocation.y + extraSpace.top; + *widthMar = allocation.width - (extraSpace.left + extraSpace.right); + *heightMar = getHeight () - (extraSpace.top + extraSpace.bottom); +} + +void Widget::getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor) +{ + getMarginArea (xBor, yBor, widthBor, heightBor); + + *xBor += style->margin.left; + *yBor += style->margin.top; + *widthBor -= style->margin.left + style->margin.right; + *heightBor -= style->margin.top + style->margin.bottom; +} + /** * \brief Return the padding area (content plus padding). * @@ -1426,18 +1530,31 @@ void Widget::scrollTo (HPosition hpos, VPosition vpos, void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad) { - *xPad = allocation.x + style->margin.left + style->borderWidth.left; - *yPad = allocation.y + style->margin.top + style->borderWidth.top; - *widthPad = allocation.width - style->margin.left - style->borderWidth.left - - style->margin.right - style->borderWidth.right; - *heightPad = getHeight () - style->margin.top - style->borderWidth.top - - style->margin.bottom - style->borderWidth.bottom; + getBorderArea (xPad, yPad, widthPad, heightPad); + + *xPad += style->borderWidth.left; + *yPad += style->borderWidth.top; + *widthPad -= style->borderWidth.left + style->borderWidth.right; + *heightPad -= style->borderWidth.top + style->borderWidth.bottom; } void Widget::sizeAllocateImpl (Allocation *allocation) { } +/** + * \brief The actual implementation for calculating + * dw::core::Widget::extraSpace. + * + * The implementation gets a clean value of + * dw::core::Widget::extraSpace, which is only corrected. To make sure + * all possible influences are considered, the implementation of the + * base class should be called, too. + */ +void Widget::calcExtraSpaceImpl () +{ +} + void Widget::markSizeChange (int ref) { } @@ -1753,6 +1870,7 @@ void Widget::leaveNotifyImpl (EventCrossing *) tooltip->onLeave(); } + void Widget::removeChild (Widget *child) { // Should be implemented. diff --git a/dw/widget.hh b/dw/widget.hh index f9d1293c..a9a38087 100644 --- a/dw/widget.hh +++ b/dw/widget.hh @@ -180,19 +180,36 @@ protected: Allocation allocation; inline int getHeight () { return allocation.ascent + allocation.descent; } - inline int getContentWidth() { return allocation.width - - style->boxDiffWidth (); } - inline int getContentHeight() { return getHeight () - - style->boxDiffHeight (); } + inline int getContentWidth() { return allocation.width - boxDiffWidth (); } + inline int getContentHeight() { return getHeight () - boxDiffHeight (); } Layout *layout; /** * \brief Space around the margin box. Allocation is extraSpace + - * margin + border + padding + contents; + * margin + border + padding + contents. + * + * See also dw::core::Widget::calcExtraSpace and + * dw::core::Widget::calcExtraSpaceImpl. Also, it is feasible to + * correct this value within dw::core::Widget::sizeRequestImpl. */ style::Box extraSpace; + /** + * \brief Set iff this widget constitutes a stacking context, as defined by + * CSS. + */ + StackingContextMgr *stackingContextMgr; + + /** + * \brief The bottom-most ancestor (or this) for which stackingContextMgr is + * set. + */ + Widget *stackingContextWidget; + + inline StackingContextMgr *getNextStackingContextMgr () + { return stackingContextWidget->stackingContextMgr; } + /*inline void printFlags () { DBG_IF_RTFL { char buf[10 * 3 - 1 + 1]; @@ -254,7 +271,6 @@ protected: inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); printFlag (f); } - inline void queueDraw () { queueDrawArea (0, 0, allocation.width, getHeight()); } void queueDrawArea (int x, int y, int width, int height); @@ -264,13 +280,16 @@ protected: /** * \brief See \ref dw-widget-sizes. */ - virtual void sizeRequestImpl (Requisition *requisition) = 0; - + virtual void sizeRequestImpl (Requisition *requisition, bool posDefined, + int x, int y) = 0; + /** * \brief See \ref dw-widget-sizes. */ virtual void getExtremesImpl (Extremes *extremes) = 0; + virtual void calcExtraSpaceImpl (); + /** * \brief See \ref dw-widget-sizes. */ @@ -292,8 +311,6 @@ protected: */ virtual void markExtremesChange (int ref); - int getMinWidth (Extremes *extremes, bool forceValue); - virtual int getAvailWidthOfChild (Widget *child, bool forceValue); virtual int getAvailHeightOfChild (Widget *child, bool forceValue); virtual void correctRequisitionOfChild (Widget *child, @@ -410,6 +427,11 @@ public: inline style::Style *getStyle () { return style; } /** \todo I do not like this. */ inline Allocation *getAllocation () { return &allocation; } + inline bool inAllocation (int x, int y) { + return wasAllocated () && x >= allocation.x && y >= allocation.y && + x <= allocation.x + allocation.width && + y <= allocation.y + getHeight (); + } inline int boxOffsetX () { return extraSpace.left + getStyle()->boxOffsetX (); } @@ -422,10 +444,18 @@ public: { return extraSpace.bottom + getStyle()->boxRestHeight (); } inline int boxDiffHeight () { return boxOffsetY () + boxRestHeight (); } - void sizeRequest (Requisition *requisition); + /** + * \brief See \ref dw-widget-sizes (or \ref dw-size-request-pos). + */ + virtual Widget *sizeRequestReference (); + + void sizeRequest (Requisition *requisition, bool posDefined = false, + int x = 0, int y = 0); void getExtremes (Extremes *extremes); void sizeAllocate (Allocation *allocation); + void calcExtraSpace (); + int getAvailWidth (bool forceValue); int getAvailHeight (bool forceValue); virtual bool getAdjustMinWidth () { return Widget::adjustMinWidth; } @@ -442,15 +472,24 @@ public: virtual int applyPerWidth (int containerWidth, style::Length perWidth); virtual int applyPerHeight (int containerHeight, style::Length perHeight); + int getMinWidth (Extremes *extremes, bool forceValue); + virtual bool isBlockLevel (); virtual bool isPossibleContainer (); void containerSizeChanged (); - bool intersects (Rectangle *area, Rectangle *intersection); + bool intersects (Widget *refWidget, Rectangle *area, + Rectangle *intersection); /** Area is given in widget coordinates. */ - virtual void draw (View *view, Rectangle *area) = 0; + virtual void draw (View *view, Rectangle *area, DrawingContext *context) = 0; + void drawInterruption (View *view, Rectangle *area, DrawingContext *context); + + virtual Widget *getWidgetAtPoint (int x, int y, + GettingWidgetAtPointContext *context); + Widget *getWidgetAtPointInterrupted (int x, int y, + GettingWidgetAtPointContext *context); bool buttonPress (EventButton *event); bool buttonRelease (EventButton *event); @@ -481,11 +520,11 @@ public: inline Layout *getLayout () { return layout; } - virtual Widget *getWidgetAtPoint (int x, int y, int level); - void scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height); + void getMarginArea (int *xMar, int *yMar, int *widthMar, int *heightMar); + void getBorderArea (int *xBor, int *yBor, int *widthBor, int *heightBor); void getPaddingArea (int *xPad, int *yPad, int *widthPad, int *heightPad); /** @@ -502,6 +541,7 @@ public: * dw::core::Iterator::prev in this case. */ virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0; + virtual void removeChild (Widget *child); }; |