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.
tdelibs/khtml/rendering/render_style.cpp

1301 lines
40 KiB

/*
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
* Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2002-2005 Apple Computer, Inc.
* Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
*
* 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 "xml/dom_stringimpl.h"
#include "css/cssstyleselector.h"
#include "css/css_valueimpl.h"
#include "render_style.h"
#include "kdebug.h"
using namespace khtml;
using namespace DOM;
/* CSS says Fixed for the default padding value, but we treat variable as 0 padding anyways, and like
* this is works fine for table paddings aswell
*/
StyleSurroundData::StyleSurroundData()
: margin( Fixed ), padding( Variable )
{
}
StyleSurroundData::StyleSurroundData(const StyleSurroundData& o )
: Shared<StyleSurroundData>(),
offset( o.offset ), margin( o.margin ), padding( o.padding ),
border( o.border )
{
}
bool StyleSurroundData::operator==(const StyleSurroundData& o) const
{
return offset==o.offset && margin==o.margin &&
padding==o.padding && border==o.border;
}
StyleBoxData::StyleBoxData()
: z_index( 0 ), z_auto( true )
{
min_width = min_height = RenderStyle::initialMinSize();
max_width = max_height = RenderStyle::initialMaxSize();
box_sizing = RenderStyle::initialBoxSizing();
}
StyleBoxData::StyleBoxData(const StyleBoxData& o )
: Shared<StyleBoxData>(),
width( o.width ), height( o.height ),
min_width( o.min_width ), max_width( o.max_width ),
min_height ( o.min_height ), max_height( o.max_height ),
box_sizing( o.box_sizing),
z_index( o.z_index ), z_auto( o.z_auto )
{
}
bool StyleBoxData::operator==(const StyleBoxData& o) const
{
return
width == o.width &&
height == o.height &&
min_width == o.min_width &&
max_width == o.max_width &&
min_height == o.min_height &&
max_height == o.max_height &&
box_sizing == o.box_sizing &&
z_index == o.z_index &&
z_auto == o.z_auto;
}
StyleVisualData::StyleVisualData()
: textDecoration(RenderStyle::initialTextDecoration()),
palette( TQApplication::palette() )
{
}
StyleVisualData::~StyleVisualData() {
}
StyleVisualData::StyleVisualData(const StyleVisualData& o )
: Shared<StyleVisualData>(),
clip( o.clip ), textDecoration(o.textDecoration),
palette( o.palette )
{
}
BackgroundLayer::BackgroundLayer()
:m_image(RenderStyle::initialBackgroundImage()),
m_bgAttachment(RenderStyle::initialBackgroundAttachment()),
m_bgClip(RenderStyle::initialBackgroundClip()),
m_bgOrigin(RenderStyle::initialBackgroundOrigin()),
m_bgRepeat(RenderStyle::initialBackgroundRepeat()),
m_backgroundSize(RenderStyle::initialBackgroundSize()),
m_next(0)
{
m_imageSet = m_attachmentSet = m_clipSet = m_originSet =
m_repeatSet = m_xPosSet = m_yPosSet = m_backgroundSizeSet = false;
}
BackgroundLayer::BackgroundLayer(const BackgroundLayer& o)
{
m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0;
m_image = o.m_image;
m_xPosition = o.m_xPosition;
m_yPosition = o.m_yPosition;
m_bgAttachment = o.m_bgAttachment;
m_bgClip = o.m_bgClip;
m_bgOrigin = o.m_bgOrigin;
m_bgRepeat = o.m_bgRepeat;
m_backgroundSize = o.m_backgroundSize;
m_imageSet = o.m_imageSet;
m_attachmentSet = o.m_attachmentSet;
m_clipSet = o.m_clipSet;
m_originSet = o.m_originSet;
m_repeatSet = o.m_repeatSet;
m_xPosSet = o.m_xPosSet;
m_yPosSet = o.m_yPosSet;
m_backgroundSizeSet = o.m_backgroundSizeSet;
}
BackgroundLayer::~BackgroundLayer()
{
delete m_next;
}
BackgroundLayer& BackgroundLayer::operator=(const BackgroundLayer& o) {
if (m_next != o.m_next) {
delete m_next;
m_next = o.m_next ? new BackgroundLayer(*o.m_next) : 0;
}
m_image = o.m_image;
m_xPosition = o.m_xPosition;
m_yPosition = o.m_yPosition;
m_bgAttachment = o.m_bgAttachment;
m_bgClip = o.m_bgClip;
m_bgOrigin = o.m_bgOrigin;
m_bgRepeat = o.m_bgRepeat;
m_backgroundSize = o.m_backgroundSize;
m_imageSet = o.m_imageSet;
m_attachmentSet = o.m_attachmentSet;
m_originSet = o.m_originSet;
m_repeatSet = o.m_repeatSet;
m_xPosSet = o.m_xPosSet;
m_yPosSet = o.m_yPosSet;
m_backgroundSizeSet = o.m_backgroundSizeSet;
return *this;
}
bool BackgroundLayer::operator==(const BackgroundLayer& o) const {
return m_image == o.m_image && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition &&
m_bgAttachment == o.m_bgAttachment && m_bgClip == o.m_bgClip && m_bgOrigin == o.m_bgOrigin && m_bgRepeat == o.m_bgRepeat &&
m_backgroundSize.width == o.m_backgroundSize.width && m_backgroundSize.height == o.m_backgroundSize.height &&
m_imageSet == o.m_imageSet && m_attachmentSet == o.m_attachmentSet && m_repeatSet == o.m_repeatSet &&
m_xPosSet == o.m_xPosSet && m_yPosSet == o.m_yPosSet && m_backgroundSizeSet == o.m_backgroundSizeSet &&
((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
}
void BackgroundLayer::fillUnsetProperties()
{
BackgroundLayer* curr;
for (curr = this; curr && curr->isBackgroundImageSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_image = pattern->m_image;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundXPositionSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_xPosition = pattern->m_xPosition;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundYPositionSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_yPosition = pattern->m_yPosition;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundAttachmentSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_bgAttachment = pattern->m_bgAttachment;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundClipSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_bgClip = pattern->m_bgClip;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundOriginSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_bgOrigin = pattern->m_bgOrigin;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundRepeatSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_bgRepeat = pattern->m_bgRepeat;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
for (curr = this; curr && curr->isBackgroundSizeSet(); curr = curr->next());
if (curr && curr != this) {
// We need to fill in the remaining values with the pattern specified.
for (BackgroundLayer* pattern = this; curr; curr = curr->next()) {
curr->m_backgroundSize = pattern->m_backgroundSize;
pattern = pattern->next();
if (pattern == curr || !pattern)
pattern = this;
}
}
}
void BackgroundLayer::cullEmptyLayers()
{
BackgroundLayer *next;
for (BackgroundLayer *p = this; p; p = next) {
next = p->m_next;
if (next && !next->isBackgroundImageSet() &&
!next->isBackgroundXPositionSet() && !next->isBackgroundYPositionSet() &&
!next->isBackgroundAttachmentSet() && !next->isBackgroundClipSet() &&
!next->isBackgroundOriginSet() && !next->isBackgroundRepeatSet() &&
!next->isBackgroundSizeSet()) {
delete next;
p->m_next = 0;
break;
}
}
}
StyleBackgroundData::StyleBackgroundData()
{}
StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o)
: Shared<StyleBackgroundData>(), m_background(o.m_background), m_outline(o.m_outline)
{}
bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const
{
return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline;
}
StyleGeneratedData::StyleGeneratedData() : Shared<StyleGeneratedData>(), content(0), counter_reset(0), counter_increment(0) {}
StyleGeneratedData::~StyleGeneratedData()
{
if (counter_reset) counter_reset->deref();
if (counter_increment) counter_increment->deref();
delete content;
}
StyleGeneratedData::StyleGeneratedData(const StyleGeneratedData& o)
: Shared<StyleGeneratedData>(), content(0),
counter_reset(o.counter_reset), counter_increment(o.counter_increment)
{
if (o.content) content = new ContentData(*o.content);
if (counter_reset) counter_reset->ref();
if (counter_increment) counter_increment->ref();
}
bool StyleGeneratedData::contentDataEquivalent(const StyleGeneratedData* otherStyle) const
{
ContentData* c1 = content;
ContentData* c2 = otherStyle->content;
while (c1 && c2) {
if (c1->_contentType != c2->_contentType)
return false;
if (c1->_contentType == CONTENT_TEXT) {
DOM::DOMString c1Str(c1->_content.text);
DOM::DOMString c2Str(c2->_content.text);
if (c1Str != c2Str)
return false;
}
else if (c1->_contentType == CONTENT_OBJECT) {
if (c1->_content.object != c2->_content.object)
return false;
}
else if (c1->_contentType == CONTENT_COUNTER) {
if (c1->_content.counter != c2->_content.counter)
return false;
}
else if (c1->_contentType == CONTENT_QUOTE) {
if (c1->_content.quote != c2->_content.quote)
return false;
}
c1 = c1->_nextContent;
c2 = c2->_nextContent;
}
return !c1 && !c2;
}
static bool compareCounterActList(const CSSValueListImpl* ca, const CSSValueListImpl* cb) {
// weeee....
CSSValueListImpl* a = const_cast<CSSValueListImpl*>(ca);
CSSValueListImpl* b = const_cast<CSSValueListImpl*>(cb);
if (!a && !b) return true;
if (!a || !b) return false;
if (a->length() != b->length()) return false;
for(uint i=0; i< a->length(); i++) {
CSSValueImpl *ai = a->item(i);
CSSValueImpl *bi = b->item(i);
assert(ai && ai->cssValueType() == CSSValue::CSS_CUSTOM);
assert(bi && bi->cssValueType() == CSSValue::CSS_CUSTOM);
CounterActImpl* caa = static_cast<CounterActImpl*>(ai);
CounterActImpl* cab = static_cast<CounterActImpl*>(bi);
if (caa->value() != cab->value()) return false;
if (caa->counter() != cab->counter()) return false;
}
return true;
}
bool StyleGeneratedData::counterDataEquivalent(const StyleGeneratedData* otherStyle) const
{
return compareCounterActList(counter_reset, otherStyle->counter_reset) &&
compareCounterActList(counter_increment, otherStyle->counter_increment);
}
bool StyleGeneratedData::operator==(const StyleGeneratedData& o) const
{
return contentDataEquivalent(&o) && counterDataEquivalent(&o);
}
StyleMarqueeData::StyleMarqueeData()
{
increment = RenderStyle::initialMarqueeIncrement();
speed = RenderStyle::initialMarqueeSpeed();
direction = RenderStyle::initialMarqueeDirection();
behavior = RenderStyle::initialMarqueeBehavior();
loops = RenderStyle::initialMarqueeLoopCount();
}
StyleMarqueeData::StyleMarqueeData(const StyleMarqueeData& o)
:Shared<StyleMarqueeData>(), increment(o.increment), speed(o.speed), loops(o.loops),
behavior(o.behavior), direction(o.direction)
{}
bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const
{
return (increment == o.increment && speed == o.speed && direction == o.direction &&
behavior == o.behavior && loops == o.loops);
}
StyleCSS3NonInheritedData::StyleCSS3NonInheritedData()
:Shared<StyleCSS3NonInheritedData>()
, opacity(RenderStyle::initialOpacity())
{
}
StyleCSS3NonInheritedData::StyleCSS3NonInheritedData(const StyleCSS3NonInheritedData& o)
:Shared<StyleCSS3NonInheritedData>(),
opacity(o.opacity),
#ifdef APPLE_CHANGES
flexibleBox(o.flexibleBox),
#endif
marquee(o.marquee)
{
}
bool StyleCSS3NonInheritedData::operator==(const StyleCSS3NonInheritedData& o) const
{
return
opacity == o.opacity &&
#ifdef APPLE_CHANGES
flexibleBox == o.flexibleBox &&
#endif
marquee == o.marquee;
}
StyleCSS3InheritedData::StyleCSS3InheritedData()
:Shared<StyleCSS3InheritedData>(), textShadow(0)
#ifdef APPLE_CHANGES
, userModify(READ_ONLY), textSizeAdjust(RenderStyle::initialTextSizeAdjust())
#endif
{
}
StyleCSS3InheritedData::StyleCSS3InheritedData(const StyleCSS3InheritedData& o)
:Shared<StyleCSS3InheritedData>()
{
textShadow = o.textShadow ? new ShadowData(*o.textShadow) : 0;
#ifdef APPLE_CHANGES
userModify = o.userModify;
textSizeAdjust = o.textSizeAdjust;
#endif
}
StyleCSS3InheritedData::~StyleCSS3InheritedData()
{
delete textShadow;
}
bool StyleCSS3InheritedData::operator==(const StyleCSS3InheritedData& o) const
{
return shadowDataEquivalent(o)
#ifdef APPLE_CHANGES
&& (userModify == o.userModify) && (textSizeAdjust == o.textSizeAdjust)
#endif
;
}
bool StyleCSS3InheritedData::shadowDataEquivalent(const StyleCSS3InheritedData& o) const
{
if (!textShadow && o.textShadow || textShadow && !o.textShadow)
return false;
if (textShadow && o.textShadow && (*textShadow != *o.textShadow))
return false;
return true;
}
StyleInheritedData::StyleInheritedData()
: indent( RenderStyle::initialTextIndent() ), line_height( RenderStyle::initialLineHeight() ),
style_image( RenderStyle::initialListStyleImage() ),
font(), color( RenderStyle::initialColor() ),
border_hspacing( RenderStyle::initialBorderHorizontalSpacing() ),
border_vspacing( RenderStyle::initialBorderVerticalSpacing() ),
widows( RenderStyle::initialWidows() ), orphans( RenderStyle::initialOrphans() ),
quotes(0)
{
}
StyleInheritedData::~StyleInheritedData()
{
if (quotes) quotes->deref();
}
StyleInheritedData::StyleInheritedData(const StyleInheritedData& o )
: Shared<StyleInheritedData>(),
indent( o.indent ), line_height( o.line_height ), style_image( o.style_image ),
font( o.font ), color( o.color ),
border_hspacing( o.border_hspacing ),
border_vspacing( o.border_vspacing ),
widows(o.widows), orphans(o.orphans)
{
quotes = o.quotes;
if (quotes) quotes->ref();
}
bool StyleInheritedData::operator==(const StyleInheritedData& o) const
{
return
indent == o.indent &&
line_height == o.line_height &&
border_hspacing == o.border_hspacing &&
border_vspacing == o.border_vspacing &&
style_image == o.style_image &&
font == o.font &&
color == o.color &&
border_hspacing == o.border_hspacing &&
border_vspacing == o.border_vspacing &&
quotes == o.quotes &&
widows == o.widows &&
orphans == o.orphans ;
// doesn't work because structs are not packed
//return memcmp(this, &o, sizeof(*this))==0;
}
RenderStyle::RenderStyle()
{
// counter++;
if (!_default)
_default = new RenderStyle(true);
box = _default->box;
visual = _default->visual;
background = _default->background;
surround = _default->surround;
generated = _default->generated;
css3NonInheritedData = _default->css3NonInheritedData;
css3InheritedData = _default->css3InheritedData;
inherited = _default->inherited;
setBitDefaults();
pseudoStyle = 0;
}
RenderStyle::RenderStyle(bool)
{
setBitDefaults();
box.init();
visual.init();
background.init();
surround.init();
generated.init();
css3NonInheritedData.init();
#ifdef APPLE_CHANGES // ### yet to be merged
css3NonInheritedData.access()->flexibleBox.init();
#endif
css3NonInheritedData.access()->marquee.init();
css3InheritedData.init();
inherited.init();
pseudoStyle = 0;
}
RenderStyle::RenderStyle(const RenderStyle& o)
: Shared<RenderStyle>(),
inherited_flags( o.inherited_flags ), noninherited_flags( o.noninherited_flags ),
box( o.box ), visual( o.visual ), background( o.background ), surround( o.surround ), generated(o.generated),
css3NonInheritedData( o.css3NonInheritedData ), css3InheritedData( o.css3InheritedData ),
inherited( o.inherited ), pseudoStyle( 0 )
{}
void RenderStyle::inheritFrom(const RenderStyle* inheritParent)
{
css3InheritedData = inheritParent->css3InheritedData;
inherited = inheritParent->inherited;
inherited_flags = inheritParent->inherited_flags;
// Simulate ":after,:before { white-space: pre-line }"
if (styleType() == AFTER || styleType() == BEFORE)
setWhiteSpace(PRE_LINE);
}
RenderStyle::~RenderStyle()
{
RenderStyle *ps = pseudoStyle;
RenderStyle *prev = 0;
while (ps) {
prev = ps;
ps = ps->pseudoStyle;
// to prevent a double deletion.
// this works only because the styles below aren't really shared
// Dirk said we need another construct as soon as these are shared
prev->pseudoStyle = 0;
prev->deref();
}
}
bool RenderStyle::operator==(const RenderStyle& o) const
{
// compare everything except the pseudoStyle pointer
return (inherited_flags == o.inherited_flags &&
noninherited_flags == o.noninherited_flags &&
box == o.box &&
visual == o.visual &&
background == o.background &&
surround == o.surround &&
generated == o.generated &&
css3NonInheritedData == o.css3NonInheritedData &&
css3InheritedData == o.css3InheritedData &&
inherited == o.inherited);
}
enum EPseudoBit { NO_BIT = 0x0,
FIRST_LINE_BIT = 0x1, FIRST_LETTER_BIT = 0x2, SELECTION_BIT = 0x4,
BEFORE_BIT = 0x8, AFTER_BIT = 0x10, MARKER_BIT = 0x20,
REPLACED_BIT = 0x40
};
static int pseudoBit(RenderStyle::PseudoId pseudo)
{
switch (pseudo) {
case RenderStyle::BEFORE:
return BEFORE_BIT;
case RenderStyle::AFTER:
return AFTER_BIT;
case RenderStyle::MARKER:
return MARKER_BIT;
case RenderStyle::REPLACED:
return REPLACED_BIT;
case RenderStyle::FIRST_LINE:
return FIRST_LINE_BIT;
case RenderStyle::FIRST_LETTER:
return FIRST_LETTER_BIT;
case RenderStyle::SELECTION:
return SELECTION_BIT;
default:
return NO_BIT;
}
}
bool RenderStyle::hasPseudoStyle(PseudoId pseudo) const
{
return (pseudoBit(pseudo) & noninherited_flags.f._pseudoBits) != 0;
}
void RenderStyle::setHasPseudoStyle(PseudoId pseudo, bool b)
{
if (b)
noninherited_flags.f._pseudoBits |= pseudoBit(pseudo);
else
noninherited_flags.f._pseudoBits &= ~(pseudoBit(pseudo));
}
RenderStyle* RenderStyle::getPseudoStyle(PseudoId pid) const
{
if (!hasPseudoStyle(pid)) return 0;
RenderStyle *ps = 0;
if (noninherited_flags.f._styleType==NOPSEUDO)
for (ps = pseudoStyle; ps; ps = ps->pseudoStyle)
if (ps->noninherited_flags.f._styleType==pid)
break;
return ps;
}
RenderStyle* RenderStyle::addPseudoStyle(PseudoId pid)
{
if (hasPseudoStyle(pid)) return getPseudoStyle(pid);
RenderStyle *ps = 0;
switch (pid) {
case FIRST_LETTER: // pseudo-elements (FIRST_LINE has a special handling)
case SELECTION:
case BEFORE:
case AFTER:
ps = new RenderStyle();
break;
default:
ps = new RenderStyle(*this); // use the real copy constructor to get an identical copy
}
ps->ref();
ps->noninherited_flags.f._styleType = pid;
ps->pseudoStyle = pseudoStyle;
pseudoStyle = ps;
setHasPseudoStyle(pid, true);
return ps;
}
void RenderStyle::removePseudoStyle(PseudoId pid)
{
RenderStyle *ps = pseudoStyle;
RenderStyle *prev = this;
while (ps) {
if (ps->noninherited_flags.f._styleType==pid) {
prev->pseudoStyle = ps->pseudoStyle;
ps->deref();
return;
}
prev = ps;
ps = ps->pseudoStyle;
}
setHasPseudoStyle(pid, false);
}
bool RenderStyle::inheritedNotEqual( RenderStyle *other ) const
{
return
(
inherited_flags != other->inherited_flags ||
inherited != other->inherited ||
css3InheritedData != other->css3InheritedData
);
}
/*
compares two styles. The result gives an idea of the action that
needs to be taken when replacing the old style with a new one.
CbLayout: The containing block of the object needs a relayout.
Layout: the RenderObject needs a relayout after the style change
Visible: The change is visible, but no relayout is needed
NonVisible: The object does need neither repaint nor relayout after
the change.
### TODO:
A lot can be optimised here based on the display type, lots of
optimisations are unimplemented, and currently result in the
worst case result causing a relayout of the containing block.
*/
RenderStyle::Diff RenderStyle::diff( const RenderStyle *other ) const
{
// we anyway assume they are the same
// EDisplay _display : 5;
// NonVisible:
// ECursor _cursor_style : 4;
// EUserInput _user_input : 2; as long as :enabled is not impl'd
// ### this needs work to know more exactly if we need a relayout
// or just a repaint
// non-inherited attributes
// DataRef<StyleBoxData> box;
// DataRef<StyleVisualData> visual;
// DataRef<StyleSurroundData> surround;
// inherited attributes
// DataRef<StyleInheritedData> inherited;
if ( *box.get() != *other->box.get() ||
*visual.get() != *other->visual.get() ||
(*surround.get() != *other->surround.get()
&& (other->position() == STATIC || other->position() != position())) ||
!(inherited->indent == other->inherited->indent) ||
!(inherited->line_height == other->inherited->line_height) ||
!(inherited->style_image == other->inherited->style_image) ||
!(inherited->font == other->inherited->font) ||
!(inherited->border_hspacing == other->inherited->border_hspacing) ||
!(inherited->border_vspacing == other->inherited->border_vspacing) ||
!(inherited_flags.f._visuallyOrdered == other->inherited_flags.f._visuallyOrdered) ||
!(inherited_flags.f._htmlHacks == other->inherited_flags.f._htmlHacks) ||
!(noninherited_flags.f._textOverflow == other->noninherited_flags.f._textOverflow) )
return CbLayout;
// changes causing Layout changes:
// only for tables:
// _border_collapse
// EEmptyCell _empty_cells : 2 ;
// ECaptionSide _caption_side : 2;
// ETableLayout _table_layout : 1;
// EPosition _position : 2;
// EFloat _floating : 2;
if ( ((int)noninherited_flags.f._display) >= TABLE ) {
if ( !(inherited_flags.f._empty_cells == other->inherited_flags.f._empty_cells) ||
!(inherited_flags.f._caption_side == other->inherited_flags.f._caption_side) ||
!(inherited_flags.f._border_collapse == other->inherited_flags.f._border_collapse) ||
!(noninherited_flags.f._table_layout == other->noninherited_flags.f._table_layout) ||
!(noninherited_flags.f._position == other->noninherited_flags.f._position) ||
!(noninherited_flags.f._floating == other->noninherited_flags.f._floating) ||
!(noninherited_flags.f._flowAroundFloats == other->noninherited_flags.f._flowAroundFloats) ||
!(noninherited_flags.f._unicodeBidi == other->noninherited_flags.f._unicodeBidi) )
return CbLayout;
}
// only for lists:
// EListStyleType _list_style_type : 5 ;
// EListStylePosition _list_style_position :1;
if (noninherited_flags.f._display == LIST_ITEM ) {
if ( !(inherited_flags.f._list_style_type == other->inherited_flags.f._list_style_type) ||
!(inherited_flags.f._list_style_position == other->inherited_flags.f._list_style_position) )
return Layout;
}
// ### These could be better optimised
// ETextAlign _text_align : 3;
// ETextTransform _text_transform : 4;
// EDirection _direction : 1;
// EWhiteSpace _white_space : 2;
// EClear _clear : 2;
if ( !(inherited_flags.f._text_align == other->inherited_flags.f._text_align) ||
!(inherited_flags.f._text_transform == other->inherited_flags.f._text_transform) ||
!(inherited_flags.f._direction == other->inherited_flags.f._direction) ||
!(inherited_flags.f._white_space == other->inherited_flags.f._white_space) ||
!(noninherited_flags.f._clear == other->noninherited_flags.f._clear)
)
return Layout;
// Overflow returns a layout hint.
if (noninherited_flags.f._overflowX != other->noninherited_flags.f._overflowX ||
noninherited_flags.f._overflowY != other->noninherited_flags.f._overflowY)
return Layout;
// only for inline:
// EVerticalAlign _vertical_align : 4;
if ( !(noninherited_flags.f._display == INLINE) &&
!(noninherited_flags.f._vertical_align == other->noninherited_flags.f._vertical_align) )
return Layout;
if (*surround.get() != *other->surround.get()) {
assert( other->position() != STATIC ); // this style is positioned or relatively positioned
if ( surround->hasSamePBMData(*other->surround.get()) && // padding/border/margin are identical
(other->position() == RELATIVE ||
!(other->left().isVariable() && other->right().isVariable()) && // X isn't static
!(other->top().isVariable() && other->bottom().isVariable()) )) // neither is Y
// therefore only the offset is different
return Position;
return Layout;
}
// Visible:
// EVisibility _visibility : 2;
// int _text_decorations : 4;
// DataRef<StyleBackgroundData> background;
if (inherited->color != other->inherited->color ||
!(inherited_flags.f._visibility == other->inherited_flags.f._visibility) ||
!(inherited_flags.f._text_decorations == other->inherited_flags.f._text_decorations) ||
!(noninherited_flags.f._hasClip == other->noninherited_flags.f._hasClip) ||
visual->textDecoration != other->visual->textDecoration ||
*background.get() != *other->background.get() ||
css3NonInheritedData->opacity != other->css3NonInheritedData->opacity ||
!css3InheritedData->shadowDataEquivalent(*other->css3InheritedData.get())
)
return Visible;
RenderStyle::Diff ch = Equal;
// Check for visible pseudo-changes:
if (hasPseudoStyle(FIRST_LINE) != other->hasPseudoStyle(FIRST_LINE))
ch = Visible;
else
if (hasPseudoStyle(FIRST_LINE) && other->hasPseudoStyle(FIRST_LINE))
ch = getPseudoStyle(FIRST_LINE)->diff(other->getPseudoStyle(FIRST_LINE));
if (ch != Equal) return ch;
// Check for visible pseudo-changes:
if (hasPseudoStyle(SELECTION) != other->hasPseudoStyle(SELECTION))
ch = Visible;
else
if (hasPseudoStyle(SELECTION) && other->hasPseudoStyle(SELECTION))
ch = getPseudoStyle(SELECTION)->diff(other->getPseudoStyle(SELECTION));
return ch;
}
RenderStyle* RenderStyle::_default = 0;
void RenderStyle::cleanup()
{
delete _default;
_default = 0;
}
void RenderStyle::setPaletteColor(TQPalette::ColorGroup g, TQColorGroup::ColorRole r, const TQColor& c)
{
visual.access()->palette.setColor(g,r,c);
}
void RenderStyle::adjustBackgroundLayers()
{
if (backgroundLayers()->next()) {
// First we cull out layers that have no properties set.
accessBackgroundLayers()->cullEmptyLayers();
// Next we repeat patterns into layers that don't have some properties set.
accessBackgroundLayers()->fillUnsetProperties();
}
}
void RenderStyle::setClip( Length top, Length right, Length bottom, Length left )
{
StyleVisualData *data = visual.access();
data->clip.top = top;
data->clip.right = right;
data->clip.bottom = bottom;
data->clip.left = left;
}
void RenderStyle::setQuotes(DOM::QuotesValueImpl* q)
{
DOM::QuotesValueImpl *t = inherited->quotes;
inherited.access()->quotes = q;
if (q) q->ref();
if (t) t->deref();
}
TQString RenderStyle::openQuote(int level) const
{
if (inherited->quotes)
return inherited->quotes->openQuote(level);
else
return "\""; // 0 is default quotes
}
TQString RenderStyle::closeQuote(int level) const
{
if (inherited->quotes)
return inherited->quotes->closeQuote(level);
else
return "\""; // 0 is default quotes
}
void RenderStyle::addContent(CachedObject* o)
{
if (!o)
return; // The object is null. Nothing to do. Just bail.
StyleGeneratedData *t_generated = generated.access();
ContentData* lastContent = t_generated->content;
while (lastContent && lastContent->_nextContent)
lastContent = lastContent->_nextContent;
ContentData* newContentData = new ContentData;
if (lastContent)
lastContent->_nextContent = newContentData;
else
t_generated->content = newContentData;
// o->ref();
newContentData->_content.object = o;
newContentData->_contentType = CONTENT_OBJECT;
}
void RenderStyle::addContent(DOM::DOMStringImpl* s)
{
if (!s)
return; // The string is null. Nothing to do. Just bail.
StyleGeneratedData *t_generated = generated.access();
ContentData* lastContent = t_generated->content;
while (lastContent && lastContent->_nextContent)
lastContent = lastContent->_nextContent;
if (lastContent) {
if (lastContent->_contentType == CONTENT_TEXT) {
// We can augment the existing string and share this ContentData node.
DOMStringImpl* oldStr = lastContent->_content.text;
DOMStringImpl* newStr = oldStr->copy();
newStr->ref();
oldStr->deref();
newStr->append(s);
lastContent->_content.text = newStr;
return;
}
}
ContentData* newContentData = new ContentData;
if (lastContent)
lastContent->_nextContent = newContentData;
else
t_generated->content = newContentData;
newContentData->_content.text = s;
newContentData->_content.text->ref();
newContentData->_contentType = CONTENT_TEXT;
}
void RenderStyle::addContent(DOM::CounterImpl* c)
{
if (!c)
return;
StyleGeneratedData *t_generated = generated.access();
ContentData* lastContent = t_generated->content;
while (lastContent && lastContent->_nextContent)
lastContent = lastContent->_nextContent;
ContentData* newContentData = new ContentData;
if (lastContent)
lastContent->_nextContent = newContentData;
else
t_generated->content = newContentData;
c->ref();
newContentData->_content.counter = c;
newContentData->_contentType = CONTENT_COUNTER;
}
void RenderStyle::addContent(EQuoteContent q)
{
if (q == NO_QUOTE)
return;
StyleGeneratedData *t_generated = generated.access();
ContentData* lastContent = t_generated->content;
while (lastContent && lastContent->_nextContent)
lastContent = lastContent->_nextContent;
ContentData* newContentData = new ContentData;
if (lastContent)
lastContent->_nextContent = newContentData;
else
t_generated->content = newContentData;
newContentData->_content.quote = q;
newContentData->_contentType = CONTENT_QUOTE;
}
// content: normal is the same as having no content at all
void RenderStyle::setContentNormal() {
if (generated->content != 0) {
delete generated->content;
generated.access()->content = 0;
}
}
// content: none, add an empty content node
void RenderStyle::setContentNone() {
setContentNormal();
generated.access()->content = new ContentData;
}
void RenderStyle::setContentData(ContentData *data) {
if (data != generated->content) {
if (data)
generated.access()->content = new ContentData(*data);
else
generated.access()->content = 0;
}
}
ContentData::ContentData(const ContentData& o) : _contentType(o._contentType)
{
switch (_contentType) {
case CONTENT_OBJECT:
_content.object = o._content.object;
break;
case CONTENT_TEXT:
_content.text = o._content.text;
_content.text->ref();
break;
case CONTENT_COUNTER:
_content.counter = o._content.counter;
_content.counter->ref();
break;
case CONTENT_QUOTE:
_content.quote = o._content.quote;
break;
case CONTENT_NONE:
default:
break;
}
_nextContent = o._nextContent ? new ContentData(*o._nextContent) : 0;
}
ContentData::~ContentData()
{
clearContent();
}
void ContentData::clearContent()
{
delete _nextContent;
_nextContent = 0;
switch (_contentType)
{
case CONTENT_OBJECT:
_content.object = 0;
break;
case CONTENT_TEXT:
_content.text->deref();
_content.text = 0;
break;
case CONTENT_COUNTER:
_content.counter->deref();
_content.counter = 0;
break;
case CONTENT_QUOTE:
_content.quote = NO_QUOTE;
break;
default:
;
}
}
void RenderStyle::setTextShadow(ShadowData* val, bool add)
{
StyleCSS3InheritedData* css3Data = css3InheritedData.access();
if (!add) {
delete css3Data->textShadow;
css3Data->textShadow = val;
return;
}
ShadowData* last = css3Data->textShadow;
while (last->next) last = last->next;
last->next = val;
}
ShadowData::ShadowData(const ShadowData& o)
:x(o.x), y(o.y), blur(o.blur), color(o.color)
{
next = o.next ? new ShadowData(*o.next) : 0;
}
bool ShadowData::operator==(const ShadowData& o) const
{
if ((next && !o.next) || (!next && o.next) ||
(next && o.next && *next != *o.next))
return false;
return x == o.x && y == o.y && blur == o.blur && color == o.color;
}
static bool hasCounter(const DOM::DOMString& c, CSSValueListImpl *l)
{
int len = l->length();
for(int i=0; i<len; i++) {
CounterActImpl* ca = static_cast<CounterActImpl*>(l->item(i));
Q_ASSERT(ca != 0);
if (ca->m_counter == c) return true;
}
return false;
}
bool RenderStyle::hasCounterReset(const DOM::DOMString& c) const
{
if (generated->counter_reset)
return hasCounter(c, generated->counter_reset);
else
return false;
}
bool RenderStyle::hasCounterIncrement(const DOM::DOMString& c) const
{
if (generated->counter_increment)
return hasCounter(c, generated->counter_increment);
else
return false;
}
static short readCounter(const DOM::DOMString& c, CSSValueListImpl *l)
{
int len = l->length();
for(int i=0; i<len; i++) {
CounterActImpl* ca = static_cast<CounterActImpl*>(l->item(i));
Q_ASSERT(ca != 0);
if (ca->m_counter == c) return ca->m_value;
}
return 0;
}
short RenderStyle::counterReset(const DOM::DOMString& c) const
{
if (generated->counter_reset)
return readCounter(c, generated->counter_reset);
else
return 0;
}
short RenderStyle::counterIncrement(const DOM::DOMString& c) const
{
if (generated->counter_increment)
return readCounter(c, generated->counter_increment);
else
return 0;
}
void RenderStyle::setCounterReset(CSSValueListImpl *l)
{
CSSValueListImpl *t = generated->counter_reset;
generated.access()->counter_reset = l;
if (l) l->ref();
if (t) t->deref();
}
void RenderStyle::setCounterIncrement(CSSValueListImpl *l)
{
CSSValueListImpl *t = generated->counter_increment;
generated.access()->counter_increment = l;
if (l) l->ref();
if (t) t->deref();
}
#ifdef ENABLE_DUMP
static TQString describeFont( const TQFont &f)
{
TQString res = "'" + f.family() + "' ";
if ( f.pointSize() > 0)
res += TQString::number( f.pointSize() ) + "pt";
else
res += TQString::number( f.pixelSize() ) + "px";
if ( f.bold() )
res += " bold";
if ( f.italic() )
res += " italic";
if ( f.underline() )
res += " underline";
if ( f.overline() )
res += " overline";
if ( f.strikeOut() )
res += " strikeout";
return res;
}
TQString RenderStyle::createDiff( const RenderStyle &parent ) const
{
TQString res;
if ( color().isValid() && parent.color() != color() )
res += " [color=" + color().name() + "]";
if ( backgroundColor().isValid() && parent.backgroundColor() != backgroundColor() )
res += " [bgcolor=" + backgroundColor().name() + "]";
if ( parent.font() != font() )
res += " [font=" + describeFont( font() ) + "]";
return res;
}
#endif
RenderPageStyle::RenderPageStyle() : next(0), m_pageType(ANY_PAGE)
{
}
RenderPageStyle::~RenderPageStyle()
{
delete next;
}
RenderPageStyle* RenderPageStyle::getPageStyle(PageType type)
{
RenderPageStyle *ps = 0;
for (ps = this; ps; ps = ps->next)
if (ps->m_pageType==type)
break;
return ps;
}
RenderPageStyle* RenderPageStyle::addPageStyle(PageType type)
{
RenderPageStyle *ps = getPageStyle(type);
if (!ps)
{
ps = new RenderPageStyle(*this); // use the real copy constructor to get an identical copy
ps->m_pageType = type;
ps->next = next;
next = ps;
}
return ps;
}
void RenderPageStyle::removePageStyle(PageType type)
{
RenderPageStyle *ps = next;
RenderPageStyle *prev = this;
while (ps) {
if (ps->m_pageType==type) {
prev->next = ps->next;
delete ps;
return;
}
prev = ps;
ps = ps->next;
}
}