diff --git a/plugins/chartshape/ChartLayout.cpp b/plugins/chartshape/ChartLayout.cpp index 38690e1f2fc..5bd4bee156d 100644 --- a/plugins/chartshape/ChartLayout.cpp +++ b/plugins/chartshape/ChartLayout.cpp @@ -1,1086 +1,1065 @@ /* This file is part of the KDE project Copyright 2017 Dag Andersen Copyright 2010 Johannes Simon 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. */ // Qt #include #include // KoChart #include "ChartLayout.h" #include "Legend.h" #include "ChartDebug.h" #include "PlotArea.h" #include "Axis.h" #include "ScreenConversions.h" // Calligra #include #include using namespace KoChart; class ChartLayout::LayoutData { public: int itemType; QRectF rect; bool inheritsTransform; bool clipped; LayoutData(int _itemType = GenericItemType) : itemType(_itemType) , inheritsTransform(true) , clipped(true) {} }; // static bool ChartLayout::autoPosition(const KoShape *shape) { return shape->additionalStyleAttribute("chart:auto-position") == "true"; } // static bool ChartLayout::autoSize(const KoShape *shape) { return shape->additionalStyleAttribute("chart:auto-size") == "true"; } ChartLayout::ChartLayout() : m_doingLayout(false) , m_relayoutScheduled(false) , m_padding(5., 5., 5., 5.) , m_spacing(5., 5.) , m_layoutingEnabled(true) { } ChartLayout::~ChartLayout() { foreach(LayoutData *data, m_layoutItems.values()) delete data; } void ChartLayout::add(KoShape *shape) { Q_ASSERT(!m_layoutItems.contains(shape)); setItemType(shape, GenericItemType); } void ChartLayout::remove(KoShape *shape) { m_shapes.remove(m_shapes.key(shape)); if (m_layoutItems.contains(shape)) { // delete LayoutData delete m_layoutItems.value(shape); m_layoutItems.remove(shape); scheduleRelayout(); } } void ChartLayout::setClipped(const KoShape *shape, bool clipping) { Q_ASSERT(m_layoutItems.contains(const_cast(shape))); m_layoutItems.value(const_cast(shape))->clipped = clipping; } bool ChartLayout::isClipped(const KoShape *shape) const { Q_ASSERT(m_layoutItems.contains(const_cast(shape))); return m_layoutItems.value(const_cast(shape))->clipped; } void ChartLayout::setInheritsTransform(const KoShape *shape, bool inherit) { m_layoutItems.value(const_cast(shape))->inheritsTransform = inherit; } bool ChartLayout::inheritsTransform(const KoShape *shape) const { return m_layoutItems.value(const_cast(shape))->inheritsTransform; } int ChartLayout::count() const { return m_layoutItems.size(); } QList ChartLayout::shapes() const { return m_layoutItems.keys(); } void ChartLayout::setContainerRect(const QRectF &rect) { if (rect != m_containerRect) { m_containerRect = rect; scheduleRelayout(); } } void ChartLayout::containerChanged(KoShapeContainer *container, KoShape::ChangeType type) { switch(type) { case KoShape::StrokeChanged: case KoShape::SizeChanged: { QRectF rect(QPointF(0,0), container->size()); KoInsets insets = container->strokeInsets(); rect.adjust(insets.left / 2., insets.top / 2., -insets.right / 2., -insets.bottom / 2.); setContainerRect(rect); break; } case KoShape::BorderChanged: warnChartLayout<<"Border not handled"; break; default: break; } } bool ChartLayout::isChildLocked(const KoShape *shape) const { return shape->isGeometryProtected(); } void ChartLayout::setItemType(const KoShape *shape, ItemType itemType) { LayoutData *data = m_layoutItems.value(const_cast(shape)); if (!data) { data = new LayoutData(); m_layoutItems.insert(const_cast(shape), data); } data->itemType = itemType; m_shapes.remove(m_shapes.key(const_cast(shape))); // in case m_shapes.insert(itemType, const_cast(shape)); debugChartLayout< m_containerRect.right()) { move.setX(m_containerRect.right() - current.right()); } if (newRect.top() < m_containerRect.top()) { move.setY(m_containerRect.top() - current.top()); } else if (newRect.bottom() > m_containerRect.bottom()) { move.setY(m_containerRect.bottom() - current.bottom()); } } void ChartLayout::childChanged(KoShape *shape, KoShape::ChangeType type) { Q_UNUSED(shape); // Do not relayout again if we're currently in the process of a relayout. // Repositioning a layout item or resizing it will result in a cull of this method. if (m_doingLayout) return; // This can be fine-tuned, but right now, simply everything will be re-layouted. switch (type) { case KoShape::PositionChanged: case KoShape::SizeChanged: scheduleRelayout(); // FIXME: There's some cases that would require relayouting but that don't make sense // for chart items, e.g. ShearChanged. How should these changes be avoided or handled? default: break; } } void ChartLayout::scheduleRelayout() { m_relayoutScheduled = true; } KChart::CartesianAxis::Position axisPosition(PlotArea *plotarea, ItemType type) { KChart::CartesianAxis::Position apos = KChart::CartesianAxis::Bottom; switch (type) { case XAxisTitleType: { if (plotarea && plotarea->xAxis()) { apos = plotarea->xAxis()->kchartAxisPosition(); } break; } case YAxisTitleType: { if (plotarea && plotarea->yAxis()) { apos = plotarea->yAxis()->kchartAxisPosition(); } break; } case SecondaryXAxisTitleType: { if (plotarea && plotarea->secondaryXAxis()) { apos = plotarea->secondaryXAxis()->kchartAxisPosition(); } break; } case SecondaryYAxisTitleType: { if (plotarea && plotarea->secondaryYAxis()) { apos = plotarea->secondaryYAxis()->kchartAxisPosition(); } break; } } return apos; } qreal ChartLayout::xOffset(const QRectF &left, const QRectF &right, bool center) const { qreal x = (left.width() + (left.width() > 0.0 ? m_spacing.x() : 0.0) - right.width() - (right.width() > 0.0 ? m_spacing.x() : 0.0)); return center ? x / 2.0 : x; } qreal ChartLayout::yOffset(const QRectF &top, const QRectF &bottom, bool center) const { qreal y = (top.height() + (top.height() > 0.0 ? m_spacing.y() : 0.0) - bottom.height() - (bottom.height() > 0.0 ? m_spacing.y() : 0.0)); return center ? y / 2.0 : y; } void ChartLayout::rotateAxisTitles(PlotArea *plotarea) { switch (plotarea->chartType()) { - case BarChartType: { - bool verticalXAxis = plotarea->isVertical(); - for (Axis *axis : plotarea->axes()) { - KoShape *title = axis->title(); - title->rotate(-title->rotation()); - switch (axis->actualAxisPosition()) { - case KChart::CartesianAxis::Bottom: - title->rotate(verticalXAxis ? -90 : 0); - break; - case KChart::CartesianAxis::Top: - title->rotate(verticalXAxis ? -90 : 0); - break; - case KChart::CartesianAxis::Left: - title->rotate(verticalXAxis ? 0 : -90); - break; - case KChart::CartesianAxis::Right: - title->rotate(verticalXAxis ? 0 : 90); - break; - } - } - break; - } + case BarChartType: case LineChartType: case AreaChartType: case ScatterChartType: case BubbleChartType: case StockChartType: { for (Axis *axis : plotarea->axes()) { KoShape *title = axis->title(); title->rotate(-title->rotation()); - switch (axis->actualAxisPosition()) { + switch (axis->kchartAxisPosition()) { case KChart::CartesianAxis::Left: title->rotate(-90); break; case KChart::CartesianAxis::Right: title->rotate(90); break; default: break; } } break; } default: break; } } void ChartLayout::layoutAxisTitles(int pos, const QMultiMap &map, const QRectF &rect, const QRectF plotarea) const { debugChartLayout< shapes = map.values(pos); if (shapes.isEmpty()) { return; } QRectF layoutRect = rect; QList rects; switch (pos) { case 1: // bottom case 3: // top layoutRect.adjust((plotarea.center().x() - layoutRect.center().x()) / 2., 0, 0, 0); for (int i = shapes.count()-1; i >= 0; --i) { if (!rects.isEmpty()) { layoutRect.adjust(0, rects.last().height(), 0, 0); } QRectF r = layoutRect; QRectF itmRect = itemRect(shapes[i]); r.setHeight(itmRect.height()); if (r.width() > itmRect.width()) { qreal w = (r.width() - itmRect.width()) / 2; r.adjust(w, 0, -w, 0); } rects.prepend(r); } for (int i = 0; i < shapes.count(); ++i) { m_layoutItems[shapes[i]]->rect = rects.at(i); debugChartLayout<<(pos==1?"bottom title":"top title")<= 0; --i) { if (!rects.isEmpty()) { layoutRect.adjust(rects.first().width(), 0, 0, 0); } QRectF r = layoutRect; QRectF itmRect = itemRect(shapes[i]); r.setWidth(itemRect(shapes[i]).width()); if (r.height() > itmRect.height()) { qreal h = (r.height() - itmRect.height()) / 2; r.adjust(0, h, 0, -h); } rects.prepend(r); } for (int i = 0; i < shapes.count(); ++i) { m_layoutItems[shapes[i]]->rect = rects.at(i); debugChartLayout<<(pos==2?"left title":"right title")<(m_shapes.value(PlotAreaType)); QRectF plotareaRect = area; if (plotarea->chartType() == BarChartType && plotarea->isVertical()) debugChartLayout<<"Vertical bar chart"; QRectF titleRect; KoShape *title = m_shapes.value(TitleLabelType); if (title && title->isVisible()) { titleRect = itemRect(title); } QRectF subtitleRect; KoShape *subtitle = m_shapes.value(SubTitleLabelType); if (subtitle && subtitle->isVisible()) { subtitleRect = itemRect(subtitle); } QRectF footerRect; KoShape *footer = m_shapes.value(FooterLabelType); if (footer && footer->isVisible()) { footerRect = itemRect(footer); } QRectF legendRect; Legend *legend = dynamic_cast(m_shapes.value(LegendType)); if (legend && legend->isVisible()) { legendRect = itemRect(legend); debugChartLayout<<"legend rect:"< axisTitles; QRectF bottomTitleRect; // 1 QRectF leftTitleRect; // 2 QRectF topTitleRect; // 3 QRectF rightTitleRect; // 4 KoShape *xtitle = m_shapes.value(XAxisTitleType); if (xtitle && xtitle->isVisible()) { switch (axisPosition(plotarea, XAxisTitleType)) { case KChart::CartesianAxis::Bottom: { QRectF r = itemRect(xtitle); if (bottomTitleRect.isNull()) { bottomTitleRect = r; } else { if (bottomTitleRect.width() < r.width()) { bottomTitleRect.setWidth(r.width()); } bottomTitleRect.setHeight(bottomTitleRect.height() + r.height()); } axisTitles.insert(1, xtitle); break; } case KChart::CartesianAxis::Left: { QRectF r = itemRect(xtitle); if (leftTitleRect.isNull()) { leftTitleRect = r; } else { leftTitleRect.setWidth(leftTitleRect.width() + r.width()); if (leftTitleRect.height() < r.height()) { leftTitleRect.setHeight(r.height()); } } axisTitles.insert(2, xtitle); break; } case KChart::CartesianAxis::Top: { QRectF r = itemRect(xtitle); if (topTitleRect.isNull()) { topTitleRect = r; } else { if (topTitleRect.width() < r.width()) { topTitleRect.setWidth(r.width()); } topTitleRect.setHeight(topTitleRect.height() + r.height()); } axisTitles.insert(3, xtitle); break; } case KChart::CartesianAxis::Right: { QRectF r = itemRect(xtitle); if (rightTitleRect.isNull()) { rightTitleRect = r; } else { rightTitleRect.setWidth(rightTitleRect.width() + r.width()); if (rightTitleRect.height() < r.height()) { rightTitleRect.setHeight(r.height()); } } axisTitles.insert(4, xtitle); break; } } } KoShape *ytitle = m_shapes.value(YAxisTitleType); if (ytitle && ytitle->isVisible()) { switch (axisPosition(plotarea, YAxisTitleType)) { case KChart::CartesianAxis::Bottom: { QRectF r = itemRect(ytitle); if (bottomTitleRect.isNull()) { bottomTitleRect = r; } else { if (bottomTitleRect.width() < r.width()) { bottomTitleRect.setWidth(r.width()); } bottomTitleRect.setHeight(bottomTitleRect.height() + r.height()); } axisTitles.insert(1, ytitle); break; } case KChart::CartesianAxis::Left: { QRectF r = itemRect(ytitle); if (leftTitleRect.isNull()) { leftTitleRect = r; } else { leftTitleRect.setWidth(leftTitleRect.width() + r.width()); if (leftTitleRect.height() < r.height()) { leftTitleRect.setHeight(r.height()); } } axisTitles.insert(2, ytitle); break; } case KChart::CartesianAxis::Top: { QRectF r = itemRect(ytitle); if (topTitleRect.isNull()) { topTitleRect = r; } else { if (topTitleRect.width() < r.width()) { topTitleRect.setWidth(r.width()); } topTitleRect.setHeight(topTitleRect.height() + r.height()); } axisTitles.insert(3, ytitle); break; } case KChart::CartesianAxis::Right: { QRectF r = itemRect(ytitle); if (rightTitleRect.isNull()) { rightTitleRect = r; } else { rightTitleRect.setWidth(rightTitleRect.width() + r.width()); if (rightTitleRect.height() < r.height()) { rightTitleRect.setHeight(r.height()); } } axisTitles.insert(4, ytitle); break; } } } KoShape *sxtitle = m_shapes.value(SecondaryXAxisTitleType); debugChartLayout<isVisible())<isVisible()) { switch (axisPosition(plotarea, SecondaryXAxisTitleType)) { case KChart::CartesianAxis::Bottom: { QRectF r = itemRect(sxtitle); if (bottomTitleRect.isNull()) { bottomTitleRect = r; } else { if (bottomTitleRect.width() < r.width()) { bottomTitleRect.setWidth(r.width()); } bottomTitleRect.setHeight(bottomTitleRect.height() + r.height()); } axisTitles.insert(1, sxtitle); break; } case KChart::CartesianAxis::Left: { QRectF r = itemRect(sxtitle); if (leftTitleRect.isNull()) { leftTitleRect = r; } else { leftTitleRect.setWidth(leftTitleRect.width() + r.width()); if (leftTitleRect.height() < r.height()) { leftTitleRect.setHeight(r.height()); } } axisTitles.insert(2, sxtitle); break; } case KChart::CartesianAxis::Top: { QRectF r = itemRect(sxtitle); if (topTitleRect.isNull()) { topTitleRect = r; } else { if (topTitleRect.width() < r.width()) { topTitleRect.setWidth(r.width()); } topTitleRect.setHeight(topTitleRect.height() + r.height()); } axisTitles.insert(3, sxtitle); break; } case KChart::CartesianAxis::Right: { QRectF r = itemRect(sxtitle); if (rightTitleRect.isNull()) { rightTitleRect = r; } else { rightTitleRect.setWidth(rightTitleRect.width() + r.width()); if (rightTitleRect.height() < r.height()) { rightTitleRect.setHeight(r.height()); } } axisTitles.insert(4, sxtitle); break; } } } KoShape *sytitle = m_shapes.value(SecondaryYAxisTitleType); if (sytitle && sytitle->isVisible()) { switch (axisPosition(plotarea, SecondaryYAxisTitleType)) { case KChart::CartesianAxis::Bottom: { QRectF r = itemRect(sytitle); if (bottomTitleRect.isNull()) { bottomTitleRect = r; } else { if (bottomTitleRect.width() < r.width()) { bottomTitleRect.setWidth(r.width()); } bottomTitleRect.setHeight(bottomTitleRect.height() + r.height()); } axisTitles.insert(1, sytitle); break; } case KChart::CartesianAxis::Left: { QRectF r = itemRect(sytitle); if (leftTitleRect.isNull()) { leftTitleRect = r; } else { leftTitleRect.setWidth(leftTitleRect.width() + r.width()); if (leftTitleRect.height() < r.height()) { leftTitleRect.setHeight(r.height()); } } axisTitles.insert(2, sytitle); break; } case KChart::CartesianAxis::Top: { QRectF r = itemRect(sytitle); if (topTitleRect.isNull()) { topTitleRect = r; } else { if (topTitleRect.width() < r.width()) { topTitleRect.setWidth(r.width()); } topTitleRect.setHeight(topTitleRect.height() + r.height()); } axisTitles.insert(3, sytitle); break; } case KChart::CartesianAxis::Right: { QRectF r = itemRect(sytitle); if (rightTitleRect.isNull()) { rightTitleRect = r; } else { rightTitleRect.setWidth(rightTitleRect.width() + r.width()); if (rightTitleRect.height() < r.height()) { rightTitleRect.setHeight(r.height()); } } axisTitles.insert(4, sytitle); break; } } } QPointF topcenter(area.center().x(), area.top()); QPointF bottomcenter(area.center().x(), area.bottom()); QPointF leftcenter(area.left(), area.center().y()); QPointF rightcenter(area.right(), area.center().y()); debugChartLayout<<"left:"<legendPosition()) { case StartPosition: switch(legend->alignment()) { case Qt::AlignLeft: { // Align at top of the area to the left of plot area qreal offset = yOffset(topTitleRect, QRectF()); legendRect.moveTop(plotareaRect.top() + offset); break; } case Qt::AlignCenter: { qreal centerOffset = yOffset(topTitleRect, bottomTitleRect, true); legendRect.moveTop(plotareaRect.center().y() + centerOffset - legendRect.height() / 2.0); break; } case Qt::AlignRight: { // Align at bottom of the area to the left of plot area qreal offset = yOffset(QRectF(), bottomTitleRect); legendRect.moveBottom(plotareaRect.bottom() + offset); break; } } legendRect.moveLeft(plotareaRect.left()); plotareaRect.setLeft(legendRect.right() + m_spacing.x()); break; case TopPosition: switch(legend->alignment()) { case Qt::AlignLeft: { // Align at left of the area on top of plot area qreal offset = xOffset(leftTitleRect, QRectF()); legendRect.moveLeft(plotareaRect.left() + offset); break; } case Qt::AlignCenter: { qreal centerOffset = xOffset(leftTitleRect, rightTitleRect, true); legendRect.moveLeft(plotareaRect.center().x() + centerOffset - legendRect.width() / 2.0); debugChartLayout<<"legend top/center:"<<"to"<alignment()) { case Qt::AlignLeft: { // Align at top of the area right of plot area qreal offset = yOffset(topTitleRect, QRectF()); legendRect.moveTop(plotareaRect.top() + offset); debugChartLayout<<"legend end/top:"<<"to"<alignment()) { case Qt::AlignLeft: { // Align at left of the area below of plot area qreal offset = xOffset(leftTitleRect, QRectF()); legendRect.moveLeft(plotareaRect.left() + offset); break; } case Qt::AlignCenter: { qreal centerOffset = xOffset(leftTitleRect, rightTitleRect, true); legendRect.moveLeft(bottomcenter.x() + centerOffset - legendRect.width() / 2.0); break; } case Qt::AlignRight: { // Align at right of the area on below plot area qreal offset = xOffset(QRectF(), rightTitleRect); legendRect.moveRight(plotareaRect.right() + offset); break; } } legendRect.moveBottom(plotareaRect.bottom()); plotareaRect.setBottom(legendRect.top() - m_spacing.y()); break; case TopStartPosition: { qreal xoffset = xOffset(leftTitleRect, QRectF()); qreal yoffset = yOffset(topTitleRect, QRectF()); legendRect.moveTopLeft(area.topLeft()); if (legendRect.right() + m_spacing.x() - xoffset > plotareaRect.left()) { plotareaRect.setLeft(legendRect.right() + m_spacing.x() - xoffset); } if (legendRect.bottom() + m_spacing.y() - yoffset > plotareaRect.top()) { plotareaRect.setTop(legendRect.bottom() + m_spacing.y() - yoffset); } debugChartLayout<<"legend top/start"< plotareaRect.top()) { plotareaRect.setTop(legendRect.bottom() + m_spacing.y() - yoffset); } debugChartLayout<<"legend top/end"< plotareaRect.left()) { plotareaRect.setLeft(legendRect.right() + m_spacing.x() - xoffset); } if (legendRect.top() - m_spacing.y() + yoffset < plotareaRect.bottom()) { plotareaRect.setBottom(legendRect.top() - m_spacing.y() - yoffset); } debugChartLayout<<"legend bottom/start"< pr.right()) { plotareaRect.setRight(qMin(plotareaRect.right(), legendRect.left() - m_spacing.x())); } else if (legendRect.bottom() < pr.top()) { plotareaRect.setTop(qMax(plotareaRect.top(), legendRect.bottom() + m_spacing.y())); } else if (legendRect.top() > pr.bottom()) { plotareaRect.setBottom(qMin(plotareaRect.bottom(), legendRect.top() - m_spacing.y())); } debugChartLayout<<"legend manual:"<legendPosition()<<"align:"<alignment(); } debugChartLayout<<"axis titles:"<isVisible()) { m_layoutItems[title]->rect = titleRect; debugChartLayout<<"title"<isVisible()) { m_layoutItems[subtitle]->rect = subtitleRect; debugChartLayout<<"subtitle"<isVisible()) { m_layoutItems[footer]->rect = footerRect; debugChartLayout<<"footer"<isVisible()) { m_layoutItems[legend]->rect = legendRect; debugChartLayout<<"legend"<isVisible()) { m_layoutItems[plotarea]->rect = plotareaRect; debugChartLayout<<"plot area"<::const_iterator it; for (it = m_layoutItems.constBegin(); it != m_layoutItems.constEnd(); ++it) { if (it.key()->isVisible()) { setItemPosition(it.key(), it.value()->rect.topLeft()); debugChartLayout<rect.topLeft()<itemType == PlotAreaType) { setItemSize(it.key(), it.value()->rect.size()); debugChartLayout<rect.size()<size()); return shape->transformation().mapRect(boundingRect).topLeft(); } /*static*/ QSizeF ChartLayout::itemSize(const KoShape *shape) { const QRectF boundingRect = QRectF(QPointF(0, 0), shape->size()); return shape->transformation().mapRect(boundingRect).size(); } /*static*/ void ChartLayout::setItemSize(KoShape *shape, const QSizeF &size) { shape->setSize(size); } /*static*/ QRectF ChartLayout::itemRect(const KoShape *shape) { const QRectF boundingRect = QRectF(itemPosition(shape), itemSize(shape)); return boundingRect; } /*static*/ void ChartLayout::setItemPosition(KoShape *shape, const QPointF& pos) { const QPointF offset = shape->position() - itemPosition(shape); shape->setPosition(pos + offset); } QRectF ChartLayout::diagramArea(const KoShape *shape) { return diagramArea(shape, itemRect(shape)); } // FIXME: Get the actual plot area ex axis labels from KChart QRectF ChartLayout::diagramArea(const KoShape *shape, const QRectF &rect) { const PlotArea* plotArea = dynamic_cast(shape); if (!plotArea) { return rect; } qreal bottom = 0.0; qreal left = 0.0; qreal top = 0.0; qreal right = 0.0; // HACK: KChart has some spacing between axis and label qreal xspace = ScreenConversions::pxToPtX(6.0) * 2.0; qreal yspace = ScreenConversions::pxToPtY(6.0) * 2.0; if (plotArea->xAxis() && plotArea->xAxis()->showLabels()) { bottom = plotArea->xAxis()->fontSize(); bottom += yspace; } if (plotArea->yAxis() && plotArea->yAxis()->showLabels()) { left = plotArea->yAxis()->fontSize(); left += xspace; } if (plotArea->secondaryXAxis() && plotArea->secondaryXAxis()->showLabels()) { top = plotArea->secondaryXAxis()->fontSize(); top += yspace; } if (plotArea->secondaryYAxis() && plotArea->secondaryYAxis()->showLabels()) { right = plotArea->secondaryYAxis()->fontSize(); right += xspace; } return rect.adjusted(left, top, -right, -bottom); } QString ChartLayout::dbg(const KoShape *shape) const { QString s; LayoutData *data = m_layoutItems[const_cast(shape)]; switch(data->itemType) { case GenericItemType: s = "KoShape[Generic:"+ shape->shapeId() + "]"; break; case TitleLabelType: s = "KoShape[ChartTitle]"; break; case SubTitleLabelType: s = "KoShape[ChartSubTitle]"; break; case FooterLabelType: s = "KoShape[ChartFooter]"; break; case PlotAreaType: s = "KoShape[PlotArea]"; break; case LegendType: s = "KoShape[Legend"; switch(static_cast(shape)->alignment()) { case Qt::AlignLeft: s += ":Start"; break; case Qt::AlignCenter: s += ":Center"; break; case Qt::AlignRight: s += ":End"; break; default: s += ":Float"; break; } s += ']'; break; case XAxisTitleType: s = "KoShape[XAxisTitle]"; break; case YAxisTitleType: s = "KoShape[YAxisTitle]"; break; case SecondaryXAxisTitleType: s = "KoShape[SXAxisTitle]"; break; case SecondaryYAxisTitleType: s = "KoShape[SYAxisTitle]"; break; default: s = "KoShape[Unknown]"; break; } return s; } QString ChartLayout::dbg(const QList &shapes) const { QString s = "("; for (int i = 0; i < shapes.count(); ++i) { if (i > 0) s += ','; s += dbg(shapes.at(i)); } s += ')'; return s; }