ossp-pkg/as/as-gui/as_dataop.cpp
//
// 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 <rse@engelschall.com>
// Copyright (c) 2002-2004 Michael Schloh von Bennewitz <michael@schloh.com>
// 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 <map>
#include <string>
// Qt general headers
#include <qregexp.h> // Portable regular expressions
#include <qdatetime.h>
#include <qmessagebox.h>
#include <qtextstream.h>
#include <qpopupmenu.h>
#include <qfile.h>
// 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<string, int> Hashnames; // Hashtable for storing names
map<int, string> Hashnums; // Hashtable for storing repetitions
map<string, int>::iterator Nameiter; // The hashtable name iterator
map<int, string>::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<string, int>::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->setNumRows(nIter); // No excess rows
m_pMaintable->setCurrentCell(nIter - 1, 0); // Move focus to last row
m_pMaintable->setUpdatesEnabled(true); // Update
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<RtTableItem *>(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
}