/*************************************************************************** mymoneyqifprofile.cpp - description ------------------- begin : Tue Dec 24 2002 copyright : (C) 2002 by Thomas Baumgart email : thb@net-bembel.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif // ---------------------------------------------------------------------------- // TQt Includes #include #include // ---------------------------------------------------------------------------- // TDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyqifprofile.h" #include "../mymoney/mymoneyexception.h" #include "../mymoney/mymoneymoney.h" /* * CENTURY_BREAK is used to identfy the century for a two digit year * * if yr is < CENTURY_BREAK it is in 2000 * if yr is >= CENTURY_BREAK it is in 1900 * * so with CENTURY_BREAK being 70 the following will happen: * * 00..69 -> 2000..2069 * 70..99 -> 1970..1999 */ #define CENTURY_BREAK 70 class MyMoneyQifProfile::Private { public: Private() { m_changeCount.resize(3, 0); m_lastValue.resize(3, 0); m_largestValue.resize(3, 0); } void getThirdPosition(void); void dissectDate(TQValueVector& parts, const TQString& txt) const; TQValueVector m_changeCount; TQValueVector m_lastValue; TQValueVector m_largestValue; TQMap m_partPos; }; void MyMoneyQifProfile::Private::dissectDate(TQValueVector& parts, const TQString& txt) const { TQRegExp nonDelimChars("[ 0-9a-zA-Z]"); int part = 0; // the current part we scan unsigned int pos; // the current scan position unsigned int maxPartSize = txt.length() > 6 ? 4 : 2; // the maximum size of a part // some fu... up MS-Money versions write two delimiter in a row // so we need to keep track of them. Example: D14/12/'08 bool lastWasDelim = false; // separate the parts of the date and keep the locations of the delimiters for(pos = 0; pos < txt.length() && part < 3; ++pos) { if(nonDelimChars.search(txt[pos]) == -1) { if(!lastWasDelim) { ++part; maxPartSize = 0; // make sure to pick the right one depending if next char is numeric or not lastWasDelim = true; } } else { lastWasDelim = false; // check if the part is over and we did not see a delimiter if((maxPartSize != 0) && (parts[part].length() == maxPartSize)) { ++part; maxPartSize = 0; } if(maxPartSize == 0) { maxPartSize = txt[pos].isDigit() ? 2 : 3; if(part == 2) maxPartSize = 4; } if(part < 3) parts[part] += txt[pos]; } } if(part == 3) { // invalid date for(int i = 0; i < 3; ++i) { parts[i] = "0"; } } } void MyMoneyQifProfile::Private::getThirdPosition(void) { // if we have detected two parts we can calculate the third and its position if(m_partPos.count() == 2) { TQValueList partsPresent = m_partPos.keys(); TQStringList partsAvail = TQStringList::split(",", "d,m,y"); int missingIndex = -1; int value = 0; for(int i = 0; i < 3; ++i) { if(!partsPresent.contains(partsAvail[i][0])) { missingIndex = i; } else { value += m_partPos[partsAvail[i][0]]; } } m_partPos[partsAvail[missingIndex][0]] = 3 - value; } } MyMoneyQifProfile::MyMoneyQifProfile() : d(new Private), m_isDirty(false) { clear(); } MyMoneyQifProfile::MyMoneyQifProfile(const TQString& name) : d(new Private), m_isDirty(false) { loadProfile(name); } MyMoneyQifProfile::~MyMoneyQifProfile() { delete d; } void MyMoneyQifProfile::clear(void) { m_dateFormat = "%d.%m.%yyyy"; m_apostropheFormat = "2000-2099"; m_valueMode = ""; m_filterScriptImport = ""; m_filterScriptExport = ""; m_filterFileType = "*.qif"; m_decimal.clear(); m_decimal['$'] = m_decimal['Q'] = m_decimal['T'] = m_decimal['O'] = m_decimal['I'] = TDEGlobal::locale()->monetaryDecimalSymbol()[0]; m_thousands.clear(); m_thousands['$'] = m_thousands['Q'] = m_thousands['T'] = m_thousands['O'] = m_thousands['I'] = TDEGlobal::locale()->monetaryThousandsSeparator()[0]; m_openingBalanceText = "Opening Balance"; m_voidMark = "VOID "; m_accountDelimiter = "["; m_profileName = ""; m_profileDescription = ""; m_profileType = "Bank"; m_attemptMatchDuplicates = true; } void MyMoneyQifProfile::loadProfile(const TQString& name) { TDEConfig* config = TDEGlobal::config(); config->setGroup(name); clear(); m_profileName = name; m_profileDescription = config->readEntry("Description", m_profileDescription); m_profileType = config->readEntry("Type", m_profileType); m_dateFormat = config->readEntry("DateFormat", m_dateFormat); m_apostropheFormat = config->readEntry("ApostropheFormat", m_apostropheFormat); m_accountDelimiter = config->readEntry("AccountDelimiter", m_accountDelimiter); m_openingBalanceText = config->readEntry("OpeningBalance", m_openingBalanceText); m_voidMark = config->readEntry("VoidMark", m_voidMark); m_filterScriptImport = config->readEntry("FilterScriptImport", m_filterScriptImport); m_filterScriptExport = config->readEntry("FilterScriptExport", m_filterScriptExport); m_filterFileType = config->readEntry("FilterFileType",m_filterFileType); m_attemptMatchDuplicates = config->readBoolEntry("AttemptMatchDuplicates", m_attemptMatchDuplicates); // make sure, we remove any old stuff for now config->deleteEntry("FilterScript"); TQString tmp = TQString(m_decimal['Q']) + m_decimal['T'] + m_decimal['I'] + m_decimal['$'] + m_decimal['O']; tmp = config->readEntry("Decimal", tmp); m_decimal['Q'] = tmp[0]; m_decimal['T'] = tmp[1]; m_decimal['I'] = tmp[2]; m_decimal['$'] = tmp[3]; m_decimal['O'] = tmp[4]; tmp = TQString(m_thousands['Q']) + m_thousands['T'] + m_thousands['I'] + m_thousands['$'] + m_thousands['O']; tmp = config->readEntry("Thousand", tmp); m_thousands['Q'] = tmp[0]; m_thousands['T'] = tmp[1]; m_thousands['I'] = tmp[2]; m_thousands['$'] = tmp[3]; m_thousands['O'] = tmp[4]; m_isDirty = false; } void MyMoneyQifProfile::saveProfile(void) { if(m_isDirty == true) { TDEConfig* config = TDEGlobal::config(); config->setGroup(m_profileName); config->writeEntry("Description", m_profileDescription); config->writeEntry("Type", m_profileType); config->writeEntry("DateFormat", m_dateFormat); config->writeEntry("ApostropheFormat", m_apostropheFormat); config->writeEntry("AccountDelimiter", m_accountDelimiter); config->writeEntry("OpeningBalance", m_openingBalanceText); config->writeEntry("VoidMark", m_voidMark); config->writeEntry("FilterScriptImport", m_filterScriptImport); config->writeEntry("FilterScriptExport", m_filterScriptExport); config->writeEntry("FilterFileType", m_filterFileType); config->writeEntry("AttemptMatchDuplicates", m_attemptMatchDuplicates); TQString tmp; tmp = TQString(m_decimal['Q']) + m_decimal['T'] + m_decimal['I'] + m_decimal['$'] + m_decimal['O']; config->writeEntry("Decimal", tmp); tmp = TQString(m_thousands['Q']) + m_thousands['T'] + m_thousands['I'] + m_thousands['$'] + m_thousands['O']; config->writeEntry("Thousand", tmp); } m_isDirty = false; } void MyMoneyQifProfile::setProfileName(const TQString& name) { if(m_profileName != name) m_isDirty = true; m_profileName = name; } void MyMoneyQifProfile::setProfileDescription(const TQString& desc) { if(m_profileDescription != desc) m_isDirty = true; m_profileDescription = desc; } void MyMoneyQifProfile::setProfileType(const TQString& type) { if(m_profileType != type) m_isDirty = true; m_profileType = type; } void MyMoneyQifProfile::setOutputDateFormat(const TQString& dateFormat) { if(m_dateFormat != dateFormat) m_isDirty = true; m_dateFormat = dateFormat; } void MyMoneyQifProfile::setInputDateFormat(const TQString& dateFormat) { int j = -1; if(dateFormat.length() > 0) { for(unsigned int i = 0; i < dateFormat.length()-1; ++i) { if(dateFormat[i] == '%') { d->m_partPos[dateFormat[++i]] = ++j; } } } } void MyMoneyQifProfile::setApostropheFormat(const TQString& apostropheFormat) { if(m_apostropheFormat != apostropheFormat) m_isDirty = true; m_apostropheFormat = apostropheFormat; } void MyMoneyQifProfile::setAmountDecimal(const TQChar& def, const TQChar& chr) { TQChar ch(chr); if(ch == TQChar()) ch = ' '; if(m_decimal[def] != ch) m_isDirty = true; m_decimal[def] = ch; } void MyMoneyQifProfile::setAmountThousands(const TQChar& def, const TQChar& chr) { TQChar ch(chr); if(ch == TQChar()) ch = ' '; if(m_thousands[def] != ch) m_isDirty = true; m_thousands[def] = ch; } TQChar MyMoneyQifProfile::amountDecimal(const TQChar& def) const { TQChar chr = m_decimal[def]; return chr; } TQChar MyMoneyQifProfile::amountThousands(const TQChar& def) const { TQChar chr = m_thousands[def]; return chr; } void MyMoneyQifProfile::setAccountDelimiter(const TQString& delim) { TQString txt(delim); if(txt.isEmpty()) txt = " "; else if(txt[0] != '[') txt = "["; if(m_accountDelimiter[0] != txt[0]) m_isDirty = true; m_accountDelimiter = txt[0]; } void MyMoneyQifProfile::setOpeningBalanceText(const TQString& txt) { if(m_openingBalanceText != txt) m_isDirty = true; m_openingBalanceText = txt; } void MyMoneyQifProfile::setVoidMark(const TQString& txt) { if(m_voidMark != txt) m_isDirty = true; m_voidMark = txt; } TQString MyMoneyQifProfile::accountDelimiter(void) const { TQString rc; switch(m_accountDelimiter[0]) { case ' ': rc = " "; break; default: rc = "[]"; break; } return rc; } TQString MyMoneyQifProfile::date(const TQDate& datein) const { const char* format = m_dateFormat.latin1(); TQString buffer; TQChar delim; int maskLen; char maskChar; while(*format) { switch(*format) { case '%': maskLen = 0; maskChar = *++format; while(*format && *format == maskChar) { ++maskLen; ++format; } switch(maskChar) { case 'd': if(delim) buffer += delim; buffer += TQString::number(datein.day()).rightJustify(2, '0'); break; case 'm': if(delim) buffer += delim; if(maskLen == 3) buffer += TDEGlobal::locale()->calendar()->monthName(datein.month(), datein.year(), true); else buffer += TQString::number(datein.month()).rightJustify(2, '0'); break; case 'y': if(maskLen == 2) { buffer += twoDigitYear(delim, datein.year()); } else { if(delim) buffer += delim; buffer += TQString::number(datein.year()); } break; default: throw new MYMONEYEXCEPTION("Invalid char in QifProfile date field"); break; } delim = 0; break; default: if(delim) buffer += delim; delim = *format++; break; } } return buffer; } const TQDate MyMoneyQifProfile::date(const TQString& datein) const { // in case we don't know the format, we return an invalid date if(d->m_partPos.count() != 3) return TQDate(); TQValueVector scannedParts(3); d->dissectDate(scannedParts, datein); int yr, mon, day; bool ok; yr = scannedParts[d->m_partPos['y']].toInt(); mon = scannedParts[d->m_partPos['m']].toInt(&ok); if(!ok) { TQStringList monthNames = TQStringList::split(",", "jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec"); int j; for(j = 1; j <= 12; ++j) { if((TDEGlobal::locale()->calendar()->monthName(j, 2000, true).lower() == scannedParts[d->m_partPos['m']].lower()) || (monthNames[j-1] == scannedParts[d->m_partPos['m']].lower())) { mon = j; break; } } if(j == 13) { tqWarning(TQString("Unknown month '%1'").arg(scannedParts[d->m_partPos['m']])); return TQDate(); } } day = scannedParts[d->m_partPos['d']].toInt(); if(yr < 100) { // two digit year information? if(yr < CENTURY_BREAK) // less than the CENTURY_BREAK we assume this century yr += 2000; else yr += 1900; } return TQDate(yr, mon, day); #if 0 TQString scannedDelim[2]; TQString formatParts[3]; TQString formatDelim[2]; int part; int delim; unsigned int i,j; part = -1; delim = 0; for(i = 0; i < m_dateFormat.length(); ++i) { if(m_dateFormat[i] == '%') { ++part; if(part == 3) { tqWarning("MyMoneyQifProfile::date(const TQString& datein) Too many parts in date format"); return TQDate(); } ++i; } switch(m_dateFormat[i].latin1()) { case 'm': case 'd': case 'y': formatParts[part] += m_dateFormat[i]; break; case '/': case '-': case '.': case '\'': if(delim == 2) { tqWarning("MyMoneyQifProfile::date(const TQString& datein) Too many delimiters in date format"); return TQDate(); } formatDelim[delim] = m_dateFormat[i]; ++delim; break; default: tqWarning("MyMoneyQifProfile::date(const TQString& datein) Invalid char in date format"); return TQDate(); } } part = 0; delim = 0; bool prevWasChar = false; for(i = 0; i < datein.length(); ++i) { switch(datein[i].latin1()) { case '/': case '.': case '-': case '\'': if(delim == 2) { tqWarning("MyMoneyQifProfile::date(const TQString& datein) Too many delimiters in date field"); return TQDate(); } scannedDelim[delim] = datein[i]; ++delim; ++part; prevWasChar = false; break; default: if(prevWasChar && datein[i].isDigit()) { ++part; prevWasChar = false; } if(datein[i].isLetter()) prevWasChar = true; // replace blank with 0 scannedParts[part] += (datein[i] == ' ') ? TQChar('0') : datein[i]; break; } } int day = 1, mon = 1, yr = 1900; bool ok = false; for(i = 0; i < 2; ++i) { if(scannedDelim[i] != formatDelim[i] && scannedDelim[i] != TQChar('\'')) { tqWarning(TQString("MyMoneyQifProfile::date(const TQString& datein) Invalid delimiter '%1' when '%2' was expected"). arg(scannedDelim[i]).arg(formatDelim[i])); return TQDate(); } } TQString msg; for(i = 0; i < 3; ++i) { switch(formatParts[i][0].latin1()) { case 'd': day = scannedParts[i].toUInt(&ok); if (!ok) msg = "Invalid numeric character in day string"; break; case 'm': if(formatParts[i].length() != 3) { mon = scannedParts[i].toUInt(&ok); if (!ok) msg = "Invalid numeric character in month string"; } else { for(j = 1; j <= 12; ++j) { if(TDEGlobal::locale()->calendar()->monthName(j, 2000, true).lower() == formatParts[i].lower()) { mon = j; ok = true; break; } } if(j == 13) { msg = "Unknown month '" + scannedParts[i] + "'"; } } break; case 'y': ok = false; if(scannedParts[i].length() == formatParts[i].length()) { yr = scannedParts[i].toUInt(&ok); if (!ok) msg = "Invalid numeric character in month string"; if(yr < 100) { // two digit year info if(i > 1) { ok = true; if(scannedDelim[i-1] == TQChar('\'')) { if(m_apostropheFormat == "1900-1949") { if(yr < 50) yr += 1900; else yr += 2000; } else if(m_apostropheFormat == "1900-1999") { yr += 1900; } else if(m_apostropheFormat == "2000-2099") { yr += 2000; } else { msg = "Unsupported apostropheFormat!"; ok = false; } } else { if(m_apostropheFormat == "1900-1949") { if(yr < 50) yr += 2000; else yr += 1900; } else if(m_apostropheFormat == "1900-1999") { yr += 2000; } else if(m_apostropheFormat == "2000-2099") { yr += 1900; } else { msg = "Unsupported apostropheFormat!"; ok = false; } } } else { msg = "Year as first parameter is not supported!"; } } else if(yr < 1900) { msg = "Year not in range < 100 or >= 1900!"; } else { ok = true; } } else { msg = TQString("Length of year (%1) does not match expected length (%2).") .arg(scannedParts[i].length()).arg(formatParts[i].length()); } break; } if(!msg.isEmpty()) { tqWarning(TQString("MyMoneyQifProfile::date(const TQString& datein) %1").arg(msg)); return TQDate(); } } return TQDate(yr, mon, day); #endif } TQString MyMoneyQifProfile::twoDigitYear(const TQChar delim, int yr) const { TQChar realDelim = delim; TQString buffer; if(delim) { if((m_apostropheFormat == "1900-1949" && yr <= 1949) || (m_apostropheFormat == "1900-1999" && yr <= 1999) || (m_apostropheFormat == "2000-2099" && yr >= 2000)) realDelim = '\''; buffer += realDelim; } yr -= 1900; if(yr > 100) yr -= 100; if(yr < 10) buffer += "0"; buffer += TQString::number(yr); return buffer; } TQString MyMoneyQifProfile::value(const TQChar& def, const MyMoneyMoney& valuein) const { unsigned char _decimalSeparator; unsigned char _thousandsSeparator; TQString res; _decimalSeparator = MyMoneyMoney::decimalSeparator(); _thousandsSeparator = MyMoneyMoney::thousandSeparator(); MyMoneyMoney::signPosition _signPosition = MyMoneyMoney::negativeMonetarySignPosition(); MyMoneyMoney::setDecimalSeparator(amountDecimal(def)); MyMoneyMoney::setThousandSeparator(amountThousands(def)); MyMoneyMoney::setNegativeMonetarySignPosition(MyMoneyMoney::BeforeQuantityMoney); res = valuein.formatMoney("", 2); MyMoneyMoney::setDecimalSeparator(_decimalSeparator); MyMoneyMoney::setThousandSeparator(_thousandsSeparator); MyMoneyMoney::setNegativeMonetarySignPosition(_signPosition); return res; } MyMoneyMoney MyMoneyQifProfile::value(const TQChar& def, const TQString& valuein) const { unsigned char _decimalSeparator; unsigned char _thousandsSeparator; MyMoneyMoney res; _decimalSeparator = MyMoneyMoney::decimalSeparator(); _thousandsSeparator = MyMoneyMoney::thousandSeparator(); MyMoneyMoney::signPosition _signPosition = MyMoneyMoney::negativeMonetarySignPosition(); MyMoneyMoney::setDecimalSeparator(amountDecimal(def)); MyMoneyMoney::setThousandSeparator(amountThousands(def)); MyMoneyMoney::setNegativeMonetarySignPosition(MyMoneyMoney::BeforeQuantityMoney); res = MyMoneyMoney(valuein); MyMoneyMoney::setDecimalSeparator(_decimalSeparator); MyMoneyMoney::setThousandSeparator(_thousandsSeparator); MyMoneyMoney::setNegativeMonetarySignPosition(_signPosition); return res; } void MyMoneyQifProfile::setFilterScriptImport(const TQString& script) { if(m_filterScriptImport != script) m_isDirty = true; m_filterScriptImport = script; } void MyMoneyQifProfile::setFilterScriptExport(const TQString& script) { if(m_filterScriptExport != script) m_isDirty = true; m_filterScriptExport = script; } void MyMoneyQifProfile::setFilterFileType(const TQString& txt) { if(m_filterFileType != txt) m_isDirty = true; m_filterFileType = txt; } void MyMoneyQifProfile::setAttemptMatchDuplicates(bool f) { if ( m_attemptMatchDuplicates != f ) m_isDirty = true; m_attemptMatchDuplicates = f; } TQString MyMoneyQifProfile::inputDateFormat(void) const { TQStringList list; possibleDateFormats(list); if(list.count() == 1) return list.first(); return TQString::null; } void MyMoneyQifProfile::possibleDateFormats(TQStringList& list) const { TQStringList defaultList = TQStringList::split(":", "y,m,d:y,d,m:m,d,y:m,y,d:d,m,y:d,y,m"); list.clear(); TQStringList::const_iterator it_d; for(it_d = defaultList.begin(); it_d != defaultList.end(); ++it_d) { TQStringList parts = TQStringList::split(",", *it_d); int i; for(i = 0; i < 3; ++i) { if(d->m_partPos.contains(parts[i][0])) { if(d->m_partPos[parts[i][0]] != i) break; } // months can't be larger than 12 if(parts[i] == "m" && d->m_largestValue[i] > 12) break; // days can't be larger than 31 if(parts[i] == "d" && d->m_largestValue[i] > 31) break; } // matches all tests if(i == 3) { TQString format = *it_d; format.replace('y', "%y"); format.replace('m', "%m"); format.replace('d', "%d"); format.replace(',', " "); list << format; } } // if we haven't found any, then there's something wrong. // in this case, we present the full list and let the user decide if(list.count() == 0) { for(it_d = defaultList.begin(); it_d != defaultList.end(); ++it_d) { TQString format = *it_d; format.replace('y', "%y"); format.replace('m', "%m"); format.replace('d', "%d"); format.replace(',', " "); list << format; } } } void MyMoneyQifProfile::autoDetect(const TQStringList& lines) { m_dateFormat = TQString(); m_decimal.clear(); m_thousands.clear(); TQString numericRecords = "BT$OIQ"; TQStringList::const_iterator it; int datesScanned = 0; // section: used to switch between different QIF sections, // because the Record identifiers are ambigous between sections // eg. in transaction records, T identifies a total amount, in // account sections it's the type. // // 0 - unknown // 1 - account // 2 - transactions // 3 - prices int section = 0; TQRegExp price("\"(.*)\",(.*),\"(.*)\""); for(it = lines.begin(); it != lines.end(); ++it) { TQChar c((*it)[0]); if(c == '!') { TQString sname = (*it).lower(); section = 0; if(sname.startsWith("!account")) section = 1; else if(sname.startsWith("!type")) { if(sname.startsWith("!type:cat") || sname.startsWith("!type:payee") || sname.startsWith("!type:security") || sname.startsWith("!type:class")) { section = 0; } else if(sname.startsWith("!type:price")) { section = 3; } else section = 2; } } switch(section) { case 1: if(c == 'B') { scanNumeric((*it).mid(1), m_decimal[c], m_thousands[c]); } break; case 2: if(numericRecords.contains(c)) { scanNumeric((*it).mid(1), m_decimal[c], m_thousands[c]); } else if((c == 'D') && (m_dateFormat.isEmpty())) { if(d->m_partPos.count() != 3) { scanDate((*it).mid(1)); ++datesScanned; if(d->m_partPos.count() == 2) { // if we have detected two parts we can calculate the third and its position d->getThirdPosition(); } } } break; case 3: if(price.search(*it) != -1) { scanNumeric(price.cap(2), m_decimal['P'], m_thousands['P']); scanDate(price.cap(3)); ++datesScanned; } break; } } // the following algorithm is only applied if we have more // than 20 dates found. Smaller numbers have shown that the // results are inaccurate which leads to a reduced number of // date formats presented to choose from. if(d->m_partPos.count() != 3 && datesScanned > 20) { TQMap sortedPos; // make sure to reset the known parts for the following algorithm if(d->m_partPos.contains('y')) { d->m_changeCount[d->m_partPos['y']] = -1; for(int i = 0; i < 3; ++i) { if(d->m_partPos['y'] == i) continue; // can we say for sure that we hit the day field? if(d->m_largestValue[i] > 12) { d->m_partPos['d'] = i; } } } if(d->m_partPos.contains('d')) d->m_changeCount[d->m_partPos['d']] = -1; if(d->m_partPos.contains('m')) d->m_changeCount[d->m_partPos['m']] = -1; for(int i = 0; i < 3; ++i) { if(d->m_changeCount[i] != -1) { sortedPos[d->m_changeCount[i]] = i; } } TQMap::const_iterator it_a; TQMap::const_iterator it_b; switch(sortedPos.count()) { case 1: // all the same // let the user decide, we can't figure it out break; case 2: // two are the same, we treat the largest as the day // if it's 20% larger than the other one and let the // user pick the other two { it_b = sortedPos.begin(); it_a = it_b; ++it_b; double a = d->m_changeCount[*it_a]; double b = d->m_changeCount[*it_b]; if(b > (a * 1.2)) { d->m_partPos['d'] = *it_b; } } break; case 3: // three different, we check if they are 20% apart each it_b = sortedPos.begin(); for(int i = 0; i < 2; ++i) { it_a = it_b; ++it_b; double a = d->m_changeCount[*it_a]; double b = d->m_changeCount[*it_b]; if(b > (a * 1.2)) { switch(i) { case 0: d->m_partPos['y'] = *it_a; break; case 1: d->m_partPos['d'] = *it_b; break; } } } break; } // extract the last if necessary and possible date position d->getThirdPosition(); } } void MyMoneyQifProfile::scanNumeric(const TQString& txt, TQChar& decimal, TQChar& thousands) const { TQChar first, second; TQRegExp numericChars("[0-9-()]"); for(unsigned int i = 0; i < txt.length(); ++i) { if(numericChars.search(txt[i]) == -1) { first = second; second = txt[i]; } } if(!second.isNull()) decimal = second; if(!first.isNull()) thousands = first; } void MyMoneyQifProfile::scanDate(const TQString& txt) const { // extract the parts from the txt TQValueVector parts(3); // the various parts of the date d->dissectDate(parts, txt); // now analyse the parts for(int i = 0; i < 3; ++i) { bool ok; int value = parts[i].toInt(&ok); if(!ok) { // this should happen only if the part is non-numeric -> month d->m_partPos['m'] = i; } else if(value != 0) { if(value != d->m_lastValue[i]) { d->m_changeCount[i]++; d->m_lastValue[i] = value; if(value > d->m_largestValue[i]) d->m_largestValue[i] = value; } // if it's > 31 it can only be years if(value > 31) { d->m_partPos['y'] = i; } // and if it's in between 12 and 32 and we already identified the // position for the year it must be days if((value > 12) && (value < 32) && d->m_partPos.contains('y')) { d->m_partPos['d'] = i; } } } } #include "mymoneyqifprofile.moc"