/*
 * Dillo Widget
 *
 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
 * Copyright 2023-2024 Rodrigo Arias Mallo <rodarima@gmail.com>
 *
 * 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/msg.h"
#include "../lout/debug.hh"

using namespace lout;
using namespace lout::object;
using namespace lout::misc;

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 = true;
int Widget::CLASS_ID = -1;

Widget::Widget ()
{
   DBG_OBJ_CREATE ("dw::core::Widget");
   registerName ("dw::core::Widget", &CLASS_ID);

   DBG_OBJ_ASSOC_CHILD (&requisitionParams);
   DBG_OBJ_ASSOC_CHILD (&extremesParams);

   flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED);
   parent = quasiParent = generator = container = NULL;
   setWidgetReference (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;

   stackingContextMgr = NULL;
}

Widget::~Widget ()
{
   if (deleteCallbackFunc)
      deleteCallbackFunc (deleteCallbackData);

   if (widgetImgRenderer) {
      if (style && style->backgroundImage)
         style->backgroundImage->removeExternalImgRenderer (widgetImgRenderer);
      delete widgetImgRenderer;
   }

   if (stackingContextMgr)
      delete stackingContextMgr;

   if (style)
      style->unref ();

   if (parent)
      parent->removeChild (this);
   else if (layout)
      layout->removeWidget ();

   DBG_OBJ_DELETE ();
}


/**
 * \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 (passing
 * "this" as "refWidget"). Returns whether intersection is not empty.
 */
bool Widget::intersects (Widget *refWidget, Rectangle *area,
                         Rectangle *intersection)
{
   DBG_OBJ_ENTER ("draw", 0, "intersects", "%p, [%d, %d, %d * %d]",
                  refWidget, area->x, area->y, area->width, area->height);
   bool r;

   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;
         }
      }

      if (r) {
         intersection->x -= allocation.x;
         intersection->y -= allocation.y;

         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_LEAVE_VAL ("true: %d, %d, %d * %d",
                         intersection->x, intersection->y,
                         intersection->width, intersection->height);
   else
      DBG_OBJ_LEAVE_VAL0 ("false");

   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;

   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);

   // 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)
{
   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 ancestors, 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");

   enterQueueResize ();

   Widget *widget2, *child;

   Flags resizeFlag, extremesFlag, totalFlags;

   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);

   totalFlags = resizeFlag;
   
   if (extremesChanged) {
      totalFlags = (Flags)(totalFlags | extremesFlag);
      
      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 entry 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);
         }

         DBG_IF_RTFL {
            if (widget2->parent)
               DBG_OBJ_MSGF ("resize", 2,
                             "checking parent %p: (%d & %d) [= %d] == %d?",
                             widget2->parent, widget2->parent->flags,
                             totalFlags, widget2->parent->flags & totalFlags,
                             totalFlags);
         }
         
         if (widget2->parent &&
             (widget2->parent->flags & totalFlags) == totalFlags) {
            widget2->parent->markSizeChange (widget2->parentRef);
            if (extremesChanged) {
               widget2->parent->markExtremesChange (widget2->parentRef);
            }
            
            break;
         }
      }

      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_LEAVE_VAL ("%s", boolToStr(ret));
   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_LEAVE_VAL ("%s", boolToStr(ret));
   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, int numPos,
                          Widget **references, int *x, int *y)
{
   assert (!queueResizeEntered ());

   DBG_OBJ_ENTER ("resize", 0, "sizeRequest", "%d, ...", numPos);

   DBG_IF_RTFL {
      DBG_OBJ_MSG_START();
      for(int i = 0; i < numPos; i++)
         DBG_OBJ_MSGF ("resize", 1, "ref #%d: %p, %d, %d",
                       i, references[i], x[i], y[i]);
      DBG_OBJ_MSG_END();
   }

   enterSizeRequest ();

   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.
   }

   SizeParams newRequisitionParams (numPos, references, x, y);
   DBG_OBJ_ASSOC_CHILD (&newRequisitionParams);

   bool callImpl;
   if (needsResize ())
      callImpl = true;
   else {
      // Even if RESIZE_QUEUED / NEEDS_RESIZE is not set, calling
      // sizeRequestImpl is necessary when the relavive positions passed here
      // have changed.
      callImpl = !newRequisitionParams.isEquivalent (&requisitionParams);
   }

   DBG_OBJ_MSGF ("resize", 1, "callImpl = %s", boolToStr (callImpl));

   requisitionParams = newRequisitionParams;

   if (callImpl) {
      calcExtraSpace (numPos, references, x, y);
      /** \todo Check requisition == &(this->requisition) and do what? */
      sizeRequestImpl (requisition, numPos, references, x, y);
      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;

   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. A value of "false" is passed for "useCorrected" in the
 * context of correctExtemes etc., to avoid cyclic dependencies.
 * 
 */
int Widget::getMinWidth (Extremes *extremes, bool forceValue)
{
   DBG_IF_RTFL {
      if (extremes)
         DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "[%d (%d) / %d (%d)], %s",
                        extremes->minWidth, extremes->minWidthIntrinsic,
                        extremes->maxWidth, extremes->maxWidthIntrinsic,
                        forceValue ? "true" : "false");
      else
         DBG_OBJ_ENTER ("resize", 0, "getMinWidth", "(nil), %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.
      if (extremes)
         minWidth = extremes->adjustmentWidth;
      else
         minWidth = 0;
   } else
      minWidth = 0;

   DBG_OBJ_LEAVE_VAL ("%d", minWidth);
   return minWidth;
}

/**
 * Return available width including margin/border/padding
 * (extraSpace?), not only the content width.
 *
 * If the widget has a parent or a quasiParent, the width computation is
 * delegated to the parent first, or the quasiParent later.
 */
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_LEAVE_VAL ("%d", width);
   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_LEAVE_VAL ("%d", height);
   return height;
}

