Wed, 07 Jan 2009 18:49:25 +0100
Bump copyright year number.
1 //
2 // OSSP asgui - Accounting system graphical user interface
3 // Copyright (c) 2002-2009 The OSSP Project (http://www.ossp.org/)
4 // Copyright (c) 2002-2009 Ralf S. Engelschall <rse@engelschall.com>
5 // Copyright (c) 2002-2009 Michael Schloh von Bennewitz <michael@schloh.com>
6 // Copyright (c) 2002-2009 Cable & Wireless Telecommunications Services GmbH
7 //
8 // This file is part of OSSP asgui, an accounting system graphical user
9 // interface which can be found at http://asgui.europalab.com/.
10 //
11 // Permission to use, copy, modify, and distribute this software for
12 // any purpose with or without fee is hereby granted, provided that
13 // the above copyright notice and this permission notice appear in all
14 // copies.
15 //
16 // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
17 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 // IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
20 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 // SUCH DAMAGE.
28 //
29 // as_dataops.cpp: ISO C++ implementation
30 //
32 #define QT3_SUPPORT
34 // System headers
35 #include <map>
36 #include <string>
38 // Qt general headers
39 #include <qregexp.h> // Portable regular expressions
40 #include <qdatetime.h>
41 #include <qmessagebox.h>
42 #include <q3textstream.h>
43 #include <q3popupmenu.h>
44 #include <qfile.h>
45 //Added by qt3to4:
46 #include <QPixmap>
48 // User interface
49 #include "as_const.h" // Application constants
50 #include "as_tableitem.h" // For class RtTableItem
51 #include "as_crc.h" // For quality strings
52 #include "as_uuid.h" // UUID classes
53 #include "as_table.h" // TiTable class
55 // Icon pixel maps
56 #include "as_gfx/statok.xpm" // static const char *s_kpcStatokay_xpm[]
57 #include "as_gfx/staterr.xpm" // static const char *s_kpcStaterror_xpm[]
58 #include "as_gfx/statwrn.xpm" // static const char *s_kpcStatwarn_xpm[]
61 //
62 // Convenience method to load accounts from a file
63 //
64 void Titraqform::loadAccounts(QFile &Fileobj)
65 {
66 if (Fileobj.isOpen()) { // Check state of file
67 Fileobj.flush(); // Begin processing file cleanly
68 Q3TextStream Account(&Fileobj); // Convert data to stream
69 this->loadAccounts(Account); // Pass off to do the real work
70 }
71 else {
72 if (!Fileobj.open(QIODevice::ReadOnly)) { // Try to open file
73 QString Readerrstr;
74 Readerrstr = trUtf8(TITRAQ_READAFILFAIL).arg(Fileobj.name());
75 throw Genexcept(Readerrstr.ascii());
76 }
77 else
78 Fileobj.flush(); // Begin processing file cleanly
79 Q3TextStream Account(&Fileobj); // Convert data to stream
80 this->loadAccounts(Account); // Pass off to do the real work
81 Fileobj.close(); // Finish fileop by closing
82 }
83 }
85 //
86 // Load accounts themselves data from a stream
87 //
88 void Titraqform::loadAccounts(Q3TextStream &Tstream)
89 {
90 using namespace std; // Needed for hash tables with hmap
91 map<string, int> Hashnames; // Hashtable for storing names
92 map<int, string> Hashnums; // Hashtable for storing repetitions
93 map<string, int>::iterator Nameiter; // The hashtable name iterator
94 map<int, string>::iterator Numiter; // The hashtable number iterator
96 QString Line; // Used for linewise editing and whitespace eating
98 // Eat lines until reading the start accounts token
99 while (!Line.startsWith(trUtf8("%!AS-ACCOUNTS")) && !Tstream.atEnd()) {
100 Line = QString(""); // Empty line for later inspection
101 Line = Tstream.readLine(); // Get a new line to examine
102 }
104 // Strip out extra line feeds in stream
105 while (Line.isEmpty() && !Tstream.atEnd()) {
106 Tstream.skipWhiteSpace(); // Strip and get
107 Line = Tstream.readLine(); // the new line
108 if (Line.at(0) == QChar('#')) // Remove comments
109 Line = QString("");
110 }
112 // Set the accounts choices by linewise reading from the input
113 // stream and parsing the corresponding account fields out of it
114 while (!Line.isEmpty()) {
115 QString Temp; // For reading from stream
116 Q3TextStream Asline(&Line, QIODevice::ReadOnly); // Convert a single line
118 Asline.skipWhiteSpace(); // Remove whitespaces
119 Asline >> Temp; // Copy revision indicator
121 if (Temp == QString(QChar('R'))) { // Copy the account field
122 Asline >> Temp; // to temporary for transfer
123 string Convstring = Temp.toStdString(); // Convert to string (can't cast?)
124 Hashnames[Convstring] += Hashnames[Convstring];
125 }
127 Line = QString(""); // Clear line for next round
128 while (Line.isEmpty() && !Tstream.atEnd()) {
129 Tstream.skipWhiteSpace(); // Strip and get
130 Line = Tstream.readLine(); // the new line
131 if (Line.at(0) == QChar('#')) // Remove comments
132 Line = QString("");
133 }
134 }
136 // Hashnames.insert(map<string, int>::value_type((string)QString, int));
137 for (Nameiter = Hashnames.begin(); Nameiter != Hashnames.end(); Nameiter++)
138 Hashnums[Nameiter->second] += Nameiter->first + '\n';
140 // FIXME: Put this in loadData, to load custom and most used task names before
141 // FIXME: default listings are sorted and inserted
142 for (Numiter = Hashnums.begin(); Numiter != Hashnums.end(); Numiter++) {
144 // Count the number of lines of sorted task names
145 int nNumlines = QString::fromStdString(Numiter->second).count('\n');
147 // Iterate through the lines of task names, feeding them into the menu
148 for (int nIter = 0; nIter < nNumlines; nIter++)
149 *m_pTaskentries << QString::fromStdString(Numiter->second).section('\n', nIter, nIter);
150 }
151 }
153 //
154 // Convenience method to load personal data from a file
155 //
156 void Titraqform::loadData(QFile &Fileobj)
157 {
158 if (Fileobj.isOpen()) { // Check state of file
159 Fileobj.flush(); // Begin processing file cleanly
160 Q3TextStream Asentry(&Fileobj); // Convert data to stream
161 this->loadData(Asentry); // Pass off to do the real work
162 }
163 else {
164 if (!Fileobj.open(QIODevice::ReadOnly)) // Try to open file
165 throw Genexcept(trUtf8(TITRAQ_READPFILFAIL));
166 else
167 Fileobj.flush(); // Begin processing file cleanly
168 Q3TextStream Asentry(&Fileobj); // Convert data to stream
169 this->loadData(Asentry); // Pass off to do the real work
170 Fileobj.close(); // Finish fileop by closing
171 }
172 }
174 //
175 // Load personal data from a stream
176 //
177 void Titraqform::loadData(Q3TextStream &Tstream)
178 {
179 bool bValid = true; // Used to warn on globally invalid accounting data
180 int nIter = 0; // Iterator used in loop and also as a count
181 QString Line; // Used for linewise editing and whitespace eating
182 QString Bitbucket; // Used for null device until we find a better way
184 QPixmap Statokay(s_kpcStatokay_xpm);
185 QPixmap Statwarn(s_kpcStatwarn_xpm);
186 QPixmap Staterror(s_kpcStaterror_xpm);
188 // Strip out extra line feeds at stream start
189 while (Line.isEmpty() && !Tstream.atEnd()) {
190 Tstream.skipWhiteSpace(); // Strip and get
191 Line = Tstream.readLine(); // the new line
192 if (Line.at(0) == QChar('#')) // Remove comments
193 Line = QString("");
194 }
196 // Strip out extra line feeds after reading the data symbol
197 Line = QString(""); // Reset our line
198 while (Line.isEmpty() && !Tstream.atEnd()) {
199 Tstream.skipWhiteSpace(); // Strip and get
200 Line = Tstream.readLine(); // the new line
201 if (Line.at(0) == QChar('#')) // Remove comments
202 Line = QString("");
203 }
205 // // Going into data churning, so prepare date and time parsing and conversion
206 // Converdate.setSeparator(trUtf8("."));
207 // Convertime.setSeparator(trUtf8(":"));
209 // Optimize viewing by repainting cells only once after processing
210 m_pMaintable->setUpdatesEnabled(false);
212 // Set the table text by linewise reading from the input stream
213 // and parsing date, time, account, and other columns out of it
214 while (!Line.isEmpty()) {
215 bool bValid = true; // Warns on linewise invalid accounting data
216 QString User, Guid, Crc, Rev; // Valid admin fields
217 QString Date, Start, Finish, Account, Amount, Remark; // Valid user fields
218 Q3TextStream Asline(&Line, QIODevice::ReadOnly); // Convert a single line now
220 if (nIter % g_knBlocks == 0) // Add blocks of rows to optimize loading
221 m_pMaintable->setNumRows(m_pMaintable->numRows() + g_knBlocks);
223 Asline.skipWhiteSpace(); // Remove whitespaces
224 Asline >> User; // Copy the user field
225 if (!User.isEmpty())
226 m_pMaintable->setText(nIter, TITRAQ_IDXUSER, User);
227 else
228 bValid = false;
230 Asline.skipWhiteSpace(); // Remove whitespaces
231 Asline >> Guid; // Copy the GUID field
232 // Postpone actual text delivery, to autoinsert GUIDs!
234 Asline.skipWhiteSpace(); // Remove whitespaces
235 Asline >> Crc; // Copy the CRC field
236 // Postpone actual text delivery, to autoinsert CRCs!
238 Asline.skipWhiteSpace(); // Remove whitespaces
239 Asline >> Rev; // Copy the rev field
240 if (!Rev.isEmpty())
241 m_pMaintable->setText(nIter, TITRAQ_IDXREV, Rev);
242 else
243 bValid = false;
245 Asline.skipWhiteSpace(); // Remove whitespaces
246 Asline >> Date; // Copy the date field
247 if (!Date.isEmpty())
248 m_pMaintable->setText(nIter, TITRAQ_IDXDATE, Date);
249 else
250 bValid = false;
252 Asline.skipWhiteSpace(); // Remove whitespaces
253 Asline >> Start; // Copy the start field
254 if (!Start.isEmpty())
255 m_pMaintable->setText(nIter, TITRAQ_IDXSTART, Start);
256 else
257 bValid = false;
259 Asline.skipWhiteSpace(); // Remove whitespaces
260 Asline >> Finish; // Copy the finish field
261 if (!Finish.isEmpty())
262 m_pMaintable->setText(nIter, TITRAQ_IDXFINISH, Finish);
263 else
264 bValid = false;
266 Asline.skipWhiteSpace(); // Remove whitespaces
267 Asline >> Amount; // Copy the amount field
268 if (!Amount.isEmpty())
269 m_pMaintable->setText(nIter, TITRAQ_IDXAMOUNT, Amount);
270 else
271 bValid = false;
273 Asline.skipWhiteSpace(); // Remove whitespaces
274 Asline >> Account; // Copy the account field
275 if (!Account.isEmpty()) {
276 Q3TableItem *pOld = m_pMaintable->item(nIter, TITRAQ_IDXTASK);
277 delete(pOld); // Get rid of the old before going with the new
278 RtTableItem *pSwapitem = new RtTableItem(m_pMaintable, Q3TableItem::WhenCurrent, Account);
279 pSwapitem->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
280 m_pMaintable->setItem(nIter, TITRAQ_IDXTASK, pSwapitem);
281 }
282 else
283 bValid = false;
285 Asline.skipWhiteSpace(); // Remove whitespaces
286 Remark = Asline.read(); // Copy the remark field
288 QRegExp Quoted("\"(.*[^\\\\])\""); // Get rid of
289 Quoted.search(Remark); // surrounding double
290 Remark = Quoted.cap(Quoted.numCaptures()); // quotes, and
291 Remark.replace(QRegExp("\\\\(.)"), QString("\\1")); // escape backslashes
293 if (!Remark.isEmpty())
294 m_pMaintable->setText(nIter, TITRAQ_IDXREMARK, Remark);
296 if (bValid) { // Show a bitmap to signal valid or error state
297 m_pMaintable->setText(nIter, TITRAQ_IDXSTATUS, QString(QChar('O')));
298 m_pMaintable->setPixmap(nIter, TITRAQ_IDXSTATUS, Statokay);
299 }
300 else {
301 m_pMaintable->setText(nIter, TITRAQ_IDXSTATUS, QString(QChar('E')));
302 m_pMaintable->setPixmap(nIter, TITRAQ_IDXSTATUS, Staterror);
303 }
305 // Set the GUID text here, in case we need to generate a fresh value
306 if (!Guid.isEmpty()) {
307 if (Guid != ".") // This means, generate a fresh GUID value
308 m_pMaintable->setText(nIter, TITRAQ_IDXGUID, Guid);
309 else {
310 AS::Uuid Guidi; // For GUID production
311 Guidi.genId();
312 m_pMaintable->setText(nIter, TITRAQ_IDXGUID, QString::fromStdString(Guidi.getString()));
313 }
314 }
315 else // if isEmpty()
316 bValid = false;
318 // Set the CRC text here, in case we need to generate a fresh value
319 if (!Crc.isEmpty()) {
320 if (Crc != ".") // This means, generate a fresh CRC value
321 m_pMaintable->setText(nIter, TITRAQ_IDXCRC, "0x" + Crc);
322 else {
323 // Make our long tuple to run through the CRC32 generator
324 Qualistring Testuple = User + Guid + Rev + Date + Start;
325 Testuple += Finish + Amount + Account + Remark;
326 U32 Valcrc = Testuple.getCrc(); // Finally set the checksum to its new value
327 QString Crcstr = QString::number(Valcrc, 16).rightJustify(8, '0');
328 m_pMaintable->setText(nIter, TITRAQ_IDXCRC, "0x" + Crcstr);
329 }
330 }
331 else // if isEmpty()
332 bValid = false;
334 nIter++; // The big increment
335 Line = QString(""); // Clear line for next round
337 while (Line.isEmpty() && !Tstream.atEnd()) { // Strip and get
338 Tstream.skipWhiteSpace(); // Remove white
339 Line = Tstream.readLine(); // the new line
340 if (Line.at(0) == QChar('#')) // Remove comments
341 Line = QString("");
342 }
343 }
345 // // Start sorting order and direction correctly according to user preferences
346 // int nSortcol = (int)m_pPrefs->getNumber(TITRAQ_PREFSORTCOL, TITRAQ_DEFSORTCOL);
347 // bool bSortdir = m_pPrefs->getBool(TITRAQ_PREFSORTDIR, TITRAQ_DEFSORTDIR);
348 // m_pMaintable->setSortdir(!bSortdir); // Hack! Fake sortdir so we start right
349 // m_pMaintable->sortColumn(nSortcol, bSortdir);
351 // Write nonsaving line numbers for all rows
352 for (int nIter = m_pMaintable->numRows() - 1; nIter >= 0; nIter--)
353 m_pMaintable->setText(nIter, TITRAQ_IDXLINE, QString::number(nIter).rightJustify(4, QChar('0')));
355 m_pMaintable->setUpdatesEnabled(true); // Update and repaint
356 m_pMaintable->setNumRows(nIter); // No excess rows
357 m_pMaintable->setCurrentCell(nIter - 1, 0); // Move focus to last row
358 m_pMaintable->ensureCellVisible(nIter - 1, 0); // Scroll please
360 if (!bValid)
361 throw Genexcept("Warning: invalid accounting data.");
362 }
364 //
365 // Convenience method to save accounting data to a file
366 //
367 void Titraqform::saveData(QFile &Fileobj)
368 {
369 if (Fileobj.isOpen()) { // Check state of file
370 Fileobj.flush(); // Begin processing file cleanly
371 Q3TextStream Asentry(&Fileobj); // Convert data to stream
372 this->saveData(Asentry); // Pass off to do the real work
373 }
374 else {
375 if (!Fileobj.open(QIODevice::WriteOnly)) // Try to open file
376 throw Genexcept(trUtf8(TITRAQ_READPFILFAIL));
377 Q3TextStream Asentry(&Fileobj); // Convert data to stream
378 this->saveData(Asentry); // Pass off to do the real work
379 Fileobj.close(); // Finish fileop by closing
380 }
381 }
383 //
384 // Save accounting data to a stream
385 //
386 void Titraqform::saveData(Q3TextStream &Tstream)
387 {
388 const int nRows = m_pMaintable->numRows(); // Max rows used in loop
389 QString Tempfield; // Current field string
390 QString Strsearch; // String to strip search
391 QRegExp Stripper("\\s*$"); // Pattern to strip off
393 // Start by prepending the AS data format version symbol
394 Tstream << TITRAQ_DATAPATTERN << TITRAQ_DATAVERSIONMAJ
395 << QChar('.') << TITRAQ_DATAVERSIONMIN << endl;
397 // Linewise save from the main table date, time, account, and others
398 for (int nIter = 0; nIter < nRows; nIter++) {
399 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXUSER); // Load user field text
400 if (!Tempfield.isNull())
401 Tstream << Tempfield; // Save user field text
403 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXGUID); // Load GUID field text
404 if (!Tempfield.isNull())
405 Tstream << trUtf8(" ") << Tempfield; // Save GUID field text
407 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXCRC); // Load CRC field text
408 Tempfield.remove("0x");
409 if (!Tempfield.isNull())
410 Tstream << trUtf8(" ") << Tempfield; // Save CRC field text
412 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXREV); // Load rev field text
413 if (!Tempfield.isNull())
414 Tstream << trUtf8(" ") << Tempfield; // Save rev field text
416 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXDATE); // Load date field text
417 if (!Tempfield.isNull())
418 Tstream << trUtf8(" ") << Tempfield; // Save date field text
420 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXSTART); // Load start field text
421 if (!Tempfield.isNull())
422 Tstream << trUtf8(" ") << Tempfield; // Save start field text
424 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXFINISH); // Load end field text
425 if (!Tempfield.isNull())
426 Tstream << trUtf8(" ") << Tempfield; // Save end field text
428 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXAMOUNT); // Load amount field text
429 if (!Tempfield.isNull())
430 Tstream << trUtf8(" ") << Tempfield; // Save amount
432 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXTASK); // Load acct field text
433 if (!Tempfield.isNull())
434 Tstream << trUtf8(" ") << Tempfield; // Save acct field text
436 Tempfield = m_pMaintable->text(nIter, TITRAQ_IDXREMARK); // Load remark field text
437 Tstream << trUtf8(" \""); // Save beginning double quote
438 if (!Tempfield.isNull()) {
439 Strsearch = QRegExp::escape(Tempfield); // Incoming string escaped
440 Stripper.search(Strsearch);
441 Tempfield.truncate(Stripper.pos()); // Cut off whitespace
442 Tempfield.replace(QChar('\\'), QString("\\\\")); // Escape back slashes
443 Tempfield.replace(QChar('\"'), QString("\\\"")); // Escape double quotes
444 Tstream << Tempfield; // Save remark field text
445 }
446 Tstream << trUtf8("\""); // Save ending double quote
448 Tstream << endl; // Append a newline
449 }
450 }
452 //
453 // Convenience method to validate AS data in a file
454 //
455 const bool Titraqform::validateData(QFile &Filin) const
456 {
457 QString Firstline; // Will contain the first line of text
458 char szReadline[QString(TITRAQ_DATAPATTERN).length() + 4L]; // File line
459 bool bRet = false; // Set the initial return value
461 if (Filin.isOpen()) { // Check open state of file
462 Filin.flush(); // Not sure if this is needed
463 Filin.reset(); // Set the file index position to 0
464 Filin.readLine(szReadline, QString(TITRAQ_DATAPATTERN).length() + 2L);
465 }
466 else {
467 if (!Filin.open(QIODevice::ReadOnly)) // Try to open file
468 throw Genexcept(trUtf8(TITRAQ_READPFILFAIL));
469 else { // File is now open
470 Filin.readLine(szReadline, QString(TITRAQ_DATAPATTERN).length() + 2L);
471 Filin.close(); // Remember to close
472 }
473 }
475 try { // Pass off to worker method
476 Firstline = QString::fromAscii(szReadline);
477 bRet = this->validateData(Firstline);
478 }
479 catch (Genexcept &) {
480 throw; // Rethrow onwards
481 }
482 return bRet;
483 }
485 //
486 // Validate the AS data pattern in a line
487 //
488 const bool Titraqform::validateData(QString &Linin) const
489 {
490 bool bRet = false; // Initial return value
492 // Ensure that the right data version pattern precedes the data
493 QString Datapattern = QString(TITRAQ_DATAPATTERN);
494 if (!Linin.startsWith(Datapattern)) { // Incompatible data format
495 QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short,
496 TITRAQ_NOPATTERNFOUND + QString(TITRAQ_DATAPATTERN) + TITRAQ_WASNOTFOUNDIN,
497 QMessageBox::Critical, QMessageBox::Ok | QMessageBox::Escape,
498 Qt::NoButton, Qt::NoButton);
499 Problema.exec(); // Give the user the bad news
500 throw Genexcept(TITRAQ_INVALIDDATA);
501 }
502 else if (Linin.section(Datapattern, 1).section('.', 0, 0).toInt() != TITRAQ_DATAVERSIONMAJ) {
503 QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short,
504 TITRAQ_BADVERSIONMAJ, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Escape,
505 Qt::NoButton, Qt::NoButton);
506 Problema.exec(); // Give the user the bad news
507 throw Genexcept(TITRAQ_INCOMPATDATA);
508 }
509 else if (Linin.section(Datapattern, 1).section('.', 1, 1).toInt() > TITRAQ_DATAVERSIONMIN) {
510 QMessageBox Problema(QString(TITRAQ_APPTITLE) + ' ' + asgui_version.v_short,
511 TITRAQ_BADVERSIONMIN, QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Escape,
512 Qt::NoButton, Qt::NoButton);
513 Problema.exec(); // Give the user the bad news
514 throw Genexcept(TITRAQ_INCOMPATDATA);
515 }
516 else
517 bRet = true;
519 return bRet; // Not reached in case of failure
520 }
522 //
523 // Get a whole row of data
524 //
525 const QString Titraqform::getRowdata(void) const
526 {
527 QString Rowdata, Tempstring; // For output string
528 Q3TableSelection Select = m_pMaintable->selection(0); // Highlighted text
529 int nTotal = Select.bottomRow() - Select.topRow() + 1; // Total row select
531 // Calculate rows to delete from selection highlight
532 for (int nIter = 0; nIter < nTotal; ++nIter) {
533 // Build the row data string one field at a time, adding seps inbetween
534 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXLINE);
535 if (!Tempstring.isEmpty())
536 Rowdata += Tempstring;
537 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXUSER);
538 if (!Tempstring.isEmpty())
539 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
540 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXGUID);
541 if (!Tempstring.isEmpty())
542 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
543 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXCRC);
544 if (!Tempstring.isEmpty())
545 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
546 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXREV);
547 if (!Tempstring.isEmpty())
548 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
549 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXDATE);
550 if (!Tempstring.isEmpty())
551 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
552 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXSTART);
553 if (!Tempstring.isEmpty())
554 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
555 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXFINISH);
556 if (!Tempstring.isEmpty())
557 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
558 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXAMOUNT);
559 if (!Tempstring.isEmpty())
560 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
561 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXTASK);
562 if (!Tempstring.isEmpty())
563 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
564 Tempstring = m_pMaintable->text(Select.topRow() + nIter, TITRAQ_IDXREMARK);
565 if (!Tempstring.isEmpty())
566 Rowdata += TITRAQ_SEPARATORTOK + Tempstring;
567 Rowdata += trUtf8("\n"); // Finish off line
568 }
570 return Rowdata;
571 }
573 //
574 // Set a whole row of data
575 //
576 void Titraqform::setRowdata(QString &Rowdata) const
577 {
578 int nRows = Rowdata.count(QChar('\n')); // Set so many rows
579 int nCurrentrow = m_pMaintable->currentRow(); // Current table row
580 Q3TableItem *pItem = NULL; // Old item to change out
581 QString Line, User, Guid, Crc, Rev; // Admin fields in table
582 QString Date, Start, Finish, Amount, Task, Remark; // Viewable fields in table
583 Q3TextStream Datastream(&Rowdata, QIODevice::ReadOnly); // Convert data to stream
585 for (int nIter = 0; nIter < nRows; ++nIter) {
586 QString Singlerow = Datastream.readLine(); // For linewise operation
587 Q3TextStream Rowstream(&Singlerow, QIODevice::ReadOnly); // Convert row to stream
589 Rowstream >> Line >> User >> Guid >> Crc >> Rev; // Stream data fields
590 Rowstream >> Date >> Start >> Finish >> Amount >> Task; // to corresponding vars
591 Remark = Rowstream.readLine(); // Remark is a whole line
593 // Set the table row data one field at a time, skipping seps inbetween
594 // Importantly, do not copy over the GUID, which must be unique each row
595 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXLINE, Line);
596 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXUSER, User);
597 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXCRC, Crc);
598 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXREV, Rev);
599 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXDATE, Date);
600 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXSTART, Start);
601 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXFINISH, Finish);
602 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXAMOUNT, Amount);
604 // // FIXME: Why doesn't this code change the RtTableItem text in place?
605 // RtTableItem *pTask = NULL; // Derived and special
606 // pItem = m_pMaintable->item(nCurrentrow + nIter, TITRAQ_IDXTASK);
607 // pTask = static_cast<RtTableItem *>(pItem);
608 // pTask->setText(Task);
610 // Change out old item and replace with new task data
611 pItem = m_pMaintable->item(nCurrentrow + nIter, TITRAQ_IDXTASK);
612 delete(pItem); // Get rid of the old before going with the new
613 RtTableItem *pSwapitem = new RtTableItem(m_pMaintable, Q3TableItem::WhenCurrent, Task);
614 pSwapitem->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
615 m_pMaintable->setItem(nCurrentrow + nIter, TITRAQ_IDXTASK, pSwapitem);
617 // Continue with field processing business as usual
618 m_pMaintable->setText(nCurrentrow + nIter, TITRAQ_IDXREMARK, Remark.simplifyWhiteSpace());
619 }
620 }
622 //
623 // Discover which column is the first in view
624 //
625 const int Titraqform::getFirstcol(void) const
626 {
627 int nIter = 0; // Is both iterator in loop and column number returned
629 // Use column selector menu popup as sole key to determining column state
630 while (nIter < TITRAQ_IDXTAIL && !m_pColspopup->isItemChecked(m_pColspopup->idAt(++nIter)))
631 TITRAQ_NOP;
633 return nIter - 1; // Return one less to account for start of item offset
634 }