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

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

Added svn:eol-style=native property to all text files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id URL
File size: 15.0 KB
RevLine 
[116]1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
4 *
5 *  $Id: tspmodel.cpp 116 2010-05-01 03:06:46Z 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.at(index.row()).at(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.at(index.row()).at(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                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
142                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()));
143                QApplication::restoreOverrideCursor();
144                return false;
145        }
146QDataStream ds(&f);
147        ds.setVersion(QDataStream::Qt_4_4);
148quint32 sig;
149        ds >> sig;
150        if (loadError(ds.status())) {
151                return false;
152        }
153        ds.device()->reset();
154        if (sig == TSPT) {
155                if (!loadTSPT(&ds)) {
156                        f.close();
157                        return false;
158                }
159        } else if ((sig >> 16) == ZKT) {
160                if (!loadZKT(&ds)) {
161                        f.close();
162                        return false;
163                }
164        } else {
165                f.close();
166                QApplication::restoreOverrideCursor();
167                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
168                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."));
169                QApplication::restoreOverrideCursor();
170                return false;
171        }
172        f.close();
173        QApplication::restoreOverrideCursor();
174        return true;
175}
176
177/*!
178 * \brief Returns the number of cities.
179 * \return Number of cities in the current task.
180 *
181 * \sa columnCount(), rowCount(), setNumCities()
182 */
183quint16 CTSPModel::numCities() const
184{
185        return nCities;
186}
187
188/*!
189 * \brief Randomizes the table by setting all its values to random ones.
190 *
191 *  Uses TSPSG settings to determine random values range.
192 *
193 * \sa clear()
194 */
195void CTSPModel::randomize()
196{
197int randMin = settings->value("Task/RandMin", DEF_RAND_MIN).toInt();
198int randMax = settings->value("Task/RandMax", DEF_RAND_MAX).toInt();
199        if (settings->value("Task/SymmetricMode", DEF_SYMMETRIC_MODE).toBool()) {
200                for (int r = 0; r < nCities; r++)
201                        for (int c = 0; c < r; c++)
202                                table[c][r] = table[r][c] = rand(randMin, randMax);
203        } else {
204                for (int r = 0; r < nCities; r++)
205                        for (int c = 0; c < nCities; c++)
206                                if (r != c)
207                                        table[r][c] = rand(randMin, randMax);
208        }
209        emit dataChanged(index(0,0), index(nCities - 1, nCities - 1));
210}
211
212/*!
213 * \brief Returns the row count in the table.
214 * \return Number of rows in the table.
215 *
216 *  Actually, this function returns the number of cities in the current task.
217 *
218 * \sa columnCount(), numCities()
219 */
220int CTSPModel::rowCount(const QModelIndex &) const
221{
222        return nCities;
223}
224
225/*!
226 * \brief Saves current task to \a fname.
227 * \param fname The name of the file to seve to.
228 * \return \c true on success, otherwise \c false.
229 *
230 * \sa loadTask()
231 */
232bool CTSPModel::saveTask(const QString &fname)
233{
234        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
235QFile f(fname);
236        if (!f.open(QIODevice::WriteOnly)) {
237                QApplication::restoreOverrideCursor();
238                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), QString(tr("Unable to create task file.\nError: %1\nMaybe, file is read-only?")).arg(f.errorString()));
239                f.remove();
240                return false;
241        }
242QDataStream ds(&f);
243        ds.setVersion(QDataStream::Qt_4_4);
244        if (f.error() != QFile::NoError) {
245                QApplication::restoreOverrideCursor();
246                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
247                f.close();
248                f.remove();
249                return false;
250        }
251        // File signature
252        ds << TSPT;
253        if (f.error() != QFile::NoError) {
254                QApplication::restoreOverrideCursor();
255                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
256                f.close();
257                f.remove();
258                return false;
259        }
260        // File version
261        ds << TSPT_VERSION;
262        if (f.error() != QFile::NoError) {
263                QApplication::restoreOverrideCursor();
264                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
265                f.close();
266                f.remove();
267                return false;
268        }
269        // File metadata version
270        ds << TSPT_META_VERSION;
271        if (f.error() != QFile::NoError) {
272                QApplication::restoreOverrideCursor();
273                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
274                f.close();
275                f.remove();
276                return false;
277        }
278        // Metadata
279        ds << OSID;
280        if (f.error() != QFile::NoError) {
281                QApplication::restoreOverrideCursor();
282                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
283                f.close();
284                f.remove();
285                return false;
286        }
287        // Number of cities
288        ds << nCities;
289        if (f.error() != QFile::NoError) {
290                QApplication::restoreOverrideCursor();
291                QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
292                f.close();
293                f.remove();
294                return false;
295        }
296        // Costs
297        for (int r = 0; r < nCities; r++)
298                for (int c = 0; c < nCities; c++)
299                        if (r != c) {
300                                ds << static_cast<double>(table[r][c]); // We cast to double because double may be float on some platforms and we store double values in file
301                                if (f.error() != QFile::NoError) {
302                                        QApplication::restoreOverrideCursor();
303                                        QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
304                                        f.close();
305                                        f.remove();
306                                        return false;
307                                }
308                        }
309        f.close();
310        QApplication::restoreOverrideCursor();
311        return true;
312}
313
314/*!
315 * \brief Sets the \a role data for the item at \a index to \a value.
316 * \param index The index of the item to set data at.
317 * \param value The value of the item data to be set.
318 * \param role The role of the item to set data for.
319 * \return \c true on success, otherwise \c false.
320 *
321 * \sa data()
322 */
323bool CTSPModel::setData(const QModelIndex &index, const QVariant &value, int role)
324{
325        if (!index.isValid())
326                return false;
327        if (role == Qt::EditRole && index.row() != index.column()) {
328                if (value.toString().compare(INFSTR) == 0)
329                        table[index.row()][index.column()] = INFINITY;
330                else {
331bool ok;
332double tmp = value.toDouble(&ok);
333                        if (!ok || tmp < 0)
334                                return false;
335                        else {
336                                table[index.row()][index.column()] = tmp;
337                                if (settings->value("Task/SymmetricMode", DEF_SYMMETRIC_MODE).toBool())
338                                        table[index.column()][index.row()] = tmp;
339                        }
340                }
341                emit dataChanged(index,index);
342                return true;
343        }
344        return false;
345}
346
347/*!
348 * \brief Sets number of cities in the current task to \a n.
349 * \param n Number of cities to set to.
350 *
351 * \sa numCities()
352 */
353void CTSPModel::setNumCities(int n)
354{
355        if (n == nCities)
356                return;
357        emit layoutAboutToBeChanged();
358        table.resize(n);
359        for (int k = 0; k < n; k++) {
360                table[k].resize(n);
361        }
362        if (n > nCities)
363                for (int k = nCities; k < n; k++)
364                        table[k][k] = INFINITY;
365        nCities = n;
366        emit layoutChanged();
367}
368
369/* Privates **********************************************************/
370
371inline bool CTSPModel::loadError(QDataStream::Status status)
372{
373QString err;
374        if (status == QDataStream::Ok)
375                return false;
376        else if (status == QDataStream::ReadPastEnd)
377                err = tr("Unexpected end of file.");
378        else if (status == QDataStream::ReadCorruptData)
379                err = tr("Corrupt data read. File possibly corrupted.");
380        else
381                err = tr("Unknown error.");
382        QApplication::restoreOverrideCursor();
383        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
384        QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + err);
385        QApplication::restoreOverrideCursor();
386        return true;
387}
388
389bool CTSPModel::loadTSPT(QDataStream *ds)
390{
391        // Skipping signature
392        ds->skipRawData(sizeof(TSPT));
393        if (loadError(ds->status()))
394                return false;
395        // File version
396quint8 version;
397        *ds >> version;
398        if (loadError(ds->status()))
399                return false;
400        if (version > TSPT_VERSION) {
401                QApplication::restoreOverrideCursor();
402                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
403                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("File version is newer than application supports.\nPlease, try to update application."));
404                QApplication::restoreOverrideCursor();
405                return false;
406        }
407        // Skipping metadata
408        ds->skipRawData(TSPT_META_SIZE);
409        if (loadError(ds->status()))
410                return false;
411        // Number of cities
412quint16 size;
413        *ds >> size;
414        if (loadError(ds->status()))
415                return false;
416        if ((size < 3) || (size > MAX_NUM_CITIES)) {
417                QApplication::restoreOverrideCursor();
418                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
419                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."));
420                QApplication::restoreOverrideCursor();
421                return false;
422        }
423        if (nCities != size) {
424                setNumCities(size);
425                emit numCitiesChanged(size);
426        }
427
428double x; // We need this as double may be float on some platforms and we store double values in file
429        // Travel costs
430        for (int r = 0; r < size; r++)
431                for (int c = 0; c < size; c++)
432                        if (r != c) {
433                                *ds >> x;
434                                table[r][c] = x;
435                                if (loadError(ds->status())) {
436                                        clear();
437                                        return false;
438                                }
439                        }
440        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
441        QApplication::restoreOverrideCursor();
442        return true;
443}
444
445bool CTSPModel::loadZKT(QDataStream *ds)
446{
447        // Skipping signature
448        ds->skipRawData(sizeof(ZKT));
449        if (loadError(ds->status()))
450                return false;
451        // File version
452quint16 version;
453        ds->readRawData(reinterpret_cast<char *>(&version),2);
454        if (loadError(ds->status()))
455                return false;
456        if (version > ZKT_VERSION) {
457                QApplication::restoreOverrideCursor();
458                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
459                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("File version is newer than application supports.\nPlease, try to update application."));
460                QApplication::restoreOverrideCursor();
461                return false;
462        }
463        // Number of cities
464quint8 size;
465        ds->readRawData(reinterpret_cast<char *>(&size),1);
466        if (loadError(ds->status()))
467                return false;
468        if ((size < 3) || (size > 5)) {
469                QApplication::restoreOverrideCursor();
470                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
471                QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."));
472                QApplication::restoreOverrideCursor();
473                return false;
474        }
475        if (nCities != size) {
476                setNumCities(size);
477                emit numCitiesChanged(size);
478        }
479        // Travel costs
480double val;
481        for (int r = 0; r < 5; r++)
482                for (int c = 0; c < 5; c++)
483                        if ((r != c) && (r < size) && (c < size)) {
484                                ds->readRawData(reinterpret_cast<char *>(&val),8);
485                                if (loadError(ds->status())) {
486                                        clear();
487                                        return false;
488                                }
489                                table[r][c] = val;
490                        } else {
491                                ds->skipRawData(8);
492                                if (loadError(ds->status())) {
493                                        clear();
494                                        return false;
495                                }
496                        }
497        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
498        QApplication::restoreOverrideCursor();
499        return true;
500}
501
502inline double CTSPModel::rand(int min, int max) const
503{
504double r;
505        if (settings->value("Task/FractionalRandom", DEF_FRACTIONAL_RANDOM).toBool()) {
506double x = qPow(10, settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt());
507                r = (double)qRound((double)qrand() / RAND_MAX * (max - min) * x) / x;
508        } else
509                r = qRound((double)qrand() / RAND_MAX * (max - min));
510        return min + r;
511}
Note: See TracBrowser for help on using the repository browser.