| /* |
| * 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; |
| } |
| |