as_dataop.cpp

Wed, 07 Jan 2009 18:49:25 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 07 Jan 2009 18:49:25 +0100
changeset 12
ceb4ba3d2d00
parent 11
d926203bd2a8
child 15
0e0eb7c91312
permissions
-rw-r--r--

Bump copyright year number.

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

mercurial