void Widget::correctRequisition (Requisition *requisition,
                                 void (*splitHeightFun) (int, int *, int *),
                                 bool allowDecreaseWidth,
                                 bool allowDecreaseHeight)
{
   // TODO Correct height by ... not extremes, but ...? (Height extremes?)

   DBG_OBJ_ENTER ("resize", 0, "correctRequisition",
                  "%d * (%d + %d), ..., %s, %s",
                  requisition->width, requisition->ascent,
                  requisition->descent, misc::boolToStr (allowDecreaseWidth),
                  misc::boolToStr (allowDecreaseHeight));

   if (parent == NULL && quasiParent == NULL) {
      DBG_OBJ_MSG ("resize", 1, "no parent, regarding viewport");
      DBG_OBJ_MSG_START ();

      int limitMinWidth = getMinWidth (NULL, true);
      if (!allowDecreaseWidth && limitMinWidth < requisition->width)
         limitMinWidth = requisition->width;
      
      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);
      adjustHeight (&height, allowDecreaseHeight, requisition->ascent,
                    requisition->descent);

      int minHeight = calcHeight (getStyle()->minHeight, false,
                                  layout->viewportHeight, NULL, false);
      adjustHeight (&minHeight, allowDecreaseHeight, requisition->ascent,
                    requisition->descent);
         
      int maxHeight = calcHeight (getStyle()->maxHeight, false,
                                  layout->viewportHeight, NULL, false);
      adjustHeight (&maxHeight, allowDecreaseHeight, requisition->ascent,
                    requisition->descent);

      // 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,
                                         allowDecreaseWidth,
                                         allowDecreaseHeight);
      DBG_OBJ_MSG_END ();
   } else /* if (quasiParent) */ {
      DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
      DBG_OBJ_MSG_START ();
      quasiParent->correctRequisitionOfChild (this, requisition,
                                              splitHeightFun,
                                              allowDecreaseWidth,
                                              allowDecreaseHeight);
      DBG_OBJ_MSG_END ();
   }

   DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", requisition->width, requisition->ascent,
                      requisition->descent);
}

