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