better money formatting for client
authorKonrad Rosenbaum <konrad@silmor.de>
Sat, 24 Mar 2012 19:28:57 +0000 (20:28 +0100)
committerKonrad Rosenbaum <konrad@silmor.de>
Sat, 24 Mar 2012 19:28:57 +0000 (20:28 +0100)
src/misc/misc.cpp
src/misc/misc.h
src/misc/sclock.cpp
wob/classes/basics.wolf
www/inc/wext/format.php
www/template/format.cfg

index bbe8039..df3aba2 100644 (file)
@@ -64,7 +64,7 @@ QByteArray xmlize(QByteArray str,QString newline)
                if(c=='>')out+="&gt;";else
                if(c=='&')out+="&amp;";else
                if(c=='\n')out+=newline;else
-               if(QChar(c).isSpace()||(c>=32&&c<=0x7f))out+=c;
+               if(QChar(c).isSpace()||(c>=32 && static_cast<unsigned char>(c)<=0x7f))out+=c;
                else out+="&#"+QString::number((unsigned char)c)+";";
        }
        return out;
@@ -112,14 +112,31 @@ QString unix2dateTime(qint64 tm,bool localize)
        return mf.formatDateTime(tm);
 }
 
+// /////////////////////////////////////
+// local formatting class
+#include <SharedDPtr>
+class MLocalFormat::Private:public SharedDPtr
+{
+       public:
+               QStringList m_day,m_sday,m_month,m_smonth;
+               QString m_am,m_pm,m_timezone,m_dateformat,m_timeformat,m_datetimeformat;
+               QChar m_decimal,m_thousand;
+
+               QString m_currency,m_moneyneg,m_moneypos;
+               int m_moneydecimals,m_thousanddigits;
+               bool m_moneysymbehind;
+               MOServerFormat::MoneyPos m_moneynegpos,m_moneypospos;
+};
+DEFINE_SHARED_DPTR(MLocalFormat);
+
 //static 
 MLocalFormat MLocalFormat::defaultformat((int)1);
 
 MLocalFormat::MLocalFormat(int)
 {
-       m_moneydecimals=2;
-       m_thousanddigits=3;
-       m_timezone="UTC";
+       d->m_moneydecimals=2;
+       d->m_thousanddigits=3;
+       d->m_timezone="UTC";
        //set defaults
        setWeekDays();setShortWeekDays();
        setMonths();setShortMonths();
@@ -137,19 +154,42 @@ MLocalFormat::MLocalFormat(const MOServerFormat& s)
        sl=s.shortweekdays();if(sl.size())setShortWeekDays(sl);
        sl=s.months();if(sl.size())setMonths(sl);
        sl=s.shortmonths();if(sl.size())setShortMonths(sl);
-       setMoneyFormat(s.currencysymbol(),s.moneydecimals(),s.moneynegative());
-       setNumberFormat(s.decimaldot().value().at(0),s.thousandseparator().value().at(0),s.thousanddigits().value());
+       setMoneyFormat(s.currencysymbol(),s.moneydecimals(),s.currencysymbolpos());
+       setMoneySign(s.moneynegative(),s.moneypositive(),s.moneynegativepos(),s.moneypositivepos());
+       setNumberFormat(s.decimaldot().value().at(0), s.thousandseparator().value().at(0), s.thousanddigits().value());
        setAP(s.amtext(),s.pmtext());
        setTimeZone(s.timezone());
        setDateTimeFormat(s.dateformat(),s.timeformat(),s.datetimeformat());
 }
 
+QStringList MLocalFormat::weekDayNames()const{return d->m_day;}
+QStringList MLocalFormat::shortWeekDayNames()const{return d->m_sday;}
+QStringList MLocalFormat:: monthNames()const{return d->m_month;}
+QStringList MLocalFormat::shortMonthNames()const{return d->m_smonth;}
+QString MLocalFormat::currency()const{return d->m_currency;}
+QChar MLocalFormat::decimalDot()const{return d->m_decimal;}
+QString MLocalFormat::thousandSeparator()const
+{
+       if(d->m_thousanddigits>0)
+               return d->m_thousand;
+       else return "";
+}
+int MLocalFormat::thousandDigits()const{return d->m_thousanddigits;}
+int MLocalFormat::moneyDecimals()const{return d->m_moneydecimals;}
+QString MLocalFormat::amText()const{return d->m_am;}
+QString MLocalFormat::pmText()const{return d->m_pm;}
+QString MLocalFormat::moneyNegativeSign()const{return d->m_moneyneg;}
+QString MLocalFormat::moneyPositiveSign()const{return d->m_moneypos;}
+QString MLocalFormat::dateFormat()const{return d->m_dateformat;}
+QString MLocalFormat::timeFormat()const{return d->m_timeformat;}
+QString MLocalFormat::dateTimeFormat()const{return d->m_datetimeformat;}
+QString MLocalFormat::timeZone()const{return d->m_timezone;}
 
 //static
 void MLocalFormat::setDefaultFormat(const MLocalFormat& f)
 {
        defaultformat=f;
-       TimeStamp::setDefaultZone(f.m_timezone);
+       TimeStamp::setDefaultZone(f.d->m_timezone);
 }
 
 
