kspread

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