diff options
Diffstat (limited to 'dw/widget.cc')
-rw-r--r-- | dw/widget.cc | 1785 |
1 files changed, 1785 insertions, 0 deletions
diff --git a/dw/widget.cc b/dw/widget.cc new file mode 100644 index 0000000..4ebce30 --- /dev/null +++ b/dw/widget.cc @@ -0,0 +1,1785 @@ +/* + * RTFL (originally part of dillo) + * + * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version; with the following exception: + * + * The copyright holders of RTFL give you permission to link this file + * statically or dynamically against all versions of the graphviz + * library, which are published by AT&T Corp. under one of the following + * licenses: + * + * - Common Public License version 1.0 as published by International + * Business Machines Corporation (IBM), or + * - Eclipse Public License version 1.0 as published by the Eclipse + * Foundation. + * + * 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/msg.h" +#include "../lout/debug.hh" + +using namespace lout; +using namespace lout::object; + +namespace dw { +namespace core { + +// ---------------------------------------------------------------------- + +bool Widget::WidgetImgRenderer::readyToDraw () +{ + return widget->wasAllocated (); +} + +void Widget::WidgetImgRenderer::getBgArea (int *x, int *y, int *width, + int *height) +{ + widget->getPaddingArea (x, y, width, height); +} + +void Widget::WidgetImgRenderer::getRefArea (int *xRef, int *yRef, int *widthRef, + int *heightRef) +{ + widget->getPaddingArea (xRef, yRef, widthRef, heightRef); +} + +style::Style *Widget::WidgetImgRenderer::getStyle () +{ + return widget->getStyle (); +} + +void Widget::WidgetImgRenderer::draw (int x, int y, int width, int height) +{ + widget->queueDrawArea (x - widget->allocation.x, y - widget->allocation.y, + width, height); +} + +// ---------------------------------------------------------------------- + +bool Widget::adjustMinWidth = false; +int Widget::CLASS_ID = -1; + +Widget::Widget () +{ + DBG_OBJ_CREATE ("dw::core::Widget"); + registerName ("dw::core::Widget", &CLASS_ID); + + flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED); + parent = quasiParent = generator = container = NULL; + DBG_OBJ_SET_PTR ("container", container); + + layout = NULL; + + allocation.x = -1; + allocation.y = -1; + allocation.width = 1; + allocation.ascent = 1; + allocation.descent = 0; + + extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0; + + style = NULL; + bgColor = NULL; + buttonSensitive = true; + buttonSensitiveSet = false; + + deleteCallbackData = NULL; + deleteCallbackFunc = NULL; + + widgetImgRenderer = NULL; +} + +Widget::~Widget () +{ + if (deleteCallbackFunc) + deleteCallbackFunc (deleteCallbackData); + + if (widgetImgRenderer) { + if (style && style->backgroundImage) + style->backgroundImage->removeExternalImgRenderer (widgetImgRenderer); + delete widgetImgRenderer; + } + + if (style) + style->unref (); + + if (parent) + parent->removeChild (this); + else if (layout) + layout->removeWidget (); + + DBG_OBJ_DELETE (); +} + + +/** + * \brief Calculates the intersection of widget->allocation and area, returned + * in intersection (in widget coordinates!). + * + * Typically used by containers when + * drawing their children. Returns whether intersection is not empty. + */ +bool Widget::intersects (Rectangle *area, Rectangle *intersection) +{ + Rectangle parentArea, childArea; + + parentArea = *area; + parentArea.x += parent->allocation.x; + parentArea.y += parent->allocation.y; + + childArea.x = allocation.x; + childArea.y = allocation.y; + childArea.width = allocation.width; + childArea.height = getHeight (); + + if (parentArea.intersectsWith (&childArea, intersection)) { + intersection->x -= allocation.x; + intersection->y -= allocation.y; + return true; + } else + return false; +} + +void Widget::setParent (Widget *parent) +{ + this->parent = parent; + layout = parent->layout; + + if (!buttonSensitiveSet) + buttonSensitive = parent->buttonSensitive; + + DBG_OBJ_ASSOC_PARENT (parent); + //printf ("The %s %p becomes a child of the %s %p\n", + // getClassName(), this, parent->getClassName(), parent); + + // Determine the container. Currently rather simple; will become + // more complicated when absolute and fixed positions are + // supported. + container = NULL; + for (Widget *widget = getParent (); widget != NULL && container == NULL; + widget = widget->getParent()) + if (widget->isPossibleContainer ()) + container = widget; + // If there is no possible container widget, there is + // (surprisingly!) also no container (i. e. the viewport is + // used). Does not occur in dillo, where the toplevel widget is a + // Textblock. + DBG_OBJ_SET_PTR ("container", container); + + notifySetParent(); +} + +void Widget::setQuasiParent (Widget *quasiParent) +{ + this->quasiParent = quasiParent; + + // More to do? Compare with setParent(). + + DBG_OBJ_SET_PTR ("quasiParent", quasiParent); +} + +void Widget::queueDrawArea (int x, int y, int width, int height) +{ + /** \todo Maybe only the intersection? */ + + DBG_OBJ_ENTER ("draw", 0, "queueDrawArea", "%d, %d, %d, %d", + x, y, width, height); + + _MSG("Widget::queueDrawArea alloc(%d %d %d %d) wid(%d %d %d %d)\n", + allocation.x, allocation.y, + allocation.width, allocation.ascent + allocation.descent, + x, y, width, height); + if (layout) + layout->queueDraw (x + allocation.x, y + allocation.y, width, height); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief This method should be called, when a widget changes its size. + * + * A "fast" queueResize will ignore the anchestors, and furthermore + * not trigger the idle function. Used only within + * viewportSizeChanged, and not available outside Layout and Widget. + */ +void Widget::queueResize (int ref, bool extremesChanged, bool fast) +{ + DBG_OBJ_ENTER ("resize", 0, "queueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); + + // queueResize() can be called recursively; calls are queued, so + // that actualQueueResize() is clean. + + if (queueResizeEntered ()) { + DBG_OBJ_MSG ("resize", 1, "put into queue"); + layout->queueQueueResizeList->pushUnder (new Layout::QueueResizeItem + (this, ref, extremesChanged, + fast)); + } else { + actualQueueResize (ref, extremesChanged, fast); + + DBG_IF_RTFL { + if (layout == NULL) + DBG_OBJ_MSG ("resize", 1, "layout is not set"); + else if (layout->queueQueueResizeList->size () == 0) + DBG_OBJ_MSG ("resize", 1, "queue item list is empty"); + } + + while (layout != NULL && layout->queueQueueResizeList->size () > 0) { + DBG_IF_RTFL { + DBG_OBJ_MSGF ("resize", 1, "queue item list has %d elements:", + layout->queueQueueResizeList->size ()); +#if 0 + // TODO This worked when queueQueueResizeList was a Vector; now, + // iterators should be used. + DBG_OBJ_MSG_START (); + for (int i = 0; i < layout->queueQueueResizeList->size (); i++) { + DBG_OBJ_MSGF + ("resize", 1, + "#%d: widget = %p, ref = %d, extremesChanged = %s, " + "fast = %s", + i, layout->queueQueueResizeList->get(i)->widget, + layout->queueQueueResizeList->get(i)->ref, + layout->queueQueueResizeList->get(i)->extremesChanged ? + "true" : "false", + layout->queueQueueResizeList->get(i)->fast ? + "true" : "false"); + } + DBG_OBJ_MSG_END (); + DBG_OBJ_MSG ("resize", 1, "taking #0 out of list"); +#endif + } + + Layout::QueueResizeItem *item = + layout->queueQueueResizeList->getTop (); + item->widget->actualQueueResize (item->ref, item->extremesChanged, + item->fast); + layout->queueQueueResizeList->pop (); + } + } + + DBG_OBJ_LEAVE (); +} + +void Widget::actualQueueResize (int ref, bool extremesChanged, bool fast) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER ("resize", 0, "actualQueueResize", "%d, %s, %s", + ref, extremesChanged ? "true" : "false", + fast ? "true" : "false"); + + enterQueueResize (); + + Widget *widget2, *child; + + Flags resizeFlag, extremesFlag; + + if (layout) { + // If RESIZE_QUEUED is set, this widget is already in the list. + if (!resizeQueued ()) + layout->queueResizeList->put (this); + + resizeFlag = RESIZE_QUEUED; + extremesFlag = EXTREMES_QUEUED; + } else { + resizeFlag = NEEDS_RESIZE; + extremesFlag = EXTREMES_CHANGED; + } + + setFlags (resizeFlag); + setFlags (ALLOCATE_QUEUED); + markSizeChange (ref); + + if (extremesChanged) { + setFlags (extremesFlag); + markExtremesChange (ref); + } + + if (fast) { + if (parent) { + // In this case, queueResize is called from top (may be a + // random entry point) to bottom, so markSizeChange and + // markExtremesChange have to be called explicitly for the + // parent. The tests (needsResize etc.) are uses to check + // whether queueResize has been called for the parent, or + // whether this widget is the enty point. + if (parent->needsResize () || parent->resizeQueued ()) + parent->markSizeChange (parentRef); + if (parent->extremesChanged () || parent->extremesQueued ()) + parent->markExtremesChange (parentRef); + } + } else { + for (widget2 = parent, child = this; widget2; + child = widget2, widget2 = widget2->parent) { + if (layout && !widget2->resizeQueued ()) + layout->queueResizeList->put (widget2); + + DBG_OBJ_MSGF ("resize", 2, "setting %s and ALLOCATE_QUEUED for %p", + resizeFlag == RESIZE_QUEUED ? + "RESIZE_QUEUED" : "NEEDS_RESIZE", + widget2); + + widget2->setFlags (resizeFlag); + widget2->markSizeChange (child->parentRef); + widget2->setFlags (ALLOCATE_QUEUED); + + if (extremesChanged) { + widget2->setFlags (extremesFlag); + widget2->markExtremesChange (child->parentRef); + } + } + + if (layout) + layout->queueResize (extremesChanged); + } + + leaveQueueResize (); + + DBG_OBJ_LEAVE (); +} + +void Widget::containerSizeChanged () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChanged"); + + // If there is a container widget (not the viewport), which has not + // changed its size (which can be determined by the respective + // flags: this method is called recursively), this widget will + // neither change its size. Also, the recursive iteration can be + // stopped, since the children of this widget will + if (container == NULL || + container->needsResize () || container->resizeQueued () || + container->extremesChanged () || container->extremesQueued ()) { + // Viewport (container == NULL) or container widget has changed + // its size. + if (affectedByContainerSizeChange ()) + queueResizeFast (0, true); + + // Even if *this* widget is not affected, children may be, so + // iterate over children. + containerSizeChangedForChildren (); + } + + DBG_OBJ_LEAVE (); +} + +bool Widget::affectedByContainerSizeChange () +{ + DBG_OBJ_ENTER0 ("resize", 0, "affectedByContainerSizeChange"); + + bool ret; + + // This standard implementation is suitable for all widgets which + // call correctRequisition() and correctExtremes(), even in the way + // how Textblock and Image do (see comments there). Has to be kept + // in sync. + + if (container == NULL) { + if (style::isAbsLength (getStyle()->width) && + style::isAbsLength (getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (getStyle()->width) || + style::isPerLength (getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (getStyle()->width == style::LENGTH_AUTO ? + usesAvailWidth () : false) || + (getStyle()->height == style::LENGTH_AUTO ? + usesAvailHeight () : false); + } else + ret = container->affectsSizeChangeContainerChild (this); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +bool Widget::affectsSizeChangeContainerChild (Widget *child) +{ + DBG_OBJ_ENTER ("resize", 0, "affectsSizeChangeContainerChild", "%p", child); + + bool ret; + + // From the point of view of the container. This standard + // implementation should be suitable for most (if not all) + // containers. + + if (style::isAbsLength (child->getStyle()->width) && + style::isAbsLength (child->getStyle()->height)) + // Both absolute, i. e. fixed: no dependency. + ret = false; + else if (style::isPerLength (child->getStyle()->width) || + style::isPerLength (child->getStyle()->height)) { + // Any percentage: certainly dependenant. + ret = true; + } else + // One or both is "auto": depends ... + ret = + (child->getStyle()->width == style::LENGTH_AUTO ? + child->usesAvailWidth () : false) || + (child->getStyle()->height == style::LENGTH_AUTO ? + child->usesAvailHeight () : false); + + DBG_OBJ_MSGF ("resize", 1, "=> %s", ret ? "true" : "false"); + DBG_OBJ_LEAVE (); + return ret; +} + +void Widget::containerSizeChangedForChildren () +{ + DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren"); + + // Working, but inefficient standard implementation. + Iterator *it = iterator ((Content::Type)(Content::WIDGET_IN_FLOW | + Content::WIDGET_OOF_CONT), + false); + while (it->next ()) + it->getContent()->widget->containerSizeChanged (); + it->unref (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailWidth() is called. + */ +bool Widget::usesAvailWidth () +{ + return false; +} + +/** + * \brief Must be implemengted by a method returning true, when + * getAvailHeight() is called. + */ +bool Widget::usesAvailHeight () +{ + return false; +} + +/** + * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls + * the latter only when needed. + */ +void Widget::sizeRequest (Requisition *requisition) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest"); + + enterSizeRequest (); + + //printf ("The %stop-level %s %p with parentRef = %d: needsResize: %s, " + // "resizeQueued = %s\n", + // parent ? "non-" : "", getClassName(), this, parentRef, + // needsResize () ? "true" : "false", + // resizeQueued () ? "true" : "false"); + + if (resizeQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (NEEDS_RESIZE); + unsetFlags (RESIZE_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + + if (needsResize ()) { + /** \todo Check requisition == &(this->requisition) and do what? */ + sizeRequestImpl (requisition); + this->requisition = *requisition; + unsetFlags (NEEDS_RESIZE); + + DBG_OBJ_SET_NUM ("requisition.width", requisition->width); + DBG_OBJ_SET_NUM ("requisition.ascent", requisition->ascent); + DBG_OBJ_SET_NUM ("requisition.descent", requisition->descent); + } else + *requisition = this->requisition; + + //printf (" ==> Result: %d x (%d + %d)\n", + // requisition->width, requisition->ascent, requisition->descent); + + leaveSizeRequest (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Used to evaluate Widget::adjustMinWidth. + * + * If extremes == NULL, getExtremes is called. ForceValue is the same + * value passed to getAvailWidth etc.; if false, getExtremes is not + * called. + */ +int Widget::getMinWidth (Extremes *extremes, bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "..., %s", + forceValue ? "true" : "false"); + int minWidth; + + if (getAdjustMinWidth ()) { + Extremes extremes2; + if (extremes == NULL) { + if (forceValue) { + getExtremes (&extremes2); + extremes = &extremes2; + } + } + + // TODO Not completely clear whether this is feasable: Within + // the context of getAvailWidth(false) etc., getExtremes may not + // be called. We ignore the minimal width then. + minWidth = extremes ? extremes->minWidthIntrinsic : 0; + } else + minWidth = 0; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", minWidth); + DBG_OBJ_LEAVE (); + + return minWidth; +} + +/** + * Return available width including margin/border/padding + * (extraSpace?), not only the content width. + */ +int Widget::getAvailWidth (bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "getAvailWidth", "%s", + forceValue ? "true" : "false"); + + int width; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + width = -1; + calcFinalWidth (getStyle (), viewportWidth, NULL, 0, forceValue, &width); + if (width == -1) + width = viewportWidth; + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + width = parent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + width = quasiParent->getAvailWidthOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +/** + * Return available height including margin/border/padding + * (extraSpace?), not only the content height. + */ +int Widget::getAvailHeight (bool forceValue) +{ + // TODO Correct by ... not extremes, but ...? (Height extremes?) + + // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as + // "getAvailHeight (true)" is not used. + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeight", "%s", + forceValue ? "true" : "false"); + + int height; + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + // TODO Consider nested layouts (e. g. <button>). + if (style::isAbsLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (getStyle()->height)); + height = style::absLengthVal (getStyle()->height) + boxDiffHeight (); + } else if (style::isPerLength (getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (getStyle()->height)); + // Notice that here -- unlike getAvailWidth() -- + // layout->hScrollbarThickness is not considered here; + // something like canvasWidthGreater (analogue to + // canvasHeightGreater) would be complicated and lead to + // possibly contradictory self-references. + height = applyPerHeight (layout->viewportHeight, getStyle()->height); + } else { + DBG_OBJ_MSG ("resize", 1, "no specification"); + height = layout->viewportHeight; + } + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + height = parent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + height = quasiParent->getAvailHeightOfChild (this, forceValue); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisition (Requisition *requisition, + void (*splitHeightFun) (int, int *, int *)) +{ + // TODO Correct height by ... not extremes, but ...? (Height extremes?) + + DBG_OBJ_ENTER ("resize", 0, "correctRequisition", "%d * (%d + %d), ...", + requisition->width, requisition->ascent, + requisition->descent); + + if (parent == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + int limitMinWidth = getMinWidth (NULL, true); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + calcFinalWidth (getStyle (), viewportWidth, NULL, limitMinWidth, false, + &requisition->width); + + // For layout->viewportHeight, see comment in getAvailHeight(). + int height = calcHeight (getStyle()->height, false, + layout->viewportHeight, NULL, false); + int minHeight = calcHeight (getStyle()->minHeight, false, + layout->viewportHeight, NULL, false); + int maxHeight = calcHeight (getStyle()->maxHeight, false, + layout->viewportHeight, NULL, false); + + // TODO Perhaps split first, then add box ascent and descent. + if (height != -1) + splitHeightFun (height, &requisition->ascent, &requisition->descent); + if (minHeight != -1 && + requisition->ascent + requisition->descent < minHeight) + splitHeightFun (minHeight, &requisition->ascent, + &requisition->descent); + if (maxHeight != -1 && + requisition->ascent + requisition->descent > maxHeight) + splitHeightFun (maxHeight, &requisition->ascent, + &requisition->descent); + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + parent->correctRequisitionOfChild (this, requisition, splitHeightFun); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + quasiParent->correctRequisitionOfChild (this, requisition, + splitHeightFun); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremes (Extremes *extremes) +{ + DBG_OBJ_ENTER ("resize", 0, "correctExtremes", "%d (%d) / %d (%d)", + extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + if (container == NULL && quasiParent == NULL) { + DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport"); + DBG_OBJ_MSG_START (); + + int limitMinWidth = getMinWidth (extremes, false); + int viewportWidth = + layout->viewportWidth - (layout->canvasHeightGreater ? + layout->vScrollbarThickness : 0); + + int width = calcWidth (getStyle()->width, viewportWidth, NULL, + limitMinWidth, false); + int minWidth = calcWidth (getStyle()->minWidth, viewportWidth, NULL, + limitMinWidth, false); + int maxWidth = calcWidth (getStyle()->maxWidth, viewportWidth, NULL, + limitMinWidth, false); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + extremes->minWidth = extremes->maxWidth = width; + if (minWidth != -1) + extremes->minWidth = minWidth; + if (maxWidth != -1) + extremes->maxWidth = maxWidth; + + DBG_OBJ_MSG_END (); + } else if (parent) { + DBG_OBJ_MSG ("resize", 1, "delegated to parent"); + DBG_OBJ_MSG_START (); + parent->correctExtremesOfChild (this, extremes); + DBG_OBJ_MSG_END (); + } else /* if (quasiParent) */ { + DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent"); + DBG_OBJ_MSG_START (); + quasiParent->correctExtremesOfChild (this, extremes); + DBG_OBJ_MSG_END (); + } + + if (extremes->maxWidth < extremes->minWidth) + extremes->maxWidth = extremes->minWidth; + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +int Widget::calcWidth (style::Length cssValue, int refWidth, Widget *refWidget, + int limitMinWidth, bool forceValue) +{ + DBG_OBJ_ENTER ("resize", 0, "calcWidth", "0x%x, %d, %p, %d", + cssValue, refWidth, refWidget, limitMinWidth); + + assert (refWidth != -1 || refWidget != NULL); + + int width; + + if (style::isAbsLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "absolute width: %dpx", + style::absLengthVal (cssValue)); + width = misc::max (style::absLengthVal (cssValue) + boxDiffWidth (), + limitMinWidth); + } else if (style::isPerLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "percentage width: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (cssValue)); + if (refWidth != -1) + width = misc::max (applyPerWidth (refWidth, cssValue), limitMinWidth); + else { + int availWidth = refWidget->getAvailWidth (forceValue); + if (availWidth != -1) { + int containerWidth = availWidth - refWidget->boxDiffWidth (); + width = misc::max (applyPerWidth (containerWidth, cssValue), + limitMinWidth); + } else + width = -1; + } + } else { + DBG_OBJ_MSG ("resize", 1, "not specified"); + width = -1; + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + return width; +} + +// *finalWidth may be -1. +void Widget::calcFinalWidth (style::Style *style, int refWidth, + Widget *refWidget, int limitMinWidth, + bool forceValue, int *finalWidth) +{ + DBG_OBJ_ENTER ("resize", 0, "calcFinalWidth", "..., %d, %p, %d, [%d]", + refWidth, refWidget, limitMinWidth, *finalWidth); + + int width = calcWidth (style->width, refWidth, refWidget, limitMinWidth, + forceValue); + int minWidth = calcWidth (style->minWidth, refWidth, refWidget, + limitMinWidth, forceValue); + int maxWidth = calcWidth (style->maxWidth, refWidth, refWidget, + limitMinWidth, forceValue); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + *finalWidth = width; + if (minWidth != -1 && *finalWidth != -1 && *finalWidth < minWidth) + *finalWidth = minWidth; + if (maxWidth != -1 && *finalWidth == -1 && *finalWidth > maxWidth) + *finalWidth = maxWidth; + + DBG_OBJ_MSGF ("resize", 1, "=> %d", *finalWidth); + DBG_OBJ_LEAVE (); +} + +int Widget::calcHeight (style::Length cssValue, bool usePercentage, + int refHeight, Widget *refWidget, bool forceValue) +{ + // TODO Search for usage of this method and check the value of + // "usePercentage"; this has to be clarified. + + DBG_OBJ_ENTER ("resize", 0, "calcHeight", "0x%x, %s, %d, %p", + cssValue, usePercentage ? "true" : "false", refHeight, + refWidget); + + assert (refHeight != -1 || refWidget != NULL); + + int height; + + if (style::isAbsLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (cssValue)); + height = + misc::max (style::absLengthVal (cssValue) + boxDiffHeight (), 0); + } else if (style::isPerLength (cssValue)) { + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * + style::perLengthVal_useThisOnlyForDebugging (cssValue)); + if (usePercentage) { + if (refHeight != -1) + height = misc::max (applyPerHeight (refHeight, cssValue), 0); + else { + int availHeight = refWidget->getAvailHeight (forceValue); + if (availHeight != -1) { + int containerHeight = availHeight - refWidget->boxDiffHeight (); + height = + misc::max (applyPerHeight (containerHeight, cssValue), 0); + } else + height = -1; + } + } else + height = -1; + } else { + DBG_OBJ_MSG ("resize", 1, "not specified"); + height = -1; + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + return height; +} + +/** + * \brief Wrapper for Widget::getExtremesImpl(). + */ +void Widget::getExtremes (Extremes *extremes) +{ + assert (!queueResizeEntered ()); + + DBG_OBJ_ENTER0 ("resize", 0, "getExtremes"); + + enterGetExtremes (); + + if (extremesQueued ()) { + // This method is called outside of Layout::resizeIdle. + setFlags (EXTREMES_CHANGED); + unsetFlags (EXTREMES_QUEUED); + // The widget is not taken out of Layout::queueResizeList, since + // other *_QUEUED flags may still be set and processed in + // Layout::resizeIdle. + } + + if (extremesChanged ()) { + // For backward compatibility (part 1/2): + extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1; + + getExtremesImpl (extremes); + + // For backward compatibility (part 2/2): + if (extremes->minWidthIntrinsic == -1) + extremes->minWidthIntrinsic = extremes->minWidth; + if (extremes->maxWidthIntrinsic == -1) + extremes->maxWidthIntrinsic = extremes->maxWidth; + + this->extremes = *extremes; + unsetFlags (EXTREMES_CHANGED); + + DBG_OBJ_SET_NUM ("extremes.minWidth", extremes->minWidth); + DBG_OBJ_SET_NUM ("extremes.minWidthIntrinsic", + extremes->minWidthIntrinsic); + DBG_OBJ_SET_NUM ("extremes.maxWidth", extremes->maxWidth); + DBG_OBJ_SET_NUM ("extremes.maxWidthIntrinsic", + extremes->maxWidthIntrinsic); + } else + *extremes = this->extremes; + + leaveGetExtremes (); + + DBG_OBJ_LEAVE (); +} + +/** + * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when + * needed. + */ +void Widget::sizeAllocate (Allocation *allocation) +{ + assert (!queueResizeEntered ()); + assert (!sizeRequestEntered ()); + assert (!getExtremesEntered ()); + assert (resizeIdleEntered ()); + + DBG_OBJ_ENTER ("resize", 0, "sizeAllocate", "%d, %d; %d * (%d + %d)", + allocation->x, allocation->y, allocation->width, + allocation->ascent, allocation->descent); + + DBG_OBJ_MSGF ("resize", 1, + "old allocation (%d, %d; %d * (%d + %d)); needsAllocate: %s", + this->allocation.x, this->allocation.y, this->allocation.width, + this->allocation.ascent, this->allocation.descent, + needsAllocate () ? "true" : "false"); + + enterSizeAllocate (); + + /*printf ("The %stop-level %s %p is allocated:\n", + parent ? "non-" : "", getClassName(), this); + printf (" old = (%d, %d, %d + (%d + %d))\n", + this->allocation.x, this->allocation.y, this->allocation.width, + this->allocation.ascent, this->allocation.descent); + printf (" new = (%d, %d, %d + (%d + %d))\n", + allocation->x, allocation->y, allocation->width, allocation->ascent, + allocation->descent); + printf (" NEEDS_ALLOCATE = %s\n", needsAllocate () ? "true" : "false");*/ + + if (needsAllocate () || + allocation->x != this->allocation.x || + allocation->y != this->allocation.y || + allocation->width != this->allocation.width || + allocation->ascent != this->allocation.ascent || + allocation->descent != this->allocation.descent) { + + if (wasAllocated ()) { + layout->queueDrawExcept ( + this->allocation.x, + this->allocation.y, + this->allocation.width, + this->allocation.ascent + this->allocation.descent, + allocation->x, + allocation->y, + allocation->width, + allocation->ascent + allocation->descent); + } + + sizeAllocateImpl (allocation); + + //DEBUG_MSG (DEBUG_ALLOC, "... to %d, %d, %d x %d x %d\n", + // widget->allocation.x, widget->allocation.y, + // widget->allocation.width, widget->allocation.ascent, + // widget->allocation.descent); + + this->allocation = *allocation; + unsetFlags (NEEDS_ALLOCATE); + setFlags (WAS_ALLOCATED); + + resizeDrawImpl (); + + DBG_OBJ_SET_NUM ("allocation.x", this->allocation.x); + DBG_OBJ_SET_NUM ("allocation.y", this->allocation.y); + DBG_OBJ_SET_NUM ("allocation.width", this->allocation.width); + DBG_OBJ_SET_NUM ("allocation.ascent", this->allocation.ascent); + DBG_OBJ_SET_NUM ("allocation.descent", this->allocation.descent); + } + + /*unsetFlags (NEEDS_RESIZE);*/ + + leaveSizeAllocate (); + + DBG_OBJ_LEAVE (); +} + +bool Widget::buttonPress (EventButton *event) +{ + return buttonPressImpl (event); +} + +bool Widget::buttonRelease (EventButton *event) +{ + return buttonReleaseImpl (event); +} + +bool Widget::motionNotify (EventMotion *event) +{ + return motionNotifyImpl (event); +} + +void Widget::enterNotify (EventCrossing *event) +{ + enterNotifyImpl (event); +} + +void Widget::leaveNotify (EventCrossing *event) +{ + leaveNotifyImpl (event); +} + +/** + * \brief Change the style of a widget. + * + * The old style is automatically unreferred, the new is referred. If this + * call causes the widget to change its size, dw::core::Widget::queueResize + * is called. + */ +void Widget::setStyle (style::Style *style) +{ + bool sizeChanged; + + if (widgetImgRenderer && this->style && this->style->backgroundImage) + this->style->backgroundImage->removeExternalImgRenderer + (widgetImgRenderer); + + style->ref (); + + if (this->style) { + sizeChanged = this->style->sizeDiffs (style); + this->style->unref (); + } else + sizeChanged = true; + + this->style = style; + + DBG_OBJ_ASSOC_CHILD (style); + + if (style && style->backgroundImage) { + // Create instance of WidgetImgRenderer when needed. Until this + // widget is deleted, "widgetImgRenderer" will be kept, since it + // is not specific to the style, but only to this widget. + if (widgetImgRenderer == NULL) + widgetImgRenderer = new WidgetImgRenderer (this); + style->backgroundImage->putExternalImgRenderer (widgetImgRenderer); + } + + if (layout != NULL) { + layout->updateCursor (); + } + + if (sizeChanged) + queueResize (0, true); + else + queueDraw (); + + // These should better be attributed to the style itself, and a + // script processing RTFL messages could transfer it to something + // equivalent: + + DBG_OBJ_SET_NUM ("style.margin.top", style->margin.top); + DBG_OBJ_SET_NUM ("style.margin.bottom", style->margin.bottom); + DBG_OBJ_SET_NUM ("style.margin.left", style->margin.left); + DBG_OBJ_SET_NUM ("style.margin.right", style->margin.right); + + DBG_OBJ_SET_NUM ("style.border-width.top", style->borderWidth.top); + DBG_OBJ_SET_NUM ("style.border-width.bottom", style->borderWidth.bottom); + DBG_OBJ_SET_NUM ("style.border-width.left", style->borderWidth.left); + DBG_OBJ_SET_NUM ("style.border-width.right", style->borderWidth.right); + + DBG_OBJ_SET_NUM ("style.padding.top", style->padding.top); + DBG_OBJ_SET_NUM ("style.padding.bottom", style->padding.bottom); + DBG_OBJ_SET_NUM ("style.padding.left", style->padding.left); + DBG_OBJ_SET_NUM ("style.padding.right", style->padding.right); + + DBG_OBJ_SET_NUM ("style.border-spacing (h)", style->hBorderSpacing); + DBG_OBJ_SET_NUM ("style.border-spacing (v)", style->vBorderSpacing); + + DBG_OBJ_SET_SYM ("style.display", + style->display == style::DISPLAY_BLOCK ? "block" : + style->display == style::DISPLAY_INLINE ? "inline" : + style->display == style::DISPLAY_INLINE_BLOCK ? + "inline-block" : + style->display == style::DISPLAY_LIST_ITEM ? "list-item" : + style->display == style::DISPLAY_NONE ? "none" : + style->display == style::DISPLAY_TABLE ? "table" : + style->display == style::DISPLAY_TABLE_ROW_GROUP ? + "table-row-group" : + style->display == style::DISPLAY_TABLE_HEADER_GROUP ? + "table-header-group" : + style->display == style::DISPLAY_TABLE_FOOTER_GROUP ? + "table-footer-group" : + style->display == style::DISPLAY_TABLE_ROW ? "table-row" : + style->display == style::DISPLAY_TABLE_CELL ? "table-cell" : + "???"); + + DBG_OBJ_SET_NUM ("style.width (raw)", style->width); + DBG_OBJ_SET_NUM ("style.min-width (raw)", style->minWidth); + DBG_OBJ_SET_NUM ("style.max-width (raw)", style->maxWidth); + DBG_OBJ_SET_NUM ("style.height (raw)", style->height); + DBG_OBJ_SET_NUM ("style.min-height (raw)", style->minHeight); + DBG_OBJ_SET_NUM ("style.max-height (raw)", style->maxHeight); +} + +/** + * \brief Set the background "behind" the widget, if it is not the + * background of the parent widget, e.g. the background of a table + * row. + */ +void Widget::setBgColor (style::Color *bgColor) +{ + this->bgColor = bgColor; +} + +/** + * \brief Get the actual background of a widget. + */ +style::Color *Widget::getBgColor () +{ + Widget *widget = this; + + while (widget != NULL) { + if (widget->style->backgroundColor) + return widget->style->backgroundColor; + if (widget->bgColor) + return widget->bgColor; + + widget = widget->parent; + } + + return layout->getBgColor (); +} + + +/** + * \brief Draw borders and background of a widget part, which allocation is + * given by (x, y, width, height) (widget coordinates). + * + * area is given in widget coordinates. + */ +void Widget::drawBox (View *view, style::Style *style, Rectangle *area, + int x, int y, int width, int height, bool inverse) +{ + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; + + style::drawBorder (view, layout, &canvasArea, + allocation.x + x, allocation.y + y, + width, height, style, inverse); + + // This method is used for inline elements, where the CSS 2 specification + // does not define what here is called "reference area". To make it look + // smoothly, the widget padding box is used. + + // 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). + + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + style::drawBackground + (view, layout, &canvasArea, + allocation.x + x + style->margin.left + style->borderWidth.left, + allocation.y + y + style->margin.top + style->borderWidth.top, + width - style->margin.left - style->borderWidth.left + - style->margin.right - style->borderWidth.right, + height - style->margin.top - style->borderWidth.top + - style->margin.bottom - style->borderWidth.bottom, + xPad, yPad, widthPad, heightPad, style, style->backgroundColor, + inverse, false); +} + +/** + * \brief Draw borders and background of a widget. + * + * area is given in widget coordinates. + * + */ +void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) +{ + Rectangle canvasArea; + canvasArea.x = area->x + allocation.x; + canvasArea.y = area->y + allocation.y; + canvasArea.width = area->width; + canvasArea.height = area->height; + + style::drawBorder (view, layout, &canvasArea, allocation.x, allocation.y, + allocation.width, getHeight (), style, inverse); + + int xPad, yPad, widthPad, heightPad; + getPaddingArea (&xPad, &yPad, &widthPad, &heightPad); + + style::Color *bgColor; + if (inverse && style->backgroundColor == NULL) { + // See style::drawBackground: for inverse drawing, we need a + // defined background color. Search through ancestors. + Widget *w = this; + while (w != NULL && w->style->backgroundColor == NULL) + w = w->parent; + + if (w != NULL && w->style->backgroundColor != NULL) + bgColor = w->style->backgroundColor; + else + bgColor = layout->getBgColor (); + } else + bgColor = style->backgroundColor; + + style::drawBackground (view, layout, &canvasArea, + xPad, yPad, widthPad, heightPad, + xPad, yPad, widthPad, heightPad, + style, bgColor, inverse, parent == NULL); +} + +/* + * This function is used by some widgets, when they are selected (as a whole). + * + * \todo This could be accelerated by using clipping bitmaps. Two important + * issues: + * + * (i) There should always been a pixel in the upper-left corner of the + * *widget*, so probably two different clipping bitmaps have to be + * used (10/01 and 01/10). + * + * (ii) Should a new GC always be created? + * + * \bug Not implemented. + */ +void Widget::drawSelected (View *view, Rectangle *area) +{ +} + + +void Widget::setButtonSensitive (bool buttonSensitive) +{ + this->buttonSensitive = buttonSensitive; + buttonSensitiveSet = true; +} + + +/** + * \brief Get the widget at the root of the tree, this widget is part from. + */ +Widget *Widget::getTopLevel () +{ + Widget *widget = this; + + while (widget->parent) + widget = widget->parent; + + return widget; +} + +/** + * \brief Get the level of the widget within the tree. + * + * The root widget has the level 0. + */ +int Widget::getLevel () +{ + Widget *widget = this; + int level = 0; + + while (widget->parent) { + level++; + widget = widget->parent; + } + + return level; +} + +/** + * \brief Get the level of the widget within the tree, regarting the + * generators, not the parents. + * + * The root widget has the level 0. + */ +int Widget::getGeneratorLevel () +{ + Widget *widget = this; + int level = 0; + + while (widget->getGenerator ()) { + level++; + widget = widget->getGenerator (); + } + + return level; +} + +/** + * \brief Get the widget with the highest level, which is a direct ancestor of + * widget1 and widget2. + */ +Widget *Widget::getNearestCommonAncestor (Widget *otherWidget) +{ + Widget *widget1 = this, *widget2 = otherWidget; + int level1 = widget1->getLevel (), level2 = widget2->getLevel(); + + /* Get both widgets onto the same level.*/ + while (level1 > level2) { + widget1 = widget1->parent; + level1--; + } + + while (level2 > level1) { + widget2 = widget2->parent; + level2--; + } + + /* Search upwards. */ + while (widget1 != widget2) { + if (widget1->parent == NULL) { + MSG_WARN("widgets in different trees\n"); + return NULL; + } + + widget1 = widget1->parent; + widget2 = widget2->parent; + } + + 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) +{ + layout->scrollToWidget (hpos, vpos, this); +} + + +void Widget::scrollTo (HPosition hpos, VPosition vpos, + int x, int y, int width, int height) +{ + // TODO This should perhaps better be done by a new Layout::ScrollTarget. + layout->scrollTo (hpos, vpos, + x + allocation.x, y + allocation.y, width, height); +} + + +/** + * \brief Return the padding area (content plus padding). + * + * Used as "reference area" (ee comment of "style::drawBackground") + * for backgrounds. + */ +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; +} + +void Widget::sizeAllocateImpl (Allocation *allocation) +{ +} + +void Widget::markSizeChange (int ref) +{ +} + +void Widget::markExtremesChange (int ref) +{ +} + +int Widget::applyPerWidth (int containerWidth, style::Length perWidth) +{ + return style::multiplyWithPerLength (containerWidth, perWidth) + + boxDiffWidth (); +} + +int Widget::applyPerHeight (int containerHeight, style::Length perHeight) +{ + return style::multiplyWithPerLength (containerHeight, perHeight) + + boxDiffHeight (); +} + +int Widget::getAvailWidthOfChild (Widget *child, bool forceValue) +{ + // This is a halfway suitable implementation for all + // containers. For simplification, this will be used during the + // development; then, a differentiation could be possible. + + DBG_OBJ_ENTER ("resize", 0, "getAvailWidthOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int width; + + if (child->getStyle()->width == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + width = misc::max (getAvailWidth (true) - boxDiffWidth (), 0); + else + width = -1; + } else { + // In most cases, the toplevel widget should be a container, so + // the container is non-NULL when the parent is non-NULL. Just + // in case, regard also parent. And quasiParent. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + width = -1; + child->calcFinalWidth (child->getStyle(), -1, this, 0, forceValue, + &width); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + width = effContainer->getAvailWidthOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", width); + DBG_OBJ_LEAVE (); + + return width; +} + +int Widget::getAvailHeightOfChild (Widget *child, bool forceValue) +{ + // Again, a suitable implementation for all widgets (perhaps). + + // TODO Consider 'min-height' and 'max-height'. (Minor priority, as long as + // "getAvailHeight (true)" is not used. + + DBG_OBJ_ENTER ("resize", 0, "getAvailHeightOfChild", "%p, %s", + child, forceValue ? "true" : "false"); + + int height; + + if (child->getStyle()->height == style::LENGTH_AUTO) { + DBG_OBJ_MSG ("resize", 1, "no specification"); + if (forceValue) + height = misc::max (getAvailHeight (true) - boxDiffHeight (), 0); + else + height = -1; + } else { + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + if (style::isAbsLength (child->getStyle()->height)) { + DBG_OBJ_MSGF ("resize", 1, "absolute height: %dpx", + style::absLengthVal (child->getStyle()->height)); + height = misc::max (style::absLengthVal (child->getStyle()->height) + + child->boxDiffHeight (), 0); + } else { + assert (style::isPerLength (child->getStyle()->height)); + DBG_OBJ_MSGF ("resize", 1, "percentage height: %g%%", + 100 * style::perLengthVal_useThisOnlyForDebugging + (child->getStyle()->height)); + + int availHeight = getAvailHeight (forceValue); + if (availHeight == -1) + height = -1; + else + height = + misc::max (child->applyPerHeight (availHeight - + boxDiffHeight (), + child->getStyle()->height), + 0); + } + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + height = effContainer->getAvailHeightOfChild (child, forceValue); + DBG_OBJ_MSG_END (); + } + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d", height); + DBG_OBJ_LEAVE (); + + return height; +} + +void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, + int*)) +{ + // Again, a suitable implementation for all widgets (perhaps). + + DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + correctReqWidthOfChild (child, requisition); + correctReqHeightOfChild (child, requisition, splitHeightFun); + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + effContainer->correctRequisitionOfChild (child, requisition, + splitHeightFun); + DBG_OBJ_MSG_END (); + } + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition) +{ + DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild", "%p, %d * (%d + %d)", + child, requisition->width, requisition->ascent, + requisition->descent); + + assert (this == child->quasiParent || this == child->container); + + int limitMinWidth = child->getMinWidth (NULL, true); + child->calcFinalWidth (child->getStyle(), -1, this, limitMinWidth, false, + &requisition->width); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition, + void (*splitHeightFun) (int, int*, int*)) +{ + // TODO Correct height by extremes? (Height extemes?) + + assert (this == child->quasiParent || this == child->container); + + DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild", + "%p, %d * (%d + %d), ...", child, requisition->width, + requisition->ascent, requisition->descent); + + int height = child->calcHeight (child->getStyle()->height, false, -1, this, + false); + int minHeight = child->calcHeight (child->getStyle()->minHeight, false, -1, + this, false); + int maxHeight = child->calcHeight (child->getStyle()->maxHeight, false, -1, + this, false); + + // TODO Perhaps split first, then add box ascent and descent. + if (height != -1) + splitHeightFun (height, &requisition->ascent, &requisition->descent); + if (minHeight != -1 && + requisition->ascent + requisition->descent < minHeight) + splitHeightFun (minHeight, &requisition->ascent, + &requisition->descent); + if (maxHeight != -1 && + requisition->ascent + requisition->descent > maxHeight) + splitHeightFun (maxHeight, &requisition->ascent, + &requisition->descent); + + DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)", + requisition->width, requisition->ascent, + requisition->descent); + DBG_OBJ_LEAVE (); +} + +void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes) +{ + // See comment in correctRequisitionOfChild. + + DBG_OBJ_ENTER ("resize", 0, "correctExtremesOfChild", + "%p, %d (%d) / %d (%d)", + child, extremes->minWidth, extremes->minWidthIntrinsic, + extremes->maxWidth, extremes->maxWidthIntrinsic); + + // See comment in Widget::getAvailWidthOfChild. + Widget *effContainer = child->quasiParent ? child->quasiParent : + (child->container ? child->container : child->parent); + + if (effContainer == this) { + int limitMinWidth = child->getMinWidth (extremes, false); + int width = child->calcWidth (child->getStyle()->width, -1, this, + limitMinWidth, false); + int minWidth = child->calcWidth (child->getStyle()->minWidth, -1, this, + limitMinWidth, false); + int maxWidth = child->calcWidth (child->getStyle()->maxWidth, -1, this, + limitMinWidth, false); + + DBG_OBJ_MSGF ("resize", 1, "width = %d, minWidth = %d, maxWidth = %d", + width, minWidth, maxWidth); + + if (width != -1) + extremes->minWidth = extremes->maxWidth = width; + if (minWidth != -1) + extremes->minWidth = minWidth; + if (maxWidth != -1) + extremes->maxWidth = maxWidth; + } else { + DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container"); + DBG_OBJ_MSG_START (); + effContainer->correctExtremesOfChild (child, extremes); + DBG_OBJ_MSG_END (); + } + + + DBG_OBJ_MSGF ("resize", 1, "=> %d / %d", + extremes->minWidth, extremes->maxWidth); + DBG_OBJ_LEAVE (); +} + +/** + * \brief This method is called after a widget has been set as the top of a + * widget tree. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetAsTopLevel() +{ +} + +/** + * \brief This method is called after a widget has been added to a parent. + * + * A widget may override this method when it is necessary to be notified. + */ +void Widget::notifySetParent() +{ +} + +bool Widget::isBlockLevel () +{ + // Most widgets are not block-level. + return false; +} + +bool Widget::isPossibleContainer () +{ + // In most (all?) cases identical to: + return isBlockLevel (); +} + +bool Widget::buttonPressImpl (EventButton *event) +{ + return false; +} + +bool Widget::buttonReleaseImpl (EventButton *event) +{ + return false; +} + +bool Widget::motionNotifyImpl (EventMotion *event) +{ + return false; +} + +void Widget::enterNotifyImpl (EventCrossing *) +{ + style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onEnter(); +} + +void Widget::leaveNotifyImpl (EventCrossing *) +{ + style::Tooltip *tooltip = getStyle()->x_tooltip; + + if (tooltip) + tooltip->onLeave(); +} + +void Widget::removeChild (Widget *child) +{ + // Should be implemented. + misc::assertNotReached (); +} + +// ---------------------------------------------------------------------- + +void splitHeightPreserveAscent (int height, int *ascent, int *descent) +{ + *descent = height - *ascent; + if (*descent < 0) { + *descent = 0; + *ascent = height; + } +} + +void splitHeightPreserveDescent (int height, int *ascent, int *descent) +{ + *ascent = height - *descent; + if (*ascent < 0) { + *ascent = 0; + *descent = height; + } +} + +} // namespace core +} // namespace dw |