@@ -159,38 +199,11 @@ MLocalFormat::MLocalFormat()
 }
 
 
-MLocalFormat::MLocalFormat(const MLocalFormat&f)
-{
-       operator=(f);
-}
-
-MLocalFormat& MLocalFormat::operator=(const MLocalFormat&f)
-{
-       m_day=f.m_day;
-       m_sday=f.m_sday;
-       m_month=f.m_month;
-       m_smonth=f.m_smonth;
-       m_currency=f.m_currency;
-       m_decimal=f.m_decimal;
-       m_thousand=f.m_thousand;
-       m_moneydecimals=f.m_moneydecimals;
-       m_thousanddigits=f.m_thousanddigits;
-       m_am=f.m_am;
-       m_pm=f.m_pm;
-       m_moneyneg=f.m_moneyneg;
-       m_timezone=f.m_timezone;
-       m_dateformat=f.m_dateformat;
-       m_timeformat=f.m_timeformat;
-       m_datetimeformat=f.m_datetimeformat;
-       return *this;
-}
-
-MLocalFormat::~MLocalFormat(){}
-
 void MLocalFormat::setWeekDays(const QStringList&w)
 {
+       d.decouple();
        if(w.size()==0){
-               m_day=QStringList()
+               d->m_day=QStringList()
                        <<QCoreApplication::translate("MLocalFormat","Sunday")
                        <<QCoreApplication::translate("MLocalFormat","Monday")
                        <<QCoreApplication::translate("MLocalFormat","Tuesday")
@@ -200,12 +213,13 @@ void MLocalFormat::setWeekDays(const QStringList&w)
                        <<QCoreApplication::translate("MLocalFormat","Saturday");
        }else
        if(w.size()==7)
-               m_day=w;
+               d->m_day=w;
 }
 void MLocalFormat::setShortWeekDays(const QStringList&w)
 {
+       d.decouple();
        if(w.size()==0){
-               m_sday=QStringList()
+               d->m_sday=QStringList()
                        <<QCoreApplication::translate("MLocalFormat","Sun","short weekday")
                        <<QCoreApplication::translate("MLocalFormat","Mon","short weekday")
                        <<QCoreApplication::translate("MLocalFormat","Tue","short weekday")
@@ -215,12 +229,13 @@ void MLocalFormat::setShortWeekDays(const QStringList&w)
                        <<QCoreApplication::translate("MLocalFormat","Sat","short weekday");
        }else
        if(w.size()==7)
-               m_sday=w;
+               d->m_sday=w;
 }
 void MLocalFormat::setMonths(const QStringList&m)
 {
+       d.decouple();
        if(m.size()==0){
-               m_month=QStringList()
+               d->m_month=QStringList()
                        <<QCoreApplication::translate("MLocalFormat","January")
                        <<QCoreApplication::translate("MLocalFormat","February")
                        <<QCoreApplication::translate("MLocalFormat","March")
@@ -235,12 +250,13 @@ void MLocalFormat::setMonths(const QStringList&m)
                        <<QCoreApplication::translate("MLocalFormat","December");
        }else
        if(m.size()==12)
-               m_month=m;
+               d->m_month=m;
 }
 void MLocalFormat::setShortMonths(const QStringList&m)
 {
+       d.decouple();
        if(m.size()==0){
-               m_smonth=QStringList()
+               d->m_smonth=QStringList()
                        <<QCoreApplication::translate("MLocalFormat","Jan","short month name")
                        <<QCoreApplication::translate("MLocalFormat","Feb","short month name")
                        <<QCoreApplication::translate("MLocalFormat","Mar","short month name")
@@ -255,93 +271,105 @@ void MLocalFormat::setShortMonths(const QStringList&m)
                        <<QCoreApplication::translate("MLocalFormat","Dec","short month name");
        }else
        if(m.size()==12)
-               m_smonth=m;
+               d->m_smonth=m;
 }
 
 void MLocalFormat::setDateTimeFormat(QString dateformat, QString timeformat, QString datetimeformat)
 {
-       if(dateformat=="")m_dateformat=QCoreApplication::translate("MLocalFormat","%Y-%M-%D","date format");
-       else m_dateformat=dateformat;
-       if(timeformat=="")m_timeformat=QCoreApplication::translate("MLocalFormat","%h:%I","time format");
-       else m_timeformat=timeformat;
-       if(datetimeformat=="")m_datetimeformat=QCoreApplication::translate("MLocalFormat","%Y-%M-%D %h:%I","date and time format");
-       else m_datetimeformat=datetimeformat;
+       d.decouple();
+       if(dateformat=="")d->m_dateformat=QCoreApplication::translate("MLocalFormat","%Y-%M-%D","date format");
+       else d->m_dateformat=dateformat;
+       if(timeformat=="")d->m_timeformat=QCoreApplication::translate("MLocalFormat","%h:%I","time format");
+       else d->m_timeformat=timeformat;
+       if(datetimeformat=="")d->m_datetimeformat=QCoreApplication::translate("MLocalFormat","%Y-%M-%D %h:%I","date and time format");
+       else d->m_datetimeformat=datetimeformat;
 }
 
 
