source: tspsg-svn/trunk/src/tspmodel.cpp @ 87

Last change on this file since 87 was 87, checked in by laleppa, 15 years ago

+ Added Fractional accuracy (in decimail places) to settings.
+ Created defaults.h file. Moved all default defines there.

  • Replaced all double types with qreal.
  • Replaced all calls to trUtf8() with tr() as we use setCodecForTr() to set UTF-8 encoding as default and tr() is enough.
  • Replaced all 2009 with 2010 in copyrights.
  • Fixed an error when loading .zkt file with less than 5 cities.
  • Property svn:keywords set to Id URL
File size: 14.3 KB
Line 
1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
4 *
5 *  $Id: tspmodel.cpp 87 2010-01-12 14:11:24Z laleppa $
6 *  $URL: https://tspsg.svn.sourceforge.net/svnroot/tspsg/trunk/src/tspmodel.cpp $
7 *
8 *  This file is part of TSPSG.
9 *
10 *  TSPSG is free software: you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation, either version 3 of the License, or
13 *  (at your option) any later version.
14 *
15 *  TSPSG is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with TSPSG.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "tspmodel.h"
25
26/*!
27 * \brief Class constructor.
28 * \param parent The parent of the table model.
29 */
30CTSPModel::CTSPModel(QObject *parent)
31        : QAbstractTableModel(parent), nCities(0)
32{
33        settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, "TSPSG", "tspsg", this);
34}
35
36/*!
37 * \brief Resets the table, setting all its elements to 0.
38 *
39 * \sa randomize()
40 */
41void CTSPModel::clear()
42{
43        for (int r = 0; r < nCities; r++)
44                for (int c = 0; c < nCities; c++)
45                        if (r != c)
46                                table[r][c] = 0;
47        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
48}
49
50/*!
51 * \brief Returns the column count in the table.
52 * \return Number of columns in the table.
53 *
54 *  Actually, this function returns the number of cities in the current task.
55 *
56 * \sa numCities(), rowCount()
57 */
58int CTSPModel::columnCount(const QModelIndex &) const
59{
60        return nCities;
61}
62
63/*!
64 * \brief Returns the data stored under the given \a role for the item referred to by the \a index.
65 * \param index An item index to get data from.
66 * \param role The role to get data for.
67 * \return Corresponding data.
68 *
69 * \sa setData(), headerData()
70 */
71QVariant CTSPModel::data(const QModelIndex &index, int role) const
72{
73        if (!index.isValid())
74                return QVariant();
75        if (role == Qt::TextAlignmentRole)
76                return int(Qt::AlignCenter);
77        else if (role == Qt::FontRole) {
78QFont font;
79                font.setBold(true);
80                return font;
81        } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
82                if (index.row() < nCities && index.column() < nCities)
83                        if (table[index.row()][index.column()] == INFINITY)
84                                return tr(INFSTR);
85                        else
86//! \hack HACK: Converting to string to prevent spinbox in edit mode
87                                return QVariant(table[index.row()][index.column()]).toString();
88                else
89                        return QVariant();
90        } else if (role == Qt::UserRole)
91                return table[index.row()][index.column()];
92        return QVariant();
93}
94
95/*!
96 * \brief Returns the item flags for the given \a index.
97 * \param index An item index to get flags from.
98 * \return Corresponding item flags.
99 */
100Qt::ItemFlags CTSPModel::flags(const QModelIndex &index) const
101{
102Qt::ItemFlags flags = QAbstractItemModel::flags(index);
103        if (index.row() != index.column())
104                flags |= Qt::ItemIsEditable;
105        return flags;
106}
107
108/*!
109 * \brief Returns the data for the given \a role and \a section in the header with the specified \a orientation.
110 * \param section The section to get header data for.
111 * \param orientation The orientation to get header data for.
112 * \param role The role to get header data for.
113 * \return Corresponding header data.
114 *
115 *  For horizontal headers, the section number corresponds to the column number of items shown beneath it. For vertical headers, the section number typically to the row number of items shown alongside it.
116 */
117QVariant CTSPModel::headerData(int section, Qt::Orientation orientation, int role) const
118{
119        if (role == Qt::DisplayRole) {
120                if (orientation == Qt::Vertical)
121                        return tr("City %1").arg(section + 1);
122                else
123                        return tr("%1").arg(section + 1);
124        }
125        return QVariant();
126}
127
128/*!
129 * \brief Loads a task from \a fname.
130 * \param fname The name of the file to be loaded.
131 * \return \c true on success, otherwise \c false.
132 *
133 * \sa saveTask()
134 */
135bool CTSPModel::loadTask(const QString &fname)
136{
137        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
138QFile f(fname);
139        if (!f.open(QIODevice::ReadOnly)) {
140                QApplication::restoreOverrideCursor();
141                QMessageBox(QMessageBox::Critical,tr("Task Load"),QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
142                return false;
143        }
144QDataStream ds(&f);
145        ds.setVersion(QDataStream::Qt_4_4);
146quint32 sig;
147        ds >> sig;
148        if (loadError(ds.status())) {
149                return false;
150        }
151        ds.device()->reset();
152        if (sig == TSPT) {
153                if (!loadTSPT(&ds)) {
154                        f.close();
155                        return false;
156                }
157        } else if ((sig >> 16) == ZKT) {
158                if (!loadZKT(&ds)) {
159                        f.close();
160                        return false;
161                }
162        } else {
163                f.close();
164                QApplication::restoreOverrideCursor();
165                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
166                return false;
167        }
168        f.close();
169        QApplication::restoreOverrideCursor();
170        return true;
171}
172
173/*!
174 * \brief Returns the number of cities.
175 * \return Number of cities in the current task.
176 *
177 * \sa columnCount(), rowCount(), setNumCities()
178 */
179quint16 CTSPModel::numCities() const
180{
181        return nCities;
182}
183
184/*!
185 * \brief Randomizes the table by setting all its values to random ones.
186 *
187 *  Uses TSPSG settings to determine random values range.
188 *
189 * \sa clear()
190 */
191void CTSPModel::randomize()
192{
193int randMin = settings->value("Task/RandMin",DEF_RAND_MIN).toInt();
194int randMax = settings->value("Task/RandMax",DEF_RAND_MAX).toInt();
195        for (int r = 0; r < nCities; r++)
196                for (int c = 0; c < nCities; c++)
197                        if (r != c)
198                                table[r][c] = rand(randMin,randMax);
199        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
200}
201
202/*!
203 * \brief Returns the row count in the table.
204 * \return Number of rows in the table.
205 *
206 *  Actually, this function returns the number of cities in the current task.
207 *
208 * \sa columnCount(), numCities()
209 */
210int CTSPModel::rowCount(const QModelIndex &) const
211{
212        return nCities;
213}
214
215/*!
216 * \brief Saves current task to \a fname.
217 * \param fname The name of the file to seve to.
218 * \return \c true on success, otherwise \c false.
219 *
220 * \sa loadTask()
221 */
222bool CTSPModel::saveTask(const QString &fname)
223{
224        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
225QFile f(fname);
226        if (!f.open(QIODevice::WriteOnly)) {
227                QApplication::restoreOverrideCursor();
228                QMessageBox(QMessageBox::Critical,tr("Task Save"),QString(tr("Unable to create task file.\nError: %1\nMaybe, file is read-only?")).arg(f.errorString()),QMessageBox::Ok).exec();
229                return false;
230        }
231QDataStream ds(&f);
232        ds.setVersion(QDataStream::Qt_4_4);
233        if (f.error() != QFile::NoError) {
234                f.close();
235                QApplication::restoreOverrideCursor();
236                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
237                return false;
238        }
239        // File signature
240        ds << TSPT;
241        if (f.error() != QFile::NoError) {
242                f.close();
243                QApplication::restoreOverrideCursor();
244                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
245                return false;
246        }
247        // File version
248        ds << TSPT_VERSION;
249        if (f.error() != QFile::NoError) {
250                f.close();
251                QApplication::restoreOverrideCursor();
252                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
253                return false;
254        }
255        // File metadata version
256        ds << TSPT_META_VERSION;
257        if (f.error() != QFile::NoError) {
258                f.close();
259                QApplication::restoreOverrideCursor();
260                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
261                return false;
262        }
263        // Metadata
264        ds << OSID;
265        if (f.error() != QFile::NoError) {
266                f.close();
267                QApplication::restoreOverrideCursor();
268                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
269                return false;
270        }
271        // Number of cities
272        ds << nCities;
273        if (f.error() != QFile::NoError) {
274                f.close();
275                QApplication::restoreOverrideCursor();
276                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
277                return false;
278        }
279        // Costs
280        for (int r = 0; r < nCities; r++)
281                for (int c = 0; c < nCities; c++)
282                        if (r != c) {
283                                ds << static_cast<double>(table[r][c]); // We cast to double because qreal may be float on some platforms and we store double values in file
284                                if (f.error() != QFile::NoError) {
285                                        f.close();
286                                        QApplication::restoreOverrideCursor();
287                                        QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
288                                        return false;
289                                }
290                        }
291        f.close();
292        QApplication::restoreOverrideCursor();
293        return true;
294}
295
296/*!
297 * \brief Sets the \a role data for the item at \a index to \a value.
298 * \param index The index of the item to set data at.
299 * \param value The value of the item data to be set.
300 * \param role The role of the item to set data for.
301 * \return \c true on success, otherwise \c false.
302 *
303 * \sa data()
304 */
305bool CTSPModel::setData(const QModelIndex &index, const QVariant &value, int role)
306{
307        if (!index.isValid())
308                return false;
309        if (role == Qt::EditRole && index.row() != index.column()) {
310                if (value.toString().compare(INFSTR) == 0)
311                        table[index.row()][index.column()] = INFINITY;
312                else {
313bool ok;
314qreal tmp = value.toReal(&ok);
315                        if (!ok || tmp < 0)
316                                return false;
317                        else
318                                table[index.row()][index.column()] = tmp;
319                }
320                emit dataChanged(index,index);
321                return true;
322        }
323        return false;
324}
325
326/*!
327 * \brief Sets number of cities in the current task to \a n.
328 * \param n Number of cities to set to.
329 *
330 * \sa numCities()
331 */
332void CTSPModel::setNumCities(int n)
333{
334        if (n == nCities)
335                return;
336        emit layoutAboutToBeChanged();
337        table.resize(n);
338        for (int k = 0; k < n; k++) {
339                table[k].resize(n);
340        }
341        if (n > nCities)
342                for (int k = nCities; k < n; k++)
343                        table[k][k] = INFINITY;
344        nCities = n;
345        emit layoutChanged();
346}
347
348/* Privates **********************************************************/
349
350inline bool CTSPModel::loadError(QDataStream::Status status)
351{
352QString err;
353        if (status == QDataStream::Ok)
354                return false;
355        else if (status == QDataStream::ReadPastEnd)
356                err = tr("Unexpected end of file.");
357        else if (status == QDataStream::ReadCorruptData)
358                err = tr("Corrupt data read. File possibly corrupted.");
359        else
360                err = tr("Unknown error.");
361        QApplication::restoreOverrideCursor();
362        QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
363        return true;
364}
365
366bool CTSPModel::loadTSPT(QDataStream *ds)
367{
368        // Skipping signature
369        ds->skipRawData(sizeof(TSPT));
370        if (loadError(ds->status()))
371                return false;
372        // File version
373quint8 version;
374        *ds >> version;
375        if (loadError(ds->status()))
376                return false;
377        if (version > TSPT_VERSION) {
378                QApplication::restoreOverrideCursor();
379                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("File version is newer than application supports.\nPlease, try to update application."),QMessageBox::Ok).exec();
380                return false;
381        }
382        // Skipping metadata
383        ds->skipRawData(TSPT_META_SIZE);
384        if (loadError(ds->status()))
385                return false;
386        // Number of cities
387quint16 size;
388        *ds >> size;
389        if (loadError(ds->status()))
390                return false;
391        if ((size < 3) || (size > MAX_NUM_CITIES)) {
392                QApplication::restoreOverrideCursor();
393                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
394                return false;
395        }
396        if (nCities != size) {
397                setNumCities(size);
398                emit numCitiesChanged(size);
399        }
400
401double x; // We need this as qreal may be float on some platforms and we store double values in file
402        // Travel costs
403        for (int r = 0; r < size; r++)
404                for (int c = 0; c < size; c++)
405                        if (r != c) {
406                                *ds >> x;
407                                table[r][c] = x;
408                                if (loadError(ds->status())) {
409                                        clear();
410                                        return false;
411                                }
412                        }
413        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
414        QApplication::restoreOverrideCursor();
415        return true;
416}
417
418bool CTSPModel::loadZKT(QDataStream *ds)
419{
420        // Skipping signature
421        ds->skipRawData(sizeof(ZKT));
422        if (loadError(ds->status()))
423                return false;
424        // File version
425quint16 version;
426        ds->readRawData(reinterpret_cast<char *>(&version),2);
427        if (loadError(ds->status()))
428                return false;
429        if (version > ZKT_VERSION) {
430                QApplication::restoreOverrideCursor();
431                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("File version is newer than application supports.\nPlease, try to update application."),QMessageBox::Ok).exec();
432                return false;
433        }
434        // Number of cities
435quint8 size;
436        ds->readRawData(reinterpret_cast<char *>(&size),1);
437        if (loadError(ds->status()))
438                return false;
439        if ((size < 3) || (size > 5)) {
440                QApplication::restoreOverrideCursor();
441                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
442                return false;
443        }
444        if (nCities != size) {
445                setNumCities(size);
446                emit numCitiesChanged(size);
447        }
448        // Travel costs
449qreal val;
450        for (int r = 0; r < 5; r++)
451                for (int c = 0; c < 5; c++)
452                        if ((r != c) && (r < size) && (c < size)) {
453                                ds->readRawData(reinterpret_cast<char *>(&val),8);
454                                if (loadError(ds->status())) {
455                                        clear();
456                                        return false;
457                                }
458                                table[r][c] = val;
459                        } else {
460                                ds->skipRawData(8);
461                                if (loadError(ds->status())) {
462                                        clear();
463                                        return false;
464                                }
465                        }
466        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
467        QApplication::restoreOverrideCursor();
468        return true;
469}
470
471inline qreal CTSPModel::rand(int min, int max) const
472{
473qreal r;
474        if (settings->value("Task/FractionalRandom", DEF_FRACTIONAL_RANDOM).toBool()) {
475qreal x = qPow(10, settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt());
476                r = (qreal)qRound((qreal)qrand() / RAND_MAX * (max - min) * x) / x;
477        } else
478                r = qRound((qreal)qrand() / RAND_MAX * (max - min));
479        return min + r;
480}
Note: See TracBrowser for help on using the repository browser.