blob: 9dd2d924908f1ca616ce04b001faa33883da4a4e [file] [log] [blame]
/*
* Copyright (C) 2004, 2005 Apple Computer, Inc. All rights reserved.
* (C) 2005 Rob Buis <buis@kde.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "KCanvasTreeDebug.h"
#include <kcanvas/KCanvas.h>
#include <kcanvas/KCanvasMatrix.h>
#include <kcanvas/KCanvasItem.h>
#include <kcanvas/KCanvasContainer.h>
#include <kcanvas/KCanvasRegistry.h>
#include <kcanvas/device/KRenderingStyle.h>
#include <kcanvas/device/KRenderingStrokePainter.h>
#include <kcanvas/device/KRenderingFillPainter.h>
#include <kcanvas/device/KRenderingPaintServerSolid.h>
#include <kcanvas/device/KRenderingPaintServerPattern.h>
#include <kcanvas/device/KRenderingPaintServerGradient.h>
#include <kcanvas/device/KRenderingPaintServerImage.h>
#include <kcanvas/KCanvasResources.h>
#include <kcanvas/KCanvasFilters.h>
#ifdef APPLE_CHANGES
#include <kcanvas/device/quartz/KRenderingDeviceQuartz.h>
#include <kcanvas/device/quartz/QuartzSupport.h>
#endif
#include <ksvg2/svg/SVGStyledElementImpl.h>
#include <kdom/DOMString.h>
#include <qtextstream.h>
/** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
* Can be used in cases where you don't know which item in the list is the first
* one to be printed, but still want to avoid strings like ", b, c", works like
* QStringList::join for streams
*/
class QTextStreamSeparator
{
public:
QTextStreamSeparator(const QString &s) : m_separator(s), m_needToSeparate(false) {}
private:
friend QTextStream& operator<<(QTextStream &ts, QTextStreamSeparator &sep);
private:
QString m_separator;
bool m_needToSeparate;
};
QTextStream& operator<<(QTextStream &ts, QTextStreamSeparator &sep)
{
if (sep.m_needToSeparate)
ts << sep.m_separator;
else
sep.m_needToSeparate = true;
return ts;
}
QTextStream &operator<<(QTextStream &ts, const QPoint &p)
{
return ts << "(" << p.x() << "," << p.y() << ")";
}
QTextStream &operator<<(QTextStream &ts, const QRect &r)
{
return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
}
QTextStream &operator<<(QTextStream &ts, const KCanvasMatrix &m)
{
if (m.qmatrix().isIdentity())
ts << "identity";
else
{
ts << "{m=((" << m.a() << "," << m.b() << ")(" << m.c() << "," << m.d() << "))";
ts << " t=(" << m.e() << "," << m.f() << ")}";
}
return ts;
}
QTextStream &operator<<(QTextStream &ts, const QColor &c)
{
return ts << c.name();
}
static void writeIndent(QTextStream &ts, int indent)
{
for (int i = 0; i != indent; ++i) {
ts << " ";
}
}
//FIXME: This should be in KRenderingStyle.cpp
static QTextStream &operator<<(QTextStream &ts, const KCDashArray &a)
{
ts << "{";
for (KCDashArray::ConstIterator it = a.begin(); it != a.end(); ++it) {
if (it != a.begin())
ts << ", ";
ts << *it;
}
ts << "}";
return ts;
}
//FIXME: This should be in KRenderingStyle.cpp
static QTextStream &operator<<(QTextStream &ts, KCCapStyle style)
{
switch (style) {
case CAP_BUTT:
ts << "BUTT"; break;
case CAP_ROUND:
ts << "ROUND"; break;
case CAP_SQUARE:
ts << "SQUARE"; break;
}
return ts;
}
//FIXME: This should be in KRenderingStyle.cpp
static QTextStream &operator<<(QTextStream &ts, KCJoinStyle style)
{
switch (style) {
case JOIN_MITER:
ts << "MITER"; break;
case JOIN_ROUND:
ts << "ROUND"; break;
case JOIN_BEVEL:
ts << "BEVEL"; break;
}
return ts;
}
static QTextStream &operator<<(QTextStream &ts, const KRenderingStrokePainter *p)
{
QTextStreamSeparator s(" ");
ts << "{";
if (p->paintServer())
{
if (!p->paintServer()->idInRegistry().isEmpty())
ts << s << "[id=\"" << p->paintServer()->idInRegistry() << "\"]";
else
ts << s << *(p->paintServer());
}
if (p->opacity() != 1.0f)
ts << s << "[opacity=" << p->opacity() << "]";
if (p->strokeWidth() != 1.0f)
ts << s << "[stroke width=" << p->strokeWidth() << "]";
if (p->strokeMiterLimit() != 4)
ts << s << "[miter limit=" << p->strokeMiterLimit() << "]";
if (p->strokeCapStyle() != 1)
ts << s << "[line cap=" << p->strokeCapStyle() << "]";
if (p->strokeJoinStyle() != 1)
ts << s << "[line join=" << p->strokeJoinStyle() << "]";
if (p->dashOffset() != 0.0f)
ts << s << "[dash offset=" << p->dashOffset() << "]";
if (!p->dashArray().isEmpty())
ts << s << "[dash array=" << p->dashArray() << "]";
ts << "}";
return ts;
}
static QTextStream &operator<<(QTextStream &ts, const KRenderingFillPainter *p)
{
QTextStreamSeparator s(" ");
ts << "{";
if (p->paintServer())
{
if (!p->paintServer()->idInRegistry().isEmpty())
ts << s << "[id=\""<< p->paintServer()->idInRegistry() << "\"]";
else
ts << s << *(p->paintServer());
}
if (p->opacity() != 1.0f)
ts << s << "[opacity=" << p->opacity() << "]";
if (p->fillRule() != RULE_NONZERO)
ts << s << "[fill rule=" << p->fillRule() << "]";
ts << "}";
return ts;
}
static QTextStream &operator<<(QTextStream &ts, const KCanvasFilter *f)
{
if (!f->idInRegistry().isEmpty())
ts << "\"" << f->idInRegistry() << "\"";
else
ts << "{" << *f << "}";
return ts;
}
static QTextStream &operator<<(QTextStream &ts, const KCanvasMarker *f)
{
if (!f->idInRegistry().isEmpty())
ts << "\"" << f->idInRegistry() << "\"";
else
ts << "{" << *f << "}";
return ts;
}
#define DIFFERS_FROM_PARENT(path) (o.parent() && (o.parent()->path != o.path))
// avoids testing path if pred is false. This is used with tests that have side-effects
// for the parent object
#define DIFFERS_FROM_PARENT_AVOID_TEST_IF_FALSE(pred, path) (o.parent() && ((!o.parent()->pred) || (o.parent()->path != o.path)))
static QTextStream &operator<<(QTextStream &ts, const KCanvasItem &o)
{
ts << " " << o.bbox();
if (DIFFERS_FROM_PARENT(style()->visible()) && !o.style()->visible())
ts << " [HIDDEN]";
if (DIFFERS_FROM_PARENT(style()->objectMatrix()))
ts << " [transform=" << o.style()->objectMatrix() << "]";
if (DIFFERS_FROM_PARENT(style()->colorInterpolation()))
ts << " [color interpolation=" << o.style()->colorInterpolation() << "]";
if (DIFFERS_FROM_PARENT(style()->imageRendering()))
ts << " [image rendering=" << o.style()->imageRendering() << "]";
if (DIFFERS_FROM_PARENT(style()->opacity()))
ts << " [opacity=" << o.style()->opacity() << "]";
if (o.style()->isStroked()
&& DIFFERS_FROM_PARENT_AVOID_TEST_IF_FALSE(style()->isStroked(), style()->strokePainter()))
ts << " [stroke=" << o.style()->strokePainter() << "]";
if (o.style()->isFilled()
&& DIFFERS_FROM_PARENT_AVOID_TEST_IF_FALSE(style()->isFilled(), style()->fillPainter()))
ts << " [fill=" << o.style()->fillPainter() << "]";
if (!o.style()->clipPaths().isEmpty())
ts << " [clip paths=\"" << o.style()->clipPaths().join(", ") << "\"]";
if (o.style()->hasMarkers()) {
if (o.style()->startMarker())
ts << " [start marker=" << o.style()->startMarker() << "]";
if (o.style()->midMarker())
ts << " [middle marker=" << o.style()->midMarker() << "]";
if (o.style()->endMarker())
ts << " [end marker=" << o.style()->endMarker() << "]";
}
if (DIFFERS_FROM_PARENT(style()->filter()) && o.style()->filter())
ts << " [filter=" << o.style()->filter() << "]";
#ifdef APPLE_CHANGES
// Print the actual path data
if (o.path()) {
CGMutablePathRef cgPath = static_cast<KCanvasQuartzPathData *>(o.path())->path;
CFStringRef pathString = CFStringFromCGPath(cgPath);
ts << " [data=\"" << QString::fromCFString(pathString) << "\"]";
CFRelease(pathString);
}
#endif
return ts;
}
#undef DIFFERS_FROM_PARENT
#undef DIFFERS_FROM_PARENT_AVOID_TEST_IF_FALSE
static QString getTagName(void *node)
{
KSVG::SVGStyledElementImpl *elem = static_cast<KSVG::SVGStyledElementImpl *>(node);
if (elem)
return KDOM::DOMString(elem->nodeName()).string();
return QString();
}
void write(QTextStream &ts, KCanvasItem *item, int indent = 0)
{
if (item)
{
writeIndent(ts, indent);
if(item->isContainer())
ts << "KCanvasContainer";
else
ts << "KCanvasItem";
if (item->userData()) {
QString tagName = getTagName(item->userData());
if (!tagName.isEmpty()) {
ts << " {" << tagName << "}";
}
}
ts << *item << endl;
if(item->isContainer()) {
KCanvasContainer *parent = static_cast<KCanvasContainer *>(item);
for (KCanvasItem *child = parent->first(); child != NULL; child = child->next())
write(ts, child, indent + 1);
}
}
}
QString externalRepresentation(KCanvasItem *item)
{
QString s;
{
QTextStream ts(&s);
ts.precision(2);
ts << *(item->canvas()->registry());
write(ts, item);
}
return s;
}
QTextStream &operator<<(QTextStream &ts, const QStringList &l)
{
ts << "[";
QStringList::ConstIterator it = l.begin();
QStringList::ConstIterator it_e = l.end();
while (it != it_e)
{
ts << *it;
++it;
if (it != it_e) ts << ", ";
}
ts << "]";
return ts;
}