-void MLocalFormat::setMoneyFormat(QString c,int  n,QString g)
+void MLocalFormat::setMoneyFormat(QString c,int  n,bool s)
 {
-       m_currency=c;
-       m_moneydecimals=n;
-       if(g.size()>0)m_moneyneg=g.left(2);
-       else m_moneyneg=QCoreApplication::translate("MLocalFormat","-","negative sign for money values, the first char is put in front, the optional second one behind the number, use a newline to omit the first char");
+       d.decouple();
+       d->m_currency=c;
+       d->m_moneydecimals=n;
+       d->m_moneysymbehind=s;
+}
+
+void MLocalFormat::setMoneySign(QString n,QString p, MOServerFormat::MoneyPos np, MOServerFormat::MoneyPos pp)
+{
+       d.decouple();
+       d->m_moneyneg=n;d->m_moneypos=p;
+       d->m_moneynegpos=np;d->m_moneypospos=pp;
 }
 
 void MLocalFormat::setAP(QString am,QString pm)
 {
-       if(am=="--")m_am=QCoreApplication::translate("MLocalFormat","am","AM/PM time component");
-       else m_am=am;
-       if(pm=="--")m_pm=QCoreApplication::translate("MLocalFormat","pm","AM/PM time component");
-       else m_pm=pm;
+       d.decouple();
+
+       if(am=="--")d->m_am=QCoreApplication::translate("MLocalFormat","am","AM/PM time component");
+       else d->m_am=am;
+       if(pm=="--")d->m_pm=QCoreApplication::translate("MLocalFormat","pm","AM/PM time component");
+       else d->m_pm=pm;
 }
 
 void MLocalFormat::setNumberFormat(QChar dec,QChar thou,int dig)
 {
-       if(dec.isNull())m_decimal=QCoreApplication::translate("MLocalFormat",".","decimal dot")[0];
-       else m_decimal=dec;
+       d.decouple();
+
+       if(dec.isNull())d->m_decimal=QCoreApplication::translate("MLocalFormat",".","decimal dot")[0];
+       else d->m_decimal=dec;
        
-       if(thou.isNull())m_thousand=QCoreApplication::translate("MLocalFormat",",","thousand division character")[0];
-       else m_thousand=thou;
+       if(thou.isNull())d->m_thousand=QCoreApplication::translate("MLocalFormat",",","thousand division character")[0];
+       else d->m_thousand=thou;
        
-       if(dig<0)m_thousanddigits=QCoreApplication::translate("MLocalFormat","0","digits between thousand division chars, <=0 means none").toInt();
-       else m_thousanddigits=dig;
+       if(dig<0)d->m_thousanddigits=QCoreApplication::translate("MLocalFormat","0","digits between thousand division chars, <=0 means none").toInt();
+       else d->m_thousanddigits=dig;
 }
 
 void MLocalFormat::setNonLocalized()
 {
+       d.decouple();
        //reset number format
-       m_decimal='.';m_thousanddigits=0;
+       d->m_decimal='.';d->m_thousanddigits=0;
        //reset money signs
-       m_moneyneg="-";
-       m_currency="";
+       setMoneyFormat();setMoneySign();
 }
 
 
 QString MLocalFormat::formatDate(const QDate&date,QString format)const
 {
-       return formatDate(TimeStamp(date,m_timezone),format);
+       return formatDate(TimeStamp(date,d->m_timezone),format);
 }
 QString MLocalFormat::formatDate(const TimeStamp& date, QString format) const
 {
-       if(format=="")format=m_dateformat;
+       if(format=="")format=d->m_dateformat;
        return formatDateTime(date,format);
 }
 QString MLocalFormat::formatDate(qint64 date,QString format)const
 {
-       return formatDate(TimeStamp(date,m_timezone),format);
+       return formatDate(TimeStamp(date,d->m_timezone),format);
 }
 QString MLocalFormat::formatTime(const TimeStamp& time, QString format) const
 {
-       if(format=="")format=m_timeformat;
+       if(format=="")format=d->m_timeformat;
        return formatDateTime(time,format);
 }
 QString MLocalFormat::formatTime(const QTime&time,QString format)const
 {
-       return formatTime(TimeStamp(time,m_timezone),format);
+       return formatTime(TimeStamp(time,d->m_timezone),format);
 }
 QString MLocalFormat::formatTime(qint64 time,QString format)const
 {
-       return formatTime(TimeStamp(time,m_timezone),format);
+       return formatTime(TimeStamp(time,d->m_timezone),format);
 }
 QString MLocalFormat::formatDateTime(const QDateTime&time,QString format)const
 {
-       return formatDateTime(TimeStamp(time,m_timezone),format);
+       return formatDateTime(TimeStamp(time,d->m_timezone),format);
 }
 QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
 {
-       if(format=="")format=m_datetimeformat;
+       if(format=="")format=d->m_datetimeformat;
        //parse
-       TimeStamp time=ts.toZone(m_timezone);
+       TimeStamp time=ts.toZone(d->m_timezone);
        QString out;
        bool inp=false;
        for(int i=0;i<format.size();i++){
@@ -362,8 +390,8 @@ QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
                                        out+=m;
                                        break;
                                }
-                               case 'n':out+=m_smonth[time.month()-1];break;
-                               case 'N':out+=m_month[time.month()-1];break;
+                               case 'n':out+=d->m_smonth[time.month()-1];break;
+                               case 'N':out+=d->m_month[time.month()-1];break;
                                case 'd':out+=QString::number(time.day());break;
                                case 'D':{
                                        QString m=QString::number(time.day());
@@ -371,8 +399,8 @@ QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
                                        out+=m;
                                        break;
                                }
-                               case 'w':out+=m_sday[time.weekDay()];break;
-                               case 'W':out+=m_day[time.weekDay()];break;
+                               case 'w':out+=d->m_sday[time.weekDay()];break;
+                               case 'W':out+=d->m_day[time.weekDay()];break;
                                //time formats
                                case 'h':out+=QString::number(time.hour());break;
                                case 'H':{
@@ -416,14 +444,14 @@ QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
                                }
                                case 'p':{
                                        int t=time.hour();
-                                       if(t>=1 && t<=12)out+=m_am.toLower();
-                                       else out+=m_pm.toLower();
+                                       if(t>=1 && t<=12)out+=d->m_am.toLower();
+                                       else out+=d->m_pm.toLower();
                                        break;
                                }
                                case 'P':{
                                        int t=time.hour();
-                                       if(t>=1 && t<=12)out+=m_am;
-                                       else out+=m_pm;
+                                       if(t>=1 && t<=12)out+=d->m_am;
+                                       else out+=d->m_pm;
                                        break;
                                }
                                case 't':
@@ -448,7 +476,7 @@ QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
                                        break;
                                }
                                case 'o':out+=time.zoneAbbreviation();break;
