kspread

kspread_condition.cpp
1/* This file is part of the KDE project
2 Copyright 1998, 1999 Torben Weis <weis@kde.org>
3 Copyright 1999- 2003 The KSpread Team
4 www.koffice.org/kspread
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22#include <float.h>
23
24#include "kspread_cell.h"
25#include "kspread_sheet.h"
26#include "kspread_doc.h"
27#include "kspread_style.h"
28#include "kspread_style_manager.h"
29#include "kspread_util.h"
30
31#include <KoGenStyles.h>
32
33#include <KoXmlWriter.h>
34#include <KoXmlNS.h>
35#include <kdebug.h>
36#include <tqdom.h>
37#include <tqbuffer.h>
38
39#include "kspread_condition.h"
40
41using namespace KSpread;
42
43Conditional::Conditional():
44 val1( 0.0 ), val2( 0.0 ), strVal1( 0 ), strVal2( 0 ),
45 colorcond( 0 ), fontcond( 0 ), styleName( 0 ),
46 style( 0 ), cond( None )
47{
48}
49
50Conditional::~Conditional()
51{
52 delete strVal1;
53 delete strVal2;
54 delete colorcond;
55 delete fontcond;
56 delete styleName;
57}
58
59Conditional::Conditional( const Conditional& c )
60{
61 operator=( c );
62}
63
64Conditional& Conditional::operator=( const Conditional& d )
65{
66 strVal1 = d.strVal1 ? new TQString( *d.strVal1 ) : 0;
67 strVal2 = d.strVal2 ? new TQString( *d.strVal2 ) : 0;
68 styleName = d.styleName ? new TQString( *d.styleName ) : 0;
69 fontcond = d.fontcond ? new TQFont( *d.fontcond ) : 0;
70 colorcond = d.colorcond ? new TQColor( *d.colorcond ) : 0;
71 val1 = d.val1;
72 val2 = d.val2;
73 style = d.style;
74 cond = d.cond;
75
76 return *this;
77}
78
79bool Conditional::operator==(const Conditional& other) const
80{
81 if ( cond == other.cond &&
82 val1 == other.val1 &&
83 val2 == other.val2 &&
84 *strVal1 == *other.strVal1 &&
85 *strVal2 == *other.strVal2 &&
86 *colorcond == *other.colorcond &&
87 *fontcond == *other.fontcond &&
88 *styleName == *other.styleName &&
89 *style == *other.style )
90 {
91 return true;
92 }
93 return false;
94}
95
96Conditions::Conditions( const Cell * ownerCell )
97 : m_cell( ownerCell ), m_matchedStyle( 0 )
98{
99 Q_ASSERT( ownerCell != NULL );
100}
101
102Conditions::~Conditions()
103{
104 m_condList.clear();
105}
106
107void Conditions::checkMatches()
108{
109 Conditional condition;
110
111 if ( currentCondition( condition ) )
112 m_matchedStyle = condition.style;
113 else
114 m_matchedStyle = 0;
115}
116
118{
119 /* for now, the first condition that is true is the one that will be used */
120
121 TQValueList<Conditional>::const_iterator it;
122 double value = m_cell->value().asFloat();
123 TQString strVal = m_cell->value().asString();
124
125
126 for ( it = m_condList.begin(); it != m_condList.end(); ++it )
127 {
128 condition = *it;
129
130// if ( (*it).styleName )
131// kdDebug()<<"*it :"<< *( ( *it ).styleName ) <<endl;
132//
133// kdDebug()<<"*it style :"<<( *it ).style <<endl;
134
135
136 if ( condition.strVal1 && m_cell->value().isNumber() )
137 continue;
138
139 switch ( condition.cond )
140 {
141 case Conditional::Equal:
142 if ( condition.strVal1 )
143 {
144 if ( strVal == *condition.strVal1 )
145 return true;
146 }
147 else
148 if ( value - condition.val1 < DBL_EPSILON &&
149 value - condition.val1 > (0.0 - DBL_EPSILON) )
150 {
151 return true;
152 }
153 break;
154
155 case Conditional::Superior:
156 if ( condition.strVal1 )
157 {
158 if ( strVal > *condition.strVal1 )
159 return true;
160 }
161 else
162 if ( value > condition.val1 )
163 {
164 return true;
165 }
166 break;
167
168 case Conditional::Inferior:
169 if ( condition.strVal1 )
170 {
171 if ( strVal < *condition.strVal1 )
172 return true;
173 }
174 else
175 if ( value < condition.val1 )
176 {
177 return true;
178 }
179 break;
180
181 case Conditional::SuperiorEqual :
182 if ( condition.strVal1 )
183 {
184 if ( strVal >= *condition.strVal1 )
185 return true;
186 }
187 else
188 if ( value >= condition.val1 )
189 {
190 return true;
191 }
192 break;
193
194 case Conditional::InferiorEqual :
195 if ( condition.strVal1 )
196 {
197 if ( strVal <= *condition.strVal1 )
198 return true;
199 }
200 else
201 if ( value <= condition.val1 )
202 {
203 return true;
204 }
205 break;
206
207 case Conditional::Between :
208 if ( condition.strVal1 && condition.strVal2 )
209 {
210 if ( strVal > *condition.strVal1 && strVal < *condition.strVal2 )
211 return true;
212 }
213 else
214 if ( ( value > TQMIN(condition.val1, condition.val2 ) )
215 && ( value < TQMAX(condition.val1, condition.val2 ) ) )
216 {
217 return true;
218 }
219 break;
220
221 case Conditional::Different :
222 if ( condition.strVal1 && condition.strVal2 )
223 {
224 if ( strVal < *condition.strVal1 || strVal > *condition.strVal2 )
225 return true;
226 }
227 else
228 if ( ( value < TQMIN(condition.val1, condition.val2 ) )
229 || ( value > TQMAX(condition.val1, condition.val2) ) )
230 {
231 return true;
232 }
233 break;
234 case Conditional::DifferentTo :
235 if ( condition.strVal1 )
236 {
237 if ( strVal != *condition.strVal1 )
238 return true;
239 }
240 else
241 if ( value != condition.val1 )
242 {
243 return true;
244 }
245 break;
246
247 default:
248 break;
249 }
250 }
251 return false;
252}
253
254TQValueList<Conditional> Conditions::conditionList() const
255{
256 return m_condList;
257}
258
259void Conditions::setConditionList( const TQValueList<Conditional> & list )
260{
261 m_condList.clear();
262
263 TQValueList<Conditional>::const_iterator it;
264 for ( it = list.begin(); it != list.end(); ++it )
265 {
266 Conditional d = *it;
267 m_condList.append( Conditional( d ) );
268 }
269}
270
271void Conditions::saveOasisConditions( KoGenStyle &currentCellStyle )
272{
273 //todo fix me with kspread old format!!!
274 if ( m_condList.isEmpty() )
275 return;
276 TQValueList<Conditional>::const_iterator it;
277 int i = 0;
278 for ( it = m_condList.begin(); it != m_condList.end(); ++it, ++i )
279 {
280 Conditional condition = *it;
281 //<style:map style:condition="cell-content()=45" style:apply-style-name="Default" style:base-cell-address="Sheet1.E10"/>
282 TQMap<TQString, TQString> map;
283 map.insert( "style:condition", saveOasisConditionValue( condition ) );
284 map.insert( "style:apply-style-name", *( condition.styleName ) );
285 //map.insert( ""style:base-cell-address", "..." );//todo
286 currentCellStyle.addStyleMap( map );
287 }
288}
289
290TQString Conditions::saveOasisConditionValue( Conditional &condition)
291{
292 //we can also compare text value.
293 //todo adapt it.
294 TQString value;
295 switch( condition.cond )
296 {
297 case Conditional::None:
298 break;
299 case Conditional::Equal:
300 value="cell-content()=";
301 if ( condition.strVal1 )
302 value+=*condition.strVal1;
303 else
304 value+=TQString::number( condition.val1 );
305 break;
306 case Conditional::Superior:
307 value="cell-content()>";
308 if ( condition.strVal1 )
309 value+=*condition.strVal1;
310 else
311 value+=TQString::number( condition.val1 );
312 break;
313 case Conditional::Inferior:
314 value="cell-content()<";
315 if ( condition.strVal1 )
316 value+=*condition.strVal1;
317 else
318 value+=TQString::number( condition.val1 );
319 break;
320 case Conditional::SuperiorEqual:
321 value="cell-content()>=";
322 if ( condition.strVal1 )
323 value+=*condition.strVal1;
324 else
325 value+=TQString::number( condition.val1 );
326 break;
327 case Conditional::InferiorEqual:
328 value="cell-content()<=";
329 if ( condition.strVal1 )
330 value+=*condition.strVal1;
331 else
332 value+=TQString::number( condition.val1 );
333 break;
334 case Conditional::Between:
335 value="cell-content-is-between(";
336 if ( condition.strVal1 )
337 {
338 value+=*condition.strVal1;
339 value+=",";
340 if ( condition.strVal2 )
341 value+=*condition.strVal2;
342 }
343 else
344 {
345 value+=TQString::number( condition.val1 );
346 value+=",";
347 value+=TQString::number( condition.val2 );
348 }
349 value+=")";
350 break;
351 case Conditional::DifferentTo:
352 value="cell-content()!="; //FIXME not good here !
353 if ( condition.strVal1 )
354 value+=*condition.strVal1;
355 else
356 value+=TQString::number( condition.val1 );
357 break;
358 case Conditional::Different:
359 value="cell-content-is-not-between(";
360 if ( condition.strVal1 )
361 {
362 value+=*condition.strVal1;
363 value+=",";
364 if ( condition.strVal2 )
365 value+=*condition.strVal2;
366 }
367 else
368 {
369 value+=TQString::number( condition.val1 );
370 value+=",";
371 value+=TQString::number( condition.val2 );
372 }
373 value+=")";
374 break;
375 }
376 return value;
377}
378
379
380TQDomElement Conditions::saveConditions( TQDomDocument & doc ) const
381{
382 TQDomElement conditions = doc.createElement("condition");
383 TQValueList<Conditional>::const_iterator it;
384 TQDomElement child;
385 int num = 0;
386 TQString name;
387
388 for ( it = m_condList.begin(); it != m_condList.end(); ++it )
389 {
390 Conditional condition = *it;
391
392 /* the name of the element will be "condition<n>"
393 * This is unimportant now but in older versions three conditions were
394 * hardcoded with names "first" "second" and "third"
395 */
396 name.setNum( num );
397 name.prepend( "condition" );
398
399 child = doc.createElement( name );
400 child.setAttribute( "cond", (int) condition.cond );
401
402 // TODO: saving in KSpread 1.1 | KSpread 1.2 format
403 if ( condition.strVal1 )
404 {
405 child.setAttribute( "strval1", *condition.strVal1 );
406 if ( condition.strVal2 )
407 child.setAttribute( "strval2", *condition.strVal2 );
408 }
409 else
410 {
411 child.setAttribute( "val1", condition.val1 );
412 child.setAttribute( "val2", condition.val2 );
413 }
414 if ( condition.styleName )
415 {
416 child.setAttribute( "style", *condition.styleName );
417 }
418 else
419 {
420 child.setAttribute( "color", condition.colorcond->name() );
421 child.appendChild( util_createElement( "font", *condition.fontcond, doc ) );
422 }
423
424 conditions.appendChild( child );
425
426 ++num;
427 }
428
429 if ( num == 0 )
430 {
431 /* there weren't any real conditions -- return a null dom element */
432 return TQDomElement();
433 }
434 else
435 {
436 return conditions;
437 }
438}
439
440void Conditions::loadOasisConditions( const TQDomElement & element )
441{
442 kdDebug(36003) << "Loading conditional styles" << endl;
443 TQDomNode node( element );
444 StyleManager * manager = m_cell->sheet()->doc()->styleManager();
445
446 while ( !node.isNull() )
447 {
448 TQDomElement elementItem = node.toElement();
449 if ( elementItem.tagName()== "map" && elementItem.namespaceURI() == KoXmlNS::style )
450 {
451 bool ok = true;
452 kdDebug(36003) << "\tcondition: "<< elementItem.attributeNS( KoXmlNS::style, "condition", TQString() )<<endl;
453 Conditional newCondition;
454 loadOasisConditionValue( elementItem.attributeNS( KoXmlNS::style, "condition", TQString() ), newCondition );
455 if ( elementItem.hasAttributeNS( KoXmlNS::style, "apply-style-name" ) )
456 {
457 kdDebug(36003)<<"\tstyle: "<<elementItem.attributeNS( KoXmlNS::style, "apply-style-name", TQString() )<<endl;
458 newCondition.styleName = new TQString( elementItem.attributeNS( KoXmlNS::style, "apply-style-name", TQString() ) );
459 newCondition.style = manager->style( *newCondition.styleName );
460 if ( !newCondition.style )
461 ok = false;
462 else
463 ok = true;
464 }
465
466 if ( ok )
467 m_condList.append( newCondition );
468 else
469 kdDebug(36003) << "Error loading condition " << elementItem.nodeName()<< endl;
470 }
471 node = node.nextSibling();
472 }
473}
474
475void Conditions::loadOasisConditionValue( const TQString &styleCondition, Conditional &newCondition )
476{
477 TQString val( styleCondition );
478 if ( val.contains( "cell-content()" ) )
479 {
480 val = val.remove( "cell-content()" );
481 loadOasisCondition( val,newCondition );
482 }
483 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
484 //for the moment we support just int/double value, not text/date/time :(
485 if ( val.contains( "cell-content-is-between(" ) )
486 {
487 val = val.remove( "cell-content-is-between(" );
488 val = val.remove( ")" );
489 TQStringList listVal = TQStringList::split( "," , val );
490 loadOasisValidationValue( listVal, newCondition );
491 newCondition.cond = Conditional::Between;
492 }
493 if ( val.contains( "cell-content-is-not-between(" ) )
494 {
495 val = val.remove( "cell-content-is-not-between(" );
496 val = val.remove( ")" );
497 TQStringList listVal = TQStringList::split( ",", val );
498 loadOasisValidationValue( listVal,newCondition );
499 newCondition.cond = Conditional::Different;
500 }
501
502}
503
504
505void Conditions::loadOasisCondition( TQString &valExpression, Conditional &newCondition )
506{
507 TQString value;
508 if (valExpression.find( "<=" )==0 )
509 {
510 value = valExpression.remove( 0,2 );
511 newCondition.cond = Conditional::InferiorEqual;
512 }
513 else if (valExpression.find( ">=" )==0 )
514 {
515 value = valExpression.remove( 0,2 );
516 newCondition.cond = Conditional::SuperiorEqual;
517 }
518 else if (valExpression.find( "!=" )==0 )
519 {
520 //add Differentto attribute
521 value = valExpression.remove( 0,2 );
522 newCondition.cond = Conditional::DifferentTo;
523 }
524 else if ( valExpression.find( "<" )==0 )
525 {
526 value = valExpression.remove( 0,1 );
527 newCondition.cond = Conditional::Inferior;
528 }
529 else if(valExpression.find( ">" )==0 )
530 {
531 value = valExpression.remove( 0,1 );
532 newCondition.cond = Conditional::Superior;
533 }
534 else if (valExpression.find( "=" )==0 )
535 {
536 value = valExpression.remove( 0,1 );
537 newCondition.cond = Conditional::Equal;
538 }
539 else
540 kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
541 kdDebug(36003) << "\tvalue: " << value << endl;
542 bool ok = false;
543 newCondition.val1 = value.toDouble(&ok);
544 if ( !ok )
545 {
546 newCondition.val1 = value.toInt(&ok);
547 if ( !ok )
548 {
549 newCondition.strVal1 = new TQString( value );
550 kdDebug()<<" Try to parse this value :"<<value<<endl;
551 }
552 }
553}
554
555
556void Conditions::loadOasisValidationValue( const TQStringList &listVal, Conditional &newCondition )
557{
558 bool ok = false;
559 kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
560
561 newCondition.val1 = listVal[0].toDouble(&ok);
562 if ( !ok )
563 {
564 newCondition.val1 = listVal[0].toInt(&ok);
565 if ( !ok )
566 {
567 newCondition.strVal1 = new TQString( listVal[0] );
568 kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
569 }
570 }
571 ok=false;
572 newCondition.val2 = listVal[1].toDouble(&ok);
573 if ( !ok )
574 {
575 newCondition.val2 = listVal[1].toInt(&ok);
576 if ( !ok )
577 {
578 newCondition.strVal2 = new TQString( listVal[1] );
579 kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
580 }
581 }
582}
583
584
585void Conditions::loadConditions( const TQDomElement & element )
586{
587 TQDomNodeList nodeList = element.childNodes();
588 Conditional newCondition;
589 bool ok;
590 StyleManager * manager = m_cell->sheet()->doc()->styleManager();
591
592 for ( int i = 0; i < (int)(nodeList.length()); ++i )
593 {
594 newCondition.strVal1 = 0;
595 newCondition.strVal2 = 0;
596 newCondition.styleName = 0;
597 newCondition.fontcond = 0;
598 newCondition.colorcond = 0;
599
600 TQDomElement conditionElement = nodeList.item( i ).toElement();
601
602 ok = conditionElement.hasAttribute( "cond" );
603
604 if ( ok )
605 newCondition.cond = (Conditional::Type) conditionElement.attribute( "cond" ).toInt( &ok );
606 else continue;
607
608 if ( conditionElement.hasAttribute( "val1" ) )
609 {
610 newCondition.val1 = conditionElement.attribute( "val1" ).toDouble( &ok );
611
612 if ( conditionElement.hasAttribute( "val2" ) )
613 newCondition.val2 = conditionElement.attribute("val2").toDouble( &ok );
614 }
615
616 if ( conditionElement.hasAttribute( "strval1" ) )
617 {
618 newCondition.strVal1 = new TQString( conditionElement.attribute( "strval1" ) );
619
620 if ( conditionElement.hasAttribute( "strval2" ) )
621 newCondition.strVal2 = new TQString( conditionElement.attribute( "strval2" ) );
622 }
623
624 if ( conditionElement.hasAttribute( "color" ) )
625 newCondition.colorcond = new TQColor( conditionElement.attribute( "color" ) );
626
627 TQDomElement font = conditionElement.namedItem( "font" ).toElement();
628 if ( !font.isNull() )
629 newCondition.fontcond = new TQFont( util_toFont( font ) );
630
631 if ( conditionElement.hasAttribute( "style" ) )
632 {
633 newCondition.styleName = new TQString( conditionElement.attribute( "style" ) );
634 newCondition.style = manager->style( *newCondition.styleName );
635 if ( !newCondition.style )
636 ok = false;
637 }
638
639 if ( ok )
640 {
641 m_condList.append( newCondition );
642 }
643 else
644 {
645 kdDebug(36001) << "Error loading condition " << conditionElement.nodeName()<< endl;
646 }
647 }
648}
649
650bool Conditions::operator==( const Conditions& other ) const
651{
652 if ( !( *m_matchedStyle == *other.m_matchedStyle ) )
653 return false;
654 if ( m_condList.count() != other.m_condList.count() )
655 return false;
656 TQValueList<Conditional>::ConstIterator end( m_condList.end() );
657 for ( TQValueList<Conditional>::ConstIterator it( m_condList.begin() ); it != end; ++it )
658 {
659 bool found = false;
660 TQValueList<Conditional>::ConstIterator otherEnd( other.m_condList.end() );
661 for ( TQValueList<Conditional>::ConstIterator otherIt( other.m_condList.begin() ); otherIt != otherEnd; ++otherIt )
662 {
663 if ( (*it) == (*otherIt) )
664 found = true;
665 }
666 if ( !found )
667 return false;
668 }
669 return true;
670}
For every cell in the spread sheet there is a Cell object.
Sheet * sheet() const
Returns the worksheet which owns this cell.
Structure to indicate the condition we're testing on a cell and the special font and color that will ...
Manages a set of conditions for a cell.
TQValueList< Conditional > conditionList() const
Retrieve the current list of conditions we're checking.
bool currentCondition(Conditional &condition)
Use this function to see what conditions actually apply currently.
void setConditionList(const TQValueList< Conditional > &list)
Replace the current list of conditions with this new one.
TQDomElement saveConditions(TQDomDocument &doc) const
Saves the conditions to a DOM tree structure.
Conditions(const Cell *ownerCell)
Constructor.
void loadConditions(const TQDomElement &element)
Takes a parsed DOM element and recreates the conditions structure out of it.
@ None
No restriction.
The KSpread namespace.
Definition commands.h:39