source: tspsg/src/tspmodel.cpp @ a218bf2cb2

0.1.3.145-beta1-symbian0.1.4.170-beta2-bb10appveyorimgbotreadme
Last change on this file since a218bf2cb2 was 0ac9690913, checked in by Oleksii Serdiuk, 15 years ago

+ Toolbar state and position is now saved and restored with Main Window state and position.

  • Made some small improvements to the code.
  • Fixed some errors in the documentation.
  • Made source code more "documentation friendly".
  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
4 *
5 *  $Id$
6 *  $URL$
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");
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 trUtf8(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 trUtf8("City %1").arg(section + 1);
122                else
123                        return trUtf8("%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,trUtf8("Task Load"),QString(trUtf8("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,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("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("MinCost",DEF_RAND_MIN).toInt();
194int randMax = settings->value("MaxCost",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,trUtf8("Task Save"),QString(trUtf8("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,trUtf8("Task Save"),trUtf8("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,trUtf8("Task Save"),trUtf8("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,trUtf8("Task Save"),trUtf8("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,trUtf8("Task Save"),trUtf8("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,trUtf8("Task Save"),trUtf8("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,trUtf8("Task Save"),trUtf8("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 << table[r][c];
284                                if (f.error() != QFile::NoError) {
285                                        f.close();
286                                        QApplication::restoreOverrideCursor();
287                                        QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("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;
314double tmp = value.toDouble(&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 = trUtf8("Unexpected end of file.");
357        else if (status == QDataStream::ReadCorruptData)
358                err = trUtf8("Corrupt data read. File possibly corrupted.");
359        else
360                err = trUtf8("Unknown error.");
361        QApplication::restoreOverrideCursor();
362        QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("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,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("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,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("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        // Travel costs
401        for (int r = 0; r < size; r++)
402                for (int c = 0; c < size; c++)
403                        if (r != c) {
404                                *ds >> table[r][c];
405                                if (loadError(ds->status())) {
406                                        clear();
407                                        return false;
408                                }
409                        }
410        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
411        QApplication::restoreOverrideCursor();
412        return true;
413}
414
415bool CTSPModel::loadZKT(QDataStream *ds)
416{
417        // Skipping signature
418        ds->skipRawData(sizeof(ZKT));
419        if (loadError(ds->status()))
420                return false;
421        // File version
422quint16 version;
423        ds->readRawData(reinterpret_cast<char *>(&version),2);
424        if (loadError(ds->status()))
425                return false;
426        if (version > ZKT_VERSION) {
427                QApplication::restoreOverrideCursor();
428                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("File version is newer than application supports.\nPlease, try to update application."),QMessageBox::Ok).exec();
429                return false;
430        }
431        // Number of cities
432quint8 size;
433        ds->readRawData(reinterpret_cast<char *>(&size),1);
434        if (loadError(ds->status()))
435                return false;
436        if ((size < 3) || (size > 5)) {
437                QApplication::restoreOverrideCursor();
438                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
439                return false;
440        }
441        if (nCities != size) {
442                setNumCities(size);
443                emit numCitiesChanged(size);
444        }
445        // Travel costs
446double val;
447        for (int r = 0; r < 5; r++)
448                for (int c = 0; c < 5; c++)
449                        if ((r != c) && (r < size)) {
450                                ds->readRawData(reinterpret_cast<char *>(&val),8);
451                                if (loadError(ds->status())) {
452                                        clear();
453                                        return false;
454                                }
455                                table[r][c] = val;
456                        } else {
457                                ds->skipRawData(8);
458                                if (loadError(ds->status())) {
459                                        clear();
460                                        return false;
461                                }
462                        }
463        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
464        QApplication::restoreOverrideCursor();
465        return true;
466}
467
468inline int CTSPModel::rand(int min, int max) const
469{
470        return min + (int)(((float)qrand() / RAND_MAX) * (max - min));
471}
Note: See TracBrowser for help on using the repository browser.