kspread

kspread_condition.cc

00001 /* This file is part of the KDE project
00002    Copyright 1998, 1999 Torben Weis <weis@kde.org>
00003    Copyright 1999- 2003 The KSpread Team
00004                         www.koffice.org/kspread
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include <float.h>
00023 
00024 #include "kspread_cell.h"
00025 #include "kspread_sheet.h"
00026 #include "kspread_doc.h"
00027 #include "kspread_style.h"
00028 #include "kspread_style_manager.h"
00029 #include "kspread_util.h"
00030 
00031 #include <KoGenStyles.h>
00032 
00033 #include <KoXmlWriter.h>
00034 #include <KoXmlNS.h>
00035 #include <kdebug.h>
00036 #include <qdom.h>
00037 #include <qbuffer.h>
00038 
00039 #include "kspread_condition.h"
00040 
00041 using namespace KSpread;
00042 
00043 Conditional::Conditional():
00044   val1( 0.0 ), val2( 0.0 ), strVal1( 0 ), strVal2( 0 ),
00045   colorcond( 0 ), fontcond( 0 ), styleName( 0 ),
00046   style( 0 ), cond( None )
00047 {
00048 }
00049 
00050 Conditional::~Conditional()
00051 {
00052   delete strVal1;
00053   delete strVal2;
00054   delete colorcond;
00055   delete fontcond;
00056   delete styleName;
00057 }
00058 
00059 Conditional::Conditional( const Conditional& c )
00060 {
00061   operator=( c );
00062 }
00063 
00064 Conditional& Conditional::operator=( const Conditional& d )
00065 {
00066   strVal1 = d.strVal1 ? new QString( *d.strVal1 ) : 0;
00067   strVal2 = d.strVal2 ? new QString( *d.strVal2 ) : 0;
00068   styleName = d.styleName ? new QString( *d.styleName ) : 0;
00069   fontcond = d.fontcond ? new QFont( *d.fontcond ) : 0;
00070   colorcond = d.colorcond ? new QColor( *d.colorcond ) : 0;
00071   val1  = d.val1;
00072   val2  = d.val2;
00073   style = d.style;
00074   cond  = d.cond;
00075 
00076   return *this;
00077 }
00078 
00079 bool Conditional::operator==(const Conditional& other) const
00080 {
00081   if ( cond == other.cond &&
00082        val1 == other.val1 &&
00083        val2 == other.val2 &&
00084        *strVal1 == *other.strVal1 &&
00085        *strVal2 == *other.strVal2 &&
00086        *colorcond == *other.colorcond &&
00087        *fontcond == *other.fontcond &&
00088        *styleName == *other.styleName &&
00089        *style == *other.style )
00090   {
00091     return true;
00092   }
00093   return false;
00094 }
00095 
00096 Conditions::Conditions( const Cell * ownerCell )
00097   : m_cell( ownerCell ), m_matchedStyle( 0 )
00098 {
00099   Q_ASSERT( ownerCell != NULL );
00100 }
00101 
00102 Conditions::~Conditions()
00103 {
00104   m_condList.clear();
00105 }
00106 
00107 void Conditions::checkMatches()
00108 {
00109   Conditional condition;
00110 
00111   if ( currentCondition( condition ) )
00112     m_matchedStyle = condition.style;
00113   else
00114     m_matchedStyle = 0;
00115 }
00116 
00117 bool Conditions::currentCondition( Conditional & condition )
00118 {
00119   /* for now, the first condition that is true is the one that will be used */
00120 
00121   QValueList<Conditional>::const_iterator it;
00122   double value   = m_cell->value().asFloat();
00123   QString strVal = m_cell->value().asString();
00124 
00125 
00126   for ( it = m_condList.begin(); it != m_condList.end(); ++it )
00127   {
00128     condition = *it;
00129 
00130 //     if ( (*it).styleName )
00131 //         kdDebug()<<"*it :"<<  *( ( *it ).styleName ) <<endl;
00132 //
00133 //     kdDebug()<<"*it style :"<<(  *it ).style <<endl;
00134 
00135 
00136     if ( condition.strVal1 && m_cell->value().isNumber() )
00137       continue;
00138 
00139     switch ( condition.cond )
00140     {
00141       case Conditional::Equal:
00142       if ( condition.strVal1 )
00143       {
00144         if ( strVal == *condition.strVal1 )
00145           return true;
00146       }
00147       else
00148       if ( value - condition.val1 < DBL_EPSILON &&
00149            value - condition.val1 > (0.0 - DBL_EPSILON) )
00150       {
00151         return true;
00152       }
00153       break;
00154 
00155       case Conditional::Superior:
00156       if ( condition.strVal1 )
00157       {
00158         if ( strVal > *condition.strVal1 )
00159           return true;
00160       }
00161       else
00162       if ( value > condition.val1 )
00163       {
00164         return true;
00165       }
00166       break;
00167 
00168       case Conditional::Inferior:
00169       if ( condition.strVal1 )
00170       {
00171         if ( strVal < *condition.strVal1 )
00172           return true;
00173       }
00174       else
00175       if ( value < condition.val1 )
00176       {
00177         return true;
00178       }
00179       break;
00180 
00181       case Conditional::SuperiorEqual :
00182       if ( condition.strVal1 )
00183       {
00184         if ( strVal >= *condition.strVal1 )
00185           return true;
00186       }
00187       else
00188       if ( value >= condition.val1 )
00189       {
00190         return true;
00191       }
00192       break;
00193 
00194       case Conditional::InferiorEqual :
00195       if ( condition.strVal1 )
00196       {
00197         if ( strVal <= *condition.strVal1 )
00198           return true;
00199       }
00200       else
00201       if ( value <= condition.val1 )
00202       {
00203         return true;
00204       }
00205       break;
00206 
00207       case Conditional::Between :
00208       if ( condition.strVal1 && condition.strVal2 )
00209       {
00210         if ( strVal > *condition.strVal1 && strVal < *condition.strVal2 )
00211           return true;
00212       }
00213       else
00214       if ( ( value > QMIN(condition.val1, condition.val2 ) )
00215            && ( value < QMAX(condition.val1, condition.val2 ) ) )
00216       {
00217         return true;
00218       }
00219       break;
00220 
00221       case Conditional::Different :
00222       if ( condition.strVal1 && condition.strVal2 )
00223       {
00224         if ( strVal < *condition.strVal1 || strVal > *condition.strVal2 )
00225           return true;
00226       }
00227       else
00228       if ( ( value < QMIN(condition.val1, condition.val2 ) )
00229            || ( value > QMAX(condition.val1, condition.val2) ) )
00230       {
00231         return true;
00232       }
00233       break;
00234       case Conditional::DifferentTo :
00235       if ( condition.strVal1 )
00236       {
00237         if ( strVal != *condition.strVal1 )
00238           return true;
00239       }
00240       else
00241       if ( value != condition.val1 )
00242       {
00243         return true;
00244       }
00245       break;
00246 
00247      default:
00248       break;
00249     }
00250   }
00251   return false;
00252 }
00253 
00254 QValueList<Conditional> Conditions::conditionList() const
00255 {
00256   return m_condList;
00257 }
00258 
00259 void Conditions::setConditionList( const QValueList<Conditional> & list )
00260 {
00261   m_condList.clear();
00262 
00263   QValueList<Conditional>::const_iterator it;
00264   for ( it = list.begin(); it != list.end(); ++it )
00265   {
00266     Conditional d = *it;
00267     m_condList.append( Conditional( d ) );
00268   }
00269 }
00270 
00271 void Conditions::saveOasisConditions( KoGenStyle &currentCellStyle )
00272 {
00273     //todo fix me with kspread old format!!!
00274     if ( m_condList.isEmpty() )
00275         return;
00276     QValueList<Conditional>::const_iterator it;
00277     int i = 0;
00278     for ( it = m_condList.begin(); it != m_condList.end(); ++it, ++i )
00279     {
00280         Conditional condition = *it;
00281         //<style:map style:condition="cell-content()=45" style:apply-style-name="Default" style:base-cell-address="Sheet1.E10"/>
00282         QMap<QString, QString> map;
00283         map.insert( "style:condition", saveOasisConditionValue( condition ) );
00284         map.insert( "style:apply-style-name",  *( condition.styleName ) );
00285         //map.insert( ""style:base-cell-address", "..." );//todo
00286         currentCellStyle.addStyleMap( map );
00287     }
00288 }
00289 
00290 QString Conditions::saveOasisConditionValue( Conditional &condition)
00291 {
00292     //we can also compare text value.
00293     //todo adapt it.
00294     QString value;
00295     switch( condition.cond )
00296     {
00297       case Conditional::None:
00298         break;
00299       case Conditional::Equal:
00300         value="cell-content()=";
00301         if ( condition.strVal1 )
00302             value+=*condition.strVal1;
00303         else
00304             value+=QString::number( condition.val1 );
00305         break;
00306       case Conditional::Superior:
00307         value="cell-content()>";
00308         if ( condition.strVal1 )
00309             value+=*condition.strVal1;
00310         else
00311             value+=QString::number( condition.val1 );
00312         break;
00313       case Conditional::Inferior:
00314         value="cell-content()<";
00315         if ( condition.strVal1 )
00316             value+=*condition.strVal1;
00317         else
00318             value+=QString::number( condition.val1 );
00319         break;
00320       case Conditional::SuperiorEqual:
00321         value="cell-content()>=";
00322         if ( condition.strVal1 )
00323             value+=*condition.strVal1;
00324         else
00325             value+=QString::number( condition.val1 );
00326         break;
00327       case Conditional::InferiorEqual:
00328         value="cell-content()<=";
00329         if ( condition.strVal1 )
00330             value+=*condition.strVal1;
00331         else
00332             value+=QString::number( condition.val1 );
00333         break;
00334       case Conditional::Between:
00335         value="cell-content-is-between(";
00336         if ( condition.strVal1 )
00337         {
00338             value+=*condition.strVal1;
00339             value+=",";
00340             if ( condition.strVal2 )
00341                 value+=*condition.strVal2;
00342         }
00343         else
00344         {
00345             value+=QString::number( condition.val1 );
00346             value+=",";
00347             value+=QString::number( condition.val2 );
00348         }
00349         value+=")";
00350         break;
00351       case Conditional::DifferentTo:
00352         value="cell-content()!="; //FIXME not good here !
00353         if ( condition.strVal1 )
00354             value+=*condition.strVal1;
00355         else
00356             value+=QString::number( condition.val1 );
00357         break;
00358       case Conditional::Different:
00359         value="cell-content-is-not-between(";
00360         if ( condition.strVal1 )
00361         {
00362             value+=*condition.strVal1;
00363             value+=",";
00364             if ( condition.strVal2 )
00365                 value+=*condition.strVal2;
00366         }
00367         else
00368         {
00369             value+=QString::number( condition.val1 );
00370             value+=",";
00371             value+=QString::number( condition.val2 );
00372         }
00373         value+=")";
00374         break;
00375     }
00376     return value;
00377 }
00378 
00379 
00380 QDomElement Conditions::saveConditions( QDomDocument & doc ) const
00381 {
00382   QDomElement conditions = doc.createElement("condition");
00383   QValueList<Conditional>::const_iterator it;
00384   QDomElement child;
00385   int num = 0;
00386   QString name;
00387 
00388   for ( it = m_condList.begin(); it != m_condList.end(); ++it )
00389   {
00390     Conditional condition = *it;
00391 
00392     /* the name of the element will be "condition<n>"
00393      * This is unimportant now but in older versions three conditions were
00394      * hardcoded with names "first" "second" and "third"
00395      */
00396     name.setNum( num );
00397     name.prepend( "condition" );
00398 
00399     child = doc.createElement( name );
00400     child.setAttribute( "cond", (int) condition.cond );
00401 
00402     // TODO: saving in KSpread 1.1 | KSpread 1.2 format
00403     if ( condition.strVal1 )
00404     {
00405       child.setAttribute( "strval1", *condition.strVal1 );
00406       if ( condition.strVal2 )
00407         child.setAttribute( "strval2", *condition.strVal2 );
00408     }
00409     else
00410     {
00411       child.setAttribute( "val1", condition.val1 );
00412       child.setAttribute( "val2", condition.val2 );
00413     }
00414     if ( condition.styleName )
00415     {
00416       child.setAttribute( "style", *condition.styleName );
00417     }
00418     else
00419     {
00420       child.setAttribute( "color", condition.colorcond->name() );
00421       child.appendChild( util_createElement( "font", *condition.fontcond, doc ) );
00422     }
00423 
00424     conditions.appendChild( child );
00425 
00426     ++num;
00427   }
00428 
00429   if ( num == 0 )
00430   {
00431     /* there weren't any real conditions -- return a null dom element */
00432     return QDomElement();
00433   }
00434   else
00435   {
00436     return conditions;
00437   }
00438 }
00439 
00440 void Conditions::loadOasisConditions( const QDomElement & element )
00441 {
00442     kdDebug(36003) << "Loading conditional styles" << endl;
00443     QDomNode node( element );
00444     StyleManager * manager = m_cell->sheet()->doc()->styleManager();
00445 
00446     while ( !node.isNull() )
00447     {
00448         QDomElement elementItem = node.toElement();
00449         if ( elementItem.tagName()== "map" && elementItem.namespaceURI() == KoXmlNS::style  )
00450         {
00451             bool ok = true;
00452             kdDebug(36003) << "\tcondition: "<< elementItem.attributeNS( KoXmlNS::style, "condition", QString::null )<<endl;
00453             Conditional newCondition;
00454             loadOasisConditionValue( elementItem.attributeNS( KoXmlNS::style, "condition", QString::null ), newCondition );
00455             if ( elementItem.hasAttributeNS( KoXmlNS::style, "apply-style-name" ) )
00456             {
00457                 kdDebug(36003)<<"\tstyle: "<<elementItem.attributeNS( KoXmlNS::style, "apply-style-name", QString::null )<<endl;
00458                 newCondition.styleName = new QString( elementItem.attributeNS( KoXmlNS::style, "apply-style-name", QString::null ) );
00459                 newCondition.style = manager->style( *newCondition.styleName );
00460                 if ( !newCondition.style )
00461                     ok = false;
00462                 else
00463                     ok = true;
00464             }
00465 
00466             if ( ok )
00467                 m_condList.append( newCondition );
00468             else
00469                 kdDebug(36003) << "Error loading condition " << elementItem.nodeName()<< endl;
00470         }
00471         node = node.nextSibling();
00472     }
00473 }
00474 
00475 void Conditions::loadOasisConditionValue( const QString &styleCondition, Conditional &newCondition )
00476 {
00477     QString val( styleCondition );
00478     if ( val.contains( "cell-content()" ) )
00479     {
00480         val = val.remove( "cell-content()" );
00481         loadOasisCondition( val,newCondition );
00482     }
00483     //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
00484     //for the moment we support just int/double value, not text/date/time :(
00485     if ( val.contains( "cell-content-is-between(" ) )
00486     {
00487         val = val.remove( "cell-content-is-between(" );
00488         val = val.remove( ")" );
00489         QStringList listVal = QStringList::split( "," , val );
00490         loadOasisValidationValue( listVal, newCondition );
00491         newCondition.cond = Conditional::Between;
00492     }
00493     if ( val.contains( "cell-content-is-not-between(" ) )
00494     {
00495         val = val.remove( "cell-content-is-not-between(" );
00496         val = val.remove( ")" );
00497         QStringList listVal = QStringList::split( ",", val );
00498         loadOasisValidationValue( listVal,newCondition );
00499         newCondition.cond = Conditional::Different;
00500     }
00501 
00502 }
00503 
00504 
00505 void Conditions::loadOasisCondition( QString &valExpression, Conditional &newCondition )
00506 {
00507     QString value;
00508     if (valExpression.find( "<=" )==0 )
00509     {
00510         value = valExpression.remove( 0,2 );
00511         newCondition.cond = Conditional::InferiorEqual;
00512     }
00513     else if (valExpression.find( ">=" )==0 )
00514     {
00515         value = valExpression.remove( 0,2 );
00516         newCondition.cond = Conditional::SuperiorEqual;
00517     }
00518     else if (valExpression.find( "!=" )==0 )
00519     {
00520         //add Differentto attribute
00521         value = valExpression.remove( 0,2 );
00522         newCondition.cond = Conditional::DifferentTo;
00523     }
00524     else if ( valExpression.find( "<" )==0 )
00525     {
00526         value = valExpression.remove( 0,1 );
00527         newCondition.cond = Conditional::Inferior;
00528     }
00529     else if(valExpression.find( ">" )==0 )
00530     {
00531         value = valExpression.remove( 0,1 );
00532         newCondition.cond = Conditional::Superior;
00533     }
00534     else if (valExpression.find( "=" )==0 )
00535     {
00536         value = valExpression.remove( 0,1 );
00537         newCondition.cond = Conditional::Equal;
00538     }
00539     else
00540         kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
00541     kdDebug(36003) << "\tvalue: " << value << endl;
00542     bool ok = false;
00543     newCondition.val1 = value.toDouble(&ok);
00544     if ( !ok )
00545     {
00546         newCondition.val1 = value.toInt(&ok);
00547         if ( !ok )
00548         {
00549             newCondition.strVal1 = new QString( value );
00550             kdDebug()<<" Try to parse this value :"<<value<<endl;
00551         }
00552     }
00553 }
00554 
00555 
00556 void Conditions::loadOasisValidationValue( const QStringList &listVal, Conditional &newCondition )
00557 {
00558     bool ok = false;
00559     kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
00560 
00561     newCondition.val1 = listVal[0].toDouble(&ok);
00562     if ( !ok )
00563     {
00564         newCondition.val1 = listVal[0].toInt(&ok);
00565         if ( !ok )
00566         {
00567             newCondition.strVal1 = new QString( listVal[0] );
00568             kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
00569         }
00570     }
00571     ok=false;
00572     newCondition.val2 = listVal[1].toDouble(&ok);
00573     if ( !ok )
00574     {
00575         newCondition.val2 = listVal[1].toInt(&ok);
00576         if ( !ok )
00577         {
00578             newCondition.strVal2 = new QString( listVal[1] );
00579             kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
00580         }
00581     }
00582 }
00583 
00584 
00585 void Conditions::loadConditions( const QDomElement & element )
00586 {
00587   QDomNodeList nodeList = element.childNodes();
00588   Conditional newCondition;
00589   bool ok;
00590   StyleManager * manager = m_cell->sheet()->doc()->styleManager();
00591 
00592   for ( int i = 0; i < (int)(nodeList.length()); ++i )
00593   {
00594     newCondition.strVal1   = 0;
00595     newCondition.strVal2   = 0;
00596     newCondition.styleName = 0;
00597     newCondition.fontcond  = 0;
00598     newCondition.colorcond = 0;
00599 
00600     QDomElement conditionElement = nodeList.item( i ).toElement();
00601 
00602     ok = conditionElement.hasAttribute( "cond" );
00603 
00604     if ( ok )
00605       newCondition.cond = (Conditional::Type) conditionElement.attribute( "cond" ).toInt( &ok );
00606     else continue;
00607 
00608     if ( conditionElement.hasAttribute( "val1" ) )
00609     {
00610       newCondition.val1 = conditionElement.attribute( "val1" ).toDouble( &ok );
00611 
00612       if ( conditionElement.hasAttribute( "val2" ) )
00613         newCondition.val2 = conditionElement.attribute("val2").toDouble( &ok );
00614     }
00615 
00616     if ( conditionElement.hasAttribute( "strval1" ) )
00617     {
00618       newCondition.strVal1 = new QString( conditionElement.attribute( "strval1" ) );
00619 
00620       if ( conditionElement.hasAttribute( "strval2" ) )
00621         newCondition.strVal2 = new QString( conditionElement.attribute( "strval2" ) );
00622     }
00623 
00624     if ( conditionElement.hasAttribute( "color" ) )
00625       newCondition.colorcond = new QColor( conditionElement.attribute( "color" ) );
00626 
00627     QDomElement font = conditionElement.namedItem( "font" ).toElement();
00628     if ( !font.isNull() )
00629       newCondition.fontcond = new QFont( util_toFont( font ) );
00630 
00631     if ( conditionElement.hasAttribute( "style" ) )
00632     {
00633       newCondition.styleName = new QString( conditionElement.attribute( "style" ) );
00634       newCondition.style = manager->style( *newCondition.styleName );
00635       if ( !newCondition.style )
00636         ok = false;
00637     }
00638 
00639     if ( ok )
00640     {
00641       m_condList.append( newCondition );
00642     }
00643     else
00644     {
00645       kdDebug(36001) << "Error loading condition " << conditionElement.nodeName()<< endl;
00646     }
00647   }
00648 }
00649 
00650 bool Conditions::operator==( const Conditions& other ) const
00651 {
00652   if ( !( *m_matchedStyle == *other.m_matchedStyle ) )
00653     return false;
00654   if ( m_condList.count() != other.m_condList.count() )
00655     return false;
00656   QValueList<Conditional>::ConstIterator end( m_condList.end() );
00657   for ( QValueList<Conditional>::ConstIterator it( m_condList.begin() ); it != end; ++it )
00658   {
00659     bool found = false;
00660     QValueList<Conditional>::ConstIterator otherEnd( other.m_condList.end() );
00661     for ( QValueList<Conditional>::ConstIterator otherIt( other.m_condList.begin() ); otherIt != otherEnd; ++otherIt )
00662     {
00663       if ( (*it) == (*otherIt) )
00664         found = true;
00665     }
00666     if ( !found )
00667       return false;
00668   }
00669   return true;
00670 }
KDE Home | KDE Accessibility Home | Description of Access Keys