michael@1: // michael@1: // OSSP asgui - Accounting system graphical user interface michael@12: // Copyright (c) 2002-2009 The OSSP Project (http://www.ossp.org/) michael@12: // Copyright (c) 2002-2009 Ralf S. Engelschall michael@12: // Copyright (c) 2002-2009 Michael Schloh von Bennewitz michael@12: // Copyright (c) 2002-2009 Cable & Wireless Telecommunications Services GmbH michael@1: // michael@1: // This file is part of OSSP asgui, an accounting system graphical user michael@3: // interface which can be found at http://asgui.europalab.com/. michael@1: // michael@1: // Permission to use, copy, modify, and distribute this software for michael@1: // any purpose with or without fee is hereby granted, provided that michael@1: // the above copyright notice and this permission notice appear in all michael@1: // copies. michael@1: // michael@1: // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED michael@1: // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF michael@1: // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. michael@1: // IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR michael@1: // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@1: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@1: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF michael@1: // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND michael@1: // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, michael@1: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT michael@1: // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@1: // SUCH DAMAGE. michael@1: // michael@1: // as_dataops.cpp: ISO C++ implementation michael@1: // michael@1: michael@3: #define QT3_SUPPORT michael@3: michael@1: // System headers michael@1: #include michael@1: #include michael@1: michael@1: // Qt general headers michael@1: #include // Portable regular expressions michael@1: #include michael@1: #include michael@3: #include michael@3: #include michael@1: #include michael@3: //Added by qt3to4: michael@3: #include michael@1: michael@1: // User interface michael@1: #include "as_const.h" // Application constants michael@1: #include "as_tableitem.h" // For class RtTableItem michael@1: #include "as_crc.h" // For quality strings michael@1: #include "as_uuid.h" // UUID classes michael@1: #include "as_table.h" // TiTable class michael@1: michael@1: // Icon pixel maps michael@1: #include "as_gfx/statok.xpm" // static const char *s_kpcStatokay_xpm[] michael@1: #include "as_gfx/staterr.xpm" // static const char *s_kpcStaterror_xpm[] michael@1: #include "as_gfx/statwrn.xpm" // static const char *s_kpcStatwarn_xpm[] michael@1: michael@1: michael@1: // michael@1: // Convenience method to load accounts from a file michael@1: // michael@1: void Titraqform::loadAccounts(QFile &Fileobj) michael@1: { michael@1: if (Fileobj.isOpen()) { // Check state of file michael@1: Fileobj.flush(); // Begin processing file cleanly michael@3: Q3TextStream Account(&Fileobj); // Convert data to stream michael@1: this->loadAccounts(Account); // Pass off to do the real work michael@1: } michael@1: else { michael@3: if (!Fileobj.open(QIODevice::ReadOnly)) { // Try to open file michael@1: QString Readerrstr; michael@1: Readerrstr = trUtf8(TITRAQ_READAFILFAIL).arg(Fileobj.name()); michael@1: throw Genexcept(Readerrstr.ascii()); michael@1: } michael@1: else michael@1: Fileobj.flush(); // Begin processing file cleanly michael@3: Q3TextStream Account(&Fileobj); // Convert data to stream michael@1: this->loadAccounts(Account); // Pass off to do the real work michael@1: Fileobj.close(); // Finish fileop by closing michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Load accounts themselves data from a stream michael@1: // michael@3: void Titraqform::loadAccounts(Q3TextStream &Tstream) michael@1: { michael@1: using namespace std; // Needed for hash tables with hmap michael@1: map Hashnames; // Hashtable for storing names michael@1: map Hashnums; // Hashtable for storing repetitions michael@1: map::iterator Nameiter; // The hashtable name iterator michael@1: map::iterator Numiter; // The hashtable number iterator michael@1: michael@1: QString Line; // Used for linewise editing and whitespace eating michael@1: michael@1: // Eat lines until reading the start accounts token michael@1: while (!Line.startsWith(trUtf8("%!AS-ACCOUNTS")) && !Tstream.atEnd()) { michael@1: Line = QString(""); // Empty line for later inspection michael@1: Line = Tstream.readLine(); // Get a new line to examine michael@1: } michael@1: michael@1: // Strip out extra line feeds in stream michael@1: while (Line.isEmpty() && !Tstream.atEnd()) { michael@1: Tstream.skipWhiteSpace(); // Strip and get michael@1: Line = Tstream.readLine(); // the new line michael@1: if (Line.at(0) == QChar('#')) // Remove comments michael@1: Line = QString(""); michael@1: } michael@1: michael@1: // Set the accounts choices by linewise reading from the input michael@1: // stream and parsing the corresponding account fields out of it michael@1: while (!Line.isEmpty()) { michael@1: QString Temp; // For reading from stream michael@3: Q3TextStream Asline(&Line, QIODevice::ReadOnly); // Convert a single line michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Temp; // Copy revision indicator michael@1: michael@1: if (Temp == QString(QChar('R'))) { // Copy the account field michael@1: Asline >> Temp; // to temporary for transfer michael@3: string Convstring = Temp.toStdString(); // Convert to string (can't cast?) michael@1: Hashnames[Convstring] += Hashnames[Convstring]; michael@1: } michael@1: michael@1: Line = QString(""); // Clear line for next round michael@1: while (Line.isEmpty() && !Tstream.atEnd()) { michael@1: Tstream.skipWhiteSpace(); // Strip and get michael@1: Line = Tstream.readLine(); // the new line michael@1: if (Line.at(0) == QChar('#')) // Remove comments michael@1: Line = QString(""); michael@1: } michael@1: } michael@1: michael@1: // Hashnames.insert(map::value_type((string)QString, int)); michael@1: for (Nameiter = Hashnames.begin(); Nameiter != Hashnames.end(); Nameiter++) michael@1: Hashnums[Nameiter->second] += Nameiter->first + '\n'; michael@1: michael@1: // FIXME: Put this in loadData, to load custom and most used task names before michael@1: // FIXME: default listings are sorted and inserted michael@1: for (Numiter = Hashnums.begin(); Numiter != Hashnums.end(); Numiter++) { michael@1: michael@1: // Count the number of lines of sorted task names michael@3: int nNumlines = QString::fromStdString(Numiter->second).count('\n'); michael@1: michael@1: // Iterate through the lines of task names, feeding them into the menu michael@1: for (int nIter = 0; nIter < nNumlines; nIter++) michael@3: *m_pTaskentries << QString::fromStdString(Numiter->second).section('\n', nIter, nIter); michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Convenience method to load personal data from a file michael@1: // michael@1: void Titraqform::loadData(QFile &Fileobj) michael@1: { michael@1: if (Fileobj.isOpen()) { // Check state of file michael@1: Fileobj.flush(); // Begin processing file cleanly michael@3: Q3TextStream Asentry(&Fileobj); // Convert data to stream michael@1: this->loadData(Asentry); // Pass off to do the real work michael@1: } michael@1: else { michael@3: if (!Fileobj.open(QIODevice::ReadOnly)) // Try to open file michael@1: throw Genexcept(trUtf8(TITRAQ_READPFILFAIL)); michael@1: else michael@1: Fileobj.flush(); // Begin processing file cleanly michael@3: Q3TextStream Asentry(&Fileobj); // Convert data to stream michael@1: this->loadData(Asentry); // Pass off to do the real work michael@1: Fileobj.close(); // Finish fileop by closing michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Load personal data from a stream michael@1: // michael@3: void Titraqform::loadData(Q3TextStream &Tstream) michael@1: { michael@1: bool bValid = true; // Used to warn on globally invalid accounting data michael@1: int nIter = 0; // Iterator used in loop and also as a count michael@1: QString Line; // Used for linewise editing and whitespace eating michael@1: QString Bitbucket; // Used for null device until we find a better way michael@1: michael@1: QPixmap Statokay(s_kpcStatokay_xpm); michael@1: QPixmap Statwarn(s_kpcStatwarn_xpm); michael@1: QPixmap Staterror(s_kpcStaterror_xpm); michael@1: michael@1: // Strip out extra line feeds at stream start michael@1: while (Line.isEmpty() && !Tstream.atEnd()) { michael@1: Tstream.skipWhiteSpace(); // Strip and get michael@1: Line = Tstream.readLine(); // the new line michael@1: if (Line.at(0) == QChar('#')) // Remove comments michael@1: Line = QString(""); michael@1: } michael@1: michael@1: // Strip out extra line feeds after reading the data symbol michael@1: Line = QString(""); // Reset our line michael@1: while (Line.isEmpty() && !Tstream.atEnd()) { michael@1: Tstream.skipWhiteSpace(); // Strip and get michael@1: Line = Tstream.readLine(); // the new line michael@1: if (Line.at(0) == QChar('#')) // Remove comments michael@1: Line = QString(""); michael@1: } michael@1: michael@1: // // Going into data churning, so prepare date and time parsing and conversion michael@1: // Converdate.setSeparator(trUtf8(".")); michael@1: // Convertime.setSeparator(trUtf8(":")); michael@1: michael@1: // Optimize viewing by repainting cells only once after processing michael@1: m_pMaintable->setUpdatesEnabled(false); michael@1: michael@1: // Set the table text by linewise reading from the input stream michael@1: // and parsing date, time, account, and other columns out of it michael@1: while (!Line.isEmpty()) { michael@1: bool bValid = true; // Warns on linewise invalid accounting data michael@1: QString User, Guid, Crc, Rev; // Valid admin fields michael@1: QString Date, Start, Finish, Account, Amount, Remark; // Valid user fields michael@3: Q3TextStream Asline(&Line, QIODevice::ReadOnly); // Convert a single line now michael@1: michael@1: if (nIter % g_knBlocks == 0) // Add blocks of rows to optimize loading michael@1: m_pMaintable->setNumRows(m_pMaintable->numRows() + g_knBlocks); michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> User; // Copy the user field michael@1: if (!User.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXUSER, User); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Guid; // Copy the GUID field michael@1: // Postpone actual text delivery, to autoinsert GUIDs! michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Crc; // Copy the CRC field michael@1: // Postpone actual text delivery, to autoinsert CRCs! michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Rev; // Copy the rev field michael@1: if (!Rev.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXREV, Rev); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Date; // Copy the date field michael@1: if (!Date.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXDATE, Date); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Start; // Copy the start field michael@1: if (!Start.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXSTART, Start); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Finish; // Copy the finish field michael@1: if (!Finish.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXFINISH, Finish); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Amount; // Copy the amount field michael@1: if (!Amount.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXAMOUNT, Amount); michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Asline >> Account; // Copy the account field michael@1: if (!Account.isEmpty()) { michael@3: Q3TableItem *pOld = m_pMaintable->item(nIter, TITRAQ_IDXTASK); michael@1: delete(pOld); // Get rid of the old before going with the new michael@3: RtTableItem *pSwapitem = new RtTableItem(m_pMaintable, Q3TableItem::WhenCurrent, Account); michael@3: pSwapitem->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); michael@1: m_pMaintable->setItem(nIter, TITRAQ_IDXTASK, pSwapitem); michael@1: } michael@1: else michael@1: bValid = false; michael@1: michael@1: Asline.skipWhiteSpace(); // Remove whitespaces michael@1: Remark = Asline.read(); // Copy the remark field michael@1: michael@1: QRegExp Quoted("\"(.*[^\\\\])\""); // Get rid of michael@1: Quoted.search(Remark); // surrounding double michael@1: Remark = Quoted.cap(Quoted.numCaptures()); // quotes, and michael@1: Remark.replace(QRegExp("\\\\(.)"), QString("\\1")); // escape backslashes michael@1: michael@1: if (!Remark.isEmpty()) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXREMARK, Remark); michael@1: michael@1: if (bValid) { // Show a bitmap to signal valid or error state michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXSTATUS, QString(QChar('O'))); michael@1: m_pMaintable->setPixmap(nIter, TITRAQ_IDXSTATUS, Statokay); michael@1: } michael@1: else { michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXSTATUS, QString(QChar('E'))); michael@1: m_pMaintable->setPixmap(nIter, TITRAQ_IDXSTATUS, Staterror); michael@1: } michael@1: michael@1: // Set the GUID text here, in case we need to generate a fresh value michael@1: if (!Guid.isEmpty()) { michael@1: if (Guid != ".") // This means, generate a fresh GUID value michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXGUID, Guid); michael@1: else { michael@1: AS::Uuid Guidi; // For GUID production michael@1: Guidi.genId(); michael@3: m_pMaintable->setText(nIter, TITRAQ_IDXGUID, QString::fromStdString(Guidi.getString())); michael@1: } michael@1: } michael@1: else // if isEmpty() michael@1: bValid = false; michael@1: michael@1: // Set the CRC text here, in case we need to generate a fresh value michael@1: if (!Crc.isEmpty()) { michael@1: if (Crc != ".") // This means, generate a fresh CRC value michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXCRC, "0x" + Crc); michael@1: else { michael@1: // Make our long tuple to run through the CRC32 generator michael@1: Qualistring Testuple = User + Guid + Rev + Date + Start; michael@1: Testuple += Finish + Amount + Account + Remark; michael@1: U32 Valcrc = Testuple.getCrc(); // Finally set the checksum to its new value michael@1: QString Crcstr = QString::number(Valcrc, 16).rightJustify(8, '0'); michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXCRC, "0x" + Crcstr); michael@1: } michael@1: } michael@1: else // if isEmpty() michael@1: bValid = false; michael@1: michael@1: nIter++; // The big increment michael@1: Line = QString(""); // Clear line for next round michael@1: michael@1: while (Line.isEmpty() && !Tstream.atEnd()) { // Strip and get michael@1: Tstream.skipWhiteSpace(); // Remove white michael@1: Line = Tstream.readLine(); // the new line michael@1: if (Line.at(0) == QChar('#')) // Remove comments michael@1: Line = QString(""); michael@1: } michael@1: } michael@1: michael@1: // // Start sorting order and direction correctly according to user preferences michael@1: // int nSortcol = (int)m_pPrefs->getNumber(TITRAQ_PREFSORTCOL, TITRAQ_DEFSORTCOL); michael@1: // bool bSortdir = m_pPrefs->getBool(TITRAQ_PREFSORTDIR, TITRAQ_DEFSORTDIR); michael@1: // m_pMaintable->setSortdir(!bSortdir); // Hack! Fake sortdir so we start right michael@1: // m_pMaintable->sortColumn(nSortcol, bSortdir); michael@1: michael@1: // Write nonsaving line numbers for all rows michael@1: for (int nIter = m_pMaintable->numRows() - 1; nIter >= 0; nIter--) michael@1: m_pMaintable->setText(nIter, TITRAQ_IDXLINE, QString::number(nIter).rightJustify(4, QChar('0'))); michael@1: michael@1: m_pMaintable->setUpdatesEnabled(true); // Update and repaint michael@1: m_pMaintable->setNumRows(nIter); // No excess rows michael@1: m_pMaintable->setCurrentCell(nIter - 1, 0); // Move focus to last row michael@1: m_pMaintable->ensureCellVisible(nIter - 1, 0); // Scroll please michael@1: michael@1: if (!bValid) michael@1: throw Genexcept("Warning: invalid accounting data."); michael@1: } michael@1: michael@1: // michael@1: // Convenience method to save accounting data to a file michael@1: // michael@1: void Titraqform::saveData(QFile &Fileobj) michael@1: { michael@1: if (Fileobj.isOpen()) { // Check state of file michael@1: Fileobj.flush(); // Begin processing file cleanly michael@3: Q3TextStream Asentry(&Fileobj); // Convert data to stream michael@1: this->saveData(Asentry); // Pass off to do the real work michael@1: } michael@1: else { michael@3: if (!Fileobj.open(QIODevice::WriteOnly)) // Try to open file michael@1: throw Genexcept(trUtf8(TITRAQ_READPFILFAIL)); michael@3: Q3TextStream Asentry(&Fileobj); // Convert data to stream michael@1: this->saveData(Asentry); // Pass off to do the real work michael@1: Fileobj.close(); // Finish fileop by closing michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Save accounting data to a stream michael@1: // michael@3: void Titraqform::saveData(Q3TextStream &Tstream) michael@1: { michael@1: const int nRows = m_pMaintable->numRows(); // Max rows used in loop michael@1: QString Tempfield; // Current field string michael@1: QString Strsearch; // String to strip search michael@1: QRegExp Stripper("\\s*$"); // Pattern to strip off michael@1: michael@1: // Start by prepending the AS data format version symbol michael@1: Tstream << TITRAQ_DATAPATTERN << TITRAQ_DATAVERSIONMAJ michael@1: << QChar('.') << TITRAQ_DATAVERSIONMIN << endl; michael@1: michael@1: // Linewise save from the main table date, time, account, and others michael@1: for (int nIter = 0; nIter < nRows; nIter++) { michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXUSER); // Load user field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << Tempfield; // Save user field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXGUID); // Load GUID field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save GUID field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXCRC); // Load CRC field text michael@1: Tempfield.remove("0x"); michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save CRC field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXREV); // Load rev field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save rev field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXDATE); // Load date field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save date field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXSTART); // Load start field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save start field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXFINISH); // Load end field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save end field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXAMOUNT); // Load amount field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save amount michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXTASK); // Load acct field text michael@11: if (!Tempfield.isNull()) michael@1: Tstream << trUtf8(" ") << Tempfield; // Save acct field text michael@1: michael@1: Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXREMARK); // Load remark field text michael@1: Tstream << trUtf8(" \""); // Save beginning double quote michael@11: if (!Tempfield.isNull()) { michael@1: Strsearch = QRegExp::escape(Tempfield); // Incoming string escaped michael@1: Stripper.search(Strsearch); michael@1: Tempfield.truncate(Stripper.pos()); // Cut off whitespace michael@1: Tempfield.replace(QChar('\\'), QString("\\\\")); // Escape back slashes michael@1: Tempfield.replace(QChar('\"'), QString("\\\"")); // Escape double quotes michael@1: Tstream << Tempfield; // Save remark field text michael@1: } michael@1: Tstream << trUtf8("\""); // Save ending double quote michael@1: michael@1: Tstream << endl; // Append a newline michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Convenience method to validate AS data in a file michael@1: // michael@1: const bool Titraqform::validateData(QFile &Filin) const michael@1: { michael@1: QString Firstline; // Will contain the first line of text michael@10: char szReadline[QString(TITRAQ_DATAPATTERN).length() + 4L]; // File line michael@1: bool bRet = false; // Set the initial return value michael@1: michael@1: if (Filin.isOpen()) { // Check open state of file michael@1: Filin.flush(); // Not sure if this is needed michael@1: Filin.reset(); // Set the file index position to 0 michael@10: Filin.readLine(szReadline, QString(TITRAQ_DATAPATTERN).length() + 2L); michael@1: } michael@1: else { michael@3: if (!Filin.open(QIODevice::ReadOnly)) // Try to open file michael@1: throw Genexcept(trUtf8(TITRAQ_READPFILFAIL)); michael@1: else { // File is now open michael@10: Filin.readLine(szReadline, QString(TITRAQ_DATAPATTERN).length() + 2L); michael@1: Filin.close(); // Remember to close michael@1: } michael@1: } michael@1: michael@1: try { // Pass off to worker method michael@10: Firstline = QString::fromAscii(szReadline); michael@1: bRet = this->validateData(Firstline); michael@1: } michael@1: catch (Genexcept &) { michael@1: throw; // Rethrow onwards michael@1: } michael@1: return bRet; michael@1: } michael@1: michael@1: // michael@1: // Validate the AS data pattern in a line michael@1: // michael@1: const bool Titraqform::validateData(QString &Linin) const michael@1: { michael@1: bool bRet = false; // Initial return value michael@1: michael@1: // Ensure that the right data version pattern precedes the data michael@1: QString Datapattern = QString(TITRAQ_DATAPATTERN); michael@1: if (!Linin.startsWith(Datapattern)) { // Incompatible data format michael@1: QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short, michael@1: TITRAQ_NOPATTERNFOUND + QString(TITRAQ_DATAPATTERN) + TITRAQ_WASNOTFOUNDIN, michael@1: QMessageBox::Critical, QMessageBox::Ok | QMessageBox::Escape, michael@3: Qt::NoButton, Qt::NoButton); michael@1: Problema.exec(); // Give the user the bad news michael@1: throw Genexcept(TITRAQ_INVALIDDATA); michael@1: } michael@1: else if (Linin.section(Datapattern, 1).section('.', 0, 0).toInt() != TITRAQ_DATAVERSIONMAJ) { michael@1: QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short, michael@1: TITRAQ_BADVERSIONMAJ, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Escape, michael@3: Qt::NoButton, Qt::NoButton); michael@1: Problema.exec(); // Give the user the bad news michael@1: throw Genexcept(TITRAQ_INCOMPATDATA); michael@1: } michael@1: else if (Linin.section(Datapattern, 1).section('.', 1, 1).toInt() > TITRAQ_DATAVERSIONMIN) { michael@1: QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short, michael@1: TITRAQ_BADVERSIONMIN, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Escape, michael@3: Qt::NoButton, Qt::NoButton); michael@1: Problema.exec(); // Give the user the bad news michael@1: throw Genexcept(TITRAQ_INCOMPATDATA); michael@1: } michael@1: else michael@1: bRet = true; michael@1: michael@1: return bRet; // Not reached in case of failure michael@1: } michael@1: michael@1: // michael@1: // Get a whole row of data michael@1: // michael@1: const QString Titraqform::getRowdata(void) const michael@1: { michael@1: QString Rowdata, Tempstring; // For output string michael@3: Q3TableSelection Select = m_pMaintable->selection(0); // Highlighted text michael@1: int nTotal = Select.bottomRow() - Select.topRow() + 1; // Total row select michael@1: michael@1: // Calculate rows to delete from selection highlight michael@1: for (int nIter = 0; nIter < nTotal; ++nIter) { michael@1: // Build the row data string one field at a time, adding seps inbetween michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXLINE); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXUSER); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXGUID); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXCRC); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXREV); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXDATE); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXSTART); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXFINISH); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXAMOUNT); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXTASK); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXREMARK); michael@1: if (!Tempstring.isEmpty()) michael@1: Rowdata += TITRAQ_SEPARATORTOK + Tempstring; michael@1: Rowdata += trUtf8("\n"); // Finish off line michael@1: } michael@1: michael@1: return Rowdata; michael@1: } michael@1: michael@1: // michael@1: // Set a whole row of data michael@1: // michael@1: void Titraqform::setRowdata(QString &Rowdata) const michael@1: { michael@3: int nRows = Rowdata.count(QChar('\n')); // Set so many rows michael@1: int nCurrentrow = m_pMaintable->currentRow(); // Current table row michael@3: Q3TableItem *pItem = NULL; // Old item to change out michael@1: QString Line, User, Guid, Crc, Rev; // Admin fields in table michael@1: QString Date, Start, Finish, Amount, Task, Remark; // Viewable fields in table michael@3: Q3TextStream Datastream(&Rowdata, QIODevice::ReadOnly); // Convert data to stream michael@1: michael@1: for (int nIter = 0; nIter < nRows; ++nIter) { michael@1: QString Singlerow = Datastream.readLine(); // For linewise operation michael@3: Q3TextStream Rowstream(&Singlerow, QIODevice::ReadOnly); // Convert row to stream michael@1: michael@1: Rowstream >> Line >> User >> Guid >> Crc >> Rev; // Stream data fields michael@1: Rowstream >> Date >> Start >> Finish >> Amount >> Task; // to corresponding vars michael@1: Remark = Rowstream.readLine(); // Remark is a whole line michael@1: michael@1: // Set the table row data one field at a time, skipping seps inbetween michael@1: // Importantly, do not copy over the GUID, which must be unique each row michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXLINE, Line); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXUSER, User); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXCRC, Crc); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXREV, Rev); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXDATE, Date); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXSTART, Start); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXFINISH, Finish); michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXAMOUNT, Amount); michael@1: michael@1: // // FIXME: Why doesn't this code change the RtTableItem text in place? michael@1: // RtTableItem *pTask = NULL; // Derived and special michael@1: // pItem = m_pMaintable->item(nCurrentrow + nIter, TITRAQ_IDXTASK); michael@1: // pTask = static_cast(pItem); michael@1: // pTask->setText(Task); michael@1: michael@1: // Change out old item and replace with new task data michael@1: pItem = m_pMaintable->item(nCurrentrow + nIter, TITRAQ_IDXTASK); michael@1: delete(pItem); // Get rid of the old before going with the new michael@3: RtTableItem *pSwapitem = new RtTableItem(m_pMaintable, Q3TableItem::WhenCurrent, Task); michael@3: pSwapitem->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); michael@1: m_pMaintable->setItem(nCurrentrow + nIter, TITRAQ_IDXTASK, pSwapitem); michael@1: michael@1: // Continue with field processing business as usual michael@1: m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXREMARK, Remark.simplifyWhiteSpace()); michael@1: } michael@1: } michael@1: michael@1: // michael@1: // Discover which column is the first in view michael@1: // michael@1: const int Titraqform::getFirstcol(void) const michael@1: { michael@1: int nIter = 0; // Is both iterator in loop and column number returned michael@1: michael@1: // Use column selector menu popup as sole key to determining column state michael@1: while (nIter < TITRAQ_IDXTAIL && !m_pColspopup->isItemChecked(m_pColspopup->idAt(++nIter))) michael@1: TITRAQ_NOP; michael@1: michael@1: return nIter - 1; // Return one less to account for start of item offset michael@1: }