kchart

kchart_params.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2001,2002,2003,2004 Laurent Montel <montel@kde.org>
00003    Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 
00022 //#include <stdlib.h>
00023 #include <iostream>
00024 using std::cout;
00025 using std::cerr;
00026 
00027 
00028 #include <dcopobject.h>
00029 #include <klocale.h>
00030 #include <kdebug.h>
00031 
00032 #include <KoXmlNS.h>
00033 #include <KoXmlWriter.h>
00034 #include <KoDom.h>
00035 #include <KoOasisLoadingContext.h>
00036 
00037 #include <qregexp.h>
00038 
00039 #include "kdchart/KDChartParams.h"
00040 #include "kdchart/KDChartAxisParams.h"
00041 
00042 #include "kchart_params.h"
00043 #include "kchart_part.h"
00044 #include "KChartParamsIface.h"
00045 
00046 
00047 namespace KChart
00048 {
00049 
00050 
00051 KChartParams::KChartParams( KChartPart *_part )
00052     : KDChartParams(),
00053       m_part( _part )
00054 {
00055     // Default values for subtypes.
00056     m_firstRowAsLabel = false;
00057     m_firstColAsLabel = false;
00058 
00059     // Default values for OpenDocument extensions.
00060     m_barNumLines     = 0;
00061 
00062     m_dcop = 0;
00063     //dcopObject(); // build it
00064 }
00065 
00066 
00067 KChartParams::~KChartParams()
00068 {
00069     delete m_dcop;
00070 }
00071 
00072 
00073 // ----------------------------------------------------------------
00074 
00075 
00076 QString KChartParams::chartTypeToString( ChartType _type) const
00077 {
00078 #if 0
00079     if (_type == BarLines )
00080     return "BarLines";
00081     else
00082 #endif
00083     return KDChartParams::chartTypeToString( (KDChartParams::ChartType) _type );
00084 }
00085 
00086 
00087 KChartParams::ChartType
00088 KChartParams::stringToChartType( const QString& _string )
00089 {
00090 #if 0
00091     if ( _string == "BarLines" )
00092     return BarLines;
00093     else
00094 #endif
00095     return (ChartType) KDChartParams::stringToChartType( _string );
00096 }
00097 
00098 
00099 void KChartParams::setFirstRowAsLabel( bool _val )
00100 {
00101     m_firstRowAsLabel = _val;
00102 
00103     // The rest of this method is only applicable if the data is from
00104     // an external source, e.g. from kspread.
00105     if ( m_part->canChangeValue() )
00106     return;
00107 
00108     m_part->doSetData( *m_part->data(),
00109                m_firstRowAsLabel, m_firstColAsLabel );
00110 }
00111 
00112 void KChartParams::setFirstColAsLabel( bool _val )
00113 {
00114     m_firstColAsLabel = _val;
00115 
00116     // The rest of this method is only applicable if the data is from
00117     // an external source, e.g. kspread.
00118     if ( m_part->canChangeValue() )
00119     return;
00120 
00121     m_part->doSetData( *m_part->data(),
00122                m_firstRowAsLabel, m_firstColAsLabel );
00123 }
00124 
00125 
00126 DCOPObject* KChartParams::dcopObject()
00127 {
00128     if ( !m_dcop )
00129     m_dcop = new KChartParamsIface( this );
00130     return m_dcop;
00131 }
00132 
00133 
00134 // ================================================================
00135 //                     Loading and Saving
00136 
00137 
00138 
00139 static const struct {
00140     const char              *oasisClass;
00141     KChartParams::ChartType  chartType;
00142 } oasisChartTypes[] = {
00143     { "chart:bar",    KChartParams::Bar },
00144     { "chart:line",   KChartParams::Line },
00145     { "chart:area",   KChartParams::Area },
00146     { "chart:circle", KChartParams::Pie },
00147     //{ "chart:xxx",    KChartParams::HiLo },
00148     //{ "chart:stock",  KChartParams::??? },
00149     //{ "chart:add-in", KChartParams::??? },
00150     //{ "chart:scatter",KChartParams::??? },
00151     { "chart:ring",   KChartParams::Ring },
00152     { "chart:radar",  KChartParams::Polar }
00153     //{ "chart:xxx",    KChartParams::BoxWhisker },
00154 
00155     //{ "chart:xxx",    KChartParams::BarLines },
00156     // FIXME: More?
00157 };
00158 
00159 
00160 static const unsigned int numOasisChartTypes
00161   = sizeof oasisChartTypes / sizeof *oasisChartTypes;
00162 
00163 
00164 #if 0  // Example code!!
00165     KoOasisLoadingContext  loadingContext( this, oasisStyles, store );
00166     KoStyleStack          &styleStack = loadingContext.styleStack();
00167 
00168     styleStack.save();
00169     styleStack.setTypeProperties( "graphic" ); // load graphic-properties
00170     loadingContext.fillStyleStack( chartElem, KoXmlNS::chart, "style-name" );
00171 
00172     const QString fillColor = styleStack.attributeNS( KoXmlNS::draw, "fill-color" );
00173     kDebug() << "fillColor=" << fillColor << endl;
00174 
00175     styleStack.restore();
00176 #endif
00177 
00178 
00179 // Load the data from an OpenDocument chart:chart element.
00180 
00181 void KChartParams::loadOasisFont( KoOasisLoadingContext& context, QFont& font, QColor& color )
00182 {
00183     KoStyleStack& styleStack = context.styleStack();
00184     styleStack.setTypeProperties( "text" ); // load all style attributes from "style:text-properties"
00185 
00186     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "color" ) ) { // 3.10.3
00187         color.setNamedColor( styleStack.attributeNS( KoXmlNS::fo, "color" ) ); // #rrggbb format
00188     }
00189     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-family" )  // 3.10.9
00190             || styleStack.hasAttributeNS( KoXmlNS::style, "font-name" ) ) { // 3.10.8
00191         // Hmm, the remove "'" could break it's in the middle of the fontname...
00192         QString fontName = styleStack.attributeNS( KoXmlNS::fo, "font-family" ).remove( "'" );
00193         if ( fontName.isEmpty() ) {
00194             // ##### TODO. This is wrong. style:font-name refers to a font-decl entry.
00195             // We have to look it up there, and retrieve _all_ font attributes from it, not just the name.
00196             fontName = styleStack.attributeNS( KoXmlNS::style, "font-name" ).remove( "'" );
00197         }
00198         // 'Thorndale' is not known outside OpenOffice so we substitute it
00199         // with 'Times New Roman' that looks nearly the same.
00200         if ( fontName == "Thorndale" )
00201             fontName = "Times New Roman";
00202 
00203         fontName.remove( QRegExp( "\\sCE$" ) ); // Arial CE -> Arial
00204         font.setFamily( fontName );
00205     }
00206     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-size" ) ) { // 3.10.14
00207         double pointSize = styleStack.fontSize();
00208         font.setPointSizeFloat( pointSize );
00209         kdDebug(35001) << "font size: " << pointSize << endl;
00210     }
00211     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-weight" ) ) { // 3.10.24
00212         QString fontWeight = styleStack.attributeNS( KoXmlNS::fo, "font-weight" );
00213         int boldness;
00214         if ( fontWeight == "normal" )
00215             boldness = 50;
00216         else if ( fontWeight == "bold" )
00217             boldness = 75;
00218         else
00219             // XSL/CSS has 100,200,300...900. Not the same scale as Qt!
00220             // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight
00221             boldness = fontWeight.toInt() / 10;
00222         font.setWeight( boldness );
00223     }
00224     if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-style" ) ) { // 3.10.19
00225         if ( styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "italic" ||
00226              styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "oblique" ) { // no difference in kotext
00227             font.setItalic( true );
00228         }
00229     }
00230 }
00231     
00232 bool KChartParams::loadOasis( const QDomElement     &chartElem,
00233                   KoOasisLoadingContext &loadingContext,
00234                               QString               &errorMessage,
00235                   KoStore               */*store*/ )
00236 {
00237     const QString chartClass = chartElem.attributeNS( KoXmlNS::chart,
00238                               "class", QString::null );
00239     bool          knownType = false;
00240 
00241     // Find out what KChart charttype the OASIS charttype corresponds to.
00242     for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) {
00243         if ( chartClass == oasisChartTypes[i].oasisClass ) {
00244             kdDebug(35001) << "found chart of type " << chartClass << endl;
00245         //cerr << "found chart of type " << chartClass.latin1() << "\n";
00246 
00247             setChartType( oasisChartTypes[i].chartType );
00248             knownType = true;
00249             break;
00250         }
00251     }
00252 
00253     // If we can't find out what charttype it is, we might as well end here.
00254     if ( !knownType ) {
00255         errorMessage = i18n( "Unknown chart type %1" ).arg( chartClass );
00256         kdDebug(35001) << errorMessage << endl;
00257         return false;
00258     }
00259 
00260     // Title TODO (more details, e.g. font, placement etc)
00261     QDomElement titleElem = KoDom::namedItemNS( chartElem,
00262                          KoXmlNS::chart, "title" );
00263     if ( !titleElem.isNull() ) {
00264         loadingContext.styleStack().save();
00265         loadingContext.fillStyleStack( titleElem, KoXmlNS::chart, "style-name", "chart" );
00266         QFont font;
00267         QColor color;
00268         loadOasisFont( loadingContext, font, color );
00269         setHeaderFooterFont( KDChartParams::HdFtPosHeader, font, true, font.pointSize()*4 );
00270         setHeaderFooterColor( KDChartParams::HdFtPosHeader, color );
00271         loadingContext.styleStack().restore();
00272 
00273     QDomElement  pElem = KoDom::namedItemNS( titleElem,
00274                          KoXmlNS::text, "p" );
00275     setHeader1Text( pElem.text() );
00276     }
00277 
00278     // Subtitle TODO (more details)
00279     QDomElement subtitleElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
00280                            "subtitle" );
00281     if ( !subtitleElem.isNull() ) {
00282         loadingContext.styleStack().save();
00283         loadingContext.fillStyleStack( subtitleElem, KoXmlNS::chart, "style-name", "chart" );
00284         QFont font;
00285         QColor color;
00286         loadOasisFont( loadingContext, font, color );
00287         setHeaderFooterFont( KDChartParams::HdFtPosHeader2, font, true, font.pointSize()*4 );
00288         setHeaderFooterColor( KDChartParams::HdFtPosHeader2, color );
00289         loadingContext.styleStack().restore();
00290 
00291     QDomElement  pElem = KoDom::namedItemNS( subtitleElem,
00292                          KoXmlNS::text, "p" );
00293     setHeader2Text( pElem.text() );
00294     }
00295 
00296     // Footer TODO (more details)
00297     QDomElement footerElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
00298                          "footer" );
00299     if ( !footerElem.isNull() ) {
00300         loadingContext.styleStack().save();
00301         loadingContext.fillStyleStack( footerElem, KoXmlNS::chart, "style-name", "chart" );
00302         QFont font;
00303         QColor color;
00304         loadOasisFont( loadingContext, font, color );
00305         setHeaderFooterFont( KDChartParams::HdFtPosFooter, font, true, font.pointSize()*4 );
00306         setHeaderFooterColor( KDChartParams::HdFtPosFooter, color );
00307         loadingContext.styleStack().restore();
00308 
00309     QDomElement  pElem = KoDom::namedItemNS( footerElem,
00310                          KoXmlNS::text, "p" );
00311     setFooterText( pElem.text() );
00312     }
00313 
00314     // TODO: Get legend settings
00315     QDomElement legendElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
00316                          "legend" );
00317     if ( !legendElem.isNull() )
00318     {
00319         loadingContext.styleStack().save();
00320         loadingContext.fillStyleStack( legendElem, KoXmlNS::chart, "style-name", "chart" );
00321         QFont font;
00322         QColor color;
00323         loadOasisFont( loadingContext, font, color );
00324         //tz I didn't find that Oasis support seperate font/colors for the title and the rest of the legent
00325         setLegendFont(             font, false );
00326         setLegendFontRelSize(      font.pointSize()*4 );
00327         setLegendTitleFont(        font, false);
00328         setLegendTitleFontRelSize( font.pointSize()*4 );
00329         setLegendTextColor( color );
00330         setLegendTitleTextColor( color );
00331         loadingContext.styleStack().restore();
00332         QString lp;
00333         if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-position" ) )
00334         {
00335             lp = legendElem.attributeNS( KoXmlNS::chart, "legend-position", QString::null );
00336         }
00337         QString lalign;
00338         if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-align" ) )
00339         {
00340             lalign = legendElem.attributeNS( KoXmlNS::chart, "legend-align", QString::null );
00341         }
00342 
00343         LegendPosition lpos = NoLegend;
00344         int align = 1;
00345         if ( lalign == "start" )
00346         {
00347             align = 0;
00348         }
00349         else if ( lalign == "end" )
00350         {
00351             align = 2;
00352         }
00353 
00354         if ( lp == "start" )
00355         {
00356             lpos = LegendLeft;
00357             if ( align == 0 )
00358                 lpos = LegendTopLeftLeft;
00359             else if ( align == 2 )    
00360                 lpos = LegendBottomLeftLeft;
00361         }
00362         else if ( lp == "end" )
00363         {
00364             lpos = LegendRight;
00365             if ( align == 0 )
00366                 lpos = LegendTopRightRight;
00367             else if ( align == 2 )    
00368                 lpos = LegendBottomRightRight;
00369         }
00370         else if ( lp == "top" )
00371         {
00372             lpos = LegendTop;
00373             if ( align == 0 )
00374                 lpos = LegendTopLeftTop;
00375             else if ( align == 2 )    
00376                 lpos = LegendTopRightTop;
00377         }
00378         else if ( lp == "bottom" )
00379         {
00380             lpos = LegendBottom;
00381             if ( align == 0 )
00382                 lpos = LegendBottomLeftBottom;
00383             else if ( align == 2 )    
00384                 lpos = LegendBottomRightBottom;
00385         }
00386         else if ( lp == "top-start" )
00387         {
00388             lpos = LegendTopLeft;
00389         }
00390         else if ( lp == "bottom-start" )
00391         {
00392             lpos = LegendBottomLeft;
00393         }
00394         else if ( lp == "top-end" )
00395         {
00396             lpos = LegendTopRight;
00397         }
00398         else if ( lp == "bottom-end" )
00399         {
00400             lpos = LegendBottomRight;
00401         }
00402 
00403         setLegendPosition( lpos );
00404         //bodyWriter->addAttribute( "koffice:title", legendTitleText() );
00405         if ( legendElem.hasAttributeNS( KoXmlNS::koffice, "title" ) )
00406         {
00407             setLegendTitleText( legendElem.attributeNS( KoXmlNS::koffice, "title", QString::null ) );
00408         }
00409     }
00410     else
00411     {
00412         setLegendPosition( NoLegend );
00413     }
00414 
00415     // Get the plot-area.  This is where the action is.
00416     QDomElement  plotareaElem = KoDom::namedItemNS( chartElem,
00417                             KoXmlNS::chart, "plot-area" );
00418     if ( !plotareaElem.isNull() ) {
00419     return loadOasisPlotarea( plotareaElem, loadingContext, errorMessage );
00420     }
00421 
00422     return false;
00423 }
00424 
00425 
00426 bool KChartParams::loadOasisPlotarea( const QDomElement     &plotareaElem,
00427                       KoOasisLoadingContext &loadingContext,
00428                       QString               &errorMessage )
00429 {
00430     QString  tmp;
00431 
00432     //cerr << ">>> ==========================================================\n";
00433 
00434     // FIXME: attribute table:cell-range-address  - the cells in a spreadsheet
00435 
00436     // Get whether there is a label on the first row or column of the data.
00437     // This info is in the attribute chart:data-source-has-labels.
00438     //
00439     // NOTE: Only used in spreadsheets.
00440     tmp = plotareaElem.attributeNS( KoXmlNS::chart,
00441                     "data-source-has-labels", QString::null );
00442     m_firstRowAsLabel = false;
00443     m_firstColAsLabel = false;
00444     if ( tmp == "none" || tmp == "" )
00445     ; // NOTHING
00446     else if ( tmp == "row" )
00447     m_firstRowAsLabel = true;
00448     else if ( tmp == "column" )
00449     m_firstColAsLabel = true;
00450     else if ( tmp == "both" ) {
00451     m_firstRowAsLabel = true;
00452     m_firstColAsLabel = true;
00453     }
00454     else {
00455     errorMessage = "Unknown value for chart:data-source-has-labels:"
00456         + tmp;
00457     return false;
00458     }
00459 
00460     // ----------------------------------------------------------------
00461     // Now get the style and use it to get the contents.
00462     // This is hidden in the attribute chart:style-name.
00463     KoStyleStack          &styleStack = loadingContext.styleStack();
00464 
00465 
00466     tmp = plotareaElem.attributeNS( KoXmlNS::chart, "style-name",
00467                     QString::null );
00468     //kdDebug(35001) << "Style name for the plot area: " << tmp << endl;
00469     styleStack.save();
00470     styleStack.setTypeProperties( "chart" ); // load chart properties
00471     loadingContext.fillStyleStack( plotareaElem, KoXmlNS::chart, "style-name", "chart" );
00472 
00473     if ( styleStack.attributeNS( KoXmlNS::chart, "three-dimensional" ) == "true" ) {
00474     setThreeDBars( true );
00475     setThreeDLines( true );
00476     setThreeDPies( true );
00477     }
00478     else {
00479     setThreeDBars( false );
00480     setThreeDLines( false );
00481     setThreeDPies( false );
00482     }
00483 
00484     switch ( m_chartType ) {
00485     case NoType:
00486     break;
00487 
00488     case Bar:
00489     // Find out subtype
00490     tmp = styleStack.attributeNS( KoXmlNS::chart, "vertical" );
00491     // FIXME: Vertical is ignored. At least store it so we can
00492     //        save it again even if we don't support it.
00493 
00494     //kdDebug(35001) << "  ======>  Vertical: " << tmp << "  <======" << endl;
00495     // Set the bar chart subtype.
00496     if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
00497         setBarChartSubType( BarStacked );
00498     else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
00499         setBarChartSubType( BarPercent );
00500     else
00501         setBarChartSubType( BarNormal );
00502 
00503     break;
00504 
00505     // chart:vertical      - true if vertical bars (only bar charts)
00506     // chart:stacked       - true for stacked bars
00507     // chart:percentage    - true for percentage  (mut. excl with stacked)
00508     // chart:connect-bars  - true if lines to connect bars should be drawn
00509     //                           only used for stacked and percentage bars.
00510     // FIXME: Support lines on bar charts.
00511     // chart:lines-used    - 0-n, number of lines on a bar chart. (def: 0)
00512 
00513     case Line:
00514     // Set the line chart subtype.
00515     if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
00516         setLineChartSubType( LineStacked );
00517     else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
00518         setLineChartSubType( LinePercent );
00519     else
00520         setLineChartSubType( LineNormal );
00521 
00522     break;
00523 
00524     // FIXME: Why is this even there?  Seems like an unnecessary attr.
00525     // chart:lines       - true for line charts, false otherwise
00526 
00527     // chart:stacked     - true for stacked bars
00528     // chart:percentage  - true for percentage  (mut. excl with stacked)
00529 
00530     // chart:symbol-type - used with line charts, should be "automatic"
00531 
00532     case Area:
00533     // Set the area chart subtype.
00534     if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
00535         setAreaChartSubType( AreaStacked );
00536     else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
00537         setAreaChartSubType( AreaPercent );
00538     else
00539         setAreaChartSubType( AreaNormal );
00540 
00541     break;
00542 
00543     // chart:stacked       - true for stacked bars
00544     // chart:percentage    - true for percentage  (mut. excl with stacked)
00545 
00546     case Pie:
00547     break;
00548 
00549     case HiLo:
00550     break;
00551 
00552     case Ring:
00553     break;
00554 
00555     case Polar:
00556     break;
00557 
00558     case BoxWhisker:
00559     break;
00560     }
00561     // TODO:
00562     // And get the info from the style.  Here is the contents:
00563 
00564 
00565     // TODO: These items are currently not used.  They should be at least
00566     //       be stored so that we can save them again.
00567     // chart:interpolation     - "cubic-spline" if using cubic splines
00568     // chart:splines           -
00569     // chart:spline-order      - "2" for cubic splines
00570     // chart:spline-resolution - how smooth (default: 20)
00571 
00572     // -- Used when chart:class == "stock:
00573     // chart:stock-updown-bars      - boolean
00574     // chart:stock-with-volume      - boolean
00575     // chart:japanese-candle-sticks - boolean
00576 
00577     // chart:series-source     - "rows" or "columns"
00578     // "columns" is the default
00579     if ( styleStack.attributeNS( KoXmlNS::chart, "series-source" ) == "rows" ) {
00580       setDataDirection( DataRows );
00581     }
00582 
00583     // chart:data-label-number - "value" / "percentage" / "none" (def: none)
00584 
00585     // chart:data-label-text   - true if data hapoints have text labels
00586     // chart:data-label-symbol - true if data hapoints have legend symbol
00587     //                           (default: false for both)
00588 
00589     styleStack.restore();
00590 
00591     // ----------------------------------------------------------------
00592     // In the plot-area element there are two chart:axis elements
00593 
00594     QDomElement  xAxisElem;
00595     QDomElement  yAxisElem;
00596 
00597     QDomElement  axisElem;
00598     forEachElement( axisElem, plotareaElem ) {
00599 
00600     // If this element is not an axis, then continue
00601     if ( axisElem.tagName() != "axis" )
00602         continue;
00603 
00604     tmp = axisElem.attributeNS( KoXmlNS::chart, "name",
00605                     QString::null);
00606     //kdDebug(35001) << "Got axis " << tmp << endl;
00607     //cerr << "Got axis " << tmp.latin1() << "\n";
00608     if ( tmp == "primary-x" )
00609         xAxisElem = axisElem;
00610     else if ( tmp == "primary-y" )
00611         yAxisElem = axisElem;
00612     else
00613         // Only supports two axes so far.
00614         continue;
00615     }
00616 
00617     // Load the axes.  Pie charts use only the y axis.
00618     if ( m_chartType != Pie
00619      && !loadOasisAxis( xAxisElem, loadingContext, errorMessage,
00620              KDChartAxisParams::AxisPosBottom) )
00621     return false;
00622     if ( !loadOasisAxis( yAxisElem, loadingContext, errorMessage,
00623                 KDChartAxisParams::AxisPosLeft) )
00624     return false;
00625 
00626     // Attributes for the axes:
00627     // chart:name       - either "primary-x" or "primary-y"
00628 
00629 #if 0
00630     const QString fillColor = styleStack.attributeNS( KoXmlNS::draw,
00631                               "fill-color" );
00632     kdDebug() << "fillColor=" << fillColor << endl;
00633 #endif
00634 
00635     //cerr << "<<< ==========================================================\n";
00636 
00637     return true;
00638 }
00639 
00640 
00641 bool KChartParams::loadOasisAxis( const QDomElement      &axisElem,
00642                   KoOasisLoadingContext  &loadingContext,
00643                   QString                &errorMessage,
00644                   KDChartAxisParams::AxisPos  axisPos )
00645 {
00646     Q_UNUSED( errorMessage );
00647 
00648     QString        tmp;
00649     QDomElement    tmpElem;
00650     KoStyleStack  &styleStack = loadingContext.styleStack();
00651 
00652     // Get the axis to manipulate.
00653     // TODO
00654 
00655     // Get the axis title (== axis label) if any.
00656     QDomElement  titleElem = KoDom::namedItemNS( axisElem,
00657                          KoXmlNS::chart, "title" );
00658     if ( !titleElem.isNull() ) {
00659     tmpElem = KoDom::namedItemNS( titleElem, KoXmlNS::text, "p" );
00660     setAxisTitle( axisPos, tmpElem.text() );
00661     }
00662 
00663 
00664 
00665     //cerr << ">>> ----------------------------------------------------------\n";
00666     //cerr << "Loading axis " << axisElem.attributeNS( KoXmlNS::chart, "name",
00667     //                           QString::null).latin1()
00668     //   << "\n";
00669 
00670     tmp = axisElem.attributeNS( KoXmlNS::chart, "style-name", QString::null );
00671     //kdDebug(35001) << "Style name for the axis: " << tmp << endl;
00672     //cerr << "Style name for the axis: " << tmp.latin1() << "\n";
00673     styleStack.save();
00674     styleStack.setTypeProperties( "chart" ); // load chart properties
00675     loadingContext.fillStyleStack( axisElem, KoXmlNS::chart, "style-name", "chart" );
00676 
00677     // chart:class      - "category" / "value" / "domain" (domain for scatter)
00678     //    child: chart:categories
00679 
00680     // child:  chart:title   - Name of title if any.
00681     // child:  chart:grid
00682     //           attr: chart:class  - "major" / "minor"
00683 
00684     // chart:style-name - Associated style with the following info:
00685     // --------------------------------
00686     // chart:display-label          - true if an axis label should be shown.
00687 
00688     // chart:tick-marks-major-inner - true if display tickmarks at major
00689     // chart:tick-marks-major-outer   or minor intervals outside / inside
00690     // chart:tick-minor-major-inner   the chart area.
00691     // chart:tick-minor-major-outer
00692 
00693     // chart:logarithmic            - true if logarithmic scale
00694 
00695     // text:line-break              - true if categories can be broken
00696 
00697     // chart:text-overlap           - true if labels can overlap
00698 
00699     // chart:label-arrangement      - "side-by-side" / "stagger-even" /
00700     //                                "stagger-odd"  (def: side-by-side)
00701 
00702     // chart:visible                - true if labels + ticks should be shown.
00703 
00704     // children:
00705     // chart:
00706     // chart:
00707     // chart:
00708     // chart:
00709     // chart:
00710     styleStack.restore();
00711 
00712     //cerr << "<<< ----------------------------------------------------------\n";
00713     return true;
00714 }
00715 
00716 
00717 // ----------------------------------------------------------------
00718 
00719 
00720 void KChartParams::saveOasis( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const
00721 {
00722     bool knownType = false;
00723     for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) {
00724         if ( m_chartType == oasisChartTypes[i].chartType ) {
00725             bodyWriter->addAttribute( "chart:class", oasisChartTypes[i].oasisClass );
00726             knownType = true;
00727             break;
00728         }
00729     }
00730 
00731     if ( !knownType ) {
00732         kdError(32001) << "Unknown chart type in KChartParams::saveOasis, extend oasisChartTypes!" << endl;
00733     }
00734 
00735     bodyWriter->startElement( "chart:title" );
00736     QRect rect( headerFooterRect( KDChartParams::HdFtPosHeader ) );
00737     bodyWriter->addAttributePt( "svg:x", rect.x() );
00738     bodyWriter->addAttributePt( "svg:y", rect.y() );
00739     bodyWriter->addAttribute( "chart:style-name", saveOasisFont( mainStyles, header1Font(), headerFooterColor( KDChartParams::HdFtPosHeader ) ) );
00740     bodyWriter->startElement( "text:p" );
00741     bodyWriter->addTextNode( header1Text() );
00742     bodyWriter->endElement(); // text:p
00743     bodyWriter->endElement(); // chart:title
00744 
00745     QString subTitle( header2Text() );
00746     if ( !subTitle.isEmpty() ) {
00747 
00748         kdDebug(32001) << "header rect: " << headerFooterRect( KDChartParams::HdFtPosHeader2 ) << endl;
00749         QRect rect( headerFooterRect( KDChartParams::HdFtPosHeader2 ) );
00750         bodyWriter->startElement( "chart:subtitle" );
00751         bodyWriter->addAttributePt( "svg:x", rect.x() );
00752         bodyWriter->addAttributePt( "svg:y", rect.y() );
00753         bodyWriter->addAttribute( "chart:style-name", 
00754                   saveOasisFont( mainStyles, 
00755                          header2Font(), 
00756                          headerFooterColor( KDChartParams::HdFtPosHeader2 ) ) );
00757 
00758         bodyWriter->startElement( "text:p" );
00759         bodyWriter->addTextNode( subTitle );
00760         bodyWriter->endElement(); // text:p
00761         bodyWriter->endElement(); // chart:subtitle
00762     }
00763 
00764 
00765     QString footer( footerText() );
00766     if ( !footer.isEmpty() ) {
00767         QRect rect( headerFooterRect( KDChartParams::HdFtPosFooter ) );
00768         bodyWriter->startElement( "chart:footer" );
00769         bodyWriter->addAttributePt( "svg:x", rect.x() );
00770         bodyWriter->addAttributePt( "svg:y", rect.y() );
00771         bodyWriter->addAttribute( "chart:style-name",
00772                   saveOasisFont( mainStyles, 
00773                          footerFont(), 
00774                          headerFooterColor( KDChartParams::HdFtPosFooter ) ) );
00775 
00776         bodyWriter->startElement( "text:p" );
00777         bodyWriter->addTextNode( footer );
00778         bodyWriter->endElement(); // text:p
00779         bodyWriter->endElement(); // chart:footer
00780     }
00781 
00782     // TODO legend
00783     LegendPosition lpos = legendPosition();
00784     if ( lpos != NoLegend ) {
00785         bodyWriter->startElement( "chart:legend" );
00786         QString lp;
00787         QString lalign;
00788         switch ( lpos ) {
00789     case LegendTop: 
00790         lp = "top";
00791         lalign = "center";
00792         break;
00793     case LegendBottom:
00794         lp = "bottom";
00795         lalign = "center";
00796         break;
00797     case LegendLeft: 
00798         lp = "start";
00799         lalign = "center";
00800         break;
00801     case LegendRight:
00802         lp = "end";
00803         lalign = "center";
00804         break;
00805     case LegendTopLeft:
00806         lp = "top-start";
00807         break;
00808     case LegendTopLeftTop:
00809         lp = "top";
00810         lalign = "start";
00811         break;
00812     case LegendTopLeftLeft:
00813         lp = "start";
00814         lalign = "start";
00815         break;
00816     case LegendTopRight:
00817         lp = "top-end";
00818         break;
00819     case LegendTopRightTop:
00820         lp = "top";
00821         lalign = "end";
00822         break;
00823     case LegendTopRightRight:
00824         lp = "end";
00825         lalign = "start";
00826         break;
00827     case LegendBottomLeft:
00828         lp = "bottom-start";
00829         break;
00830     case LegendBottomLeftBottom:
00831         lp = "bottom";
00832         lalign = "start";
00833         break;
00834     case LegendBottomLeftLeft:
00835         lp = "start";
00836         lalign = "end";
00837         break;
00838     case LegendBottomRight:
00839         lp = "bottom-end";
00840         break;
00841     case LegendBottomRightBottom:
00842         lp = "bottom";
00843         lalign = "end";
00844         break;
00845     case LegendBottomRightRight:
00846         lp = "end";
00847         lalign = "end";
00848         break;
00849     default:
00850         lp = "end";
00851         lalign = "center";
00852         break;
00853         }
00854 
00855         bodyWriter->addAttribute( "chart:legend-position", lp );
00856         bodyWriter->addAttribute( "chart:legend-align", lalign );
00857         bodyWriter->addAttribute( "chart:style-name",
00858                   saveOasisFont( mainStyles, 
00859                          legendFont(), 
00860                          legendTextColor() ) );
00861         bodyWriter->addAttribute( "koffice:title", legendTitleText() );
00862         bodyWriter->endElement(); // chart:legend
00863     }
00864 
00865     bodyWriter->startElement( "chart:plot-area" );
00866     saveOasisPlotArea( bodyWriter, mainStyles );
00867     bodyWriter->endElement();
00868 
00869     // TODO...
00870 }
00871 
00872 
00873 void KChartParams::saveOasisPlotArea( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const
00874 {
00875     QString dataSourceHasLabels;
00876     if ( m_firstRowAsLabel )
00877         if ( m_firstColAsLabel )
00878             dataSourceHasLabels = "both";
00879         else
00880             dataSourceHasLabels = "row";
00881     else
00882         if ( m_firstColAsLabel )
00883             dataSourceHasLabels = "column";
00884         else
00885             dataSourceHasLabels = "none";
00886     bodyWriter->addAttribute( "chart:data-source-has-labels", dataSourceHasLabels );
00887 
00888     // Prepare the style for the plot area
00889     KoGenStyle plotAreaStyle( KoGenStyle::STYLE_AUTO, "chart" );
00890 
00891     switch ( m_chartType ) {
00892     case NoType:
00893     break;
00894 
00895     case Bar:
00896         switch( barChartSubType() ) {
00897         case BarStacked:
00898             plotAreaStyle.addProperty( "chart:stacked", "true" );
00899             break;
00900         case BarPercent:
00901             plotAreaStyle.addProperty( "chart:percentage", "true" );
00902             break;
00903         case BarNormal:
00904             break;
00905     case BarMultiRows:
00906         break;
00907         }
00908         plotAreaStyle.addProperty( "chart:vertical", "false" ); // #### always?
00909         plotAreaStyle.addProperty( "chart:lines-used", 0 ); // FIXME: for now
00910 
00911     if ( threeDBars() )
00912         plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
00913 
00914     case Line:
00915         switch( lineChartSubType() ) {
00916         case LineStacked:
00917             plotAreaStyle.addProperty( "chart:stacked", "true" );
00918             break;
00919         case LinePercent:
00920             plotAreaStyle.addProperty( "chart:percentage", "true" );
00921             break;
00922         case LineNormal:
00923             break;
00924         }
00925         plotAreaStyle.addProperty( "chart:symbol-type", "automatic" );
00926 
00927     if ( threeDLines() )
00928         plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
00929 
00930         break;
00931 
00932     case Area:
00933         switch( areaChartSubType() ) {
00934         case AreaStacked:
00935             plotAreaStyle.addProperty( "chart:stacked", "true" );
00936             break;
00937         case AreaPercent:
00938             plotAreaStyle.addProperty( "chart:percentage", "true" );
00939             break;
00940         case AreaNormal:
00941             break;
00942         }
00943         //plotAreaStyle.addProperty( "chart:lines-used", 0 ); // #### for now
00944 
00945         // TODO - very similar
00946 
00947     case Pie:
00948     if ( threeDPies() )
00949         plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
00950 
00951     break;
00952 
00953     case HiLo:
00954     break;
00955 
00956     case Ring:
00957     break;
00958 
00959     case Polar:
00960     break;
00961 
00962     case BoxWhisker:
00963     break;
00964     }
00965 
00966     // chart:series-source
00967     plotAreaStyle.addProperty( "chart:series-source",
00968                    ( dataDirection() == DataRows ) ? "rows" : "columns" );
00969 
00970     // Register the style, and get back its auto-generated name
00971     const QString styleName = mainStyles.lookup( plotAreaStyle, "ch" );
00972 
00973     bodyWriter->addAttribute( "chart:style-name", styleName );
00974 
00975     saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosBottom, "x" );
00976     saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosLeft, "y" );
00977 
00978     // TODO chart:series
00979     // TODO chart:wall
00980     // TODO chart:floor
00981 }
00982 
00983 
00984 void KChartParams::saveOasisAxis( KoXmlWriter* bodyWriter, 
00985                   KoGenStyles& mainStyles,
00986                                   KDChartAxisParams::AxisPos axisPos, 
00987                   const char* axisName ) const
00988 {
00989     bodyWriter->startElement( "chart:axis" );
00990 
00991     bodyWriter->addAttribute( "chart:dimension", axisName );
00992     bodyWriter->addAttribute( "chart:name", QCString( "primary-" ) + axisName );
00993 
00994     KoGenStyle axisStyle( KoGenStyle::STYLE_AUTO, "chart" );
00995 
00996     // TODO: Save axis style properties, like
00997     axisStyle.addProperty( "chart:display-label", "true" ); // ###
00998 
00999 
01000     const QString styleName = mainStyles.lookup( axisStyle, "ch" );
01001     bodyWriter->addAttribute( "chart:style-name", styleName );
01002 
01003     // Write axis titles if any.
01004     QString tmpStr = axisTitle( axisPos );
01005     if ( tmpStr != "" ) {
01006     bodyWriter->startElement( "chart:title" );
01007     // TODO: Save style, svg:x, svg:y
01008 
01009     // Write the text in the axis title.
01010     bodyWriter->startElement( "text:p" );
01011     bodyWriter->addTextNode( tmpStr
01012                  .remove( QRegExp( "^<qt><center>" ) )
01013                  .remove( QRegExp( "</center></qt>$" ) ) );
01014     bodyWriter->endElement(); // text:p
01015 
01016     bodyWriter->endElement(); // chart:title
01017     }
01018 
01019     // TODO x axis has chart:categories, y axis has chart:grid ?
01020     // Maybe that part should be done by the caller of saveOasisAxis then
01021     // including the opening/closing of the chart:axis element...
01022 
01023     bodyWriter->endElement(); // chart:axis
01024 }
01025 
01026 
01027 QString KChartParams::saveOasisFont( KoGenStyles& mainStyles, const QFont& font, const QColor& color ) const
01028 {
01029     KoGenStyle::PropertyType tt = KoGenStyle::TextType;
01030     KoGenStyle autoStyle( KoGenStyle::STYLE_AUTO, "chart", 0 );
01031     autoStyle.addProperty( "fo:font-family", font.family(), tt );
01032     autoStyle.addPropertyPt( "fo:font-size", font.pointSize(), tt );
01033     autoStyle.addProperty( "fo:color", color.isValid() ? color.name() : "#000000", tt );
01034     int w = font.weight();
01035     autoStyle.addProperty( "fo:font-weight", w == 50 ? "normal" : w == 75 ? "bold" : QString::number( qRound(  w / 10 ) * 100 ), tt );
01036     autoStyle.addProperty( "fo:font-style", font.italic() ? "italic" : "normal", tt );
01037 
01038     return mainStyles.lookup( autoStyle, "ch", KoGenStyles::ForceNumbering );
01039 }
01040 
01041 
01042 }  //KChart namespace
01043 
KDE Home | KDE Accessibility Home | Description of Access Keys