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

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

+ Added Symmetric mode: in this mode the cost of travel from city 1 to city 2 and vice versa is the same.
+ Added the ability to reset all settings to their defaults: hold Shift while clicking Save button in Settings Dialog.

  • Better SIP show/hide handling under wince: no need to resize the Main Window, when it isn't active.
  • Property svn:keywords set to Id URL
File size: 14.7 KB
RevLine 
[14]1/*
[42]2 *  TSPSG: TSP Solver and Generator
[87]3 *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
[14]4 *
5 *  $Id: tspmodel.cpp 95 2010-02-27 13:13:50Z 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
[67]26/*!
27 * \brief Class constructor.
28 * \param parent The parent of the table model.
29 */
[14]30CTSPModel::CTSPModel(QObject *parent)
[21]31        : QAbstractTableModel(parent), nCities(0)
[14]32{
[80]33        settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, "TSPSG", "tspsg", this);
[14]34}
35
[67]36/*!
37 * \brief Resets the table, setting all its elements to 0.
38 *
39 * \sa randomize()
40 */
41void CTSPModel::clear()
[14]42{
[67]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));
[14]48}
49
[67]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 */
[36]58int CTSPModel::columnCount(const QModelIndex &) const
[14]59{
60        return nCities;
61}
62
[67]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 */
[14]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);
[15]77        else if (role == Qt::FontRole) {
78QFont font;
79                font.setBold(true);
80                return font;
81        } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
[14]82                if (index.row() < nCities && index.column() < nCities)
[15]83                        if (table[index.row()][index.column()] == INFINITY)
[87]84                                return tr(INFSTR);
[15]85                        else
[67]86//! \hack HACK: Converting to string to prevent spinbox in edit mode
[15]87                                return QVariant(table[index.row()][index.column()]).toString();
[14]88                else
89                        return QVariant();
[15]90        } else if (role == Qt::UserRole)
91                return table[index.row()][index.column()];
[14]92        return QVariant();
93}
94
[67]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)
[87]121                        return tr("City %1").arg(section + 1);
[67]122                else
[87]123                        return tr("%1").arg(section + 1);
[67]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 */
[71]135bool CTSPModel::loadTask(const QString &fname)
[67]136{
137        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
138QFile f(fname);
139        if (!f.open(QIODevice::ReadOnly)) {
140                QApplication::restoreOverrideCursor();
[87]141                QMessageBox(QMessageBox::Critical,tr("Task Load"),QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
[67]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();
[87]165                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
[67]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{
[95]193int randMin = settings->value("Task/RandMin", DEF_RAND_MIN).toInt();
194int randMax = settings->value("Task/RandMax", DEF_RAND_MAX).toInt();
195        if (settings->value("Task/SymmetricMode", DEF_SYMMETRIC_MODE).toBool()) {
196                for (int r = 0; r < nCities; r++)
197                        for (int c = 0; c < r; c++)
198                                table[c][r] = table[r][c] = rand(randMin, randMax);
199        } else {
200                for (int r = 0; r < nCities; r++)
201                        for (int c = 0; c < nCities; c++)
202                                if (r != c)
203                                        table[r][c] = rand(randMin, randMax);
204        }
205        emit dataChanged(index(0,0), index(nCities - 1, nCities - 1));
[67]206}
207
208/*!
209 * \brief Returns the row count in the table.
210 * \return Number of rows in the table.
211 *
212 *  Actually, this function returns the number of cities in the current task.
213 *
214 * \sa columnCount(), numCities()
215 */
216int CTSPModel::rowCount(const QModelIndex &) const
217{
218        return nCities;
219}
220
221/*!
222 * \brief Saves current task to \a fname.
223 * \param fname The name of the file to seve to.
224 * \return \c true on success, otherwise \c false.
225 *
226 * \sa loadTask()
227 */
[71]228bool CTSPModel::saveTask(const QString &fname)
[67]229{
230        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
231QFile f(fname);
232        if (!f.open(QIODevice::WriteOnly)) {
233                QApplication::restoreOverrideCursor();
[87]234                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();
[67]235                return false;
236        }
237QDataStream ds(&f);
238        ds.setVersion(QDataStream::Qt_4_4);
239        if (f.error() != QFile::NoError) {
240                f.close();
241                QApplication::restoreOverrideCursor();
[87]242                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]243                return false;
244        }
245        // File signature
246        ds << TSPT;
247        if (f.error() != QFile::NoError) {
248                f.close();
249                QApplication::restoreOverrideCursor();
[87]250                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]251                return false;
252        }
253        // File version
254        ds << TSPT_VERSION;
255        if (f.error() != QFile::NoError) {
256                f.close();
257                QApplication::restoreOverrideCursor();
[87]258                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]259                return false;
260        }
261        // File metadata version
262        ds << TSPT_META_VERSION;
263        if (f.error() != QFile::NoError) {
264                f.close();
265                QApplication::restoreOverrideCursor();
[87]266                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]267                return false;
268        }
269        // Metadata
270        ds << OSID;
271        if (f.error() != QFile::NoError) {
272                f.close();
273                QApplication::restoreOverrideCursor();
[87]274                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]275                return false;
276        }
277        // Number of cities
278        ds << nCities;
279        if (f.error() != QFile::NoError) {
280                f.close();
281                QApplication::restoreOverrideCursor();
[87]282                QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]283                return false;
284        }
285        // Costs
286        for (int r = 0; r < nCities; r++)
287                for (int c = 0; c < nCities; c++)
288                        if (r != c) {
[89]289                                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
[67]290                                if (f.error() != QFile::NoError) {
291                                        f.close();
292                                        QApplication::restoreOverrideCursor();
[87]293                                        QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
[67]294                                        return false;
295                                }
296                        }
297        f.close();
298        QApplication::restoreOverrideCursor();
299        return true;
300}
301
302/*!
303 * \brief Sets the \a role data for the item at \a index to \a value.
304 * \param index The index of the item to set data at.
305 * \param value The value of the item data to be set.
306 * \param role The role of the item to set data for.
307 * \return \c true on success, otherwise \c false.
308 *
309 * \sa data()
310 */
[14]311bool CTSPModel::setData(const QModelIndex &index, const QVariant &value, int role)
312{
[15]313        if (!index.isValid())
314                return false;
315        if (role == Qt::EditRole && index.row() != index.column()) {
316                if (value.toString().compare(INFSTR) == 0)
317                        table[index.row()][index.column()] = INFINITY;
318                else {
319bool ok;
[89]320double tmp = value.toDouble(&ok);
[15]321                        if (!ok || tmp < 0)
322                                return false;
[95]323                        else {
[15]324                                table[index.row()][index.column()] = tmp;
[95]325                                if (settings->value("Task/SymmetricMode", DEF_SYMMETRIC_MODE).toBool())
326                                        table[index.column()][index.row()] = tmp;
327                        }
[15]328                }
329                emit dataChanged(index,index);
330                return true;
331        }
332        return false;
[14]333}
334
[67]335/*!
336 * \brief Sets number of cities in the current task to \a n.
337 * \param n Number of cities to set to.
338 *
339 * \sa numCities()
340 */
[14]341void CTSPModel::setNumCities(int n)
342{
[15]343        if (n == nCities)
344                return;
345        emit layoutAboutToBeChanged();
[42]346        table.resize(n);
347        for (int k = 0; k < n; k++) {
348                table[k].resize(n);
[14]349        }
[42]350        if (n > nCities)
351                for (int k = nCities; k < n; k++)
352                        table[k][k] = INFINITY;
[14]353        nCities = n;
[15]354        emit layoutChanged();
[14]355}
356
[67]357/* Privates **********************************************************/
[29]358
[42]359inline bool CTSPModel::loadError(QDataStream::Status status)
[35]360{
361QString err;
362        if (status == QDataStream::Ok)
363                return false;
364        else if (status == QDataStream::ReadPastEnd)
[87]365                err = tr("Unexpected end of file.");
[35]366        else if (status == QDataStream::ReadCorruptData)
[87]367                err = tr("Corrupt data read. File possibly corrupted.");
[35]368        else
[87]369                err = tr("Unknown error.");
[59]370        QApplication::restoreOverrideCursor();
[87]371        QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
[35]372        return true;
373}
374
[47]375bool CTSPModel::loadTSPT(QDataStream *ds)
[31]376{
377        // Skipping signature
378        ds->skipRawData(sizeof(TSPT));
[35]379        if (loadError(ds->status()))
[47]380                return false;
[31]381        // File version
382quint8 version;
383        *ds >> version;
[35]384        if (loadError(ds->status()))
[47]385                return false;
[31]386        if (version > TSPT_VERSION) {
[59]387                QApplication::restoreOverrideCursor();
[87]388                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();
[47]389                return false;
[31]390        }
391        // Skipping metadata
392        ds->skipRawData(TSPT_META_SIZE);
[35]393        if (loadError(ds->status()))
[47]394                return false;
[50]395        // Number of cities
[31]396quint16 size;
397        *ds >> size;
[35]398        if (loadError(ds->status()))
[47]399                return false;
[50]400        if ((size < 3) || (size > MAX_NUM_CITIES)) {
[59]401                QApplication::restoreOverrideCursor();
[87]402                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
[47]403                return false;
[35]404        }
[50]405        if (nCities != size) {
406                setNumCities(size);
[31]407                emit numCitiesChanged(size);
[50]408        }
[87]409
[89]410double x; // We need this as double may be float on some platforms and we store double values in file
[50]411        // Travel costs
[31]412        for (int r = 0; r < size; r++)
413                for (int c = 0; c < size; c++)
[35]414                        if (r != c) {
[87]415                                *ds >> x;
416                                table[r][c] = x;
[35]417                                if (loadError(ds->status())) {
418                                        clear();
[47]419                                        return false;
[35]420                                }
421                        }
[31]422        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
[59]423        QApplication::restoreOverrideCursor();
[47]424        return true;
[31]425}
426
[47]427bool CTSPModel::loadZKT(QDataStream *ds)
[31]428{
429        // Skipping signature
430        ds->skipRawData(sizeof(ZKT));
[35]431        if (loadError(ds->status()))
[47]432                return false;
[31]433        // File version
434quint16 version;
435        ds->readRawData(reinterpret_cast<char *>(&version),2);
[35]436        if (loadError(ds->status()))
[47]437                return false;
[31]438        if (version > ZKT_VERSION) {
[59]439                QApplication::restoreOverrideCursor();
[87]440                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();
[47]441                return false;
[31]442        }
[50]443        // Number of cities
[31]444quint8 size;
445        ds->readRawData(reinterpret_cast<char *>(&size),1);
[35]446        if (loadError(ds->status()))
[47]447                return false;
[35]448        if ((size < 3) || (size > 5)) {
[59]449                QApplication::restoreOverrideCursor();
[87]450                QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
[47]451                return false;
[35]452        }
[50]453        if (nCities != size) {
454                setNumCities(size);
[31]455                emit numCitiesChanged(size);
[50]456        }
457        // Travel costs
[89]458double val;
[42]459        for (int r = 0; r < 5; r++)
460                for (int c = 0; c < 5; c++)
[87]461                        if ((r != c) && (r < size) && (c < size)) {
[31]462                                ds->readRawData(reinterpret_cast<char *>(&val),8);
[35]463                                if (loadError(ds->status())) {
464                                        clear();
[47]465                                        return false;
[35]466                                }
[31]467                                table[r][c] = val;
[35]468                        } else {
[31]469                                ds->skipRawData(8);
[35]470                                if (loadError(ds->status())) {
471                                        clear();
[47]472                                        return false;
[35]473                                }
474                        }
[31]475        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
[59]476        QApplication::restoreOverrideCursor();
[47]477        return true;
[31]478}
479
[89]480inline double CTSPModel::rand(int min, int max) const
[31]481{
[89]482double r;
[87]483        if (settings->value("Task/FractionalRandom", DEF_FRACTIONAL_RANDOM).toBool()) {
[89]484double x = qPow(10, settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt());
485                r = (double)qRound((double)qrand() / RAND_MAX * (max - min) * x) / x;
[87]486        } else
[89]487                r = qRound((double)qrand() / RAND_MAX * (max - min));
[81]488        return min + r;
[31]489}
Note: See TracBrowser for help on using the repository browser.