summaryrefslogtreecommitdiffstats
path: root/khtml/html/html_imageimpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/html/html_imageimpl.cpp')
-rw-r--r--khtml/html/html_imageimpl.cpp560
1 files changed, 560 insertions, 0 deletions
diff --git a/khtml/html/html_imageimpl.cpp b/khtml/html/html_imageimpl.cpp
new file mode 100644
index 000000000..1c755920b
--- /dev/null
+++ b/khtml/html/html_imageimpl.cpp
@@ -0,0 +1,560 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "html/html_imageimpl.h"
+#include "html/html_formimpl.h"
+#include "html/html_documentimpl.h"
+
+#include "misc/htmlhashes.h"
+#include "khtmlview.h"
+#include "khtml_part.h"
+
+#include <kstringhandler.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include "rendering/render_image.h"
+#include "rendering/render_flow.h"
+#include "css/cssstyleselector.h"
+#include "css/cssproperties.h"
+#include "css/cssvalues.h"
+#include "css/csshelper.h"
+#include "xml/dom2_eventsimpl.h"
+
+#include <qstring.h>
+#include <qpoint.h>
+#include <qregion.h>
+#include <qptrstack.h>
+#include <qimage.h>
+#include <qpointarray.h>
+
+using namespace DOM;
+using namespace khtml;
+
+// -------------------------------------------------------------------------
+
+HTMLImageElementImpl::HTMLImageElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
+ : HTMLElementImpl(doc), ismap(false), loadEventSent(true), m_image(0), m_form(f)
+{
+ if (m_form)
+ m_form->registerImgElement(this);
+}
+
+HTMLImageElementImpl::~HTMLImageElementImpl()
+{
+ if (getDocument())
+ getDocument()->removeImage(this);
+
+ if (m_image)
+ m_image->deref(this);
+
+ if (m_form)
+ m_form->removeImgElement(this);
+}
+
+NodeImpl::Id HTMLImageElementImpl::id() const
+{
+ return ID_IMG;
+}
+
+void HTMLImageElementImpl::parseAttribute(AttributeImpl *attr)
+{
+ switch (attr->id())
+ {
+ case ATTR_ALT:
+ setChanged();
+ break;
+ case ATTR_SRC: {
+ setChanged();
+
+ //Start loading the image already, to generate events
+ DOMString url = attr->value();
+ if (!url.isEmpty()) { //### why do we not hide or something when setting this?
+ CachedImage* newImage = getDocument()->docLoader()->requestImage(khtml::parseURL(url));
+ if (newImage && newImage != m_image) {
+ CachedImage* oldImage = m_image;
+ loadEventSent = false;
+ m_image = newImage;
+ m_image->ref(this);
+ if (oldImage)
+ oldImage->deref(this);
+ }
+ }
+ }
+ break;
+ case ATTR_WIDTH:
+ if (!attr->value().isEmpty())
+ addCSSLength(CSS_PROP_WIDTH, attr->value());
+ else
+ removeCSSProperty(CSS_PROP_WIDTH);
+ break;
+ case ATTR_HEIGHT:
+ if (!attr->value().isEmpty())
+ addCSSLength(CSS_PROP_HEIGHT, attr->value());
+ else
+ removeCSSProperty(CSS_PROP_HEIGHT);
+ break;
+ case ATTR_BORDER:
+ // border="noborder" -> border="0"
+ if(attr->value().toInt()) {
+ addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value());
+ addCSSProperty( CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID );
+ addCSSProperty( CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID );
+ addCSSProperty( CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID );
+ addCSSProperty( CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID );
+ } else {
+ removeCSSProperty( CSS_PROP_BORDER_WIDTH );
+ removeCSSProperty( CSS_PROP_BORDER_TOP_STYLE );
+ removeCSSProperty( CSS_PROP_BORDER_RIGHT_STYLE );
+ removeCSSProperty( CSS_PROP_BORDER_BOTTOM_STYLE );
+ removeCSSProperty( CSS_PROP_BORDER_LEFT_STYLE );
+ }
+ break;
+ case ATTR_VSPACE:
+ addCSSLength(CSS_PROP_MARGIN_TOP, attr->value());
+ addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value());
+ break;
+ case ATTR_HSPACE:
+ addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value());
+ addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value());
+ break;
+ case ATTR_ALIGN:
+ addHTMLAlignment( attr->value() );
+ break;
+ case ATTR_VALIGN:
+ addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower());
+ break;
+ case ATTR_USEMAP:
+ if ( attr->value()[0] == '#' )
+ usemap = attr->value().lower();
+ else {
+ QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() );
+ // ### we remove the part before the anchor and hope
+ // the map is on the same html page....
+ usemap = url;
+ }
+ m_hasAnchor = attr->val() != 0;
+ case ATTR_ISMAP:
+ ismap = true;
+ break;
+ case ATTR_ONABORT: // ### add support for this
+ setHTMLEventListener(EventImpl::ABORT_EVENT,
+ getDocument()->createHTMLEventListener(attr->value().string(), "onabort", this));
+ break;
+ case ATTR_ONERROR:
+ setHTMLEventListener(EventImpl::ERROR_EVENT,
+ getDocument()->createHTMLEventListener(attr->value().string(), "onerror", this));
+ break;
+ case ATTR_ONLOAD:
+ setHTMLEventListener(EventImpl::LOAD_EVENT,
+ getDocument()->createHTMLEventListener(attr->value().string(), "onload", this));
+ break;
+ case ATTR_NOSAVE:
+ break;
+ case ATTR_NAME:
+ if (inDocument() && m_name != attr->value()) {
+ getDocument()->underDocNamedCache().remove(m_name.string(), this);
+ getDocument()->underDocNamedCache().add (attr->value().string(), this);
+ }
+ m_name = attr->value();
+ //fallthrough
+ default:
+ HTMLElementImpl::parseAttribute(attr);
+ }
+}
+
+void HTMLImageElementImpl::notifyFinished(CachedObject *finishedObj)
+{
+ if (m_image == finishedObj) {
+ getDocument()->dispatchImageLoadEventSoon(this);
+ }
+}
+
+void HTMLImageElementImpl::dispatchLoadEvent()
+{
+ if (!loadEventSent) {
+ loadEventSent = true;
+ if (m_image->isErrorImage()) {
+ dispatchHTMLEvent(EventImpl::ERROR_EVENT, false, false);
+ } else {
+ dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false);
+ }
+ }
+}
+
+DOMString HTMLImageElementImpl::altText() const
+{
+ // lets figure out the alt text.. magic stuff
+ // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
+ // also heavily discussed by Hixie on bugzilla
+ DOMString alt( getAttribute( ATTR_ALT ) );
+ // fall back to title attribute
+ if ( alt.isNull() )
+ alt = getAttribute( ATTR_TITLE );
+#if 0
+ if ( alt.isNull() ) {
+ QString p = KURL( getDocument()->completeURL( getAttribute(ATTR_SRC).string() ) ).prettyURL();
+ int pos;
+ if ( ( pos = p.findRev( '.' ) ) > 0 )
+ p.truncate( pos );
+ alt = DOMString( KStringHandler::csqueeze( p ) );
+ }
+#endif
+
+ return alt;
+}
+
+void HTMLImageElementImpl::attach()
+{
+ assert(!attached());
+ assert(!m_render);
+ assert(parentNode());
+
+ RenderStyle* _style = getDocument()->styleSelector()->styleForElement(this);
+ _style->ref();
+ if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
+ _style->display() != NONE)
+ {
+ m_render = new (getDocument()->renderArena()) RenderImage(this);
+ m_render->setStyle(_style);
+ parentNode()->renderer()->addChild(m_render, nextRenderer());
+ }
+ _style->deref();
+
+ NodeBaseImpl::attach();
+ if (m_render)
+ m_render->updateFromElement();
+}
+
+void HTMLImageElementImpl::removedFromDocument()
+{
+ getDocument()->underDocNamedCache().remove(m_name.string(), this);
+ HTMLElementImpl::removedFromDocument();
+}
+
+void HTMLImageElementImpl::insertedIntoDocument()
+{
+ getDocument()->underDocNamedCache().add(m_name.string(), this);
+ HTMLElementImpl::insertedIntoDocument();
+}
+
+void HTMLImageElementImpl::removeId(const QString& id)
+{
+ getDocument()->underDocNamedCache().remove(id, this);
+ HTMLElementImpl::removeId(id);
+}
+
+void HTMLImageElementImpl::addId (const QString& id)
+{
+ getDocument()->underDocNamedCache().add(id, this);
+ HTMLElementImpl::addId(id);
+}
+
+
+long HTMLImageElementImpl::width() const
+{
+ if (!m_render) {
+ DOMString widthAttr = getAttribute(ATTR_WIDTH);
+ if (!widthAttr.isNull())
+ return widthAttr.toInt();
+ else if (m_image && m_image->pixmap_size().isValid())
+ return m_image->pixmap_size().width();
+ else
+ return 0;
+ }
+
+ // ### make a unified call for this
+ if (changed()) {
+ getDocument()->updateRendering();
+ if (getDocument()->view())
+ getDocument()->view()->layout();
+ }
+
+ return m_render ? m_render->contentWidth() :
+ getAttribute(ATTR_WIDTH).toInt();
+}
+
+long HTMLImageElementImpl::height() const
+{
+ if (!m_render) {
+ DOMString heightAttr = getAttribute(ATTR_HEIGHT);
+ if (!heightAttr.isNull())
+ return heightAttr.toInt();
+ else if (m_image && m_image->pixmap_size().isValid())
+ return m_image->pixmap_size().height();
+ else
+ return 0;
+ }
+
+ // ### make a unified call for this
+ if (changed()) {
+ getDocument()->updateRendering();
+ if (getDocument()->view())
+ getDocument()->view()->layout();
+ }
+
+ return m_render ? m_render->contentHeight() :
+ getAttribute(ATTR_HEIGHT).toInt();
+}
+
+QImage HTMLImageElementImpl::currentImage() const
+{
+ RenderImage *r = static_cast<RenderImage*>(renderer());
+
+ if(r)
+ return r->pixmap().convertToImage();
+
+ return QImage();
+}
+
+QPixmap HTMLImageElementImpl::currentPixmap() const
+{
+ RenderImage *r = static_cast<RenderImage*>(renderer());
+
+ if(r)
+ return r->pixmap();
+
+ return QPixmap();
+}
+
+bool HTMLImageElementImpl::complete() const
+{
+ return m_image && m_image->valid_rect().size() == m_image->pixmap_size();
+}
+
+// -------------------------------------------------------------------------
+
+HTMLMapElementImpl::HTMLMapElementImpl(DocumentImpl *doc)
+ : HTMLElementImpl(doc)
+{
+}
+
+HTMLMapElementImpl::~HTMLMapElementImpl()
+{
+ if(getDocument() && getDocument()->isHTMLDocument())
+ static_cast<HTMLDocumentImpl*>(getDocument())->mapMap.remove(name);
+}
+
+NodeImpl::Id HTMLMapElementImpl::id() const
+{
+ return ID_MAP;
+}
+
+bool
+HTMLMapElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
+ RenderObject::NodeInfo& info)
+{
+ //cout << "map:mapMouseEvent " << endl;
+ //cout << x_ << " " << y_ <<" "<< width_ <<" "<< height_ << endl;
+ QPtrStack<NodeImpl> nodeStack;
+
+ NodeImpl *current = firstChild();
+ while(1)
+ {
+ if(!current)
+ {
+ if(nodeStack.isEmpty()) break;
+ current = nodeStack.pop();
+ current = current->nextSibling();
+ continue;
+ }
+ if(current->id()==ID_AREA)
+ {
+ //cout << "area found " << endl;
+ HTMLAreaElementImpl* area=static_cast<HTMLAreaElementImpl*>(current);
+ if (area->mapMouseEvent(x_,y_,width_,height_, info))
+ return true;
+ }
+ NodeImpl *child = current->firstChild();
+ if(child)
+ {
+ nodeStack.push(current);
+ current = child;
+ }
+ else
+ {
+ current = current->nextSibling();
+ }
+ }
+
+ return false;
+}
+
+void HTMLMapElementImpl::parseAttribute(AttributeImpl *attr)
+{
+ switch (attr->id())
+ {
+ case ATTR_ID:
+ if (getDocument()->htmlMode() != DocumentImpl::XHtml) {
+ HTMLElementImpl::parseAttribute(attr);
+ break;
+ }
+ else {
+ // add name with full url:
+ QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() );
+ if(getDocument()->isHTMLDocument())
+ static_cast<HTMLDocumentImpl*>(getDocument())->mapMap[url] = this;
+ }
+ // fall through
+ case ATTR_NAME:
+ {
+ DOMString s = attr->value();
+ if(*s.unicode() == '#')
+ name = QString(s.unicode()+1, s.length()-1).lower();
+ else
+ name = s.string().lower();
+ // ### make this work for XML documents, e.g. in case of <html:map...>
+ if(getDocument()->isHTMLDocument())
+ static_cast<HTMLDocumentImpl*>(getDocument())->mapMap[name] = this;
+
+ //fallthrough
+ }
+ default:
+ HTMLElementImpl::parseAttribute(attr);
+ }
+}
+
+// -------------------------------------------------------------------------
+
+HTMLAreaElementImpl::HTMLAreaElementImpl(DocumentImpl *doc)
+ : HTMLAnchorElementImpl(doc)
+{
+ m_coords=0;
+ m_coordsLen = 0;
+ nohref = false;
+ shape = Unknown;
+ lasth = lastw = -1;
+}
+
+HTMLAreaElementImpl::~HTMLAreaElementImpl()
+{
+ delete [] m_coords;
+}
+
+NodeImpl::Id HTMLAreaElementImpl::id() const
+{
+ return ID_AREA;
+}
+
+void HTMLAreaElementImpl::parseAttribute(AttributeImpl *attr)
+{
+ switch (attr->id())
+ {
+ case ATTR_SHAPE:
+ if ( strcasecmp( attr->value(), "default" ) == 0 )
+ shape = Default;
+ else if ( strcasecmp( attr->value(), "circle" ) == 0 )
+ shape = Circle;
+ else if ( strcasecmp( attr->value(), "poly" ) == 0 || strcasecmp( attr->value(), "polygon" ) == 0 )
+ shape = Poly;
+ else if ( strcasecmp( attr->value(), "rect" ) == 0 )
+ shape = Rect;
+ break;
+ case ATTR_COORDS:
+ delete [] m_coords;
+ m_coords = attr->val()->toCoordsArray(m_coordsLen);
+ break;
+ case ATTR_NOHREF:
+ nohref = attr->val() != 0;
+ break;
+ case ATTR_TARGET:
+ m_hasTarget = attr->val() != 0;
+ break;
+ case ATTR_ALT:
+ break;
+ case ATTR_ACCESSKEY:
+ break;
+ default:
+ HTMLAnchorElementImpl::parseAttribute(attr);
+ }
+}
+
+bool HTMLAreaElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
+ RenderObject::NodeInfo& info)
+{
+ bool inside = false;
+ if (width_ != lastw || height_ != lasth)
+ {
+ region=getRegion(width_, height_);
+ lastw=width_; lasth=height_;
+ }
+ if (region.contains(QPoint(x_,y_)))
+ {
+ inside = true;
+ info.setInnerNode(this);
+ info.setURLElement(this);
+ }
+
+ return inside;
+}
+
+QRect HTMLAreaElementImpl::getRect() const
+{
+ if (parentNode()->renderer()==0)
+ return QRect();
+ int dx, dy;
+ if (!parentNode()->renderer()->absolutePosition(dx, dy))
+ return QRect();
+ QRegion region = getRegion(lastw,lasth);
+ region.translate(dx, dy);
+ return region.boundingRect();
+}
+
+QRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const
+{
+ QRegion region;
+ if (!m_coords)
+ return region;
+
+ // added broken HTML support (Dirk): some pages omit the SHAPE
+ // attribute, so we try to guess by looking at the coords count
+ // what the HTML author tried to tell us.
+
+ // a Poly needs at least 3 points (6 coords), so this is correct
+ if ((shape==Poly || shape==Unknown) && m_coordsLen > 5) {
+ // make sure its even
+ int len = m_coordsLen >> 1;
+ QPointArray points(len);
+ for (int i = 0; i < len; ++i)
+ points.setPoint(i, m_coords[(i<<1)].minWidth(width_),
+ m_coords[(i<<1)+1].minWidth(height_));
+ region = QRegion(points);
+ }
+ else if (shape==Circle && m_coordsLen>=3 || shape==Unknown && m_coordsLen == 3) {
+ int r = kMin(m_coords[2].minWidth(width_), m_coords[2].minWidth(height_));
+ region = QRegion(m_coords[0].minWidth(width_)-r,
+ m_coords[1].minWidth(height_)-r, 2*r, 2*r,QRegion::Ellipse);
+ }
+ else if (shape==Rect && m_coordsLen>=4 || shape==Unknown && m_coordsLen == 4) {
+ int x0 = m_coords[0].minWidth(width_);
+ int y0 = m_coords[1].minWidth(height_);
+ int x1 = m_coords[2].minWidth(width_);
+ int y1 = m_coords[3].minWidth(height_);
+ region = QRegion(x0,y0,x1-x0,y1-y0);
+ }
+ else if (shape==Default)
+ region = QRegion(0,0,width_,height_);
+ // else
+ // return null region
+
+ return region;
+}