void Widget::correctExtremes (Extremes *extremes, bool useAdjustmentWidth)
{
   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 =
         useAdjustmentWidth ? getMinWidth (extremes, false) : 0;
      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, useAdjustmentWidth);
      DBG_OBJ_MSG_END ();
   } else /* if (quasiParent) */ {
      DBG_OBJ_MSG ("resize", 1, "delegated to quasiParent");
      DBG_OBJ_MSG_START ();
      quasiParent->correctExtremesOfChild (this, extremes, useAdjustmentWidth);
      DBG_OBJ_MSG_END ();
   }

   if (extremes->maxWidth < extremes->minWidth)
      extremes->maxWidth = extremes->minWidth;

   DBG_OBJ_LEAVE_VAL ("%d / %d", extremes->minWidth, extremes->maxWidth);
}

/** Computes a width value in pixels from cssValue.
 *
 * If cssValue is absolute, the absolute value is used.
 * If cssValue is relative, then it is applied to refWidth.
 * Otherwise, -1 is used.
 *
 * In any case, the returned value is clamped so that is not smaller
 * than limitMinWidth.
 *
 */
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_LEAVE_VAL ("%d", width);
   return width;
}

// *finalWidth may be -1.
/*
 * If style has minWidth or maxWidth set, the returned value in
 * *finalWidth is constrained to not exceed any of the set limits.
 * */
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;

   /* Set the width if the min or max value is set and finalWidth is
    * still -1 or exceeds the limit. Start by maxWidth so it defaults to
    * the maximum available size. */
   if (maxWidth != -1 && (*finalWidth == -1 || *finalWidth > maxWidth))
      *finalWidth = maxWidth;
   if (minWidth != -1 && (*finalWidth == -1 || *finalWidth < minWidth))
      *finalWidth = minWidth;

   DBG_OBJ_LEAVE_VAL ("%d", *finalWidth);
}

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_LEAVE_VAL ("%d", height);
   return height;
}

void Widget::adjustHeight (int *height, bool allowDecreaseHeight, int ascent,
                           int descent)
{
   if (!allowDecreaseHeight && *height != -1 && *height < ascent + descent)
      *height = ascent + descent;
}

/**
 * \brief Wrapper for Widget::getExtremesImpl().
 */
void Widget::getExtremes (Extremes *extremes, int numPos, Widget **references,
                          int *x, int *y)
{
   assert (!queueResizeEntered ());

   DBG_OBJ_ENTER ("resize", 0, "getExtremes", "%d, ...", numPos);

   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.
   }

   bool callImpl;
   if (extremesChanged ())
      callImpl = true;
   else {
      // Even if EXTREMES_QUEUED / EXTREMES_CHANGED is not set, calling
      // getExtremesImpl is necessary when the relavive positions passed here
      // have changed.
      SizeParams newParams (numPos, references, x, y);
      DBG_OBJ_ASSOC_CHILD (&newParams);
      if (newParams.isEquivalent (&extremesParams))
         callImpl = false;
      else {
         callImpl = true;
         extremesParams = newParams;
      }
   }
   
   if (callImpl) {
      // For backward compatibility (part 1/2):
      extremes->minWidthIntrinsic = extremes->maxWidthIntrinsic = -1;

      getExtremesImpl (extremes, numPos, references, x, y);

      // 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);
      DBG_OBJ_SET_NUM ("extremes.adjustmentWidth", extremes->adjustmentWidth);
   } else
      *extremes = this->extremes;

   leaveGetExtremes ();

   DBG_OBJ_LEAVE ();
}

/**
 * \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 (int numPos, Widget **references, int *x, int *y)
{
   DBG_OBJ_ENTER0 ("resize", 0, "calcExtraSpace");

   extraSpace.top = extraSpace.right = extraSpace.bottom = extraSpace.left = 0;
   calcExtraSpaceImpl (numPos, references, x, y);

   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);

   DBG_OBJ_LEAVE ();
}

int Widget::numSizeRequestReferences ()
{
   return 0;
}

Widget *Widget::sizeRequestReference (int index)
{
   misc::notImplemented ("Widget::sizeRequestReference");
   return NULL;
}

int Widget::numGetExtremesReferences ()
{
   return 0;
}

Widget *Widget::getExtremesReference (int index)
{
   misc::notImplemented ("Widget::getExtremesReference");
   return NULL;
}

/**
 * \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 ();
   }

   // 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
      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);

   if (style->backgroundColor)
      DBG_OBJ_SET_COL ("style.background-color",
                       style->backgroundColor->getColor ());
   else
      DBG_OBJ_SET_SYM ("style.background-color", "transparent");
}

/**
 * \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 Get the actual foreground color of a widget.
 */