-                               case 'O':out+=m_timezone;break;
+                               case 'O':out+=d->m_timezone;break;
                                // % sign
                                case '%':out+="%";break;
                                //mistakes
@@ -468,13 +496,13 @@ QString MLocalFormat::formatDateTime(const TimeStamp& ts, QString format) const
 
 QString MLocalFormat::formatDateTime(qint64 time,QString format)const
 {
-       return formatDateTime(TimeStamp(time,m_timezone),format);
+       return formatDateTime(TimeStamp(time,d->m_timezone),format);
 }
 
 QString MLocalFormat::formatNumber(qint64 n)const
 {
        //shortcut if we use standard format anyway
-       if(m_thousanddigits<=0)return QString::number(n);
+       if(d->m_thousanddigits<=0)return QString::number(n);
        //convert number to decimals
        bool neg=false;
        if(n<0){n*=-1;neg=true;}
@@ -483,7 +511,7 @@ QString MLocalFormat::formatNumber(qint64 n)const
        QString r;
        int l=s.size();
        for(int i=0;i<s.size();i++){
-               if(i>0 && ((l-i)%m_thousanddigits)==0)r+=m_thousand;
+               if(i>0 && ((l-i)%d->m_thousanddigits)==0)r+=d->m_thousand;
                r+=s[i];
        }
        if(neg)r="-"+r;
@@ -500,7 +528,7 @@ QString MLocalFormat::formatNumber(double num,uint decimals)const
        QString r=formatNumber(be);
        //fractions
        if(decimals>0){
-               r+=m_decimal;
+               r+=d->m_decimal;
                num-=be;
                for(int i=0;i<(int)decimals;i++){
                        num*=10.;
@@ -517,35 +545,50 @@ QString MLocalFormat::formatNumber(double num,uint decimals)const
 
 QString MLocalFormat::formatMoney(qint64 num,bool usethousand)const
 {
+       return formatMoney(num,(usethousand?UseThousand:0)|ShowCurrencySymbol);
+}
+
+
+QString MLocalFormat::formatMoney(qint64 num, MLocalFormat::MoneyFlags flags) const
+{
        //check value
        bool neg=false;
        if(num<0){num*=-1;neg=true;}
        //split main and fraction
-       qint64 mod=1;for(int i=0;i<m_moneydecimals;i++)mod*=10;
+       qint64 mod=1;for(int i=0;i<d->m_moneydecimals;i++)mod*=10;
        qint64 frac=num%mod;
        num/=mod;
        //create main unit
        QString ms;
-       if(usethousand)ms=formatNumber(num);
+       if(flags&UseThousand)ms=formatNumber(num);
        else ms=QString::number(num);
        //create fractional unit
        QString fs=QString::number(frac);
-       for(int i=fs.size();i<m_moneydecimals;i++)fs="0"+fs;
-       //combine and add negative
-       QString r;
-       if(neg){
-               if(m_moneyneg.size()>=2){
-                       if(m_moneyneg[0]!='\n')r+=m_moneyneg[0];
-               }else{
-                       if(m_moneyneg.size()>=1)r+=m_moneyneg[0];
-                       else r+='-';//emergency fallback
-               }
+       for(int i=fs.size();i<d->m_moneydecimals;i++)fs="0"+fs;
+       //combine and get currency symbol
+       QString r=ms+d->m_decimal+fs;
+       QString curr;
+       if(flags&ShowCurrencySymbol)curr=d->m_currency;
+       //add sign
+       QString sign;
+       if(neg)sign=d->m_moneyneg;
+       else sign=d->m_moneypos;
+       switch(neg?d->m_moneynegpos:d->m_moneypospos){
+               case MOServerFormat::NoSign:break;
+               case MOServerFormat::SignParen:r="("+r+")";break;
+               case MOServerFormat::SignAfterSym:curr+=sign;break;
+               case MOServerFormat::SignBeforeSym:curr=sign+curr;break;
+               case MOServerFormat::SignAfterNum:r+=sign;break;
+               case MOServerFormat::SignBeforeNum:
+               default:
+                       r=sign+r;
+                       break;
        }
-       r+=ms+m_decimal+fs;
-       if(neg && m_moneyneg.size()>=2)
-               r+=m_moneyneg[1];
        //add currency
-       r+=m_currency;
+       if(!curr.isEmpty()){
+               if(d->m_moneysymbehind)r+=" "+curr;
+               else r=curr+" "+r;
+       }
        //return result
        return r;
 }
@@ -565,7 +608,7 @@ qint64 MLocalFormat::scanInt(QString num)const
                        r+=c.digitValue();
                }else
                //ignore thousand divider
-               if(c==m_thousand)continue;
+               if(c==d->m_thousand)continue;
                //non-numericals break
                else break;
        }
@@ -576,35 +619,35 @@ qint64 MLocalFormat::scanMoney(QString num)const
 {
        //init
        num=num.trimmed();
-       qint64 r=0,d=0,nd=0;
+       qint64 r=0,dd=0,nd=0;
        bool neg=false,frc=false;
        //scan string; r=main unit; d=fractions
        for(int i=0;i<num.size();i++){
                QChar c=num[i];
                //negative sign
-               if(m_moneyneg.contains(c) || c=='-')neg=true;else
+               if(d->m_moneyneg.contains(c) || c=='-')neg=true;else
                //decimal dot
-               if(c==m_decimal)frc=true;else
+               if(c==d->m_decimal)frc=true;else
                //digits
                if(c.isDigit()){
                        if(frc){
-                               if(nd>=m_moneydecimals)break;
-                               d*=10;d+=c.digitValue();
+                               if(nd>=d->m_moneydecimals)break;
+                               dd*=10;dd+=c.digitValue();
                                nd++;
                        }else{
                                r*=10;r+=c.digitValue();
                        }
                }else
                //ignore thousand divider
-               if(c==m_thousand)continue;
+               if(c==d->m_thousand)continue;
                //non-numericals break
                else break;
        }
        //move main to left
-       for(int i=0;i<m_moneydecimals;i++)r*=10;
+       for(int i=0;i<d->m_moneydecimals;i++)r*=10;
        //add fractions
-       for(;nd<m_moneydecimals;nd++)d*=10;
-       r+=d;
+       for(;nd<d->m_moneydecimals;nd++)dd*=10;
+       r+=dd;
        //negative?
        if(neg)r*=-1;
        //result
@@ -624,7 +667,7 @@ double MLocalFormat::scanFloat(QString num)const
                //negative sign
                if(c=='-')neg=true;else
                //decimal dot
-               if(c==m_decimal)frc=true;else
+               if(c==d->m_decimal)frc=true;else
                //digits
                if(c.isDigit()){
                        if(frc){
@@ -636,7 +679,7 @@ double MLocalFormat::scanFloat(QString num)const
                        }
                }else
                //ignore thousand divider
-               if(c==m_thousand)continue;
+               if(c==d->m_thousand)continue;
                //non-numericals break
                else break;
        }
@@ -648,36 +691,37 @@ double MLocalFormat::scanFloat(QString num)const
 QRegExp MLocalFormat::moneyRegExp(bool allownegative,bool allowcurrency)const
 {
        //main unit
-       QString r="[\\d"+QRegExp::escape(m_thousand)+"]+";
+       QString r="[\\d"+QRegExp::escape(d->m_thousand)+"]+";
        //fractions
-       if(m_moneydecimals>0){
-               r+=QRegExp::escape(m_decimal);
-               r+="\\d{"+QString::number(m_moneydecimals)+"}";
+       if(d->m_moneydecimals>0){
+               r+=QRegExp::escape(d->m_decimal);
+               r+="\\d{"+QString::number(d->m_moneydecimals)+"}";
        }
        //negative
        if(allownegative){
                //front
                QString f="[-";
-               if(m_moneyneg.size()>0)
-                       if(m_moneyneg[0]!='\n' && m_moneyneg[0]!='-')
-                               f+=QRegExp::escape(m_moneyneg[0]);
+               if(d->m_moneyneg.size()>0)
+                       if(d->m_moneyneg[0]!='\n' && d->m_moneyneg[0]!='-')
+                               f+=QRegExp::escape(d->m_moneyneg[0]);
                f+="]?";
                r=f+r;
                //back
-               if(m_moneyneg.size()>1)
-                       r+=QRegExp::escape(m_moneyneg[1])+"?";
+               if(d->m_moneyneg.size()>1)
+                       r+=QRegExp::escape(d->m_moneyneg[1])+"?";
        }
        //currency
-       if(allowcurrency && m_currency!="")
-               r+="("+QRegExp::escape(m_currency)+")?";
+       if(allowcurrency && d->m_currency!="")
+               r+="("+QRegExp::escape(d->m_currency)+")?";
        //return
        return QRegExp(r);
 }
 
 void MLocalFormat::setTimeZone(QString olsonname)
 {
+       d.decouple();
        if(TimeStamp::loadZone(olsonname))
-               m_timezone=olsonname;
+               d->m_timezone=olsonname;
 }
 
 QString currentDir()
index 009e915..2d4c496 100644 (file)
@@ -19,7 +19,8 @@
 #include <QRegExp>
 
 class TimeStamp;
-class MOServerFormat;
+#include <MOServerFormat>
+#include <DPtrBase>
 
 /**converts special HTML characters into harmless &-codes, so the text can be included*/
 QString htmlize(QString str);
@@ -96,21 +97,22 @@ For example "%w the %d'th of %N in the year of the lord %Y at %a:%I %P" will be
 */
 class MLocalFormat
 {
+       DECLARE_SHARED_DPTR(d);
        public:
                /**constructs a formatter object that corresponds to the default*/
                MLocalFormat();
                /**copies a formatter object inheriting its overrides*/
-               MLocalFormat(const MLocalFormat&);
+               MLocalFormat(const MLocalFormat&)=default;
                /**initializes the formatter object from a server format object*/
                MLocalFormat(const MOServerFormat&);
                /**deletes the formatter object*/
-               virtual ~MLocalFormat();
+               virtual ~MLocalFormat(){}
                
                /**sets a new default format*/
                static void setDefaultFormat(const MLocalFormat&);
                
                /**copies a formatter object*/
-               virtual MLocalFormat& operator=(const MLocalFormat&);
+               virtual MLocalFormat& operator=(const MLocalFormat&)=default;
                
                /**overrides the full names of week days, an empty list resets to the local translation, otherwise the list must be exactly seven entries long and starts with Sunday*/
                virtual void setWeekDays(const QStringList&l=QStringList());
@@ -125,8 +127,16 @@ class MLocalFormat
                /**overrides the formatting of money - the settings of numbers are re-used
                \param digitsCents defines how many digits are used for sub-amounts (cents), if zero no cents are used, normal are the values 0, 2 and 3; the default is 2
                \param currency defines the symbol of the currency used, the default is an empty string
-               \param negative defines how negative values are formatted, the first character is placed in front of the number, the optional second one behind the number and in front of the currency symbol; if you want no first character use a \n newline instead; the default falls back onto the translation, the default translation uses "-"*/
-               virtual void setMoneyFormat(QString currency=QString(),int digitsCents=2,QString negative=QString());
+               \param symbolBehind defines whether the currency symbol is shown before (false) or behind (true) the numeric value
+               */
+               virtual void setMoneyFormat(QString currency=QString(),int digitsCents=2, bool symbolBehind=true);
+               /**overrides the formatting of money - the settings of numbers are re-used
+               \param negative defines the sign for negative values, the default is "-"
+               \param positive defines the sign for positive values, the default is an empty string
+               \param negativePos defines where the negative sign is printed
+               \param positivePos defines where the positive sign is printed
+               */
+               virtual void setMoneySign(QString negative="-",QString positive=QString(), MOServerFormat::MoneyPos negativePos=MOServerFormat::SignBeforeNum, MOServerFormat::MoneyPos positivePos=MOServerFormat::NoSign);
                
                /**overrides the formatting of numbers and money;
                \param decimal defines the character used to separate decimals from the main body, if QChar() is used it returns to the translation default (a dot "." in default localization)
@@ -145,7 +155,7 @@ class MLocalFormat
                virtual void setDateTimeFormat(QString dateformat=QString(),QString timeformat=QString(),QString datetimeformat=QString());
                
                /**returns the currently set time zone of this formatter*/
-               virtual QString timeZone()const{return m_timezone;}
+               virtual QString timeZone()const;
                
                /**overrides the formatting to be non-localized, numbers use decimal dot and no thousand separator, no currency symbol and "-" as negative sign; otherwise things stay the same; this is a helper for the shortcut methods like cent2str*/
                virtual void setNonLocalized();
@@ -202,10 +212,23 @@ class MLocalFormat
                \param decimals the maximum number of digits after the decimal dot*/
                virtual QString formatNumber(double num,uint decimals=4)const;
                
+               enum MoneyFlag {
+                       PlainMoneyFormat=0,
+                       UseThousand=1,
+                       ShowCurrencySymbol=2,
+                       
+                       DefaultFormat=UseThousand|ShowCurrencySymbol
+               };
+               Q_DECLARE_FLAGS(MoneyFlags,MoneyFlag);
+               
+               /**formats a money value
+               \param num the amount in cents
+               \param flags determines how the string will be formatted */
+               virtual QString formatMoney(qint64 num,MoneyFlags flags=DefaultFormat)const;
                /**formats a money value
                \param num the amount in cents
                \param usethousand if true the thousand block division is inserted */
-               virtual QString formatMoney(qint64 num,bool usethousand=true)const;
+               virtual QString formatMoney(qint64 num,bool usethousand)const;
                
                /**scans an integer number and returns the value, non-numerical chars are ignored*/
                virtual qint64 scanInt(QString)const;
@@ -217,37 +240,39 @@ class MLocalFormat
                virtual double scanFloat(QString)const;
                
                /**returns the names used for week days*/
-               QStringList weekDayNames()const{return m_day;}
+               QStringList weekDayNames()const;
                /**returns the abbreviations used for week days*/
-               QStringList shortWeekDayNames()const{return m_sday;}
+               QStringList shortWeekDayNames()const;
                
                /**returns the names used for months*/
-               QStringList monthNames()const{return m_month;}
+               QStringList monthNames()const;
                /**returns the names used for months*/
-               QStringList shortMonthNames()const{return m_smonth;}
+               QStringList shortMonthNames()const;
                
                /**returns the currency symbol*/
-               QString currency()const{return m_currency;}
+               QString currency()const;
                
                /**returns the decimal dot symbol*/
-               QChar decimalDot()const{return m_decimal;}
+               QChar decimalDot()const;
                
                /**returns the thousand separator if used, otherwise an empty string*/
-               QString thousandSeparator()const{if(m_thousanddigits>0)return m_thousand;else return "";}
+               QString thousandSeparator()const;
                
                /**returns the amount of digits between thousand separators*/
-               int thousandDigits()const{return m_thousanddigits;}
+               int thousandDigits()const;
                
                /**returns the amount of decimals in a money value*/
-               int moneyDecimals()const{return m_moneydecimals;}
+               int moneyDecimals()const;
                
                /**returns the text for AM in 12-hour clock format*/
-               QString amText()const{return m_am;}
+               QString amText()const;
                /**returns the text for PM in 12-hour clock format*/
-               QString pmText()const{return m_pm;}
+               QString pmText()const;
                
-               /**returns the negative sign chars for money values*/
-               QString moneyNegativeSigns()const{return m_moneyneg;}
+               /**returns the negative sign for money values*/
+               QString moneyNegativeSign()const;
+               /**returns the positive sign for money values*/
+               QString moneyPositiveSign()const;
                
                /**returns a regular expression matching money values
                \param allownegative if given the resulting RegExp allows to use negative values
@@ -255,23 +280,19 @@ class MLocalFormat
                QRegExp moneyRegExp(bool allownegative=false,bool allowcurrency=false)const;
                
                /**returns the default format for dates*/
-               QString dateFormat()const{return m_dateformat;}
+               QString dateFormat()const;
                
                /**returns the default format for times*/
-               QString timeFormat()const{return m_timeformat;}
+               QString timeFormat()const;
                
                /**returns the default format for date/time*/
-               QString dateTimeFormat()const{return m_datetimeformat;}
+               QString dateTimeFormat()const;
        protected:
-               QStringList m_day,m_sday,m_month,m_smonth;
-               QString m_currency,m_am,m_pm,m_moneyneg,m_timezone,m_dateformat,m_timeformat,m_datetimeformat;
-               QChar m_decimal,m_thousand;
-               int m_moneydecimals,m_thousanddigits;
-               
                /** \internal constructs the default object from the translation*/
                MLocalFormat(int);
                /** \internal default format*/
                static MLocalFormat defaultformat;
 };
+Q_DECLARE_OPERATORS_FOR_FLAGS(MLocalFormat::MoneyFlags);
 
 #endif
index 5afd6ab..3284d12 100644 (file)
@@ -119,7 +119,8 @@ void MServerClock::showInfo()
        fl->addRow(new QLabel(tr("Currency Settings:")));
        fl->addRow(tr("Currency Symbol:"),new QLabel(lf.currency()));
        fl->addRow(tr("Division Digits:"),new QLabel(QString::number(lf.moneyDecimals())));
-       fl->addRow(tr("Negative Sign:"),new QLabel(lf.moneyNegativeSigns()));
+       fl->addRow(tr("Negative Sign:"),new QLabel(lf.moneyNegativeSign()));
+       fl->addRow(tr("Positive Sign:"),new QLabel(lf.moneyPositiveSign()));
        fl->addRow(tr("Example:"),new QLabel(lf.formatMoney(-1234567890)));
        
        fl->addRow(new QLabel);
index 5fbc4bd..ef5e9c2 100644 (file)
                <Property name="thousanddigits" type="int">The amount of digits between thousand separators, 0 for no thousand separator</Property>
                <Property name="moneydecimals" type="int">The amount of decimals for money values - this must be the same for all languages on a server, the default is 2</Property>
                <Property name="currencysymbol" type="astring">The currency used</Property>
-               <Property name="moneynegative" type="astring">One or two characters: for negative money values the first one is placed in front of the money value and the second one behind the money value</Property>
+               <Property name="currencysymbolhtml" type="astring">The currency used in HTML notation</Property>
+               <Property name="currencysymbolplain" type="astring">The currency used in ASCII notation</Property>
+               <Property name="currencysymbolpos" type="bool">Where the currency symbol is found: false: before the number, true: behind the number</Property>
+               <Property name="moneynegative" type="astring">Zero to three characters: for negative money values the first one is placed in front of the money value and the second one behind the money value</Property>
+               <Property name="moneypositive" type="astring">Zero to three characters: for positive money values the first one is placed in front of the money value and the second one behind the money value</Property>
+               <Enum name="MoneyPos">
+                 <Doc>Position of the money sign in relation to number and currency symbol</Doc>
+                 <Value name="NoSign" value="0">no sign is shown, regardless of what symbol is set</Value>
+                 <Value name="SignBeforeNum" value="1">the sign is shown before the numeric value</Value>
+                 <Value name="SignAfterNum" value="2">the sign is shown after the numeric value</Value>
+                 <Value name="SignBeforeSym" value="3">the sign is shown before the currency symbol</Value>
+                 <Value name="SignAfterSym" value="4">the sign is shown after the currency symbol</Value>
+                 <Value name="SignParen" value="5">parentheses are drawn around the numeric value</Value>
+               </Enum>
+               <Property name="moneypositivepos" type="MoneyPos">position of the positive money sign</Property>
+               <Property name="moneynegativepos" type="MoneyPos">position of the negative money sign</Property>
                <Property name="timezone" type="astring">Olsen database time zone name</Property>
                <!-- <Property name="TZtransitions" type="List:TimeTransition">list of all precalculated transitions in this timezone, how far into the future calculations go depends on the server, usually at the moment till 2037</Property> -->
        </Class>
index 375bf8e..2a58fb2 100644 (file)
@@ -59,12 +59,35 @@ class WOServerFormat extends WOServerFormatAbstract
                                case "thousanddigits":$this->setthousanddigits($val);break;
                                case "moneydecimals":$this->setmoneydecimals($val);break;
                                case "currencysymbol":$this->setcurrencysymbol($val);break;
+                               case "currencysymbolplain":$this->setcurrencysymbolplain($val);break;
+                               case "currencysymbolhtml":$this->setcurrencysymbolhtml($val);break;
+                               case "currencysymbolpos":$this->setcurrencysymbolpos($val);break;
                                case "moneynegative":$this->setmoneynegative($val);break;
+                               case "moneypositive":$this->setmoneypositive($val);break;
+                               case "moneynegativepos":
+                                       $this->setmoneynegativepos($this->mps2e($val));break;
+                               case "moneypositivepos":
+                                       $this->setmoneypositivepos($this->mps2e($val));break;
                                case "timezone":$this->settimezone($val);break;
                                default: die("Unknown keyword in format config $file line ".($num+1));break;
                        }
                }
        }
