diff options
Diffstat (limited to 'kchart/kchart_params.cpp')
-rw-r--r-- | kchart/kchart_params.cpp | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/kchart/kchart_params.cpp b/kchart/kchart_params.cpp new file mode 100644 index 00000000..2cd8da51 --- /dev/null +++ b/kchart/kchart_params.cpp @@ -0,0 +1,1043 @@ +/* This file is part of the KDE project + Copyright (C) 2001,2002,2003,2004 Laurent Montel <montel@kde.org> + Copyright (C) 2006 Thorsten Zachmann <zachmann@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 <stdlib.h> +#include <iostream> +using std::cout; +using std::cerr; + + +#include <dcopobject.h> +#include <tdelocale.h> +#include <kdebug.h> + +#include <KoXmlNS.h> +#include <KoXmlWriter.h> +#include <KoDom.h> +#include <KoOasisLoadingContext.h> + +#include <tqregexp.h> + +#include "kdchart/KDChartParams.h" +#include "kdchart/KDChartAxisParams.h" + +#include "kchart_params.h" +#include "kchart_part.h" +#include "KChartParamsIface.h" + + +namespace KChart +{ + + +KChartParams::KChartParams( KChartPart *_part ) + : KDChartParams(), + m_part( _part ) +{ + // Default values for subtypes. + m_firstRowAsLabel = false; + m_firstColAsLabel = false; + + // Default values for OpenDocument extensions. + m_barNumLines = 0; + + m_dcop = 0; + //dcopObject(); // build it +} + + +KChartParams::~KChartParams() +{ + delete m_dcop; +} + + +// ---------------------------------------------------------------- + + +TQString KChartParams::chartTypeToString( ChartType _type) const +{ +#if 0 + if (_type == BarLines ) + return "BarLines"; + else +#endif + return KDChartParams::chartTypeToString( (KDChartParams::ChartType) _type ); +} + + +KChartParams::ChartType +KChartParams::stringToChartType( const TQString& _string ) +{ +#if 0 + if ( _string == "BarLines" ) + return BarLines; + else +#endif + return (ChartType) KDChartParams::stringToChartType( _string ); +} + + +void KChartParams::setFirstRowAsLabel( bool _val ) +{ + m_firstRowAsLabel = _val; + + // The rest of this method is only applicable if the data is from + // an external source, e.g. from kspread. + if ( m_part->canChangeValue() ) + return; + + m_part->doSetData( *m_part->data(), + m_firstRowAsLabel, m_firstColAsLabel ); +} + +void KChartParams::setFirstColAsLabel( bool _val ) +{ + m_firstColAsLabel = _val; + + // The rest of this method is only applicable if the data is from + // an external source, e.g. kspread. + if ( m_part->canChangeValue() ) + return; + + m_part->doSetData( *m_part->data(), + m_firstRowAsLabel, m_firstColAsLabel ); +} + + +DCOPObject* KChartParams::dcopObject() +{ + if ( !m_dcop ) + m_dcop = new KChartParamsIface( this ); + return m_dcop; +} + + +// ================================================================ +// Loading and Saving + + + +static const struct { + const char *oasisClass; + KChartParams::ChartType chartType; +} oasisChartTypes[] = { + { "chart:bar", KChartParams::Bar }, + { "chart:line", KChartParams::Line }, + { "chart:area", KChartParams::Area }, + { "chart:circle", KChartParams::Pie }, + //{ "chart:xxx", KChartParams::HiLo }, + //{ "chart:stock", KChartParams::??? }, + //{ "chart:add-in", KChartParams::??? }, + //{ "chart:scatter",KChartParams::??? }, + { "chart:ring", KChartParams::Ring }, + { "chart:radar", KChartParams::Polar } + //{ "chart:xxx", KChartParams::BoxWhisker }, + + //{ "chart:xxx", KChartParams::BarLines }, + // FIXME: More? +}; + + +static const unsigned int numOasisChartTypes + = sizeof oasisChartTypes / sizeof *oasisChartTypes; + + +#if 0 // Example code!! + KoOasisLoadingContext loadingContext( this, oasisStyles, store ); + KoStyleStack &styleStack = loadingContext.styleStack(); + + styleStack.save(); + styleStack.setTypeProperties( "graphic" ); // load graphic-properties + loadingContext.fillStyleStack( chartElem, KoXmlNS::chart, "style-name" ); + + const TQString fillColor = styleStack.attributeNS( KoXmlNS::draw, "fill-color" ); + kDebug() << "fillColor=" << fillColor << endl; + + styleStack.restore(); +#endif + + +// Load the data from an OpenDocument chart:chart element. + +void KChartParams::loadOasisFont( KoOasisLoadingContext& context, TQFont& font, TQColor& color ) +{ + KoStyleStack& styleStack = context.styleStack(); + styleStack.setTypeProperties( "text" ); // load all style attributes from "style:text-properties" + + if ( styleStack.hasAttributeNS( KoXmlNS::fo, "color" ) ) { // 3.10.3 + color.setNamedColor( styleStack.attributeNS( KoXmlNS::fo, "color" ) ); // #rrggbb format + } + if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-family" ) // 3.10.9 + || styleStack.hasAttributeNS( KoXmlNS::style, "font-name" ) ) { // 3.10.8 + // Hmm, the remove "'" could break it's in the middle of the fontname... + TQString fontName = styleStack.attributeNS( KoXmlNS::fo, "font-family" ).remove( "'" ); + if ( fontName.isEmpty() ) { + // ##### TODO. This is wrong. style:font-name refers to a font-decl entry. + // We have to look it up there, and retrieve _all_ font attributes from it, not just the name. + fontName = styleStack.attributeNS( KoXmlNS::style, "font-name" ).remove( "'" ); + } + // 'Thorndale' is not known outside OpenOffice so we substitute it + // with 'Times New Roman' that looks nearly the same. + if ( fontName == "Thorndale" ) + fontName = "Times New Roman"; + + fontName.remove( TQRegExp( "\\sCE$" ) ); // Arial CE -> Arial + font.setFamily( fontName ); + } + if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-size" ) ) { // 3.10.14 + double pointSize = styleStack.fontSize(); + font.setPointSizeFloat( pointSize ); + kdDebug(35001) << "font size: " << pointSize << endl; + } + if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-weight" ) ) { // 3.10.24 + TQString fontWeight = styleStack.attributeNS( KoXmlNS::fo, "font-weight" ); + int boldness; + if ( fontWeight == "normal" ) + boldness = 50; + else if ( fontWeight == "bold" ) + boldness = 75; + else + // XSL/CSS has 100,200,300...900. Not the same scale as TQt! + // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight + boldness = fontWeight.toInt() / 10; + font.setWeight( boldness ); + } + if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-style" ) ) { // 3.10.19 + if ( styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "italic" || + styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "oblique" ) { // no difference in kotext + font.setItalic( true ); + } + } +} + +bool KChartParams::loadOasis( const TQDomElement &chartElem, + KoOasisLoadingContext &loadingContext, + TQString &errorMessage, + KoStore */*store*/ ) +{ + const TQString chartClass = chartElem.attributeNS( KoXmlNS::chart, + "class", TQString() ); + bool knownType = false; + + // Find out what KChart charttype the OASIS charttype corresponds to. + for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) { + if ( chartClass == oasisChartTypes[i].oasisClass ) { + kdDebug(35001) << "found chart of type " << chartClass << endl; + //cerr << "found chart of type " << chartClass.latin1() << "\n"; + + setChartType( oasisChartTypes[i].chartType ); + knownType = true; + break; + } + } + + // If we can't find out what charttype it is, we might as well end here. + if ( !knownType ) { + errorMessage = i18n( "Unknown chart type %1" ).arg( chartClass ); + kdDebug(35001) << errorMessage << endl; + return false; + } + + // Title TODO (more details, e.g. font, placement etc) + TQDomElement titleElem = KoDom::namedItemNS( chartElem, + KoXmlNS::chart, "title" ); + if ( !titleElem.isNull() ) { + loadingContext.styleStack().save(); + loadingContext.fillStyleStack( titleElem, KoXmlNS::chart, "style-name", "chart" ); + TQFont font; + TQColor color; + loadOasisFont( loadingContext, font, color ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader, font, true, font.pointSize()*4 ); + setHeaderFooterColor( KDChartParams::HdFtPosHeader, color ); + loadingContext.styleStack().restore(); + + TQDomElement pElem = KoDom::namedItemNS( titleElem, + KoXmlNS::text, "p" ); + setHeader1Text( pElem.text() ); + } + + // Subtitle TODO (more details) + TQDomElement subtitleElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart, + "subtitle" ); + if ( !subtitleElem.isNull() ) { + loadingContext.styleStack().save(); + loadingContext.fillStyleStack( subtitleElem, KoXmlNS::chart, "style-name", "chart" ); + TQFont font; + TQColor color; + loadOasisFont( loadingContext, font, color ); + setHeaderFooterFont( KDChartParams::HdFtPosHeader2, font, true, font.pointSize()*4 ); + setHeaderFooterColor( KDChartParams::HdFtPosHeader2, color ); + loadingContext.styleStack().restore(); + + TQDomElement pElem = KoDom::namedItemNS( subtitleElem, + KoXmlNS::text, "p" ); + setHeader2Text( pElem.text() ); + } + + // Footer TODO (more details) + TQDomElement footerElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart, + "footer" ); + if ( !footerElem.isNull() ) { + loadingContext.styleStack().save(); + loadingContext.fillStyleStack( footerElem, KoXmlNS::chart, "style-name", "chart" ); + TQFont font; + TQColor color; + loadOasisFont( loadingContext, font, color ); + setHeaderFooterFont( KDChartParams::HdFtPosFooter, font, true, font.pointSize()*4 ); + setHeaderFooterColor( KDChartParams::HdFtPosFooter, color ); + loadingContext.styleStack().restore(); + + TQDomElement pElem = KoDom::namedItemNS( footerElem, + KoXmlNS::text, "p" ); + setFooterText( pElem.text() ); + } + + // TODO: Get legend settings + TQDomElement legendElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart, + "legend" ); + if ( !legendElem.isNull() ) + { + loadingContext.styleStack().save(); + loadingContext.fillStyleStack( legendElem, KoXmlNS::chart, "style-name", "chart" ); + TQFont font; + TQColor color; + loadOasisFont( loadingContext, font, color ); + //tz I didn't find that Oasis support seperate font/colors for the title and the rest of the legent + setLegendFont( font, false ); + setLegendFontRelSize( font.pointSize()*4 ); + setLegendTitleFont( font, false); + setLegendTitleFontRelSize( font.pointSize()*4 ); + setLegendTextColor( color ); + setLegendTitleTextColor( color ); + loadingContext.styleStack().restore(); + TQString lp; + if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-position" ) ) + { + lp = legendElem.attributeNS( KoXmlNS::chart, "legend-position", TQString() ); + } + TQString lalign; + if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-align" ) ) + { + lalign = legendElem.attributeNS( KoXmlNS::chart, "legend-align", TQString() ); + } + + LegendPosition lpos = NoLegend; + int align = 1; + if ( lalign == "start" ) + { + align = 0; + } + else if ( lalign == "end" ) + { + align = 2; + } + + if ( lp == "start" ) + { + lpos = LegendLeft; + if ( align == 0 ) + lpos = LegendTopLeftLeft; + else if ( align == 2 ) + lpos = LegendBottomLeftLeft; + } + else if ( lp == "end" ) + { + lpos = LegendRight; + if ( align == 0 ) + lpos = LegendTopRightRight; + else if ( align == 2 ) + lpos = LegendBottomRightRight; + } + else if ( lp == "top" ) + { + lpos = LegendTop; + if ( align == 0 ) + lpos = LegendTopLeftTop; + else if ( align == 2 ) + lpos = LegendTopRightTop; + } + else if ( lp == "bottom" ) + { + lpos = LegendBottom; + if ( align == 0 ) + lpos = LegendBottomLeftBottom; + else if ( align == 2 ) + lpos = LegendBottomRightBottom; + } + else if ( lp == "top-start" ) + { + lpos = LegendTopLeft; + } + else if ( lp == "bottom-start" ) + { + lpos = LegendBottomLeft; + } + else if ( lp == "top-end" ) + { + lpos = LegendTopRight; + } + else if ( lp == "bottom-end" ) + { + lpos = LegendBottomRight; + } + + setLegendPosition( lpos ); + //bodyWriter->addAttribute( "koffice:title", legendTitleText() ); + if ( legendElem.hasAttributeNS( KoXmlNS::koffice, "title" ) ) + { + setLegendTitleText( legendElem.attributeNS( KoXmlNS::koffice, "title", TQString() ) ); + } + } + else + { + setLegendPosition( NoLegend ); + } + + // Get the plot-area. This is where the action is. + TQDomElement plotareaElem = KoDom::namedItemNS( chartElem, + KoXmlNS::chart, "plot-area" ); + if ( !plotareaElem.isNull() ) { + return loadOasisPlotarea( plotareaElem, loadingContext, errorMessage ); + } + + return false; +} + + +bool KChartParams::loadOasisPlotarea( const TQDomElement &plotareaElem, + KoOasisLoadingContext &loadingContext, + TQString &errorMessage ) +{ + TQString tmp; + + //cerr << ">>> ==========================================================\n"; + + // FIXME: attribute table:cell-range-address - the cells in a spreadsheet + + // Get whether there is a label on the first row or column of the data. + // This info is in the attribute chart:data-source-has-labels. + // + // NOTE: Only used in spreadsheets. + tmp = plotareaElem.attributeNS( KoXmlNS::chart, + "data-source-has-labels", TQString() ); + m_firstRowAsLabel = false; + m_firstColAsLabel = false; + if ( tmp == "none" || tmp == "" ) + ; // NOTHING + else if ( tmp == "row" ) + m_firstRowAsLabel = true; + else if ( tmp == "column" ) + m_firstColAsLabel = true; + else if ( tmp == "both" ) { + m_firstRowAsLabel = true; + m_firstColAsLabel = true; + } + else { + errorMessage = "Unknown value for chart:data-source-has-labels:" + + tmp; + return false; + } + + // ---------------------------------------------------------------- + // Now get the style and use it to get the contents. + // This is hidden in the attribute chart:style-name. + KoStyleStack &styleStack = loadingContext.styleStack(); + + + tmp = plotareaElem.attributeNS( KoXmlNS::chart, "style-name", + TQString() ); + //kdDebug(35001) << "Style name for the plot area: " << tmp << endl; + styleStack.save(); + styleStack.setTypeProperties( "chart" ); // load chart properties + loadingContext.fillStyleStack( plotareaElem, KoXmlNS::chart, "style-name", "chart" ); + + if ( styleStack.attributeNS( KoXmlNS::chart, "three-dimensional" ) == "true" ) { + setThreeDBars( true ); + setThreeDLines( true ); + setThreeDPies( true ); + } + else { + setThreeDBars( false ); + setThreeDLines( false ); + setThreeDPies( false ); + } + + switch ( m_chartType ) { + case NoType: + break; + + case Bar: + // Find out subtype + tmp = styleStack.attributeNS( KoXmlNS::chart, "vertical" ); + // FIXME:Qt::Vertical is ignored. At least store it so we can + // save it again even if we don't support it. + + //kdDebug(35001) << " ======> Qt::Vertical: " << tmp << " <======" << endl; + // Set the bar chart subtype. + if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" ) + setBarChartSubType( BarStacked ); + else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" ) + setBarChartSubType( BarPercent ); + else + setBarChartSubType( BarNormal ); + + break; + + // chart:vertical - true if vertical bars (only bar charts) + // chart:stacked - true for stacked bars + // chart:percentage - true for percentage (mut. excl with stacked) + // chart:connect-bars - true if lines to connect bars should be drawn + // only used for stacked and percentage bars. + // FIXME: Support lines on bar charts. + // chart:lines-used - 0-n, number of lines on a bar chart. (def: 0) + + case Line: + // Set the line chart subtype. + if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" ) + setLineChartSubType( LineStacked ); + else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" ) + setLineChartSubType( LinePercent ); + else + setLineChartSubType( LineNormal ); + + break; + + // FIXME: Why is this even there? Seems like an unnecessary attr. + // chart:lines - true for line charts, false otherwise + + // chart:stacked - true for stacked bars + // chart:percentage - true for percentage (mut. excl with stacked) + + // chart:symbol-type - used with line charts, should be "automatic" + + case Area: + // Set the area chart subtype. + if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" ) + setAreaChartSubType( AreaStacked ); + else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" ) + setAreaChartSubType( AreaPercent ); + else + setAreaChartSubType( AreaNormal ); + + break; + + // chart:stacked - true for stacked bars + // chart:percentage - true for percentage (mut. excl with stacked) + + case Pie: + break; + + case HiLo: + break; + + case Ring: + break; + + case Polar: + break; + + case BoxWhisker: + break; + } + // TODO: + // And get the info from the style. Here is the contents: + + + // TODO: These items are currently not used. They should be at least + // be stored so that we can save them again. + // chart:interpolation - "cubic-spline" if using cubic splines + // chart:splines - + // chart:spline-order - "2" for cubic splines + // chart:spline-resolution - how smooth (default: 20) + + // -- Used when chart:class == "stock: + // chart:stock-updown-bars - boolean + // chart:stock-with-volume - boolean + // chart:japanese-candle-sticks - boolean + + // chart:series-source - "rows" or "columns" + // "columns" is the default + if ( styleStack.attributeNS( KoXmlNS::chart, "series-source" ) == "rows" ) { + setDataDirection( DataRows ); + } + + // chart:data-label-number - "value" / "percentage" / "none" (def: none) + + // chart:data-label-text - true if data hapoints have text labels + // chart:data-label-symbol - true if data hapoints have legend symbol + // (default: false for both) + + styleStack.restore(); + + // ---------------------------------------------------------------- + // In the plot-area element there are two chart:axis elements + + TQDomElement xAxisElem; + TQDomElement yAxisElem; + + TQDomElement axisElem; + forEachElement( axisElem, plotareaElem ) { + + // If this element is not an axis, then continue + if ( axisElem.tagName() != "axis" ) + continue; + + tmp = axisElem.attributeNS( KoXmlNS::chart, "name", + TQString()); + //kdDebug(35001) << "Got axis " << tmp << endl; + //cerr << "Got axis " << tmp.latin1() << "\n"; + if ( tmp == "primary-x" ) + xAxisElem = axisElem; + else if ( tmp == "primary-y" ) + yAxisElem = axisElem; + else + // Only supports two axes so far. + continue; + } + + // Load the axes. Pie charts use only the y axis. + if ( m_chartType != Pie + && !loadOasisAxis( xAxisElem, loadingContext, errorMessage, + KDChartAxisParams::AxisPosBottom) ) + return false; + if ( !loadOasisAxis( yAxisElem, loadingContext, errorMessage, + KDChartAxisParams::AxisPosLeft) ) + return false; + + // Attributes for the axes: + // chart:name - either "primary-x" or "primary-y" + +#if 0 + const TQString fillColor = styleStack.attributeNS( KoXmlNS::draw, + "fill-color" ); + kdDebug() << "fillColor=" << fillColor << endl; +#endif + + //cerr << "<<< ==========================================================\n"; + + return true; +} + + +bool KChartParams::loadOasisAxis( const TQDomElement &axisElem, + KoOasisLoadingContext &loadingContext, + TQString &errorMessage, + KDChartAxisParams::AxisPos axisPos ) +{ + Q_UNUSED( errorMessage ); + + TQString tmp; + TQDomElement tmpElem; + KoStyleStack &styleStack = loadingContext.styleStack(); + + // Get the axis to manipulate. + // TODO + + // Get the axis title (== axis label) if any. + TQDomElement titleElem = KoDom::namedItemNS( axisElem, + KoXmlNS::chart, "title" ); + if ( !titleElem.isNull() ) { + tmpElem = KoDom::namedItemNS( titleElem, KoXmlNS::text, "p" ); + setAxisTitle( axisPos, tmpElem.text() ); + } + + + + //cerr << ">>> ----------------------------------------------------------\n"; + //cerr << "Loading axis " << axisElem.attributeNS( KoXmlNS::chart, "name", + // TQString()).latin1() + // << "\n"; + + tmp = axisElem.attributeNS( KoXmlNS::chart, "style-name", TQString() ); + //kdDebug(35001) << "Style name for the axis: " << tmp << endl; + //cerr << "Style name for the axis: " << tmp.latin1() << "\n"; + styleStack.save(); + styleStack.setTypeProperties( "chart" ); // load chart properties + loadingContext.fillStyleStack( axisElem, KoXmlNS::chart, "style-name", "chart" ); + + // chart:class - "category" / "value" / "domain" (domain for scatter) + // child: chart:categories + + // child: chart:title - Name of title if any. + // child: chart:grid + // attr: chart:class - "major" / "minor" + + // chart:style-name - Associated style with the following info: + // -------------------------------- + // chart:display-label - true if an axis label should be shown. + + // chart:tick-marks-major-inner - true if display tickmarks at major + // chart:tick-marks-major-outer or minor intervals outside / inside + // chart:tick-minor-major-inner the chart area. + // chart:tick-minor-major-outer + + // chart:logarithmic - true if logarithmic scale + + // text:line-break - true if categories can be broken + + // chart:text-overlap - true if labels can overlap + + // chart:label-arrangement - "side-by-side" / "stagger-even" / + // "stagger-odd" (def: side-by-side) + + // chart:visible - true if labels + ticks should be shown. + + // children: + // chart: + // chart: + // chart: + // chart: + // chart: + styleStack.restore(); + + //cerr << "<<< ----------------------------------------------------------\n"; + return true; +} + + +// ---------------------------------------------------------------- + + +void KChartParams::saveOasis( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const +{ + bool knownType = false; + for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) { + if ( m_chartType == oasisChartTypes[i].chartType ) { + bodyWriter->addAttribute( "chart:class", oasisChartTypes[i].oasisClass ); + knownType = true; + break; + } + } + + if ( !knownType ) { + kdError(32001) << "Unknown chart type in KChartParams::saveOasis, extend oasisChartTypes!" << endl; + } + + bodyWriter->startElement( "chart:title" ); + TQRect rect( headerFooterRect( KDChartParams::HdFtPosHeader ) ); + bodyWriter->addAttributePt( "svg:x", rect.x() ); + bodyWriter->addAttributePt( "svg:y", rect.y() ); + bodyWriter->addAttribute( "chart:style-name", saveOasisFont( mainStyles, header1Font(), headerFooterColor( KDChartParams::HdFtPosHeader ) ) ); + bodyWriter->startElement( "text:p" ); + bodyWriter->addTextNode( header1Text() ); + bodyWriter->endElement(); // text:p + bodyWriter->endElement(); // chart:title + + TQString subTitle( header2Text() ); + if ( !subTitle.isEmpty() ) { + + kdDebug(32001) << "header rect: " << headerFooterRect( KDChartParams::HdFtPosHeader2 ) << endl; + TQRect rect( headerFooterRect( KDChartParams::HdFtPosHeader2 ) ); + bodyWriter->startElement( "chart:subtitle" ); + bodyWriter->addAttributePt( "svg:x", rect.x() ); + bodyWriter->addAttributePt( "svg:y", rect.y() ); + bodyWriter->addAttribute( "chart:style-name", + saveOasisFont( mainStyles, + header2Font(), + headerFooterColor( KDChartParams::HdFtPosHeader2 ) ) ); + + bodyWriter->startElement( "text:p" ); + bodyWriter->addTextNode( subTitle ); + bodyWriter->endElement(); // text:p + bodyWriter->endElement(); // chart:subtitle + } + + + TQString footer( footerText() ); + if ( !footer.isEmpty() ) { + TQRect rect( headerFooterRect( KDChartParams::HdFtPosFooter ) ); + bodyWriter->startElement( "chart:footer" ); + bodyWriter->addAttributePt( "svg:x", rect.x() ); + bodyWriter->addAttributePt( "svg:y", rect.y() ); + bodyWriter->addAttribute( "chart:style-name", + saveOasisFont( mainStyles, + footerFont(), + headerFooterColor( KDChartParams::HdFtPosFooter ) ) ); + + bodyWriter->startElement( "text:p" ); + bodyWriter->addTextNode( footer ); + bodyWriter->endElement(); // text:p + bodyWriter->endElement(); // chart:footer + } + + // TODO legend + LegendPosition lpos = legendPosition(); + if ( lpos != NoLegend ) { + bodyWriter->startElement( "chart:legend" ); + TQString lp; + TQString lalign; + switch ( lpos ) { + case LegendTop: + lp = "top"; + lalign = "center"; + break; + case LegendBottom: + lp = "bottom"; + lalign = "center"; + break; + case LegendLeft: + lp = "start"; + lalign = "center"; + break; + case LegendRight: + lp = "end"; + lalign = "center"; + break; + case LegendTopLeft: + lp = "top-start"; + break; + case LegendTopLeftTop: + lp = "top"; + lalign = "start"; + break; + case LegendTopLeftLeft: + lp = "start"; + lalign = "start"; + break; + case LegendTopRight: + lp = "top-end"; + break; + case LegendTopRightTop: + lp = "top"; + lalign = "end"; + break; + case LegendTopRightRight: + lp = "end"; + lalign = "start"; + break; + case LegendBottomLeft: + lp = "bottom-start"; + break; + case LegendBottomLeftBottom: + lp = "bottom"; + lalign = "start"; + break; + case LegendBottomLeftLeft: + lp = "start"; + lalign = "end"; + break; + case LegendBottomRight: + lp = "bottom-end"; + break; + case LegendBottomRightBottom: + lp = "bottom"; + lalign = "end"; + break; + case LegendBottomRightRight: + lp = "end"; + lalign = "end"; + break; + default: + lp = "end"; + lalign = "center"; + break; + } + + bodyWriter->addAttribute( "chart:legend-position", lp ); + bodyWriter->addAttribute( "chart:legend-align", lalign ); + bodyWriter->addAttribute( "chart:style-name", + saveOasisFont( mainStyles, + legendFont(), + legendTextColor() ) ); + bodyWriter->addAttribute( "koffice:title", legendTitleText() ); + bodyWriter->endElement(); // chart:legend + } + + bodyWriter->startElement( "chart:plot-area" ); + saveOasisPlotArea( bodyWriter, mainStyles ); + bodyWriter->endElement(); + + // TODO... +} + + +void KChartParams::saveOasisPlotArea( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const +{ + TQString dataSourceHasLabels; + if ( m_firstRowAsLabel ) + if ( m_firstColAsLabel ) + dataSourceHasLabels = "both"; + else + dataSourceHasLabels = "row"; + else + if ( m_firstColAsLabel ) + dataSourceHasLabels = "column"; + else + dataSourceHasLabels = "none"; + bodyWriter->addAttribute( "chart:data-source-has-labels", dataSourceHasLabels ); + + // Prepare the style for the plot area + KoGenStyle plotAreaStyle( KoGenStyle::STYLE_AUTO, "chart" ); + + switch ( m_chartType ) { + case NoType: + break; + + case Bar: + switch( barChartSubType() ) { + case BarStacked: + plotAreaStyle.addProperty( "chart:stacked", "true" ); + break; + case BarPercent: + plotAreaStyle.addProperty( "chart:percentage", "true" ); + break; + case BarNormal: + break; + case BarMultiRows: + break; + } + plotAreaStyle.addProperty( "chart:vertical", "false" ); // #### always? + plotAreaStyle.addProperty( "chart:lines-used", 0 ); // FIXME: for now + + if ( threeDBars() ) + plotAreaStyle.addProperty( "chart:three-dimensional", "true" ); + + case Line: + switch( lineChartSubType() ) { + case LineStacked: + plotAreaStyle.addProperty( "chart:stacked", "true" ); + break; + case LinePercent: + plotAreaStyle.addProperty( "chart:percentage", "true" ); + break; + case LineNormal: + break; + } + plotAreaStyle.addProperty( "chart:symbol-type", "automatic" ); + + if ( threeDLines() ) + plotAreaStyle.addProperty( "chart:three-dimensional", "true" ); + + break; + + case Area: + switch( areaChartSubType() ) { + case AreaStacked: + plotAreaStyle.addProperty( "chart:stacked", "true" ); + break; + case AreaPercent: + plotAreaStyle.addProperty( "chart:percentage", "true" ); + break; + case AreaNormal: + break; + } + //plotAreaStyle.addProperty( "chart:lines-used", 0 ); // #### for now + + // TODO - very similar + + case Pie: + if ( threeDPies() ) + plotAreaStyle.addProperty( "chart:three-dimensional", "true" ); + + break; + + case HiLo: + break; + + case Ring: + break; + + case Polar: + break; + + case BoxWhisker: + break; + } + + // chart:series-source + plotAreaStyle.addProperty( "chart:series-source", + ( dataDirection() == DataRows ) ? "rows" : "columns" ); + + // Register the style, and get back its auto-generated name + const TQString styleName = mainStyles.lookup( plotAreaStyle, "ch" ); + + bodyWriter->addAttribute( "chart:style-name", styleName ); + + saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosBottom, "x" ); + saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosLeft, "y" ); + + // TODO chart:series + // TODO chart:wall + // TODO chart:floor +} + + +void KChartParams::saveOasisAxis( KoXmlWriter* bodyWriter, + KoGenStyles& mainStyles, + KDChartAxisParams::AxisPos axisPos, + const char* axisName ) const +{ + bodyWriter->startElement( "chart:axis" ); + + bodyWriter->addAttribute( "chart:dimension", axisName ); + bodyWriter->addAttribute( "chart:name", TQCString( "primary-" ) + axisName ); + + KoGenStyle axisStyle( KoGenStyle::STYLE_AUTO, "chart" ); + + // TODO: Save axis style properties, like + axisStyle.addProperty( "chart:display-label", "true" ); // ### + + + const TQString styleName = mainStyles.lookup( axisStyle, "ch" ); + bodyWriter->addAttribute( "chart:style-name", styleName ); + + // Write axis titles if any. + TQString tmpStr = axisTitle( axisPos ); + if ( tmpStr != "" ) { + bodyWriter->startElement( "chart:title" ); + // TODO: Save style, svg:x, svg:y + + // Write the text in the axis title. + bodyWriter->startElement( "text:p" ); + bodyWriter->addTextNode( tmpStr + .remove( TQRegExp( "^<qt><center>" ) ) + .remove( TQRegExp( "</center></qt>$" ) ) ); + bodyWriter->endElement(); // text:p + + bodyWriter->endElement(); // chart:title + } + + // TODO x axis has chart:categories, y axis has chart:grid ? + // Maybe that part should be done by the caller of saveOasisAxis then + // including the opening/closing of the chart:axis element... + + bodyWriter->endElement(); // chart:axis +} + + +TQString KChartParams::saveOasisFont( KoGenStyles& mainStyles, const TQFont& font, const TQColor& color ) const +{ + KoGenStyle::PropertyType tt = KoGenStyle::TextType; + KoGenStyle autoStyle( KoGenStyle::STYLE_AUTO, "chart", 0 ); + autoStyle.addProperty( "fo:font-family", font.family(), tt ); + autoStyle.addPropertyPt( "fo:font-size", font.pointSize(), tt ); + autoStyle.addProperty( "fo:color", color.isValid() ? color.name() : "#000000", tt ); + int w = font.weight(); + autoStyle.addProperty( "fo:font-weight", w == 50 ? "normal" : w == 75 ? "bold" : TQString::number( tqRound( w / 10 ) * 100 ), tt ); + autoStyle.addProperty( "fo:font-style", font.italic() ? "italic" : "normal", tt ); + + return mainStyles.lookup( autoStyle, "ch", KoGenStyles::ForceNumbering ); +} + + +} //KChart namespace + |