Mercurial > dillo_port1.3
view dw/widget.cc @ 2084:5b8e5855ab8e
support !important in style attributes
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> |
---|---|
date | Tue, 07 Jun 2011 12:06:52 +0200 |
parents | 4eabd51a5d96 |
children |
line wrap: on
line source
/* * Dillo Widget * * 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. * * 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 { // ---------------------------------------------------------------------- int Widget::CLASS_ID = -1; Widget::Widget () { registerName ("dw::core::Widget", &CLASS_ID); flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS); parent = NULL; layout = NULL; allocation.x = -1; allocation.y = -1; allocation.width = 1; allocation.ascent = 1; allocation.descent = 0; style = NULL; bgColor = NULL; buttonSensitive = true; buttonSensitiveSet = false; deleteCallbackData = NULL; deleteCallbackFunc = NULL; } Widget::~Widget () { if (deleteCallbackFunc) deleteCallbackFunc (deleteCallbackData); if (style) style->unref (); if (parent) parent->removeChild (this); else layout->removeWidget (); } /** * \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 (widget, parent); } void Widget::queueDrawArea (int x, int y, int width, int height) { /** \todo Maybe only the intersection? */ layout->queueDraw (x + allocation.x, y + allocation.y, width, height); _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height); } /** * \brief This method should be called, when a widget changes its size. */ void Widget::queueResize (int ref, bool extremesChanged) { Widget *widget2, *child; //DEBUG_MSG (DEBUG_SIZE, // "a %stop-level %s with parent_ref = %d has changed its size\n", // widget->parent ? "non-" : "", // gtk_type_name (GTK_OBJECT_TYPE (widget)), widget->parent_ref); setFlags (NEEDS_RESIZE); setFlags (NEEDS_ALLOCATE); markSizeChange (ref); if (extremesChanged) { setFlags (EXTREMES_CHANGED); markExtremesChange (ref); } for (widget2 = parent, child = this; widget2; child = widget2, widget2 = widget2->parent) { widget2->setFlags (NEEDS_RESIZE); widget2->markSizeChange (child->parentRef); widget2->setFlags (NEEDS_ALLOCATE); //DEBUG_MSG (DEBUG_ALLOC, // "setting DW_NEEDS_ALLOCATE for a %stop-level %s " // "with parent_ref = %d\n", // widget2->parent ? "non-" : "", // gtk_type_name (GTK_OBJECT_TYPE (widget2)), // widget2->parent_ref); if (extremesChanged) { widget2->setFlags (EXTREMES_CHANGED); widget2->markExtremesChange (child->parentRef); } } if (layout) layout->queueResize (); } /** * \brief This method is a wrapper for Widget::sizeRequestImpl(); it calls * the latter only when needed. */ void Widget::sizeRequest (Requisition *requisition) { if (needsResize ()) { /** \todo Check requisition == &(this->requisition) and do what? */ sizeRequestImpl (requisition); this->requisition = *requisition; unsetFlags (NEEDS_RESIZE); DBG_OBJ_SET_NUM (this, "requisition->width", requisition->width); DBG_OBJ_SET_NUM (this, "requisition->ascent", requisition->ascent); DBG_OBJ_SET_NUM (this, "requisition->descent", requisition->descent); } else *requisition = this->requisition; } /** * \brief Wrapper for Widget::getExtremesImpl(). */ void Widget::getExtremes (Extremes *extremes) { if (extremesChanged ()) { getExtremesImpl (extremes); this->extremes = *extremes; unsetFlags (EXTREMES_CHANGED); DBG_OBJ_SET_NUM (this, "extremes->minWidth", extremes->minWidth); DBG_OBJ_SET_NUM (this, "extremes->maxWidth", extremes->maxWidth); } else *extremes = this->extremes; } /** * \brief Wrapper for Widget::sizeAllocateImpl, calls the latter only when * needed. */ void Widget::sizeAllocate (Allocation *allocation) { 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) { //DEBUG_MSG (DEBUG_ALLOC, // "a %stop-level %s with parent_ref = %d is newly allocated " // "from %d, %d, %d x %d x %d ...\n", // widget->parent ? "non-" : "", // (GTK_OBJECT_TYPE_NAME (widget), widget->parent_ref, // widget->allocation.x, widget->allocation.y, // widget->allocation.width, widget->allocation.ascent, // widget->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 (this, "allocation.x", this->allocation.x); DBG_OBJ_SET_NUM (this, "allocation.y", this->allocation.y); DBG_OBJ_SET_NUM (this, "allocation.width", this->allocation.width); DBG_OBJ_SET_NUM (this, "allocation.ascent", this->allocation.ascent); DBG_OBJ_SET_NUM (this, "allocation.descent", this->allocation.descent); } /*unsetFlags (NEEDS_RESIZE);*/ } 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; style->ref (); if (this->style) { sizeChanged = this->style->sizeDiffs (style); this->style->unref (); } else sizeChanged = true; this->style = style; if (layout != NULL) { layout->updateCursor (); } if (sizeChanged) queueResize (0, true); else queueDraw (); } /** * \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 viewArea; viewArea.x = area->x + allocation.x; viewArea.y = area->y + allocation.y; viewArea.width = area->width; viewArea.height = area->height; style::drawBorder (view, &viewArea, allocation.x + x, allocation.y + y, width, height, style, inverse); /** \todo Background images? */ if (style->backgroundColor) style::drawBackground (view, &viewArea, allocation.x + x, allocation.y + y, width, height, style, inverse); } /** * \brief Draw borders and background of a widget. * * area is given in widget coordinates. * */ void Widget::drawWidgetBox (View *view, Rectangle *area, bool inverse) { Rectangle viewArea; viewArea.x = area->x + allocation.x; viewArea.y = area->y + allocation.y; viewArea.width = area->width; viewArea.height = area->height; style::drawBorder (view, &viewArea, allocation.x, allocation.y, allocation.width, getHeight (), style, inverse); /** \todo Adjust following comment from the old dw sources. */ /* * - Toplevel widget background colors are set as viewport * background color. This is not crucial for the rendering, but * looks a bit nicer when scrolling. Furthermore, the viewport * does anything else in this case. * * - Since widgets are always drawn from top to bottom, it is * *not* necessary to draw the background if * widget->style->background_color is NULL (shining through). */ /** \todo Background images? */ if (style->backgroundColor && (parent || layout->getBgColor () != style->backgroundColor)) style::drawBackground (view, &viewArea, allocation.x, allocation.y, allocation.width, getHeight (), style, inverse); } /* * 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 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; //_MSG ("%*s-> examining the %s %p (%d, %d, %d x (%d + %d))\n", // 3 * level, "", gtk_type_name (GTK_OBJECT_TYPE (widget)), widget, // 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::WIDGET, false); while (childAtPoint == NULL && it->next ()) childAtPoint = it->getContent()->widget->getWidgetAtPoint (x, y, level + 1); it->unref (); if (childAtPoint) return childAtPoint; else return this; } else return NULL; } void Widget::scrollTo (HPosition hpos, VPosition vpos, int x, int y, int width, int height) { layout->scrollTo (hpos, vpos, x + allocation.x, y + allocation.y, width, height); } void Widget::getExtremesImpl (Extremes *extremes) { /* Simply return the requisition width */ Requisition requisition; sizeRequest (&requisition); extremes->minWidth = extremes->maxWidth = requisition.width; } void Widget::sizeAllocateImpl (Allocation *allocation) { } void Widget::markSizeChange (int ref) { } void Widget::markExtremesChange (int ref) { } void Widget::setWidth (int width) { } void Widget::setAscent (int ascent) { } void Widget::setDescent (int descent) { } 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 *) { core::style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onEnter(); } void Widget::leaveNotifyImpl (EventCrossing *) { core::style::Tooltip *tooltip = getStyle()->x_tooltip; if (tooltip) tooltip->onLeave(); } void Widget::removeChild (Widget *child) { // Should be implemented. misc::assertNotReached (); } } // namespace dw } // namespace core