kchart

kchart_params.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001,2002,2003,2004 Laurent Montel <montel@kde.org>
3 Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21
22//#include <stdlib.h>
23#include <iostream>
24using std::cout;
25using std::cerr;
26
27
28#include <dcopobject.h>
29#include <tdelocale.h>
30#include <kdebug.h>
31
32#include <KoXmlNS.h>
33#include <KoXmlWriter.h>
34#include <KoDom.h>
35#include <KoOasisLoadingContext.h>
36
37#include <tqregexp.h>
38
41
42#include "kchart_params.h"
43#include "kchart_part.h"
44#include "KChartParamsIface.h"
45
46
47namespace KChart
48{
49
50
51KChartParams::KChartParams( KChartPart *_part )
52 : KDChartParams(),
53 m_part( _part )
54{
55 // Default values for subtypes.
56 m_firstRowAsLabel = false;
57 m_firstColAsLabel = false;
58
59 // Default values for OpenDocument extensions.
60 m_barNumLines = 0;
61
62 m_dcop = 0;
63 //dcopObject(); // build it
64}
65
66
67KChartParams::~KChartParams()
68{
69 delete m_dcop;
70}
71
72
73// ----------------------------------------------------------------
74
75
76TQString KChartParams::chartTypeToString( ChartType _type) const
77{
78#if 0
79 if (_type == BarLines )
80 return "BarLines";
81 else
82#endif
84}
85
86
87KChartParams::ChartType
88KChartParams::stringToChartType( const TQString& _string )
89{
90#if 0
91 if ( _string == "BarLines" )
92 return BarLines;
93 else
94#endif
95 return (ChartType) KDChartParams::stringToChartType( _string );
96}
97
98
99void KChartParams::setFirstRowAsLabel( bool _val )
100{
101 m_firstRowAsLabel = _val;
102
103 // The rest of this method is only applicable if the data is from
104 // an external source, e.g. from kspread.
105 if ( m_part->canChangeValue() )
106 return;
107
108 m_part->doSetData( *m_part->data(),
109 m_firstRowAsLabel, m_firstColAsLabel );
110}
111
112void KChartParams::setFirstColAsLabel( bool _val )
113{
114 m_firstColAsLabel = _val;
115
116 // The rest of this method is only applicable if the data is from
117 // an external source, e.g. kspread.
118 if ( m_part->canChangeValue() )
119 return;
120
121 m_part->doSetData( *m_part->data(),
122 m_firstRowAsLabel, m_firstColAsLabel );
123}
124
125
126DCOPObject* KChartParams::dcopObject()
127{
128 if ( !m_dcop )
129 m_dcop = new KChartParamsIface( this );
130 return m_dcop;
131}
132
133
134// ================================================================
135// Loading and Saving
136
137
138
139static const struct {
140 const char *oasisClass;
141 KChartParams::ChartType chartType;
142} oasisChartTypes[] = {
143 { "chart:bar", KChartParams::Bar },
144 { "chart:line", KChartParams::Line },
145 { "chart:area", KChartParams::Area },
146 { "chart:circle", KChartParams::Pie },
147 //{ "chart:xxx", KChartParams::HiLo },
148 //{ "chart:stock", KChartParams::??? },
149 //{ "chart:add-in", KChartParams::??? },
150 //{ "chart:scatter",KChartParams::??? },
151 { "chart:ring", KChartParams::Ring },
152 { "chart:radar", KChartParams::Polar }
153 //{ "chart:xxx", KChartParams::BoxWhisker },
154
155 //{ "chart:xxx", KChartParams::BarLines },
156 // FIXME: More?
157};
158
159
160static const unsigned int numOasisChartTypes
161 = sizeof oasisChartTypes / sizeof *oasisChartTypes;
162
163
164#if 0 // Example code!!
165 KoOasisLoadingContext loadingContext( this, oasisStyles, store );
166 KoStyleStack &styleStack = loadingContext.styleStack();
167
168 styleStack.save();
169 styleStack.setTypeProperties( "graphic" ); // load graphic-properties
170 loadingContext.fillStyleStack( chartElem, KoXmlNS::chart, "style-name" );
171
172 const TQString fillColor = styleStack.attributeNS( KoXmlNS::draw, "fill-color" );
173 kDebug() << "fillColor=" << fillColor << endl;
174
175 styleStack.restore();
176#endif
177
178
179// Load the data from an OpenDocument chart:chart element.
180
181void KChartParams::loadOasisFont( KoOasisLoadingContext& context, TQFont& font, TQColor& color )
182{
183 KoStyleStack& styleStack = context.styleStack();
184 styleStack.setTypeProperties( "text" ); // load all style attributes from "style:text-properties"
185
186 if ( styleStack.hasAttributeNS( KoXmlNS::fo, "color" ) ) { // 3.10.3
187 color.setNamedColor( styleStack.attributeNS( KoXmlNS::fo, "color" ) ); // #rrggbb format
188 }
189 if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-family" ) // 3.10.9
190 || styleStack.hasAttributeNS( KoXmlNS::style, "font-name" ) ) { // 3.10.8
191 // Hmm, the remove "'" could break it's in the middle of the fontname...
192 TQString fontName = styleStack.attributeNS( KoXmlNS::fo, "font-family" ).remove( "'" );
193 if ( fontName.isEmpty() ) {
194 // ##### TODO. This is wrong. style:font-name refers to a font-decl entry.
195 // We have to look it up there, and retrieve _all_ font attributes from it, not just the name.
196 fontName = styleStack.attributeNS( KoXmlNS::style, "font-name" ).remove( "'" );
197 }
198 // 'Thorndale' is not known outside OpenOffice so we substitute it
199 // with 'Times New Roman' that looks nearly the same.
200 if ( fontName == "Thorndale" )
201 fontName = "Times New Roman";
202
203 fontName.remove( TQRegExp( "\\sCE$" ) ); // Arial CE -> Arial
204 font.setFamily( fontName );
205 }
206 if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-size" ) ) { // 3.10.14
207 double pointSize = styleStack.fontSize();
208 font.setPointSizeFloat( pointSize );
209 kdDebug(35001) << "font size: " << pointSize << endl;
210 }
211 if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-weight" ) ) { // 3.10.24
212 TQString fontWeight = styleStack.attributeNS( KoXmlNS::fo, "font-weight" );
213 int boldness;
214 if ( fontWeight == "normal" )
215 boldness = 50;
216 else if ( fontWeight == "bold" )
217 boldness = 75;
218 else
219 // XSL/CSS has 100,200,300...900. Not the same scale as TQt!
220 // See http://www.w3.org/TR/2001/REC-xsl-20011015/slice7.html#font-weight
221 boldness = fontWeight.toInt() / 10;
222 font.setWeight( boldness );
223 }
224 if ( styleStack.hasAttributeNS( KoXmlNS::fo, "font-style" ) ) { // 3.10.19
225 if ( styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "italic" ||
226 styleStack.attributeNS( KoXmlNS::fo, "font-style" ) == "oblique" ) { // no difference in kotext
227 font.setItalic( true );
228 }
229 }
230}
231
232bool KChartParams::loadOasis( const TQDomElement &chartElem,
233 KoOasisLoadingContext &loadingContext,
234 TQString &errorMessage,
235 KoStore */*store*/ )
236{
237 const TQString chartClass = chartElem.attributeNS( KoXmlNS::chart,
238 "class", TQString() );
239 bool knownType = false;
240
241 // Find out what KChart charttype the OASIS charttype corresponds to.
242 for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) {
243 if ( chartClass == oasisChartTypes[i].oasisClass ) {
244 kdDebug(35001) << "found chart of type " << chartClass << endl;
245 //cerr << "found chart of type " << chartClass.latin1() << "\n";
246
247 setChartType( oasisChartTypes[i].chartType );
248 knownType = true;
249 break;
250 }
251 }
252
253 // If we can't find out what charttype it is, we might as well end here.
254 if ( !knownType ) {
255 errorMessage = i18n( "Unknown chart type %1" ).arg( chartClass );
256 kdDebug(35001) << errorMessage << endl;
257 return false;
258 }
259
260 // Title TODO (more details, e.g. font, placement etc)
261 TQDomElement titleElem = KoDom::namedItemNS( chartElem,
262 KoXmlNS::chart, "title" );
263 if ( !titleElem.isNull() ) {
264 loadingContext.styleStack().save();
265 loadingContext.fillStyleStack( titleElem, KoXmlNS::chart, "style-name", "chart" );
266 TQFont font;
267 TQColor color;
268 loadOasisFont( loadingContext, font, color );
269 setHeaderFooterFont( KDChartParams::HdFtPosHeader, font, true, font.pointSize()*4 );
270 setHeaderFooterColor( KDChartParams::HdFtPosHeader, color );
271 loadingContext.styleStack().restore();
272
273 TQDomElement pElem = KoDom::namedItemNS( titleElem,
274 KoXmlNS::text, "p" );
275 setHeader1Text( pElem.text() );
276 }
277
278 // Subtitle TODO (more details)
279 TQDomElement subtitleElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
280 "subtitle" );
281 if ( !subtitleElem.isNull() ) {
282 loadingContext.styleStack().save();
283 loadingContext.fillStyleStack( subtitleElem, KoXmlNS::chart, "style-name", "chart" );
284 TQFont font;
285 TQColor color;
286 loadOasisFont( loadingContext, font, color );
287 setHeaderFooterFont( KDChartParams::HdFtPosHeader2, font, true, font.pointSize()*4 );
288 setHeaderFooterColor( KDChartParams::HdFtPosHeader2, color );
289 loadingContext.styleStack().restore();
290
291 TQDomElement pElem = KoDom::namedItemNS( subtitleElem,
292 KoXmlNS::text, "p" );
293 setHeader2Text( pElem.text() );
294 }
295
296 // Footer TODO (more details)
297 TQDomElement footerElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
298 "footer" );
299 if ( !footerElem.isNull() ) {
300 loadingContext.styleStack().save();
301 loadingContext.fillStyleStack( footerElem, KoXmlNS::chart, "style-name", "chart" );
302 TQFont font;
303 TQColor color;
304 loadOasisFont( loadingContext, font, color );
305 setHeaderFooterFont( KDChartParams::HdFtPosFooter, font, true, font.pointSize()*4 );
306 setHeaderFooterColor( KDChartParams::HdFtPosFooter, color );
307 loadingContext.styleStack().restore();
308
309 TQDomElement pElem = KoDom::namedItemNS( footerElem,
310 KoXmlNS::text, "p" );
311 setFooterText( pElem.text() );
312 }
313
314 // TODO: Get legend settings
315 TQDomElement legendElem = KoDom::namedItemNS( chartElem, KoXmlNS::chart,
316 "legend" );
317 if ( !legendElem.isNull() )
318 {
319 loadingContext.styleStack().save();
320 loadingContext.fillStyleStack( legendElem, KoXmlNS::chart, "style-name", "chart" );
321 TQFont font;
322 TQColor color;
323 loadOasisFont( loadingContext, font, color );
324 //tz I didn't find that Oasis support seperate font/colors for the title and the rest of the legent
325 setLegendFont( font, false );
326 setLegendFontRelSize( font.pointSize()*4 );
327 setLegendTitleFont( font, false);
328 setLegendTitleFontRelSize( font.pointSize()*4 );
329 setLegendTextColor( color );
330 setLegendTitleTextColor( color );
331 loadingContext.styleStack().restore();
332 TQString lp;
333 if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-position" ) )
334 {
335 lp = legendElem.attributeNS( KoXmlNS::chart, "legend-position", TQString() );
336 }
337 TQString lalign;
338 if ( legendElem.hasAttributeNS( KoXmlNS::chart, "legend-align" ) )
339 {
340 lalign = legendElem.attributeNS( KoXmlNS::chart, "legend-align", TQString() );
341 }
342
343 LegendPosition lpos = NoLegend;
344 int align = 1;
345 if ( lalign == "start" )
346 {
347 align = 0;
348 }
349 else if ( lalign == "end" )
350 {
351 align = 2;
352 }
353
354 if ( lp == "start" )
355 {
356 lpos = LegendLeft;
357 if ( align == 0 )
358 lpos = LegendTopLeftLeft;
359 else if ( align == 2 )
360 lpos = LegendBottomLeftLeft;
361 }
362 else if ( lp == "end" )
363 {
364 lpos = LegendRight;
365 if ( align == 0 )
366 lpos = LegendTopRightRight;
367 else if ( align == 2 )
368 lpos = LegendBottomRightRight;
369 }
370 else if ( lp == "top" )
371 {
372 lpos = LegendTop;
373 if ( align == 0 )
374 lpos = LegendTopLeftTop;
375 else if ( align == 2 )
376 lpos = LegendTopRightTop;
377 }
378 else if ( lp == "bottom" )
379 {
380 lpos = LegendBottom;
381 if ( align == 0 )
382 lpos = LegendBottomLeftBottom;
383 else if ( align == 2 )
384 lpos = LegendBottomRightBottom;
385 }
386 else if ( lp == "top-start" )
387 {
388 lpos = LegendTopLeft;
389 }
390 else if ( lp == "bottom-start" )
391 {
392 lpos = LegendBottomLeft;
393 }
394 else if ( lp == "top-end" )
395 {
396 lpos = LegendTopRight;
397 }
398 else if ( lp == "bottom-end" )
399 {
400 lpos = LegendBottomRight;
401 }
402
403 setLegendPosition( lpos );
404 //bodyWriter->addAttribute( "koffice:title", legendTitleText() );
405 if ( legendElem.hasAttributeNS( KoXmlNS::koffice, "title" ) )
406 {
407 setLegendTitleText( legendElem.attributeNS( KoXmlNS::koffice, "title", TQString() ) );
408 }
409 }
410 else
411 {
412 setLegendPosition( NoLegend );
413 }
414
415 // Get the plot-area. This is where the action is.
416 TQDomElement plotareaElem = KoDom::namedItemNS( chartElem,
417 KoXmlNS::chart, "plot-area" );
418 if ( !plotareaElem.isNull() ) {
419 return loadOasisPlotarea( plotareaElem, loadingContext, errorMessage );
420 }
421
422 return false;
423}
424
425
426bool KChartParams::loadOasisPlotarea( const TQDomElement &plotareaElem,
427 KoOasisLoadingContext &loadingContext,
428 TQString &errorMessage )
429{
430 TQString tmp;
431
432 //cerr << ">>> ==========================================================\n";
433
434 // FIXME: attribute table:cell-range-address - the cells in a spreadsheet
435
436 // Get whether there is a label on the first row or column of the data.
437 // This info is in the attribute chart:data-source-has-labels.
438 //
439 // NOTE: Only used in spreadsheets.
440 tmp = plotareaElem.attributeNS( KoXmlNS::chart,
441 "data-source-has-labels", TQString() );
442 m_firstRowAsLabel = false;
443 m_firstColAsLabel = false;
444 if ( tmp == "none" || tmp == "" )
445 ; // NOTHING
446 else if ( tmp == "row" )
447 m_firstRowAsLabel = true;
448 else if ( tmp == "column" )
449 m_firstColAsLabel = true;
450 else if ( tmp == "both" ) {
451 m_firstRowAsLabel = true;
452 m_firstColAsLabel = true;
453 }
454 else {
455 errorMessage = "Unknown value for chart:data-source-has-labels:"
456 + tmp;
457 return false;
458 }
459
460 // ----------------------------------------------------------------
461 // Now get the style and use it to get the contents.
462 // This is hidden in the attribute chart:style-name.
463 KoStyleStack &styleStack = loadingContext.styleStack();
464
465
466 tmp = plotareaElem.attributeNS( KoXmlNS::chart, "style-name",
467 TQString() );
468 //kdDebug(35001) << "Style name for the plot area: " << tmp << endl;
469 styleStack.save();
470 styleStack.setTypeProperties( "chart" ); // load chart properties
471 loadingContext.fillStyleStack( plotareaElem, KoXmlNS::chart, "style-name", "chart" );
472
473 if ( styleStack.attributeNS( KoXmlNS::chart, "three-dimensional" ) == "true" ) {
474 setThreeDBars( true );
475 setThreeDLines( true );
476 setThreeDPies( true );
477 }
478 else {
479 setThreeDBars( false );
480 setThreeDLines( false );
481 setThreeDPies( false );
482 }
483
484 switch ( m_chartType ) {
485 case NoType:
486 break;
487
488 case Bar:
489 // Find out subtype
490 tmp = styleStack.attributeNS( KoXmlNS::chart, "vertical" );
491 // FIXME: vertical is ignored. At least store it so we can
492 // save it again even if we don't support it.
493
494 //kdDebug(35001) << " ======> vertical: " << tmp << " <======" << endl;
495 // Set the bar chart subtype.
496 if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
497 setBarChartSubType( BarStacked );
498 else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
499 setBarChartSubType( BarPercent );
500 else
501 setBarChartSubType( BarNormal );
502
503 break;
504
505 // chart:vertical - true if vertical bars (only bar charts)
506 // chart:stacked - true for stacked bars
507 // chart:percentage - true for percentage (mut. excl with stacked)
508 // chart:connect-bars - true if lines to connect bars should be drawn
509 // only used for stacked and percentage bars.
510 // FIXME: Support lines on bar charts.
511 // chart:lines-used - 0-n, number of lines on a bar chart. (def: 0)
512
513 case Line:
514 // Set the line chart subtype.
515 if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
516 setLineChartSubType( LineStacked );
517 else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
518 setLineChartSubType( LinePercent );
519 else
520 setLineChartSubType( LineNormal );
521
522 break;
523
524 // FIXME: Why is this even there? Seems like an unnecessary attr.
525 // chart:lines - true for line charts, false otherwise
526
527 // chart:stacked - true for stacked bars
528 // chart:percentage - true for percentage (mut. excl with stacked)
529
530 // chart:symbol-type - used with line charts, should be "automatic"
531
532 case Area:
533 // Set the area chart subtype.
534 if ( styleStack.attributeNS( KoXmlNS::chart, "stacked" ) == "true" )
535 setAreaChartSubType( AreaStacked );
536 else if ( styleStack.attributeNS( KoXmlNS::chart, "percentage" ) == "true" )
537 setAreaChartSubType( AreaPercent );
538 else
539 setAreaChartSubType( AreaNormal );
540
541 break;
542
543 // chart:stacked - true for stacked bars
544 // chart:percentage - true for percentage (mut. excl with stacked)
545
546 case Pie:
547 break;
548
549 case HiLo:
550 break;
551
552 case Ring:
553 break;
554
555 case Polar:
556 break;
557
558 case BoxWhisker:
559 break;
560 }
561 // TODO:
562 // And get the info from the style. Here is the contents:
563
564
565 // TODO: These items are currently not used. They should be at least
566 // be stored so that we can save them again.
567 // chart:interpolation - "cubic-spline" if using cubic splines
568 // chart:splines -
569 // chart:spline-order - "2" for cubic splines
570 // chart:spline-resolution - how smooth (default: 20)
571
572 // -- Used when chart:class == "stock:
573 // chart:stock-updown-bars - boolean
574 // chart:stock-with-volume - boolean
575 // chart:japanese-candle-sticks - boolean
576
577 // chart:series-source - "rows" or "columns"
578 // "columns" is the default
579 if ( styleStack.attributeNS( KoXmlNS::chart, "series-source" ) == "rows" ) {
580 setDataDirection( DataRows );
581 }
582
583 // chart:data-label-number - "value" / "percentage" / "none" (def: none)
584
585 // chart:data-label-text - true if data hapoints have text labels
586 // chart:data-label-symbol - true if data hapoints have legend symbol
587 // (default: false for both)
588
589 styleStack.restore();
590
591 // ----------------------------------------------------------------
592 // In the plot-area element there are two chart:axis elements
593
594 TQDomElement xAxisElem;
595 TQDomElement yAxisElem;
596
597 TQDomElement axisElem;
598 forEachElement( axisElem, plotareaElem ) {
599
600 // If this element is not an axis, then continue
601 if ( axisElem.tagName() != "axis" )
602 continue;
603
604 tmp = axisElem.attributeNS( KoXmlNS::chart, "name",
605 TQString());
606 //kdDebug(35001) << "Got axis " << tmp << endl;
607 //cerr << "Got axis " << tmp.latin1() << "\n";
608 if ( tmp == "primary-x" )
609 xAxisElem = axisElem;
610 else if ( tmp == "primary-y" )
611 yAxisElem = axisElem;
612 else
613 // Only supports two axes so far.
614 continue;
615 }
616
617 // Load the axes. Pie charts use only the y axis.
618 if ( m_chartType != Pie
619 && !loadOasisAxis( xAxisElem, loadingContext, errorMessage,
620 KDChartAxisParams::AxisPosBottom) )
621 return false;
622 if ( !loadOasisAxis( yAxisElem, loadingContext, errorMessage,
623 KDChartAxisParams::AxisPosLeft) )
624 return false;
625
626 // Attributes for the axes:
627 // chart:name - either "primary-x" or "primary-y"
628
629#if 0
630 const TQString fillColor = styleStack.attributeNS( KoXmlNS::draw,
631 "fill-color" );
632 kdDebug() << "fillColor=" << fillColor << endl;
633#endif
634
635 //cerr << "<<< ==========================================================\n";
636
637 return true;
638}
639
640
641bool KChartParams::loadOasisAxis( const TQDomElement &axisElem,
642 KoOasisLoadingContext &loadingContext,
643 TQString &errorMessage,
644 KDChartAxisParams::AxisPos axisPos )
645{
646 Q_UNUSED( errorMessage );
647
648 TQString tmp;
649 TQDomElement tmpElem;
650 KoStyleStack &styleStack = loadingContext.styleStack();
651
652 // Get the axis to manipulate.
653 // TODO
654
655 // Get the axis title (== axis label) if any.
656 TQDomElement titleElem = KoDom::namedItemNS( axisElem,
657 KoXmlNS::chart, "title" );
658 if ( !titleElem.isNull() ) {
659 tmpElem = KoDom::namedItemNS( titleElem, KoXmlNS::text, "p" );
660 setAxisTitle( axisPos, tmpElem.text() );
661 }
662
663
664
665 //cerr << ">>> ----------------------------------------------------------\n";
666 //cerr << "Loading axis " << axisElem.attributeNS( KoXmlNS::chart, "name",
667 // TQString()).latin1()
668 // << "\n";
669
670 tmp = axisElem.attributeNS( KoXmlNS::chart, "style-name", TQString() );
671 //kdDebug(35001) << "Style name for the axis: " << tmp << endl;
672 //cerr << "Style name for the axis: " << tmp.latin1() << "\n";
673 styleStack.save();
674 styleStack.setTypeProperties( "chart" ); // load chart properties
675 loadingContext.fillStyleStack( axisElem, KoXmlNS::chart, "style-name", "chart" );
676
677 // chart:class - "category" / "value" / "domain" (domain for scatter)
678 // child: chart:categories
679
680 // child: chart:title - Name of title if any.
681 // child: chart:grid
682 // attr: chart:class - "major" / "minor"
683
684 // chart:style-name - Associated style with the following info:
685 // --------------------------------
686 // chart:display-label - true if an axis label should be shown.
687
688 // chart:tick-marks-major-inner - true if display tickmarks at major
689 // chart:tick-marks-major-outer or minor intervals outside / inside
690 // chart:tick-minor-major-inner the chart area.
691 // chart:tick-minor-major-outer
692
693 // chart:logarithmic - true if logarithmic scale
694
695 // text:line-break - true if categories can be broken
696
697 // chart:text-overlap - true if labels can overlap
698
699 // chart:label-arrangement - "side-by-side" / "stagger-even" /
700 // "stagger-odd" (def: side-by-side)
701
702 // chart:visible - true if labels + ticks should be shown.
703
704 // children:
705 // chart:
706 // chart:
707 // chart:
708 // chart:
709 // chart:
710 styleStack.restore();
711
712 //cerr << "<<< ----------------------------------------------------------\n";
713 return true;
714}
715
716
717// ----------------------------------------------------------------
718
719
720void KChartParams::saveOasis( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const
721{
722 bool knownType = false;
723 for ( unsigned int i = 0 ; i < numOasisChartTypes ; ++i ) {
724 if ( m_chartType == oasisChartTypes[i].chartType ) {
725 bodyWriter->addAttribute( "chart:class", oasisChartTypes[i].oasisClass );
726 knownType = true;
727 break;
728 }
729 }
730
731 if ( !knownType ) {
732 kdError(32001) << "Unknown chart type in KChartParams::saveOasis, extend oasisChartTypes!" << endl;
733 }
734
735 bodyWriter->startElement( "chart:title" );
736 TQRect rect( headerFooterRect( KDChartParams::HdFtPosHeader ) );
737 bodyWriter->addAttributePt( "svg:x", rect.x() );
738 bodyWriter->addAttributePt( "svg:y", rect.y() );
739 bodyWriter->addAttribute( "chart:style-name", saveOasisFont( mainStyles, header1Font(), headerFooterColor( KDChartParams::HdFtPosHeader ) ) );
740 bodyWriter->startElement( "text:p" );
741 bodyWriter->addTextNode( header1Text() );
742 bodyWriter->endElement(); // text:p
743 bodyWriter->endElement(); // chart:title
744
745 TQString subTitle( header2Text() );
746 if ( !subTitle.isEmpty() ) {
747
748 kdDebug(32001) << "header rect: " << headerFooterRect( KDChartParams::HdFtPosHeader2 ) << endl;
749 TQRect rect( headerFooterRect( KDChartParams::HdFtPosHeader2 ) );
750 bodyWriter->startElement( "chart:subtitle" );
751 bodyWriter->addAttributePt( "svg:x", rect.x() );
752 bodyWriter->addAttributePt( "svg:y", rect.y() );
753 bodyWriter->addAttribute( "chart:style-name",
754 saveOasisFont( mainStyles,
755 header2Font(),
756 headerFooterColor( KDChartParams::HdFtPosHeader2 ) ) );
757
758 bodyWriter->startElement( "text:p" );
759 bodyWriter->addTextNode( subTitle );
760 bodyWriter->endElement(); // text:p
761 bodyWriter->endElement(); // chart:subtitle
762 }
763
764
765 TQString footer( footerText() );
766 if ( !footer.isEmpty() ) {
767 TQRect rect( headerFooterRect( KDChartParams::HdFtPosFooter ) );
768 bodyWriter->startElement( "chart:footer" );
769 bodyWriter->addAttributePt( "svg:x", rect.x() );
770 bodyWriter->addAttributePt( "svg:y", rect.y() );
771 bodyWriter->addAttribute( "chart:style-name",
772 saveOasisFont( mainStyles,
773 footerFont(),
774 headerFooterColor( KDChartParams::HdFtPosFooter ) ) );
775
776 bodyWriter->startElement( "text:p" );
777 bodyWriter->addTextNode( footer );
778 bodyWriter->endElement(); // text:p
779 bodyWriter->endElement(); // chart:footer
780 }
781
782 // TODO legend
783 LegendPosition lpos = legendPosition();
784 if ( lpos != NoLegend ) {
785 bodyWriter->startElement( "chart:legend" );
786 TQString lp;
787 TQString lalign;
788 switch ( lpos ) {
789 case LegendTop:
790 lp = "top";
791 lalign = "center";
792 break;
793 case LegendBottom:
794 lp = "bottom";
795 lalign = "center";
796 break;
797 case LegendLeft:
798 lp = "start";
799 lalign = "center";
800 break;
801 case LegendRight:
802 lp = "end";
803 lalign = "center";
804 break;
805 case LegendTopLeft:
806 lp = "top-start";
807 break;
808 case LegendTopLeftTop:
809 lp = "top";
810 lalign = "start";
811 break;
812 case LegendTopLeftLeft:
813 lp = "start";
814 lalign = "start";
815 break;
816 case LegendTopRight:
817 lp = "top-end";
818 break;
819 case LegendTopRightTop:
820 lp = "top";
821 lalign = "end";
822 break;
823 case LegendTopRightRight:
824 lp = "end";
825 lalign = "start";
826 break;
827 case LegendBottomLeft:
828 lp = "bottom-start";
829 break;
830 case LegendBottomLeftBottom:
831 lp = "bottom";
832 lalign = "start";
833 break;
834 case LegendBottomLeftLeft:
835 lp = "start";
836 lalign = "end";
837 break;
838 case LegendBottomRight:
839 lp = "bottom-end";
840 break;
841 case LegendBottomRightBottom:
842 lp = "bottom";
843 lalign = "end";
844 break;
845 case LegendBottomRightRight:
846 lp = "end";
847 lalign = "end";
848 break;
849 default:
850 lp = "end";
851 lalign = "center";
852 break;
853 }
854
855 bodyWriter->addAttribute( "chart:legend-position", lp );
856 bodyWriter->addAttribute( "chart:legend-align", lalign );
857 bodyWriter->addAttribute( "chart:style-name",
858 saveOasisFont( mainStyles,
859 legendFont(),
860 legendTextColor() ) );
861 bodyWriter->addAttribute( "koffice:title", legendTitleText() );
862 bodyWriter->endElement(); // chart:legend
863 }
864
865 bodyWriter->startElement( "chart:plot-area" );
866 saveOasisPlotArea( bodyWriter, mainStyles );
867 bodyWriter->endElement();
868
869 // TODO...
870}
871
872
873void KChartParams::saveOasisPlotArea( KoXmlWriter* bodyWriter, KoGenStyles& mainStyles ) const
874{
875 TQString dataSourceHasLabels;
876 if ( m_firstRowAsLabel )
877 if ( m_firstColAsLabel )
878 dataSourceHasLabels = "both";
879 else
880 dataSourceHasLabels = "row";
881 else
882 if ( m_firstColAsLabel )
883 dataSourceHasLabels = "column";
884 else
885 dataSourceHasLabels = "none";
886 bodyWriter->addAttribute( "chart:data-source-has-labels", dataSourceHasLabels );
887
888 // Prepare the style for the plot area
889 KoGenStyle plotAreaStyle( KoGenStyle::STYLE_AUTO, "chart" );
890
891 switch ( m_chartType ) {
892 case NoType:
893 break;
894
895 case Bar:
896 switch( barChartSubType() ) {
897 case BarStacked:
898 plotAreaStyle.addProperty( "chart:stacked", "true" );
899 break;
900 case BarPercent:
901 plotAreaStyle.addProperty( "chart:percentage", "true" );
902 break;
903 case BarNormal:
904 break;
905 case BarMultiRows:
906 break;
907 }
908 plotAreaStyle.addProperty( "chart:vertical", "false" ); // #### always?
909 plotAreaStyle.addProperty( "chart:lines-used", 0 ); // FIXME: for now
910
911 if ( threeDBars() )
912 plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
913
914 case Line:
915 switch( lineChartSubType() ) {
916 case LineStacked:
917 plotAreaStyle.addProperty( "chart:stacked", "true" );
918 break;
919 case LinePercent:
920 plotAreaStyle.addProperty( "chart:percentage", "true" );
921 break;
922 case LineNormal:
923 break;
924 }
925 plotAreaStyle.addProperty( "chart:symbol-type", "automatic" );
926
927 if ( threeDLines() )
928 plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
929
930 break;
931
932 case Area:
933 switch( areaChartSubType() ) {
934 case AreaStacked:
935 plotAreaStyle.addProperty( "chart:stacked", "true" );
936 break;
937 case AreaPercent:
938 plotAreaStyle.addProperty( "chart:percentage", "true" );
939 break;
940 case AreaNormal:
941 break;
942 }
943 //plotAreaStyle.addProperty( "chart:lines-used", 0 ); // #### for now
944
945 // TODO - very similar
946
947 case Pie:
948 if ( threeDPies() )
949 plotAreaStyle.addProperty( "chart:three-dimensional", "true" );
950
951 break;
952
953 case HiLo:
954 break;
955
956 case Ring:
957 break;
958
959 case Polar:
960 break;
961
962 case BoxWhisker:
963 break;
964 }
965
966 // chart:series-source
967 plotAreaStyle.addProperty( "chart:series-source",
968 ( dataDirection() == DataRows ) ? "rows" : "columns" );
969
970 // Register the style, and get back its auto-generated name
971 const TQString styleName = mainStyles.lookup( plotAreaStyle, "ch" );
972
973 bodyWriter->addAttribute( "chart:style-name", styleName );
974
975 saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosBottom, "x" );
976 saveOasisAxis( bodyWriter, mainStyles, KDChartAxisParams::AxisPosLeft, "y" );
977
978 // TODO chart:series
979 // TODO chart:wall
980 // TODO chart:floor
981}
982
983
984void KChartParams::saveOasisAxis( KoXmlWriter* bodyWriter,
985 KoGenStyles& mainStyles,
986 KDChartAxisParams::AxisPos axisPos,
987 const char* axisName ) const
988{
989 bodyWriter->startElement( "chart:axis" );
990
991 bodyWriter->addAttribute( "chart:dimension", axisName );
992 bodyWriter->addAttribute( "chart:name", TQCString( "primary-" ) + axisName );
993
994 KoGenStyle axisStyle( KoGenStyle::STYLE_AUTO, "chart" );
995
996 // TODO: Save axis style properties, like
997 axisStyle.addProperty( "chart:display-label", "true" ); // ###
998
999
1000 const TQString styleName = mainStyles.lookup( axisStyle, "ch" );
1001 bodyWriter->addAttribute( "chart:style-name", styleName );
1002
1003 // Write axis titles if any.
1004 TQString tmpStr = axisTitle( axisPos );
1005 if ( tmpStr != "" ) {
1006 bodyWriter->startElement( "chart:title" );
1007 // TODO: Save style, svg:x, svg:y
1008
1009 // Write the text in the axis title.
1010 bodyWriter->startElement( "text:p" );
1011 bodyWriter->addTextNode( tmpStr
1012 .remove( TQRegExp( "^<qt><center>" ) )
1013 .remove( TQRegExp( "</center></qt>$" ) ) );
1014 bodyWriter->endElement(); // text:p
1015
1016 bodyWriter->endElement(); // chart:title
1017 }
1018
1019 // TODO x axis has chart:categories, y axis has chart:grid ?
1020 // Maybe that part should be done by the caller of saveOasisAxis then
1021 // including the opening/closing of the chart:axis element...
1022
1023 bodyWriter->endElement(); // chart:axis
1024}
1025
1026
1027TQString KChartParams::saveOasisFont( KoGenStyles& mainStyles, const TQFont& font, const TQColor& color ) const
1028{
1029 KoGenStyle::PropertyType tt = KoGenStyle::TextType;
1030 KoGenStyle autoStyle( KoGenStyle::STYLE_AUTO, "chart", 0 );
1031 autoStyle.addProperty( "fo:font-family", font.family(), tt );
1032 autoStyle.addPropertyPt( "fo:font-size", font.pointSize(), tt );
1033 autoStyle.addProperty( "fo:color", color.isValid() ? color.name() : "#000000", tt );
1034 int w = font.weight();
1035 autoStyle.addProperty( "fo:font-weight", w == 50 ? "normal" : w == 75 ? "bold" : TQString::number( tqRound( w / 10 ) * 100 ), tt );
1036 autoStyle.addProperty( "fo:font-style", font.italic() ? "italic" : "normal", tt );
1037
1038 return mainStyles.lookup( autoStyle, "ch", KoGenStyles::ForceNumbering );
1039}
1040
1041
1042} //KChart namespace
1043
Provide access to the chart axis parameters.
Header for all common chart parameters.
Bundles the display parameters of a chart.
static ChartType stringToChartType(const TQString &string)
Converts the specified string to a chart type enum value.
static TQString chartTypeToString(ChartType type)
Converts the specified chart type enum to a string representation.
ChartType
The chart type.