+       
+       ///helper function to convert string money sign position to enum
+       protected function mps2e($v)
+       {
+               switch(strtolower(trim($v))){
+                       case "none":return self::NoSign;
+                       case "beforenum":return self::SignBeforeNum;
+                       case "afternum":return self::SignAfterNum;
+                       case "beforesym":return self::SignBeforeSym;
+                       case "aftersym":return self::SignAfterSym;
+                       case "paren":return self::SignParen;
+                       //fallback
+                       default:return self::SignBeforeNum;
+               }
+       }
 }
 
 
index 98dff3f..f8009ca 100644 (file)
@@ -80,16 +80,34 @@ thousanddigits "0"
 #  changes the actual monetary value expressed by a stored int value changes!
 moneydecimals "2"
 
-# currencysymbol: the currency used by this server
+# currencysymbol: the currency used by this server as ASCII/Unicode string (used by the clients)
+# currencysymbolplain: the currency symbol in plain ASCII formatting (e.g. used in eMails)
+# currencysymbolhtml: the exact same symbol in HTML notation (used by the web pages, may be identical)
 # If you change this value in a translation, make sure it still reflects the
 # same currency, otherwise your customers might be surprised if the Web UI shows
 # amounts in US$, but they are billed in Euro.
-currencysymbol "Euro"
+currencysymbol "€"
+currencysymbolplain "Euro"
+currencysymbolhtml "&euro;"
 
-# the negative notation of money, it has one or two characters:
-#  character 1: the character placed in front of the number, or \n for none
-#  character 2: the character placed between number and currency symbol
+# position of the currency symbol:
+#  value "0": before the number (e.g. "$ 123.00")
+#  value "1": behind the number (e.g. "123.00 $")
+currencysymbolpos "1"
+
+# the negative/positive notation of money
 moneynegative "-"
+moneypositive ""
+
+# the position of the negative and positive signs, possible values are:
+#  none      - do not print the sign ("123.00 $")
+#  beforenum - before the number ("-123.00 $")
+#  afternum  - after the number ("123.00- $")
+#  beforesym - before the symbol ("123.00 -$")
+#  aftersym  - after the symbol ("123.00 $-")
+#  paren     - use parentheses ("(123.00) $")
+moneynegativepos "beforenum"
+moneypositivepos "none"
 
 #Time Zone in Olson DB notation
 # THIS VALUE SHOULD NOT BE CHANGED IN LANGUAGE FOLDERS