/* This file is part of the KDE project Copyright (C) 2001,2002,2003,2004 Laurent Montel Copyright (C) 2006 Thorsten Zachmann 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 #include using std::cout; using std::cerr; #include #include #include #include #include #include #include #include #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: vertical is ignored. At least store it so we can // save it again even if we don't support it. //kdDebug(35001) << " ======> 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( "^
" ) ) .remove( TQRegExp( "
$" ) ) ); 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