kspread Library API Documentation

kspread_numformat.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003 Norbert Andres, nandres@web.de
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 
00020 /*
00021 The only function visible from the outside is formatNumber, whose
00022 implementation is at the very bottom of this file. Its prototype
00023 is declared in kspread_util.h. However, it is not used anywhere.
00024 TODO: Find out whether it is supposed to be used instead of
00025 something else (locale()->formatNumber() maybe?) and either use it
00026 or get rid of it.
00027 Tomas
00028 */
00029 #include <qdatetime.h>
00030 #include <qmap.h>
00031 #include <qstring.h>
00032 #include <klocale.h>
00033 
00034 #include <ctype.h>
00035 #include <math.h>
00036 
00037 #include "kspread_util.h"
00038 #include "kspread_value.h"
00039 
00040 enum { Unkown, TimeDate, Number, Scientific, Fraction } Type;
00041 
00042 namespace KSpreadNumFormat_Local
00043 {
00044   QString g_Monday;
00045   QString g_Tuesday;
00046   QString g_Wednesday;
00047   QString g_Thursday;
00048   QString g_Friday;
00049   QString g_Saturday;
00050   QString g_Sunday;
00051   QString g_Mon;
00052   QString g_Tue;
00053   QString g_Wed;
00054   QString g_Thu;
00055   QString g_Fri;
00056   QString g_Sat;
00057   QString g_Sun;
00058   QString g_January;
00059   QString g_February;
00060   QString g_March;
00061   QString g_April;
00062   QString g_MayL;
00063   QString g_June;
00064   QString g_July;
00065   QString g_August;
00066   QString g_September;
00067   QString g_October;
00068   QString g_November;
00069   QString g_December;
00070   QString g_Jan;
00071   QString g_Feb;
00072   QString g_Mar;
00073   QString g_Apr;
00074   QString g_May;
00075   QString g_Jun;
00076   QString g_Jul;
00077   QString g_Aug;
00078   QString g_Sep;
00079   QString g_Oct;
00080   QString g_Nov;
00081   QString g_Dec;
00082 
00083   struct DateTime
00084   {
00085     int year;
00086     int month;
00087     int day;
00088     int hour;
00089     int minute;
00090     int second;
00091   };
00092 
00093   struct ConvertionInfo
00094   {
00095     DateTime * dt;
00096 
00097     int        rightOpt;
00098     int        rightReq;
00099     int        leftReq;
00100     int        rightSpace;
00101     int        leftSpace;
00102     int        upReq;
00103 
00104     int        reqCounter;
00105     int        reqFirst;
00106     int        optFirst;
00107 
00108     bool       ampm;
00109 
00110     bool       thSet;
00111     bool       showMinus;
00112     bool       negRed;
00113     bool       negBr;
00114     QString    postfix;
00115     QString    prefix;
00116   };
00117 
00118   class BaseFormat
00119   {
00120    public: 
00121     int        type;
00122 
00123     QString    postfix;
00124     QString    prefix;
00125   };
00126 
00127   class NumberFormat : public BaseFormat
00128   {
00129    public:
00130     bool       thSet;
00131     bool       showMinus;
00132     bool       negRed;
00133     bool       negBr;
00134     int        rightOpt;
00135     int        rightReq;
00136     int        leftReq;
00137     int        rightSpace;
00138     int        leftSpace;
00139   };
00140 
00141   class FractionFormat : public BaseFormat
00142   {
00143    public:
00144     bool       thSet;
00145     bool       showMinus;
00146     bool       negRed;
00147     bool       negBr;
00148     int        optFirst;
00149     int        reqFirst;
00150     int        reqCounter;
00151     int        fraction;
00152     int        fractionDigists;
00153   };
00154 
00155   class DateTimeFormat : public BaseFormat
00156   {
00157    public:
00158     bool       ampm;    
00159     QString    format;
00160   };
00161 
00162   class ScientificFormat : public BaseFormat
00163   {
00164    public:
00165     bool       thSet;
00166     int        leftReq;
00167     int        rightReq;
00168     int        rightOpt;
00169     int        upReq;
00170     bool       showMinus;
00171     bool       negRed;
00172     bool       negBr;
00173     int        rightSpace;
00174     int        leftSpace;
00175   };
00176 
00177   class FormatStore
00178   {
00179    public:
00180 
00181     int  getType( QString const & format, BaseFormat * f ) const
00182     {
00183       FormatMap::const_iterator iter = m_formats.find( format );
00184       if ( iter == m_formats.end() )
00185       {
00186         f = 0;
00187         return -1;
00188       }
00189 
00190       f = iter.data();
00191       return f->type;
00192     }
00193 
00194     void addFraction( QString const & format, FractionFormat * f )
00195     {
00196       m_formats.insert( format, f );
00197     }
00198 
00199     void addNumber( QString const & format, NumberFormat * n )
00200     {
00201       m_formats.insert( format, n );
00202     }
00203 
00204     void addDateTime( QString const & format, DateTimeFormat * d )
00205     {
00206       m_formats.insert( format, d );
00207     }
00208 
00209     void addScientific( QString const & format, ScientificFormat * d )
00210     {
00211       m_formats.insert( format, d );
00212     }
00213 
00214    private:
00215     class FormatMap : public QMap<QString, BaseFormat *> {};
00216     FormatMap    m_formats;
00217   };
00218 
00219   QChar          g_dcSymbol;
00220   QChar          g_thSymbol;
00221   QChar          g_posSymbol;
00222   QChar          g_negSymbol;
00223   DateTime       g_dateTime;
00224   ConvertionInfo g_convertionInfo;
00225   bool           g_init = false;
00226 
00227   FormatStore    g_formatStore;
00228 }
00229 
00230 using namespace KSpreadNumFormat_Local;
00231 
00232 void resetGlobals()
00233 {
00234   g_convertionInfo.dt = 0;
00235   g_convertionInfo.thSet = false;
00236   g_convertionInfo.showMinus = true;
00237   g_convertionInfo.negRed = false;
00238   g_convertionInfo.negBr = false;
00239   g_convertionInfo.reqCounter = 0;
00240   g_convertionInfo.reqFirst = 0;
00241   g_convertionInfo.prefix = "";
00242   g_convertionInfo.postfix = "";
00243   g_convertionInfo.rightOpt = 0;
00244   g_convertionInfo.rightReq = 0;
00245   g_convertionInfo.leftReq = 0;
00246   g_convertionInfo.rightSpace = 0;
00247   g_convertionInfo.leftSpace = 0;
00248   g_convertionInfo.optFirst = 0;
00249   g_convertionInfo.upReq = 0;
00250   g_convertionInfo.ampm = false;
00251 }
00252 
00253 void initGlobals( KLocale const * const locale )
00254 {
00255   g_Monday    = locale->weekDayName( 1, false );
00256   g_Tuesday   = locale->weekDayName( 2, false );
00257   g_Wednesday = locale->weekDayName( 3, false );
00258   g_Thursday  = locale->weekDayName( 4, false );
00259   g_Friday    = locale->weekDayName( 5, false );
00260   g_Saturday  = locale->weekDayName( 6, false );
00261   g_Sunday    = locale->weekDayName( 7, false );
00262   g_Mon       = locale->weekDayName( 1, true );
00263   g_Tue       = locale->weekDayName( 2, true );
00264   g_Wed       = locale->weekDayName( 3, true );
00265   g_Thu       = locale->weekDayName( 4, true );
00266   g_Fri       = locale->weekDayName( 5, true );
00267   g_Sat       = locale->weekDayName( 6, true );
00268   g_Sun       = locale->weekDayName( 7, true );
00269   g_January   = locale->monthName(  1, false );
00270   g_February  = locale->monthName(  2, false );
00271   g_March     = locale->monthName(  3, false );
00272   g_April     = locale->monthName(  4, false );
00273   g_MayL      = locale->monthName(  5, false );
00274   g_June      = locale->monthName(  6, false );
00275   g_July      = locale->monthName(  7, false );
00276   g_August    = locale->monthName(  8, false );
00277   g_September = locale->monthName(  9, false );
00278   g_October   = locale->monthName( 10, false );
00279   g_November  = locale->monthName( 11, false );
00280   g_December  = locale->monthName( 12, false );
00281   g_Jan       = locale->monthName(  1, true );
00282   g_Feb       = locale->monthName(  2, true );
00283   g_Mar       = locale->monthName(  3, true );
00284   g_Apr       = locale->monthName(  4, true );
00285   g_May       = locale->monthName(  5, true );
00286   g_Jun       = locale->monthName(  6, true );
00287   g_Jul       = locale->monthName(  7, true );
00288   g_Aug       = locale->monthName(  8, true );
00289   g_Sep       = locale->monthName(  9, true );
00290   g_Oct       = locale->monthName( 10, true );
00291   g_Nov       = locale->monthName( 11, true );
00292   g_Dec       = locale->monthName( 12, true );
00293 
00294   g_dcSymbol  = locale->decimalSymbol()[0];
00295   g_thSymbol  = locale->thousandsSeparator()[0];
00296   g_posSymbol = locale->positiveSign()[0];
00297   g_negSymbol = locale->negativeSign()[0];
00298 
00299   g_init = true;
00300 }
00301 
00302 void convertDateTime( KSpreadValue const & value )
00303 {
00304   QDateTime dt( value.asDateTime() );
00305   QDate d( dt.date() );
00306   QTime t( dt.time() );
00307 
00308   g_dateTime.year   = d.year();
00309   g_dateTime.month  = d.month();
00310   g_dateTime.day    = d.day();
00311   g_dateTime.hour   = t.hour();
00312   g_dateTime.minute = t.minute();
00313   g_dateTime.second = t.second();
00314 
00315   g_convertionInfo.dt = &g_dateTime;
00316 }
00317 
00318 void parseNegativePart( QString & format, int i, 
00319                         int l, bool acceptDigits )
00320 {
00321   g_convertionInfo.showMinus = false;
00322   g_convertionInfo.negRed    = false;
00323   g_convertionInfo.negRed    = false;
00324   bool end = false;
00325 
00326   while ( i < l && !end)
00327   {
00328     QChar c( format[i] );
00329     switch( c )
00330     {
00331      case '-':
00332       g_convertionInfo.showMinus = true;
00333       break;
00334      case '(':
00335       g_convertionInfo.negBr = true;
00336       break;
00337      case '[':
00338       if ( format.find( "[red]", i, false ) == i )
00339       {
00340         g_convertionInfo.negRed = true;
00341         i += 5;
00342       }
00343       break;
00344      default:
00345       end = true;
00346     }
00347     ++i;
00348   }
00349 
00350   // find postfix
00351   bool quote = false;
00352   for ( int j = l - 1; j > i; --j )
00353   {
00354     if ( format[j] == '"' )
00355     {
00356       quote = !quote;
00357       continue;
00358     }
00359 
00360     if ( !quote && ( format[j] == '0' || format[j] != '?' 
00361                      || format[j] != '#' 
00362                      || ( format[j].isDigit() && acceptDigits ) ) )
00363     {
00364       g_convertionInfo.postfix = format.mid( j + 1 );
00365       format.remove( (unsigned int) (j + 1), (unsigned int) (l - j) );
00366       break;
00367     }
00368   }
00369 
00370   int p = g_convertionInfo.postfix.find( '"' );
00371   while ( p != -1 )
00372   {
00373     g_convertionInfo.postfix.remove( p, 1 );
00374 
00375     p = g_convertionInfo.postfix.find( '"', p );
00376   }
00377 }
00378 
00379 void createNumberStruct( BaseFormat * data, QString const & format, bool insert )
00380 {
00381   NumberFormat * d = new NumberFormat();
00382   d->type       = Number;
00383   d->prefix     = g_convertionInfo.prefix;
00384   d->postfix    = g_convertionInfo.postfix;
00385   d->thSet      = g_convertionInfo.thSet;
00386   d->showMinus  = g_convertionInfo.showMinus;
00387   d->negRed     = g_convertionInfo.negRed;
00388   d->negBr      = g_convertionInfo.negBr;
00389   d->rightOpt   = g_convertionInfo.rightOpt;
00390   d->rightReq   = g_convertionInfo.rightReq;
00391   d->leftReq    = g_convertionInfo.leftReq;
00392   d->rightSpace = g_convertionInfo.rightSpace;
00393   d->leftSpace  = g_convertionInfo.leftSpace;
00394 
00395   if ( insert )
00396     g_formatStore.addNumber( format, d );
00397   data = d;
00398 }
00399 
00400 void createDateTimeStruct( BaseFormat * data, QString const & format, 
00401                            QString const & optFormat, bool insert )
00402 {
00403   DateTimeFormat * d = new DateTimeFormat();
00404   d->type       = TimeDate;
00405   d->prefix     = g_convertionInfo.prefix;
00406   d->postfix    = g_convertionInfo.postfix;
00407   d->ampm       = g_convertionInfo.ampm;
00408   d->format     = optFormat;
00409 
00410   if ( insert )
00411     g_formatStore.addDateTime( format, d );
00412   data = d;
00413 }
00414 
00415 void createScientificStruct( BaseFormat * data, QString const & format, bool insert )
00416 {
00417   ScientificFormat * d = new ScientificFormat();
00418   d->type       = Scientific;
00419   d->prefix     = g_convertionInfo.prefix;
00420   d->postfix    = g_convertionInfo.postfix;
00421   d->thSet      = g_convertionInfo.thSet;
00422   d->showMinus  = g_convertionInfo.showMinus;
00423   d->negRed     = g_convertionInfo.negRed;
00424   d->negBr      = g_convertionInfo.negBr;
00425   d->rightOpt   = g_convertionInfo.rightOpt;
00426   d->rightReq   = g_convertionInfo.rightReq;
00427   d->leftReq    = g_convertionInfo.leftReq;
00428   d->rightSpace = g_convertionInfo.rightSpace;
00429   d->leftSpace  = g_convertionInfo.leftSpace;
00430   d->upReq      = g_convertionInfo.upReq;
00431 
00432   if ( insert )
00433     g_formatStore.addScientific( format, d );
00434   data = d;
00435 }
00436 
00437 
00438 int doPreScan( QString & format, QString const & formatBack, KLocale const * const /* locale */,
00439                bool insert, BaseFormat * data )
00440 {  
00441   int type = g_formatStore.getType( format, data );
00442   if ( data != 0 )
00443     return type;
00444 
00445   resetGlobals();
00446 
00447   int l = format.length();
00448   int i = 0;
00449   int thFound = false;
00450   int leftReq  = 0;
00451   int leftOpt  = 0;
00452   int rightOpt = 0;
00453   int spaceInNum = -1;
00454   bool dcSeen = false;
00455   bool endFixed = false;
00456 
00457   FractionFormat * df = 0;
00458   int f   = 0;
00459   int d   = 0;
00460   int len = 0;
00461   int n   = 0;
00462   bool ok = false;
00463   QString frac;
00464 
00465   while ( i < l )
00466   {
00467     QString s;
00468     if ( endFixed )
00469     {
00470       g_convertionInfo.postfix += format.mid( i );
00471       format.remove( i, l - i );
00472       break;
00473     }
00474     QChar ch( format[i] );
00475     switch( ch )
00476     {
00477      case '[':
00478       if ( type == Number )
00479         endFixed = true;
00480 
00481       if ( format[ i + 1] == '$' )
00482       {
00483         i += 2;
00484         bool found = false;
00485         while ( i < l && format[i] != ']' )
00486         {
00487           if ( format[i] == '-' )
00488             found = true;
00489           if ( !found )
00490           {
00491             if ( !endFixed )
00492               g_convertionInfo.prefix += format[i];
00493             else
00494               g_convertionInfo.postfix += format[i];
00495             format.remove( i, 1 );
00496             --i; --l;
00497           }
00498           ++i;
00499         }
00500       }
00501       else
00502       {
00503         if ( i + 1 >= l )  
00504         {
00505           g_convertionInfo.postfix += '[';
00506           format.remove( i, 1 );
00507           --l; --i;
00508         }
00509         else
00510           if ( ( format[ i + 1].lower() != 's' )
00511                && ( format[ i + 1].lower() != 'm' )
00512                && ( format[ i + 1].lower() != 'h' ) )
00513           {
00514             // strange!
00515 
00516             if ( endFixed )
00517               g_convertionInfo.postfix += format[i];
00518             else
00519               g_convertionInfo.prefix  += format[i];
00520             format.remove( i, 1 );
00521             --l; --i;
00522           }
00523           else
00524           {
00525             type = TimeDate;
00526             ++i;
00527             QChar c( format[i] );
00528             ++i;
00529             while ( i < l && format[i] != ']' )
00530             {
00531               if ( format[i] != c )
00532               {
00533                 format.remove( i, 1 );
00534                 --l; --i;
00535                 break;
00536               }
00537               ++i;
00538             }
00539           }
00540       }
00541       break;
00542 
00543      case '¤':
00544      case '$':
00545      case '¥':
00546      case '£':
00547      case '%':
00548       if ( type == Number )
00549         endFixed = true;
00550 
00551       if ( endFixed )
00552         g_convertionInfo.postfix += format[i];
00553       else
00554         g_convertionInfo.prefix  += format[i];
00555       format.remove( i, 1 );
00556       --i; --l;
00557       break;
00558 
00559      case '#':
00560       type = Number;
00561       if ( !dcSeen && leftReq > 0 )
00562       { // 00##.00 <= remove the '#'
00563         format.remove( i, 1 );
00564         --l; --i;
00565       }
00566       if ( !dcSeen )
00567         ++leftOpt;
00568       else
00569         ++g_convertionInfo.rightOpt;
00570       break;
00571 
00572      case '0':
00573       if ( spaceInNum > 0 )
00574       { // for fractions
00575         ++g_convertionInfo.reqCounter;
00576         break;
00577       }
00578       type = Number;
00579       if ( !dcSeen && rightOpt > 0 )
00580       { // 00##.##00 <= remove the '0'
00581         format.remove( i, 1 );
00582         --l; --i;
00583       }
00584       if ( !dcSeen )
00585         ++g_convertionInfo.leftReq;
00586       else
00587         ++g_convertionInfo.rightReq;
00588       break;
00589 
00590      case '/':
00591       if ( ( i + 1 < l ) && ( format[i + 1] == ' ' ) )
00592         ++i;
00593       while ( i < l )
00594       {
00595         if ( format[i] != '?' && !format[i].isDigit() && format[i] != '#' )
00596         {
00597           g_convertionInfo.postfix = format.mid(i);
00598           format.remove( i, l - i );
00599           break;
00600         }
00601         else 
00602         {
00603           ++d;
00604           frac += format[i];
00605         }
00606         ++i;
00607       }
00608       if ( i < l )
00609       {
00610         if ( format[i] == ';' )
00611         {
00612           ++i;
00613           parseNegativePart( format, i, l, true );
00614         }
00615         else
00616           if ( i + 3 < l )
00617           {
00618             if ( ( format[i + 1] == ')' ) && ( format[i + 2] == ';' ) )
00619             {
00620               i += 3;
00621               parseNegativePart( format, i, l, true );
00622             }
00623           }
00624       }      
00625 
00626       ok = false;
00627       f = frac.toInt( &ok );
00628 
00629       df = new FractionFormat();
00630       if ( ok )
00631         df->fraction = f;
00632       else
00633         df->fraction = -1;
00634       df->type       = Fraction;
00635       df->thSet      = g_convertionInfo.thSet;
00636       df->showMinus  = g_convertionInfo.showMinus;
00637       df->negRed     = g_convertionInfo.negRed;
00638       df->negBr      = g_convertionInfo.negBr;
00639       df->fractionDigists = d;
00640       df->reqCounter = g_convertionInfo.reqCounter;
00641       df->reqFirst   = g_convertionInfo.reqFirst;
00642       df->prefix     = g_convertionInfo.prefix;
00643       df->postfix    = g_convertionInfo.postfix;
00644 
00645       if ( insert )
00646         g_formatStore.addFraction( formatBack, df );
00647       data = df;
00648 
00649       return Fraction;
00650       break;
00651 
00652      case ',':
00653       if ( type == Unkown )
00654       {
00655         g_convertionInfo.prefix += ',';
00656       }      
00657       else if ( type == Number )
00658       {
00659         if ( dcSeen )
00660         {
00661           g_convertionInfo.postfix += ',';
00662           format.remove( i, 1 );
00663           --i; --l;
00664         }
00665         else
00666         {
00667           if ( thFound )
00668           {
00669             format.remove( i, 1 );
00670             --l; --i;
00671           }
00672           else
00673             thFound = true;
00674         }
00675       }
00676 
00677      case '.': // decimal point
00678       if ( type == Unkown )
00679       {
00680         int j = i + 1;
00681         if ( ( j < l ) 
00682              && ( format[j] == '0' || format[j] == '#' ) )
00683         {
00684           type = Number;
00685           dcSeen = true;
00686         }
00687         else
00688         {
00689           if ( j == l )
00690             g_convertionInfo.postfix += '.';
00691           else
00692             g_convertionInfo.prefix += '.';
00693           format.remove( i, 1 );
00694           --i; --l;
00695         }
00696       }
00697       else if ( type == Number )
00698       {
00699         dcSeen = true;
00700       }
00701       break;
00702 
00703      case '*':
00704       break;
00705       
00706      case '"':
00707       n = i;
00708       ++i;
00709       while ( i < l && format[i] != '"' )
00710       {
00711         s += format[i];
00712         ++i;
00713       }
00714       if ( type == Unkown )
00715         g_convertionInfo.prefix += s;
00716       else 
00717       {
00718         g_convertionInfo.postfix += s;
00719       }
00720       len = s.length();
00721       format.remove( i, len );
00722       i -= len; l -= len;
00723       break;
00724 
00725      case '_':
00726       if ( type == Number )
00727       {
00728         bool pr = false;
00729         if ( i + 3 < l )
00730         {
00731           if ( ( format[i + 1] != ')' ) || ( format[i + 2] != ';' ) )
00732             pr = true;
00733           else
00734           {
00735             i += 3;
00736             parseNegativePart( format, i, l, false );
00737 
00738             createNumberStruct( data, formatBack, insert );
00739 
00740             return Number;
00741           }
00742         }
00743 
00744         if ( pr )
00745         {
00746           g_convertionInfo.postfix += format.mid( i );
00747           format.remove( i, l - i );
00748 
00749           createNumberStruct( data, formatBack, insert );
00750 
00751           return Number;
00752         }
00753       }    
00754       break;
00755 
00756      case ';':
00757       if ( type == Unkown )
00758       {
00759         g_convertionInfo.postfix += ';';
00760         format.remove( i, 1 );
00761         --i; --l;
00762       }
00763       else
00764       {
00765         if ( type == Number )
00766         {
00767           ++i;
00768           parseNegativePart( format, i, l, false );
00769 
00770           createNumberStruct( data, formatBack, insert );
00771 
00772           return Number;
00773         }
00774         else
00775           if ( type == Scientific )
00776           {
00777             ++i;
00778             parseNegativePart( format, i, l, false );
00779 
00780             createScientificStruct( data, formatBack, insert );
00781 
00782             return Scientific;
00783           }
00784       }
00785 
00786      case ' ':
00787       if ( type == Number )
00788       {
00789         g_convertionInfo.optFirst = (leftOpt > 0 ? leftOpt : 0);
00790         g_convertionInfo.reqFirst = (leftReq > 0 ? leftReq : 0);
00791 
00792         spaceInNum = i;
00793         g_convertionInfo.postfix += ' ';
00794       }
00795       else if ( type == Unkown )
00796       {
00797         g_convertionInfo.prefix += ' ';
00798         format.remove( i, 1 );
00799         --i; --l;
00800       }      
00801       break;
00802 
00803      case 'A':
00804      case 'a':
00805       if ( type == TimeDate || type == Unkown )
00806       {
00807         if ( ( i + 1 < l ) && ( format[i + 1].lower() == 'm' ) )
00808         {
00809           g_convertionInfo.ampm = true;
00810           ++i;
00811           if ( ( i + 3 < l ) && ( format[i + 1] == '/' )
00812                && ( format[i + 2].lower() == 'p' ) 
00813                && ( format[i + 3].lower() == 'm' ) )
00814           {
00815             i += 3;
00816           }
00817         }
00818         else if ( type == Unkown )
00819         {
00820           g_convertionInfo.prefix += format[i];
00821           format.remove( i, 1 );
00822           --i; --l;
00823         }
00824       }
00825       else 
00826       {
00827         if ( !endFixed )
00828           endFixed = true;
00829         g_convertionInfo.postfix += format[i];
00830         format.remove( i, 1 );
00831         --i; --l;
00832       }
00833       break;
00834 
00835      case 'P':
00836      case 'p':
00837       if ( type == TimeDate || type == Unkown )
00838       {
00839         if ( ( i + 1 < l ) && ( format[i + 1].lower() == 'm' ) )
00840         {
00841           g_convertionInfo.ampm = true;
00842           i += 1;
00843         }
00844         else if ( type == Unkown )
00845         {
00846           g_convertionInfo.prefix += format[i];
00847           format.remove( i, 1 );
00848           --i; --l;
00849         }
00850       }
00851       else
00852       {
00853         if ( !endFixed )
00854           endFixed = true;
00855         g_convertionInfo.postfix += format[i];
00856         format.remove( i, 1 );
00857         --i; --l;
00858       }
00859       break;
00860 
00861      case 'M':
00862      case 'm':
00863       if ( type == Unkown )
00864         type = TimeDate;
00865       else if ( type != TimeDate )
00866         endFixed = true;        
00867       break;
00868 
00869      case 'S':
00870      case 's':
00871      case 'H':
00872      case 'h':
00873       if ( type != Unkown && type != TimeDate )
00874         endFixed = true;        
00875       else
00876         type = TimeDate;
00877       break;
00878 
00879      case 'D':
00880      case 'd':
00881      case 'Y':
00882      case 'y':
00883       if ( type != Unkown && type != TimeDate )
00884         endFixed = true;        
00885       else
00886         type = TimeDate;
00887       break;
00888 
00889      default:
00890       if ( type == Unkown )
00891       {
00892         g_convertionInfo.prefix += format[i];
00893         format.remove( i, 1 );
00894         --i; --l;
00895       }
00896       else if ( type == Number || type == Scientific 
00897                 || type == Fraction )
00898       {
00899         endFixed = true;
00900         g_convertionInfo.postfix += format[i];
00901         format.remove( i, 1 );
00902         --l; --i;      
00903       }
00904     }
00905 
00906     ++i;
00907   }
00908 
00909   if ( type == Number )
00910     createNumberStruct( data, formatBack, insert );
00911   else if ( type == TimeDate )
00912     createDateTimeStruct( data, formatBack, format, insert );
00913   else if ( type == Scientific )
00914     createScientificStruct( data, formatBack, insert );
00915 
00916   return type;
00917 }
00918 
00919 void createNumber( QString & result, KSpreadValue const & value, 
00920                    QString const & /*format*/, bool & setRed,
00921                    NumberFormat const * const data )
00922 {
00923   int    prec = data->rightReq + data->rightOpt;
00924   double num  = value.asFloat();
00925   double m[]  = { 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10 };
00926   double mm   = ( prec > 10 ) ? pow( 10.0, prec ) : m[prec];
00927 
00928   num = floor( fabs( num ) * mm + 0.5 ) / mm;
00929 
00930   bool negative = ( num < 0 ? true : false );
00931   double nnum = ( negative ? -num : num );
00932 
00933   result = QString::number( nnum, 'f', prec );
00934 
00935   int pos = result.find( '.' );
00936   if ( pos >= 0 )
00937   {
00938     result = result.replace( pos, 1, g_dcSymbol );
00939 
00940     // remove '0' from the end if not required
00941     if ( data->rightOpt > 0 )
00942     {      
00943       int i = result.length() - 1; // index
00944       int n = result.length() - data->rightOpt;
00945 
00946       for ( ; i > n; --i )
00947       {
00948         if ( result[i] != '0' )
00949           break;
00950       }
00951       result = result.left( i + 1 ); //length
00952 
00953       if ( i == pos ) // just decimal point
00954         result = result.remove( i, 1 );
00955     }
00956 
00957     // prepend '0' if wanted
00958     while ( data->leftReq > pos )
00959     {
00960       result.prepend( '0' );
00961       ++pos;
00962     }
00963 
00964     // put in thousand symbol if wanted
00965     if ( data->thSet && pos > 3 )
00966     {
00967       int l = pos - 3;
00968       while ( 0 < l )
00969       {
00970         result.insert( l, g_thSymbol );
00971         l -= 3;
00972       }
00973     }
00974   }
00975 
00976   if ( data->leftSpace > 0 )
00977   {
00978     for ( int i = 0; i < data->leftSpace; ++i )
00979       result.prepend( ' ' );
00980   }
00981   if ( data->rightSpace > 0 )
00982   {
00983     for ( int i = 0; i < data->rightSpace; ++i )
00984       result.append( ' ' );
00985   }
00986 
00987   if ( negative )
00988   { 
00989     if ( data->showMinus )
00990       result.prepend( g_negSymbol );
00991 
00992     if ( data->negBr )
00993     {
00994       result.prepend( '(' );
00995       result.append( ')' );
00996     }
00997 
00998     if ( data->negRed )
00999       setRed = true;
01000   }
01001 
01002   result.prepend( data->prefix );
01003   result.append( data->postfix );
01004 }
01005 
01006 void createFraction( QString & result, KSpreadValue const & value, 
01007                      QString const & /*format*/,  bool & setRed,
01008                      FractionFormat const * const data )
01009 {
01010   double num = value.asFloat();
01011 
01012   bool negative = ( num < 0 ? true : false );
01013 
01014   double fnum = floor( negative ? -num : num );
01015   
01016   double dec = num - fnum;
01017   double fraction;
01018   int index = 0;
01019 
01020   if ( data->fraction <= 0 )
01021   {
01022     // #,### ?/???
01023     double nnum = ( negative ? -num : num );
01024     double precision, denominator, numerator;
01025     int index = 2 + data->fractionDigists;
01026     int limit = 9;
01027     if ( data->fractionDigists == 2 )
01028       limit += 90;
01029     if ( data->fractionDigists >= 3 )
01030       limit += 990;
01031 
01032     do 
01033     {
01034       double val1   = nnum;
01035       double val2   = rint( nnum );
01036       double inter2 = 1;
01037       double inter4, p,  q;
01038       inter4 = p = q = 0.0;
01039 
01040       precision   = pow( 10.0, - index );
01041       numerator   = val2;
01042       denominator = 1;
01043 
01044       while ( fabs( numerator / denominator - nnum ) > precision ) 
01045       {
01046         val1 = (1 / ( val1 - val2 ) );
01047         val2 = rint( val1 );
01048         p = val2 * numerator + inter2;
01049         q = val2 * denominator + inter4;
01050         inter2 = numerator;
01051         inter4 = denominator;
01052 
01053         numerator = p;
01054         denominator = q;
01055       }
01056       --index;
01057     } while ( fabs( denominator ) > limit );
01058 
01059     index    = (int) fabs( numerator );
01060     fraction = (int) fabs( denominator );
01061   }
01062   else
01063   {
01064     // # #/4
01065     fraction = data->fraction;
01066 
01067     double calc = 0.0;
01068     double diff = dec;
01069     double d;
01070     for ( int i = 1; i <= fraction; ++i ) 
01071     {
01072       calc = i * 1.0 / index;
01073       d = fabs( dec - calc );
01074       if ( d < diff ) 
01075       {
01076         index = i;
01077         diff = d;
01078       }
01079     }
01080   }
01081    
01082   // ? index/fraction
01083 
01084   // 2.25:  #/4 => 9/4
01085   if ( data->optFirst == 0 && data->reqFirst == 0 && fnum > 0 )
01086     index += (int) (fnum * fraction);
01087   
01088   QString frac;
01089   QString left;
01090   if ( index > 0 )
01091   {
01092     QString numerator;
01093     QString denominator;
01094     
01095     numerator = QString::number( index );
01096     int n = numerator.length() - data->reqCounter;
01097     for ( int i = 0; i < n; ++i )
01098     {
01099       numerator.prepend( '0' );
01100     }
01101     
01102     denominator = QString::number( fraction );
01103     frac = numerator + '/' + denominator;
01104   }
01105   
01106   if ( data->optFirst > 0 || data->reqFirst > 0 )
01107   {
01108     if ( fnum == 0 && data->reqFirst > 0 )
01109     {
01110       for ( int i = 0; i < data->reqFirst; ++i )
01111         left += '0';
01112     }
01113     else if ( fnum > 0 )
01114     {
01115       left = QString::number( fnum );
01116       int n = data->reqFirst - left.length();
01117       if ( n > 0 )
01118       {
01119         for ( int i = 0; i < n; ++i )
01120         {
01121           left.prepend( '0' );
01122         }
01123       }
01124     }
01125   }
01126   
01127   if ( data->thSet )
01128   {
01129     int l = left.length() - 3;
01130     while ( 0 < l )
01131     {
01132       left.insert( l, g_thSymbol );
01133       l -= 3;
01134     }
01135   }
01136 
01137   left = left + ' ' + frac;
01138 
01139   if ( negative )
01140   {
01141     if ( data->showMinus )
01142       left.prepend( g_negSymbol );
01143 
01144     if ( data->negBr )
01145     {
01146       left.prepend( '(' );
01147       left.append( ')' );
01148     }
01149 
01150     if ( data->negRed )
01151       setRed = true;
01152   }
01153 
01154   result = left;
01155 }
01156 
01157 void createScientific( QString & result, KSpreadValue const & value, 
01158                        QString const & /*format*/, bool & setRed,
01159                        ScientificFormat const * const data )
01160 {
01161   double num = value.asFloat();
01162 
01163   bool negative   = ( num < 0 ? true : false );
01164   double nnum     = ( negative ? -num : num );
01165 
01166   result = QString::number( nnum, 'E', data->rightReq + data->rightOpt );
01167   
01168   int pos = result.find( '.' );
01169   if ( pos >= 0 )
01170   {
01171     result = result.replace( pos, 1, g_dcSymbol );
01172     if ( data->rightOpt > 0 )
01173     {      
01174       int i   = result.find( 'E', pos, false ) - 1;
01175       int n   = result.length() - data->rightOpt;
01176 
01177       if ( i > 0 )
01178       {
01179         int rem = 0;
01180         for ( ; i > n; --i )
01181         {
01182           if ( result[i] != '0' )
01183             break;
01184           else
01185             ++rem;
01186         }
01187         result = result.remove( i + 1, rem ); 
01188       }
01189     }
01190 
01191     while ( data->leftReq > pos )
01192     {
01193       result.prepend( '0' );
01194       ++pos;
01195     }
01196 
01197     if ( data->thSet && pos > 3 )
01198     {
01199       int l = pos - 3;
01200       while ( 0 < l )
01201       {
01202         result.insert( l, g_thSymbol );
01203         l -= 3;
01204       }
01205     }
01206   }
01207 
01208   if ( negative )
01209   { 
01210     if ( data->showMinus )
01211       result.prepend( g_negSymbol );
01212 
01213     if ( data->negBr )
01214     {
01215       result.prepend( '(' );
01216       result.append( ')' );
01217     }
01218 
01219     if ( data->negRed )
01220       setRed = true;
01221   }
01222 
01223   result.prepend( data->prefix );
01224   result.append( data->postfix );
01225 }
01226 
01227 void appendAMPM( QString & result, KSpreadValue const & value )
01228 {
01229   if ( !g_convertionInfo.dt )
01230     convertDateTime( value );
01231 
01232   int hour = g_convertionInfo.dt->hour;
01233   if ( hour > 12 )
01234     result.append( i18n("PM") );
01235   else
01236     result.append( i18n("AM") );
01237 }
01238 
01239 void appendHour( QString & result, KSpreadValue const & value, 
01240                  int digits, bool elapsed, bool ampm )
01241 {
01242   if ( !g_convertionInfo.dt )
01243     convertDateTime( value );
01244     
01245   int hour = g_convertionInfo.dt->hour;
01246   if ( elapsed )
01247   {
01248     QDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
01249     QDate d2( 1900, 1, 1 );
01250     hour += ( d2.daysTo( d1 ) * 24 );
01251   }
01252   if ( hour < 10 && digits == 2 )
01253     result += '0';
01254   else
01255     if ( hour > 12 && ampm )
01256     {
01257       hour -= 12;
01258       if ( digits == 2 && hour < 10 )
01259         result += '0';
01260     }
01261 
01262   result += QString::number( hour );
01263 }
01264 
01265 void appendMinutes( QString & result, KSpreadValue const & value, 
01266                     int digits, bool elapsed )
01267 {
01268   if ( !g_convertionInfo.dt )
01269     convertDateTime( value );
01270     
01271   int minute = g_convertionInfo.dt->minute;
01272   if ( elapsed )
01273   {
01274     QDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
01275     QDate d2( 1900, 1, 1 );
01276     minute += ( d2.daysTo( d1 ) * 24 * 60 );
01277   }
01278   if ( minute < 10 && digits == 2 )
01279     result += '0';
01280 
01281   result += QString::number( minute );
01282 }
01283 
01284 void appendSecond( QString & result, KSpreadValue const & value, 
01285                    int digits, bool elapsed )
01286 {
01287   if ( !g_convertionInfo.dt )
01288     convertDateTime( value );
01289     
01290   int second = g_convertionInfo.dt->second;
01291   if ( elapsed )
01292   {
01293     QDate d1( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
01294     QDate d2( 1900, 1, 1 );
01295     second += ( d2.daysTo( d1 ) * 24 * 60 * 60 );
01296   }
01297   if ( second < 10 && digits == 2 )
01298     result += '0';
01299 
01300   result += QString::number( second );
01301 }
01302 
01303 void appendYear( QString & result, KSpreadValue const & value, 
01304                  int digits )
01305 {
01306   if ( !g_convertionInfo.dt )
01307     convertDateTime( value );
01308 
01309   int year = g_convertionInfo.dt->year;
01310   if ( digits <= 2 )
01311     result += QString::number( year ).right( 2 );
01312   else
01313     result += QString::number( year );
01314 }
01315 
01316 void appendMonth( QString & result, KSpreadValue const & value, 
01317                   int digits )
01318 {
01319   if ( !g_convertionInfo.dt )
01320     convertDateTime( value );
01321 
01322   int month = g_convertionInfo.dt->month;
01323   if ( digits == 1 )
01324     result += QString::number( month );
01325   else
01326     if ( digits == 2 )
01327     {
01328       if ( month < 10 )
01329         result += '0';
01330 
01331       result += QString::number( month );
01332     }
01333     else
01334     {
01335       switch ( month )
01336       {
01337        case 1:
01338         result += ( digits != 3 ? g_January : g_Jan );
01339         break;
01340 
01341        case 2:
01342         result += ( digits != 3 ? g_February : g_Feb );
01343         break;
01344 
01345        case 3:
01346         result += ( digits != 3 ? g_March : g_Mar );
01347         break;
01348 
01349        case 4:
01350         result += ( digits != 3 ? g_April : g_Apr );
01351         break;
01352 
01353        case 5:
01354         result += ( digits != 3 ? g_MayL : g_May );
01355         break;
01356 
01357        case 6:
01358         result += ( digits != 3 ? g_June : g_Jun );
01359         break;
01360 
01361        case 7:
01362         result += ( digits != 3 ? g_July : g_Jul );
01363         break;
01364 
01365        case 8:
01366         result += ( digits != 3 ? g_August : g_Aug );
01367         break;
01368 
01369        case 9:
01370         result += ( digits != 3 ? g_September : g_Sep );
01371         break;
01372 
01373        case 10:
01374         result += ( digits != 3 ? g_October : g_Oct );
01375         break;
01376 
01377        case 11:
01378         result += ( digits != 3 ? g_November : g_Nov );
01379         break;
01380 
01381        case 12:
01382         result += ( digits != 3 ? g_December : g_Dec );
01383         break;
01384       }    
01385     }
01386 }
01387 
01388 void appendDays( QString & result, KSpreadValue const & value, 
01389                  int digits )
01390 {
01391   if ( !g_convertionInfo.dt )
01392     convertDateTime( value );
01393 
01394   int day = g_convertionInfo.dt->day;
01395   if ( digits == 1 )
01396     result += QString::number( day );
01397   else
01398     if ( digits == 2 )
01399     {
01400       if ( day < 10 )
01401         result += '0';
01402 
01403       result += QString::number( day );
01404     }    
01405     else
01406     {
01407       QDate date( g_convertionInfo.dt->year, g_convertionInfo.dt->month, g_convertionInfo.dt->day );
01408       int weekDay = date.dayOfWeek();
01409       switch ( weekDay )
01410       {
01411        case 1:
01412         result += ( digits != 3 ? g_Monday : g_Mon );
01413         break;
01414 
01415        case 2:
01416         result += ( digits != 3 ? g_Tuesday : g_Tue );
01417         break;
01418 
01419        case 3:
01420         result += ( digits != 3 ? g_Wednesday : g_Wed );
01421         break;
01422 
01423        case 4:
01424         result += ( digits != 3 ? g_Thursday : g_Thu );
01425         break;
01426 
01427        case 5:
01428         result += ( digits != 3 ? g_Friday : g_Fri );
01429         break;
01430 
01431        case 6:
01432         result += ( digits != 3 ? g_Saturday : g_Sat );
01433         break;
01434 
01435        case 7:
01436         result += ( digits != 3 ? g_Sunday : g_Sun );
01437         break;
01438       }
01439     }
01440 }
01441 
01442 void createDateTime( QString & result, KSpreadValue const & value, 
01443                      QString const & /*format*/, 
01444                      DateTimeFormat const * const data )
01445 {
01446   result = data->prefix;
01447   bool elapsed = false;
01448   bool elapsedFound = false;
01449   bool minute = false; // how to interpret 'm'
01450   int digits = 1;
01451   int i = 0;
01452   int l = (int) data->format.length();
01453   while ( i < l )
01454   {
01455     switch( data->format[i].lower() )
01456     {
01457      case '"':
01458       ++i;
01459       while ( i < l )
01460       {
01461         if ( data->format[i] == '"' )
01462           break;
01463         else
01464           result += data->format[i];
01465       }
01466       break;
01467 
01468      case '[':
01469       if ( elapsedFound )
01470         result += '[';
01471       else
01472       {
01473         elapsed = true;
01474         elapsedFound = true;
01475       }
01476       break;
01477 
01478      case ']':
01479       if ( elapsed )
01480         elapsed = false;
01481       else
01482         result += ']';
01483       break;
01484 
01485      case 'h':
01486       minute = true;
01487       if ( data->format[i + 1] == 'h' )
01488       {
01489         appendHour( result, value, 2, elapsed, data->ampm );
01490         ++i;
01491       }
01492       else
01493         appendHour( result, value, 1, elapsed, data->ampm );
01494       break;
01495 
01496      case 'm':
01497       digits = 1;
01498       
01499       while ( data->format[i + 1] == 'm' )
01500       {
01501         ++i;
01502         ++digits;
01503       }
01504 
01505       if ( minute )
01506         appendMinutes( result, value, digits, elapsed );
01507       else
01508         appendMonth( result, value, digits );
01509       
01510       break;
01511 
01512      case 's':
01513       minute = true;
01514       if ( data->format[i + 1] == 's' )
01515       {
01516         appendSecond( result, value, 2, elapsed );
01517         ++i;
01518       }
01519       else
01520         appendSecond( result, value, 1, elapsed );
01521       break;
01522 
01523      case 'd':
01524       minute = false;
01525       digits = 1;
01526       
01527       while ( data->format[i + 1] == 'd' )
01528       {
01529         ++i;
01530         ++digits;
01531       }
01532       appendDays( result, value, digits );
01533       break;
01534 
01535      case 'y':
01536       minute = false;
01537       digits = 1;
01538       
01539       while ( data->format[i + 1] == 'y' )
01540       {
01541         ++i;
01542         ++digits;
01543       }
01544       appendYear( result, value, digits );
01545       break;
01546 
01547      case 'a':
01548      case 'p':
01549       if ( data->format[i + 1] == 'm' )
01550       {
01551         ++i;
01552         if ( data->format[i + 1] == '/' 
01553              && data->format[i + 2].lower() == 'p' 
01554              && data->format[i + 3].lower() == 'm' )
01555           i += 3;
01556 
01557         appendAMPM( result, value );
01558       }
01559       
01560      default:
01561       result += data->format[i];
01562     }
01563 
01564     ++i;
01565   }
01566 
01567   result += data->postfix;
01568 }
01569 
01570 QString formatNumber( KSpreadValue const & value, QString format, bool & setRed,
01571                       KLocale const * const locale, bool insert )
01572 {
01573   // need delocalized strings: dcSymbol: '.', thSymbol = ','
01574   if ( !g_init )
01575     initGlobals( locale );
01576 
01577   QString backup( format );
01578   QString result;
01579   BaseFormat * data = 0;
01580   setRed = false;
01581 
01582   int t = doPreScan( format, backup, locale, insert, data );
01583 
01584   if ( t == Number )
01585   {
01586     createNumber( result, value, format, setRed, (NumberFormat *) data );
01587 
01588     if ( !insert )
01589       delete (NumberFormat *) data;
01590 
01591     return result;
01592   }
01593   else if ( t == Fraction )
01594   {
01595     createFraction( result, value, format, setRed, (FractionFormat *) data );
01596 
01597     if ( !insert )
01598       delete (FractionFormat *) data;
01599 
01600     return result;
01601   }
01602   else if ( t == Scientific )
01603   {
01604     createScientific( result, value, format, setRed, (ScientificFormat *) data );
01605 
01606     if ( !insert )
01607       delete (ScientificFormat *) data;
01608 
01609     return result;
01610   }
01611   else if ( t == TimeDate )
01612   {
01613     createDateTime( result, value, format, (DateTimeFormat *) data );
01614 
01615     if ( !insert )
01616       delete (DateTimeFormat *) data;
01617 
01618     return result;
01619   }
01620   else if ( data != 0 )
01621   {
01622     result = data->prefix + data->postfix;
01623 
01624     if ( !insert )
01625       delete data;
01626 
01627     return result;
01628   }
01629 
01630   return result;
01631 }
KDE Logo
This file is part of the documentation for kspread Library Version 1.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Feb 13 09:43:13 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003