style::Color *Widget::getFgColor ()
{
   Widget *widget = this;

   while (widget != NULL) {
      if (widget->style->color)
         return widget->style->color;

      widget = widget->parent;
   }

   return NULL;
}


/**
 * \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 here, 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;

   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);

   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;
}

void Widget::scrollTo (HPosition hpos, VPosition vpos,
               int x, int y, int width, int height)
{
   layout->scrollTo (hpos, 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).
 *
 * Used as "reference area" (ee comment of "style::drawBackground")
 * for backgrounds.
 */
void Widget::getPaddingArea (int *xPad, int *yPad, int *widthPad,
                             int *heightPad)
{
   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::sizeRequestImpl (Requisition *requisition, int numPos,
                              Widget **references, int *x, int *y)
{
   // Use the simple variant.
   DBG_OBJ_ENTER0 ("resize", 0, "Widget::sizeRequestImpl");
   sizeRequestSimpl (requisition);
   DBG_OBJ_LEAVE ();
}

void Widget::sizeRequestSimpl (Requisition *requisition)
{
   // Either variant should be implemented.
   misc::notImplemented ("Widget::sizeRequestSimpl");
}

void Widget::getExtremesImpl (Extremes *extremes, int numPos,
                              Widget **references, int *x, int *y)
{
   // Use the simple variant.
   DBG_OBJ_ENTER0 ("resize", 0, "Widget::getExtremesImpl");
   getExtremesSimpl (extremes);
   DBG_OBJ_LEAVE ();
}

void Widget::getExtremesSimpl (Extremes *extremes)
{
    // Either variant should be implemented.
   misc::notImplemented ("Widget::getExtremesSimpl");
}

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 (int numPos, Widget **references, int *x,
                                 int *y)
{
}

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 ();
}

/**
 * Computes the content width available of a child widget.
 *
 * @param child      The child widget of which the available width will be
 *                   computed.
 * @param forceValue If true, computes the width of the child with value
 *                   "auto". Otherwise, it wont.
 *
 * @return The available width in pixels or -1.
 */
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");

      /* We only compute when forceValue is true */
      if (forceValue) {
         width = misc::max (getAvailWidth (true) - boxDiffWidth (), 0);

         if (width != -1) {
            /* Clamp to min-width and max-width if given */
            int maxWidth = child->calcWidth (child->getStyle()->maxWidth,
                  -1, this, -1, false);
            if (maxWidth != -1 && width > maxWidth)
               width = maxWidth;

            int minWidth = child->calcWidth (child->getStyle()->minWidth,
                  -1, this, -1, false);
            if (minWidth != -1 && width < minWidth)
               width = minWidth;
         }

      } 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_LEAVE_VAL ("%d", width);
   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_LEAVE_VAL ("%d", height);
   return height;
}

void Widget::correctRequisitionOfChild (Widget *child, Requisition *requisition,
                                        void (*splitHeightFun) (int, int*,
                                                                int*),
                                        bool allowDecreaseWidth,
                                        bool allowDecreaseHeight)
{
   // Again, a suitable implementation for all widgets (perhaps).

   DBG_OBJ_ENTER ("resize", 0, "correctRequisitionOfChild",
                  "%p, %d * (%d + %d), ..., %s, %s", child, requisition->width,
                  requisition->ascent, requisition->descent,
                  misc::boolToStr (allowDecreaseWidth),
                  misc::boolToStr (allowDecreaseHeight));

   // See comment in Widget::getAvailWidthOfChild.
   Widget *effContainer = child->quasiParent ? child->quasiParent :
      (child->container ? child->container : child->parent);

   if (effContainer == this) {
      correctReqWidthOfChild (child, requisition, allowDecreaseWidth);
      correctReqHeightOfChild (child, requisition, splitHeightFun,
                               allowDecreaseHeight);
   } else {
      DBG_OBJ_MSG ("resize", 1, "delegated to (effective) container");
      DBG_OBJ_MSG_START ();
      effContainer->correctRequisitionOfChild (child, requisition,
                                               splitHeightFun,
                                               allowDecreaseWidth,
                                               allowDecreaseHeight);
      DBG_OBJ_MSG_END ();
   }

   DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", requisition->width, requisition->ascent,
                      requisition->descent);
}

