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

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

+ Implemented File/Save? action.
+ Added "Save Solution" and "Back to Task" buttons to Solution tab for better usability.

  • Increased maximum number of cities to 20. Solving for 18-20 cities is already takes much time, so I thought it doesn't make sense to increase more.
  • Columns and rows are now resized to contents on all platforms.
  • Property svn:keywords set to Id URL
File size: 10.3 KB
Line 
1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
4 *
5 *  $Id: tspmodel.cpp 50 2009-08-03 15:15: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
26CTSPModel::CTSPModel(QObject *parent)
27        : QAbstractTableModel(parent), nCities(0)
28{
29        settings = new QSettings(QSettings::IniFormat,QSettings::UserScope,"TSPSG","tspsg");
30}
31
32inline int CTSPModel::rand(int min, int max) const
33{
34        return min + (int)(((float)qrand() / RAND_MAX) * max);
35}
36
37int CTSPModel::rowCount(const QModelIndex &) const
38{
39        return nCities;
40}
41
42int CTSPModel::columnCount(const QModelIndex &) const
43{
44        return nCities;
45}
46
47QVariant CTSPModel::headerData(int section, Qt::Orientation orientation, int role) const
48{
49        if (role == Qt::DisplayRole) {
50                if (orientation == Qt::Vertical)
51                        return trUtf8("City %1").arg(section + 1);
52                else
53                        return trUtf8("%1").arg(section + 1);
54        }
55        return QVariant();
56}
57
58QVariant CTSPModel::data(const QModelIndex &index, int role) const
59{
60        if (!index.isValid())
61                return QVariant();
62        if (role == Qt::TextAlignmentRole)
63                return int(Qt::AlignCenter);
64        else if (role == Qt::FontRole) {
65QFont font;
66                font.setBold(true);
67                return font;
68        } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
69                if (index.row() < nCities && index.column() < nCities)
70                        if (table[index.row()][index.column()] == INFINITY)
71                                return trUtf8(INFSTR);
72                        else
73                                // HACK: Converting to string to prevent spinbox in edit mode
74                                return QVariant(table[index.row()][index.column()]).toString();
75                else
76                        return QVariant();
77        } else if (role == Qt::UserRole)
78                return table[index.row()][index.column()];
79        return QVariant();
80}
81
82bool CTSPModel::setData(const QModelIndex &index, const QVariant &value, int role)
83{
84        if (!index.isValid())
85                return false;
86        if (role == Qt::EditRole && index.row() != index.column()) {
87                if (value.toString().compare(INFSTR) == 0)
88                        table[index.row()][index.column()] = INFINITY;
89                else {
90bool ok;
91double tmp = value.toDouble(&ok);
92                        if (!ok || tmp < 0)
93                                return false;
94                        else
95                                table[index.row()][index.column()] = tmp;
96                }
97                emit dataChanged(index,index);
98                return true;
99        }
100        return false;
101}
102
103Qt::ItemFlags CTSPModel::flags(const QModelIndex &index) const
104{
105Qt::ItemFlags flags = QAbstractItemModel::flags(index);
106        if (index.row() != index.column())
107                flags |= Qt::ItemIsEditable;
108        return flags;
109}
110
111quint16 CTSPModel::numCities() const
112{
113        return nCities;
114}
115
116void CTSPModel::setNumCities(int n)
117{
118        if (n == nCities)
119                return;
120        emit layoutAboutToBeChanged();
121        table.resize(n);
122        for (int k = 0; k < n; k++) {
123                table[k].resize(n);
124        }
125        if (n > nCities)
126                for (int k = nCities; k < n; k++)
127                        table[k][k] = INFINITY;
128        nCities = n;
129        emit layoutChanged();
130}
131
132void CTSPModel::clear()
133{
134        for (int r = 0; r < nCities; r++)
135                for (int c = 0; c < nCities; c++)
136                        if (r != c)
137                                table[r][c] = 0;
138        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
139}
140
141inline bool CTSPModel::loadError(QDataStream::Status status)
142{
143QString err;
144        if (status == QDataStream::Ok)
145                return false;
146        else if (status == QDataStream::ReadPastEnd)
147                err = trUtf8("Unexpected end of file.");
148        else if (status == QDataStream::ReadCorruptData)
149                err = trUtf8("Corrupt data read. File possibly corrupted.");
150        else
151                err = trUtf8("Unknown error.");
152        QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
153        return true;
154}
155
156bool CTSPModel::loadTask(QString fname)
157{
158QFile f(fname);
159        if (!f.open(QIODevice::ReadOnly)) {
160                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),QString(trUtf8("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
161                return false;
162        }
163QDataStream ds(&f);
164        ds.setVersion(QDataStream::Qt_4_4);
165quint32 sig;
166        ds >> sig;
167        if (loadError(ds.status()))
168                return false;
169        ds.device()->reset();
170        if (sig == TSPT) {
171                if (!loadTSPT(&ds)) {
172                        f.close();
173                        return false;
174                }
175        } else if ((sig >> 16) == ZKT) {
176                if (!loadZKT(&ds)) {
177                        f.close();
178                        return false;
179                }
180        } else {
181                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
182                f.close();
183                return false;
184        }
185        f.close();
186        return true;
187}
188
189bool CTSPModel::loadTSPT(QDataStream *ds)
190{
191        // Skipping signature
192        ds->skipRawData(sizeof(TSPT));
193        if (loadError(ds->status()))
194                return false;
195        // File version
196quint8 version;
197        *ds >> version;
198        if (loadError(ds->status()))
199                return false;
200        if (version > TSPT_VERSION) {
201                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();
202                return false;
203        }
204        // Skipping metadata
205        ds->skipRawData(TSPT_META_SIZE);
206        if (loadError(ds->status()))
207                return false;
208        // Number of cities
209quint16 size;
210        *ds >> size;
211        if (loadError(ds->status()))
212                return false;
213        if ((size < 3) || (size > MAX_NUM_CITIES)) {
214                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
215                return false;
216        }
217        if (nCities != size) {
218                setNumCities(size);
219                emit numCitiesChanged(size);
220        }
221        // Travel costs
222        for (int r = 0; r < size; r++)
223                for (int c = 0; c < size; c++)
224                        if (r != c) {
225                                *ds >> table[r][c];
226                                if (loadError(ds->status())) {
227                                        clear();
228                                        return false;
229                                }
230                        }
231        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
232        return true;
233}
234
235bool CTSPModel::loadZKT(QDataStream *ds)
236{
237        // Skipping signature
238        ds->skipRawData(sizeof(ZKT));
239        if (loadError(ds->status()))
240                return false;
241        // File version
242quint16 version;
243        ds->readRawData(reinterpret_cast<char *>(&version),2);
244        if (loadError(ds->status()))
245                return false;
246        if (version > ZKT_VERSION) {
247                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();
248                return false;
249        }
250        // Number of cities
251quint8 size;
252        ds->readRawData(reinterpret_cast<char *>(&size),1);
253        if (loadError(ds->status()))
254                return false;
255        if ((size < 3) || (size > 5)) {
256                QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
257                return false;
258        }
259        if (nCities != size) {
260                setNumCities(size);
261                emit numCitiesChanged(size);
262        }
263        // Travel costs
264double val;
265        for (int r = 0; r < 5; r++)
266                for (int c = 0; c < 5; c++)
267                        if ((r != c) && (r < size)) {
268                                ds->readRawData(reinterpret_cast<char *>(&val),8);
269                                if (loadError(ds->status())) {
270                                        clear();
271                                        return false;
272                                }
273                                table[r][c] = val;
274                        } else {
275                                ds->skipRawData(8);
276                                if (loadError(ds->status())) {
277                                        clear();
278                                        return false;
279                                }
280                        }
281        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
282        return true;
283}
284
285bool CTSPModel::saveTask(QString fname)
286{
287QFile f(fname);
288        if (!f.open(QIODevice::WriteOnly)) {
289                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();
290                return false;
291        }
292QDataStream ds(&f);
293        ds.setVersion(QDataStream::Qt_4_4);
294        if (f.error() != QFile::NoError) {
295                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
296                f.close();
297                return false;
298        }
299        // File signature
300        ds << TSPT;
301        if (f.error() != QFile::NoError) {
302                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
303                f.close();
304                return false;
305        }
306        // File version
307        ds << TSPT_VERSION;
308        if (f.error() != QFile::NoError) {
309                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
310                f.close();
311                return false;
312        }
313        // File metadata version
314        ds << TSPT_META_VERSION;
315        if (f.error() != QFile::NoError) {
316                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
317                f.close();
318                return false;
319        }
320        // Metadata
321        ds << OSID;
322        if (f.error() != QFile::NoError) {
323                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
324                f.close();
325                return false;
326        }
327        // Number of cities
328        ds << nCities;
329        if (f.error() != QFile::NoError) {
330                QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
331                f.close();
332                return false;
333        }
334        // Costs
335        for (int r = 0; r < nCities; r++)
336                for (int c = 0; c < nCities; c++)
337                        if (r != c) {
338                                ds << table[r][c];
339                                if (f.error() != QFile::NoError) {
340                                        QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
341                                        f.close();
342                                        return false;
343                                }
344                        }
345        f.close();
346        return true;
347}
348
349void CTSPModel::randomize()
350{
351int randMin = settings->value("MinCost",DEF_RAND_MIN).toInt();
352int randMax = settings->value("MaxCost",DEF_RAND_MAX).toInt();
353        for (int r = 0; r < nCities; r++)
354                for (int c = 0; c < nCities; c++)
355                        if (r != c)
356                                table[r][c] = rand(randMin,randMax);
357        emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
358}
Note: See TracBrowser for help on using the repository browser.