You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
16 KiB
561 lines
16 KiB
/**
|
|
* 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 <tqstring.h>
|
|
#include <tqpoint.h>
|
|
#include <tqregion.h>
|
|
#include <tqptrstack.h>
|
|
#include <tqimage.h>
|
|
#include <tqpointarray.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 {
|
|
TQString 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() ) {
|
|
TQString 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 TQString& id)
|
|
{
|
|
getDocument()->underDocNamedCache().remove(id, this);
|
|
HTMLElementImpl::removeId(id);
|
|
}
|
|
|
|
void HTMLImageElementImpl::addId (const TQString& 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();
|
|
}
|
|
|
|
TQImage HTMLImageElementImpl::currentImage() const
|
|
{
|
|
RenderImage *r = static_cast<RenderImage*>(renderer());
|
|
|
|
if(r)
|
|
return r->pixmap().convertToImage();
|
|
|
|
return TQImage();
|
|
}
|
|
|
|
TQPixmap HTMLImageElementImpl::currentPixmap() const
|
|
{
|
|
RenderImage *r = static_cast<RenderImage*>(renderer());
|
|
|
|
if(r)
|
|
return r->pixmap();
|
|
|
|
return TQPixmap();
|
|
}
|
|
|
|
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;
|
|
TQPtrStack<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:
|
|
TQString 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 = TQString(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(TQPoint(x_,y_)))
|
|
{
|
|
inside = true;
|
|
info.setInnerNode(this);
|
|
info.setURLElement(this);
|
|
}
|
|
|
|
return inside;
|
|
}
|
|
|
|
TQRect HTMLAreaElementImpl::getRect() const
|
|
{
|
|
if (parentNode()->renderer()==0)
|
|
return TQRect();
|
|
int dx, dy;
|
|
if (!parentNode()->renderer()->absolutePosition(dx, dy))
|
|
return TQRect();
|
|
TQRegion region = getRegion(lastw,lasth);
|
|
region.translate(dx, dy);
|
|
return region.boundingRect();
|
|
}
|
|
|
|
TQRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const
|
|
{
|
|
TQRegion 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;
|
|
TQPointArray 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 = TQRegion(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 = TQRegion(m_coords[0].minWidth(width_)-r,
|
|
m_coords[1].minWidth(height_)-r, 2*r, 2*r,TQRegion::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 = TQRegion(x0,y0,x1-x0,y1-y0);
|
|
}
|
|
else if (shape==Default)
|
|
region = TQRegion(0,0,width_,height_);
|
|
// else
|
|
// return null region
|
|
|
|
return region;
|
|
}
|