void Widget::correctReqWidthOfChild (Widget *child, Requisition *requisition,
                                     bool allowDecreaseWidth)
{
   DBG_OBJ_ENTER ("resize", 0, "correctReqWidthOfChild",
                  "%p, %d * (%d + %d), %s",
                  child, requisition->width, requisition->ascent,
                  requisition->descent, misc::boolToStr (allowDecreaseWidth));

   assert (this == child->quasiParent || this == child->container);

   int limitMinWidth = child->getMinWidth (NULL, true);
   if (!allowDecreaseWidth && limitMinWidth < requisition->width)
      limitMinWidth = requisition->width;

   child->calcFinalWidth (child->getStyle(), -1, this, limitMinWidth, true,
                          &requisition->width);

   DBG_OBJ_LEAVE_VAL ("%d * (%d + %d)", requisition->width, requisition->ascent,
                      requisition->descent);
}

void Widget::correctReqHeightOfChild (Widget *child, Requisition *requisition,
                                      void (*splitHeightFun) (int, int*, int*),
                                      bool allowDecreaseHeight)
{
   // TODO Correct height by extremes? (Height extemes?)

   assert (this == child->quasiParent || this == child->container);

   DBG_OBJ_ENTER ("resize", 0, "correctReqHeightOfChild",
                  "%p, %d * (%d + %d), ..., %s", child, requisition->width,
                  requisition->ascent, requisition->descent,
                  misc::boolToStr (allowDecreaseHeight));

   int height = child->calcHeight (child->getStyle()->height, false, -1, this,
                                   false);
   adjustHeight (&height, allowDecreaseHeight, requisition->ascent,
                 requisition->descent);

   int minHeight = child->calcHeight (child->getStyle()->minHeight, false, -1,
                                      this, false);
   adjustHeight (&minHeight, allowDecreaseHeight, requisition->ascent,
                 requisition->descent);

   int maxHeight = child->calcHeight (child->getStyle()->maxHeight, false, -1,
                                      this, false);
   adjustHeight (&maxHeight, allowDecreaseHeight, requisition->ascent,
                 requisition->descent);

   // 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_LEAVE_VAL ("%d * (%d + %d)", requisition->width, requisition->ascent,
                      requisition->descent);
}

void Widget::correctExtremesOfChild (Widget *child, Extremes *extremes,
                                     bool useAdjustmentWidth)
{
   // 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 =
         useAdjustmentWidth ? child->getMinWidth (extremes, false) : 0;
      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,
                                            useAdjustmentWidth);
      DBG_OBJ_MSG_END ();
   }


   DBG_OBJ_LEAVE_VAL ("%d / %d", extremes->minWidth, extremes->maxWidth);
}

/**
 * \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::notImplemented ("Widget::removeChild");
}

// ----------------------------------------------------------------------

void splitHeightPreserveAscent (int height, int *ascent, int *descent)
{
   DBG_OBJ_ENTER_S ("resize", 1, "splitHeightPreserveAscent", "%d, %d, %d",
                    height, *ascent, *descent);
   
   *descent = height - *ascent;
   if (*descent < 0) {
      *descent = 0;
      *ascent = height;
   }

   DBG_OBJ_LEAVE_VAL_S ("%d, %d", *ascent, *descent);
}

void splitHeightPreserveDescent (int height, int *ascent, int *descent)
{
   DBG_OBJ_ENTER_S ("resize", 1, "splitHeightPreserveDescent", "%d, %d, %d",
                    height, *ascent, *descent);

   *ascent = height - *descent;
   if (*ascent < 0) {
      *ascent = 0;
      *descent = height;
   }

   DBG_OBJ_LEAVE_VAL_S ("%d, %d", *ascent, *descent);
}

} // namespace core
} // namespace dw
