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

Last change on this file since 154 was 151, checked in by laleppa, 14 years ago

Changed 2010 to 2011 in the source code copyrights.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id URL
File size: 16.7 KB
Line 
1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2011 Lёppa <contacts[at]oleksii[dot]name>
4 *
5 *  $Id: tspmodel.cpp 151 2011-01-19 22:27:49Z 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.