kspread

kspread_condition.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00003    Copyright (C) 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 Conditions::Conditions( const Cell * ownerCell )
00080   : m_cell( ownerCell ), m_matchedStyle( 0 )
00081 {
00082   Q_ASSERT( ownerCell != NULL );
00083 }
00084 
00085 Conditions::~Conditions()
00086 {
00087   m_condList.clear();
00088 }
00089 
00090 void Conditions::checkMatches()
00091 {
00092   Conditional condition;
00093 
00094   if ( currentCondition( condition ) )
00095     m_matchedStyle = condition.style;
00096   else
00097     m_matchedStyle = 0;
00098 }
00099 
00100 bool Conditions::currentCondition( Conditional & condition )
00101 {
00102   /* for now, the first condition that is true is the one that will be used */
00103 
00104   QValueList<Conditional>::const_iterator it;
00105   double value   = m_cell->value().asFloat();
00106   QString strVal = m_cell->text();
00107 
00108 
00109   for ( it = m_condList.begin(); it != m_condList.end(); ++it )
00110   {
00111     condition = *it;
00112 
00113     if ( (*it).styleName )
00114         kdDebug()<<"*it :"<<  *( ( *it ).styleName ) <<endl;
00115         
00116     kdDebug()<<"*it style :"<<(  *it ).style <<endl;
00117 
00118 
00119     if ( condition.strVal1 && m_cell->value().isNumber() )
00120       continue;
00121 
00122     switch ( condition.cond )
00123     {
00124       case Conditional::Equal:
00125       if ( condition.strVal1 )
00126       {
00127         if ( strVal == *condition.strVal1 )
00128           return true;
00129       }
00130       else
00131       if ( value - condition.val1 < DBL_EPSILON &&
00132            value - condition.val1 > (0.0 - DBL_EPSILON) )
00133       {
00134         return true;
00135       }
00136       break;
00137 
00138       case Conditional::Superior:
00139       if ( condition.strVal1 )
00140       {
00141         if ( strVal > *condition.strVal1 )
00142           return true;
00143       }
00144       else
00145       if ( value > condition.val1 )
00146       {
00147         return true;
00148       }
00149       break;
00150 
00151       case Conditional::Inferior:
00152       if ( condition.strVal1 )
00153       {
00154         if ( strVal < *condition.strVal1 )
00155           return true;
00156       }
00157       else
00158       if ( value < condition.val1 )
00159       {
00160         return true;
00161       }
00162       break;
00163 
00164       case Conditional::SuperiorEqual :
00165       if ( condition.strVal1 )
00166       {
00167         if ( strVal >= *condition.strVal1 )
00168           return true;
00169       }
00170       else
00171       if ( value >= condition.val1 )
00172       {
00173         return true;
00174       }
00175       break;
00176 
00177       case Conditional::InferiorEqual :
00178       if ( condition.strVal1 )
00179       {
00180         if ( strVal <= *condition.strVal1 )
00181           return true;
00182       }
00183       else
00184       if ( value <= condition.val1 )
00185       {
00186         return true;
00187       }
00188       break;
00189 
00190       case Conditional::Between :
00191       if ( condition.strVal1 && condition.strVal2 )
00192       {
00193         if ( strVal > *condition.strVal1 && strVal < *condition.strVal2 )
00194           return true;
00195       }
00196       else
00197       if ( ( value > QMIN(condition.val1, condition.val2 ) )
00198            && ( value < QMAX(condition.val1, condition.val2 ) ) )
00199       {
00200         return true;
00201       }
00202       break;
00203 
00204       case Conditional::Different :
00205       if ( condition.strVal1 && condition.strVal2 )
00206       {
00207         if ( strVal < *condition.strVal1 || strVal > *condition.strVal2 )
00208           return true;
00209       }
00210       else
00211       if ( ( value < QMIN(condition.val1, condition.val2 ) )
00212            || ( value > QMAX(condition.val1, condition.val2) ) )
00213       {
00214         return true;
00215       }
00216       break;
00217       case Conditional::DifferentTo :
00218       if ( condition.strVal1 )
00219       {
00220         if ( strVal != *condition.strVal1 )
00221           return true;
00222       }
00223       else
00224       if ( value != condition.val1 )
00225       {
00226         return true;
00227       }
00228       break;
00229 
00230      default:
00231       break;
00232     }
00233   }
00234   return false;
00235 }
00236 
00237 QValueList<Conditional> Conditions::conditionList() const
00238 {
00239   return m_condList;
00240 }
00241 
00242 void Conditions::setConditionList( const QValueList<Conditional> & list )
00243 {
00244   m_condList.clear();
00245 
00246   QValueList<Conditional>::const_iterator it;
00247   for ( it = list.begin(); it != list.end(); ++it )
00248   {
00249     Conditional d = *it;
00250     m_condList.append( Conditional( d ) );
00251   }
00252 }
00253 
00254 void Conditions::saveOasisConditions( KoGenStyle &currentCellStyle )
00255 {
00256     //todo fix me with kspread old format!!!
00257     if ( m_condList.isEmpty() )
00258         return;
00259     QValueList<Conditional>::const_iterator it;
00260     int i = 0;
00261     for ( it = m_condList.begin(); it != m_condList.end(); ++it, ++i )
00262     {
00263         Conditional condition = *it;
00264         //<style:map style:condition="cell-content()=45" style:apply-style-name="Default" style:base-cell-address="Sheet1.E10"/>
00265         QMap<QString, QString> map;
00266         map.insert( "style:condition", saveOasisConditionValue( condition ) );
00267         map.insert( "style:apply-style-name",  *( condition.styleName ) );
00268         //map.insert( ""style:base-cell-address", "..." );//todo
00269         currentCellStyle.addStyleMap( map );
00270     }
00271 }
00272 
00273 QString Conditions::saveOasisConditionValue( Conditional &condition)
00274 {
00275     //we can also compare text value.
00276     //todo adapt it.
00277     QString value;
00278     switch( condition.cond )
00279     {
00280       case Conditional::None:
00281         break;
00282       case Conditional::Equal:
00283         value="cell-content()=";
00284         if ( condition.strVal1 )
00285             value+=*condition.strVal1;
00286         else
00287             value+=QString::number( condition.val1 );
00288         break;
00289       case Conditional::Superior:
00290         value="cell-content()>";
00291         if ( condition.strVal1 )
00292             value+=*condition.strVal1;
00293         else
00294             value+=QString::number( condition.val1 );
00295         break;
00296       case Conditional::Inferior:
00297         value="cell-content()<";
00298         if ( condition.strVal1 )
00299             value+=*condition.strVal1;
00300         else
00301             value+=QString::number( condition.val1 );
00302         break;
00303       case Conditional::SuperiorEqual:
00304         value="cell-content()>=";
00305         if ( condition.strVal1 )
00306             value+=*condition.strVal1;
00307         else
00308             value+=QString::number( condition.val1 );
00309         break;
00310       case Conditional::InferiorEqual:
00311         value="cell-content()<=";
00312         if ( condition.strVal1 )
00313             value+=*condition.strVal1;
00314         else
00315             value+=QString::number( condition.val1 );
00316         break;
00317       case Conditional::Between:
00318         value="cell-content-is-between(";
00319         if ( condition.strVal1 )
00320         {
00321             value+=*condition.strVal1;
00322             value+=",";
00323             if ( condition.strVal2 )
00324                 value+=*condition.strVal2;
00325         }
00326         else
00327         {
00328             value+=QString::number( condition.val1 );
00329             value+=",";
00330             value+=QString::number( condition.val2 );
00331         }
00332         value+=")";
00333         break;
00334       case Conditional::DifferentTo:
00335         value="cell-content()!="; //FIXME not good here !
00336         if ( condition.strVal1 )
00337             value+=*condition.strVal1;
00338         else
00339             value+=QString::number( condition.val1 );
00340         break;
00341       case Conditional::Different:
00342         value="cell-content-is-not-between(";
00343         if ( condition.strVal1 )
00344         {
00345             value+=*condition.strVal1;
00346             value+=",";
00347             if ( condition.strVal2 )
00348                 value+=*condition.strVal2;
00349         }
00350         else
00351         {
00352             value+=QString::number( condition.val1 );
00353             value+=",";
00354             value+=QString::number( condition.val2 );
00355         }
00356         value+=")";
00357         break;
00358     }
00359     return value;
00360 }
00361 
00362 
00363 QDomElement Conditions::saveConditions( QDomDocument & doc ) const
00364 {
00365   QDomElement conditions = doc.createElement("condition");
00366   QValueList<Conditional>::const_iterator it;
00367   QDomElement child;
00368   int num = 0;
00369   QString name;
00370 
00371   for ( it = m_condList.begin(); it != m_condList.end(); ++it )
00372   {
00373     Conditional condition = *it;
00374 
00375     /* the name of the element will be "condition<n>"
00376      * This is unimportant now but in older versions three conditions were
00377      * hardcoded with names "first" "second" and "third"
00378      */
00379     name.setNum( num );
00380     name.prepend( "condition" );
00381 
00382     child = doc.createElement( name );
00383     child.setAttribute( "cond", (int) condition.cond );
00384 
00385     // TODO: saving in KSpread 1.1 | KSpread 1.2 format
00386     if ( condition.strVal1 )
00387     {
00388       child.setAttribute( "strval1", *condition.strVal1 );
00389       if ( condition.strVal2 )
00390         child.setAttribute( "strval2", *condition.strVal2 );
00391     }
00392     else
00393     {
00394       child.setAttribute( "val1", condition.val1 );
00395       child.setAttribute( "val2", condition.val2 );
00396     }
00397     if ( condition.styleName )
00398     {
00399       child.setAttribute( "style", *condition.styleName );
00400     }
00401     else
00402     {
00403       child.setAttribute( "color", condition.colorcond->name() );
00404       child.appendChild( util_createElement( "font", *condition.fontcond, doc ) );
00405     }
00406 
00407     conditions.appendChild( child );
00408 
00409     ++num;
00410   }
00411 
00412   if ( num == 0 )
00413   {
00414     /* there weren't any real conditions -- return a null dom element */
00415     return QDomElement();
00416   }
00417   else
00418   {
00419     return conditions;
00420   }
00421 }
00422 
00423 void Conditions::loadOasisConditions( const QDomElement & element )
00424 {
00425     kdDebug()<<"void Conditions::loadOasisConditions( const QDomElement & element )\n";
00426     QDomElement elementItem( element );
00427     StyleManager * manager = m_cell->sheet()->doc()->styleManager();
00428 
00429     while ( !elementItem.isNull() )
00430     {
00431         kdDebug()<<"elementItem.tagName() :"<<elementItem.tagName()<<endl;
00432         if ( elementItem.tagName()== "map" && elementItem.namespaceURI() == KoXmlNS::style  )
00433         {
00434             bool ok = true;
00435             kdDebug()<<"elementItem.attribute(style:condition ) :"<<elementItem.attributeNS( KoXmlNS::style, "condition", QString::null )<<endl;
00436             Conditional newCondition;
00437             loadOasisConditionValue( elementItem.attributeNS( KoXmlNS::style, "condition", QString::null ), newCondition );
00438             if ( elementItem.hasAttributeNS( KoXmlNS::style, "apply-style-name" ) )
00439             {
00440                 kdDebug()<<"elementItem.attribute( style:apply-style-name ) :"<<elementItem.attributeNS( KoXmlNS::style, "apply-style-name", QString::null )<<endl;
00441                 newCondition.styleName = new QString( elementItem.attributeNS( KoXmlNS::style, "apply-style-name", QString::null ) );
00442                 newCondition.style = manager->style( *newCondition.styleName );
00443                 if ( !newCondition.style )
00444                     ok = false;
00445                 else
00446                     ok = true;
00447             }
00448 
00449             if ( ok )
00450                 m_condList.append( newCondition );
00451             else
00452                 kdDebug(36001) << "Error loading condition " << elementItem.nodeName()<< endl;
00453         }
00454         elementItem = elementItem.nextSibling().toElement();
00455     }
00456 }
00457 
00458 void Conditions::loadOasisConditionValue( const QString &styleCondition, Conditional &newCondition )
00459 {
00460     QString val( styleCondition );
00461     if ( val.contains( "cell-content()" ) )
00462     {
00463         val = val.remove( "cell-content()" );
00464         loadOasisCondition( val,newCondition );
00465     }
00466     //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
00467     //for the moment we support just int/double value, not text/date/time :(
00468     if ( val.contains( "cell-content-is-between(" ) )
00469     {
00470         val = val.remove( "cell-content-is-between(" );
00471         val = val.remove( ")" );
00472         QStringList listVal = QStringList::split( "," , val );
00473         loadOasisValidationValue( listVal, newCondition );
00474         newCondition.cond = Conditional::Between;
00475     }
00476     if ( val.contains( "cell-content-is-not-between(" ) )
00477     {
00478         val = val.remove( "cell-content-is-not-between(" );
00479         val = val.remove( ")" );
00480         QStringList listVal = QStringList::split( ",", val );
00481         loadOasisValidationValue( listVal,newCondition );
00482         newCondition.cond = Conditional::Different;
00483     }
00484 
00485 }
00486 
00487 
00488 void Conditions::loadOasisCondition( QString &valExpression, Conditional &newCondition )
00489 {
00490     QString value;
00491     if (valExpression.find( "<=" )==0 )
00492     {
00493         value = valExpression.remove( 0,2 );
00494         newCondition.cond = Conditional::InferiorEqual;
00495     }
00496     else if (valExpression.find( ">=" )==0 )
00497     {
00498         value = valExpression.remove( 0,2 );
00499         newCondition.cond = Conditional::SuperiorEqual;
00500     }
00501     else if (valExpression.find( "!=" )==0 )
00502     {
00503         //add Differentto attribute
00504         value = valExpression.remove( 0,2 );
00505         newCondition.cond = Conditional::DifferentTo;
00506     }
00507     else if ( valExpression.find( "<" )==0 )
00508     {
00509         value = valExpression.remove( 0,1 );
00510         newCondition.cond = Conditional::Inferior;
00511     }
00512     else if(valExpression.find( ">" )==0 )
00513     {
00514         value = valExpression.remove( 0,1 );
00515         newCondition.cond = Conditional::Superior;
00516     }
00517     else if (valExpression.find( "=" )==0 )
00518     {
00519         value = valExpression.remove( 0,1 );
00520         newCondition.cond = Conditional::Equal;
00521     }
00522     else
00523         kdDebug()<<" I don't know how to parse it :"<<valExpression<<endl;
00524     kdDebug()<<" value :"<<value<<endl;
00525     bool ok = false;
00526     newCondition.val1 = value.toDouble(&ok);
00527     if ( !ok )
00528     {
00529         newCondition.val1 = value.toInt(&ok);
00530         if ( !ok )
00531         {
00532             newCondition.strVal1 = new QString( value );
00533             kdDebug()<<" Try to parse this value :"<<value<<endl;
00534         }
00535 
00536     }
00537 }
00538 
00539 
00540 void Conditions::loadOasisValidationValue( const QStringList &listVal, Conditional &newCondition )
00541 {
00542     bool ok = false;
00543     kdDebug()<<" listVal[0] :"<<listVal[0]<<" listVal[1] :"<<listVal[1]<<endl;
00544 
00545     newCondition.val1 = listVal[0].toDouble(&ok);
00546     if ( !ok )
00547     {
00548         newCondition.val1 = listVal[0].toInt(&ok);
00549         if ( !ok )
00550         {
00551             newCondition.strVal1 = new QString( listVal[0] );
00552             kdDebug()<<" Try to parse this value :"<<listVal[0]<<endl;
00553         }
00554     }
00555     ok=false;
00556     newCondition.val2 = listVal[1].toDouble(&ok);
00557     if ( !ok )
00558     {
00559         newCondition.val2 = listVal[1].toInt(&ok);
00560         if ( !ok )
00561         {
00562             newCondition.strVal2 = new QString( listVal[1] );
00563             kdDebug()<<" Try to parse this value :"<<listVal[1]<<endl;
00564         }
00565     }
00566 }
00567 
00568 
00569 void Conditions::loadConditions( const QDomElement & element )
00570 {
00571   QDomNodeList nodeList = element.childNodes();
00572   Conditional newCondition;
00573   bool ok;
00574   StyleManager * manager = m_cell->sheet()->doc()->styleManager();
00575 
00576   for ( int i = 0; i < (int)(nodeList.length()); ++i )
00577   {
00578     newCondition.strVal1   = 0;
00579     newCondition.strVal2   = 0;
00580     newCondition.styleName = 0;
00581     newCondition.fontcond  = 0;
00582     newCondition.colorcond = 0;
00583 
00584     QDomElement conditionElement = nodeList.item( i ).toElement();
00585 
00586     ok = conditionElement.hasAttribute( "cond" );
00587 
00588     if ( ok )
00589       newCondition.cond = (Conditional::Type) conditionElement.attribute( "cond" ).toInt( &ok );
00590     else continue;
00591 
00592     if ( conditionElement.hasAttribute( "val1" ) )
00593     {
00594       newCondition.val1 = conditionElement.attribute( "val1" ).toDouble( &ok );
00595 
00596       if ( conditionElement.hasAttribute( "val2" ) )
00597         newCondition.val2 = conditionElement.attribute("val2").toDouble( &ok );
00598     }
00599 
00600     if ( conditionElement.hasAttribute( "strval1" ) )
00601     {
00602       newCondition.strVal1 = new QString( conditionElement.attribute( "strval1" ) );
00603 
00604       if ( conditionElement.hasAttribute( "strval2" ) )
00605         newCondition.strVal2 = new QString( conditionElement.attribute( "strval2" ) );
00606     }
00607 
00608     if ( conditionElement.hasAttribute( "color" ) )
00609       newCondition.colorcond = new QColor( conditionElement.attribute( "color" ) );
00610 
00611     QDomElement font = conditionElement.namedItem( "font" ).toElement();
00612     if ( !font.isNull() )
00613       newCondition.fontcond = new QFont( util_toFont( font ) );
00614 
00615     if ( conditionElement.hasAttribute( "style" ) )
00616     {
00617       newCondition.styleName = new QString( conditionElement.attribute( "style" ) );
00618       newCondition.style = manager->style( *newCondition.styleName );
00619       if ( !newCondition.style )
00620         ok = false;
00621     }
00622 
00623     if ( ok )
00624     {
00625       m_condList.append( newCondition );
00626     }
00627     else
00628     {
00629       kdDebug(36001) << "Error loading condition " << conditionElement.nodeName()<< endl;
00630     }
00631   }
00632 }
KDE Home | KDE Accessibility Home | Description of Access Keys