Changeset 99 in tspsg-svn for trunk/src


Ignore:
Timestamp:
Mar 22, 2010, 9:45:16 PM (15 years ago)
Author:
laleppa
Message:
  • Fixed a bug when a solution couldn't be found for some tasks while the task had at least one solution (mostly, tasks with a lot of restrictions).
  • Fixed a bug when Save As dialog always appeared (even for non-Untitled files) when selecting Save in Unsaved Changes dialog.
  • Improved the solution algorithm.
  • Moved progress dialog from CTSPSolver to MainWindow?. CTSPSolver doesn't contain any GUI related code now.

+ Added routePartFound() signal to CTSPSolver which is emitted once every time a part of the route is found.
+ Added cancel() slot and wasCanceled() public function to CTSPSolver to be able to cancel a solution process and to know whether it was canceled.
+ Progress is now shown when generating a solution output.
+ Check for updates functionality (only in Windows version at this moment).

Location:
trunk
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk

    • Property svn:ignore
      •  

        old new  
        11*.idb
        2 *.ini
        32*.ncb
        43*.pdb
  • trunk/src/main.cpp

    r98 r99  
    4545        QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
    4646        QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
    47         app.setOrganizationName("..::Lёppsville::.. Homes");
    48         app.setOrganizationDomain("l-homes.org");
     47        app.setOrganizationName("Oleksii \"Lёppa\" Serdiuk");
     48        app.setOrganizationDomain("oleksii.name");
    4949        app.setApplicationName("TSPSG");
    5050        app.setApplicationVersion(BUILD_VERSION);
  • trunk/src/mainwindow.cpp

    r98 r99  
    6060#endif // QT_NO_PRINTER
    6161        connect(actionSettingsPreferences,SIGNAL(triggered()),this,SLOT(actionSettingsPreferencesTriggered()));
     62#ifdef Q_OS_WIN32
     63        connect(actionHelpCheck4Updates, SIGNAL(triggered()), SLOT(actionHelpCheck4UpdatesTriggered()));
     64#endif // Q_OS_WIN32
    6265        connect(actionSettingsLanguageAutodetect,SIGNAL(triggered(bool)),this,SLOT(actionSettingsLanguageAutodetectTriggered(bool)));
    6366        connect(groupSettingsLanguageList,SIGNAL(triggered(QAction *)),this,SLOT(groupSettingsLanguageListTriggered(QAction *)));
     
    134137        filters.append(tr("All Files") + " (*)");
    135138
     139QString file = QFileInfo(fileName).canonicalPath();
    136140QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
    137 QString file = QFileDialog::getOpenFileName(this, tr("Task Load"), QString(), filters.join(";;"), NULL, opts);
     141        file = QFileDialog::getOpenFileName(this, tr("Task Load"), file, filters.join(";;"), NULL, opts);
    138142        if (file.isEmpty() || !QFileInfo(file).isFile())
    139143                return;
     
    147151}
    148152
    149 void MainWindow::actionFileSaveTriggered()
    150 {
    151         qDebug() << tr("Untitled");
     153bool MainWindow::actionFileSaveTriggered()
     154{
    152155        if ((fileName == tr("Untitled") + ".tspt") || (!fileName.endsWith(".tspt", Qt::CaseInsensitive)))
    153                 saveTask();
     156                return saveTask();
    154157        else
    155                 if (tspmodel->saveTask(fileName))
     158                if (tspmodel->saveTask(fileName)) {
    156159                        setWindowModified(false);
     160                        return true;
     161                } else
     162                        return false;
    157163}
    158164
     
    165171{
    166172static QString selectedFile;
    167         if (selectedFile.isEmpty()) {
    168                 if (fileName == tr("Untitled") + ".tspt") {
    169 #ifndef QT_NO_PRINTER
    170                         selectedFile = "solution.pdf";
     173        if (selectedFile.isEmpty())
     174                selectedFile = QFileInfo(fileName).canonicalPath();
     175        else
     176                selectedFile = QFileInfo(selectedFile).canonicalPath();
     177        if (!selectedFile.isEmpty())
     178                selectedFile += "/";
     179        if (fileName == tr("Untitled") + ".tspt") {
     180#ifndef QT_NO_PRINTER
     181                selectedFile += "solution.pdf";
    171182#else
    172                         selectedFile = "solution.html";
     183                selectedFile += "solution.html";
    173184#endif // QT_NO_PRINTER
    174                 } else {
    175 #ifndef QT_NO_PRINTER
    176                         selectedFile = QFileInfo(fileName).canonicalPath() + "/" + QFileInfo(fileName).completeBaseName() + ".pdf";
     185        } else {
     186#ifndef QT_NO_PRINTER
     187                selectedFile += QFileInfo(fileName).completeBaseName() + ".pdf";
    177188#else
    178                         selectedFile = QFileInfo(fileName).canonicalPath() + "/" + QFileInfo(fileName).completeBaseName() + ".html";
     189                selectedFile += QFileInfo(fileName).completeBaseName() + ".html";
    179190#endif // QT_NO_PRINTER
    180                 }
    181191        }
    182192
     
    191201        filters.append(tr("All Files") + " (*)");
    192202
    193 QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
     203QFileDialog::Options opts(settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog);
    194204QString file = QFileDialog::getSaveFileName(this, QString(), selectedFile, filters.join(";;"), NULL, opts);
    195205        if (file.isEmpty())
     
    211221        if (!(selectedFile.endsWith(".htm",Qt::CaseInsensitive) || selectedFile.endsWith(".html",Qt::CaseInsensitive) || selectedFile.endsWith(".odt",Qt::CaseInsensitive) || selectedFile.endsWith(".txt",Qt::CaseInsensitive)))
    212222                dw.setFormat("plaintext");
    213         dw.write(solutionText->document());
    214 #else
     223        if (!dw.write(solutionText->document()))
     224                QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(dw.device()->errorString()));
     225#else // QT_VERSION >= 0x040500
    215226        // Qt < 4.5 has no QTextDocumentWriter class
    216227QFile file(selectedFile);
    217228        if (!file.open(QFile::WriteOnly)) {
    218229                QApplication::restoreOverrideCursor();
     230                QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(file->errorString()));
    219231                return;
    220232        }
     
    258270        if (sd.colorChanged() || sd.fontChanged()) {
    259271                initDocStyleSheet();
    260                 if (!output.isEmpty() && sd.colorChanged() && (QMessageBox(QMessageBox::Question,tr("Settings Changed"),tr("You have changed color settings.\nDo you wish to apply them to current solution text?"),QMessageBox::Yes | QMessageBox::No,this).exec() == QMessageBox::Yes)) {
    261                         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    262                         solutionText->clear();
    263                         solutionText->setHtml(output.join(""));
    264                         QApplication::restoreOverrideCursor();
    265                 }
    266         }
    267         if (sd.translucencyChanged() != 0) {
     272                if (!solutionText->document()->isEmpty() && sd.colorChanged())
     273                        QMessageBox::information(this, tr("Settings Changed"), tr("You have changed color settings.\nThey will be applied to the next solution output."));
     274        }
     275        if (sd.translucencyChanged() != 0)
    268276                toggleTranclucency(sd.translucencyChanged() == 1);
    269         }
    270277}
    271278
     
    283290        if (actionSettingsLanguageAutodetect->isChecked()) {
    284291                // We have language autodetection. It needs to be disabled to change language.
    285                 if (QMessageBox(QMessageBox::Question,tr("Language change"),tr("You have language autodetection turned on.\nIt needs to be off.\nDo you wish to turn it off?"),QMessageBox::Yes | QMessageBox::No,this).exec() == QMessageBox::Yes) {
     292                if (QMessageBox::question(this, tr("Language change"), tr("You have language autodetection turned on.\nIt needs to be off.\nDo you wish to turn it off?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
    286293                        actionSettingsLanguageAutodetect->trigger();
    287294                } else
     
    302309#endif
    303310                QApplication::restoreOverrideCursor();
    304         }
    305 }
     311                QMessageBox::information(this, tr("Settings Changed"), tr("You have changed the application language.\nTo get current solution output in the new language\nyou need to re-run the solution process."));
     312        }
     313}
     314
     315#ifdef Q_OS_WIN32
     316void MainWindow::actionHelpCheck4UpdatesTriggered()
     317{
     318        if (!hasUpdater()) {
     319                QMessageBox::warning(this, tr("Unsupported Feature"), tr("Sorry, but this feature is not supported on your platform\nor support for this feature was not installed."));
     320                return;
     321        }
     322
     323        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
     324        QProcess::execute("updater/Update.exe -name=\"TSPSG: TSP Solver and Generator\" -check=\"freeupdate\"");
     325        QApplication::restoreOverrideCursor();
     326}
     327#endif // Q_OS_WIN32
    306328
    307329void MainWindow::actionHelpAboutTriggered()
    308330{
    309 //! \todo TODO: Normal about window :-)
    310331QString title;
    311332#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
     
    314335        title += QString("<b>TSPSG: TSP Solver and Generator</b><br>");
    315336#endif // Q_OS_WINCE || Q_OS_SYMBIAN
    316         title += QString("%1: <b>%2</b><br>").arg(tr("Version"), BUILD_VERSION);
     337        title += QString("%1: <b>%2</b><br>").arg(tr("Version"), QApplication::applicationVersion());
    317338#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
    318         title += QString("<b>&copy; 2007-%1 Oleksii \"Lёppa\" Serdiuk</b><br>").arg(QDate::currentDate().toString("yyyy"));
     339        title += QString("<b>&copy; 2007-%1 <a href=\"http://%2/\">%3</a></b><br>").arg(QDate::currentDate().toString("yyyy"), QApplication::organizationDomain(), QApplication::organizationName());
    319340        title += QString("<b><a href=\"http://tspsg.sourceforge.net/\">http://tspsg.sourceforge.net/</a></b>");
    320341#else
     
    410431        vb->addLayout(hb2);
    411432
    412         dlg->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint);
     433        dlg->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
    413434        dlg->setWindowTitle(tr("About TSPSG"));
    414435        dlg->setLayout(vb);
     
    453474                        row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
    454475                        if (!ok) {
    455                                 QMessageBox(QMessageBox::Critical,tr("Data error"),tr("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1),QMessageBox::Ok,this).exec();
     476                                QMessageBox::critical(this, tr("Data error"), tr("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1));
    456477                                return;
    457478                        }
     
    459480                matrix.append(row);
    460481        }
     482
     483QProgressDialog pd(this);
     484QProgressBar *pb = new QProgressBar(&pd);
     485        pb->setAlignment(Qt::AlignCenter);
     486        pb->setFormat(tr("%v of %1 parts found").arg(n));
     487        pd.setBar(pb);
     488        pd.setMaximum(n * 2 + 3);
     489        pd.setMinimumDuration(1000);
     490        pd.setLabelText(tr("Calculating optimal route..."));
     491        pd.setWindowTitle(tr("Solution Progress"));
     492        pd.setWindowModality(Qt::ApplicationModal);
     493        pd.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
     494        pd.setValue(0);
     495
    461496CTSPSolver solver;
    462 SStep *root = solver.solve(n,matrix,this);
    463         if (!root)
    464                 return;
    465         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    466 QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
    467         output.clear();
    468         output.append("<p>" + tr("Variant #%1").arg(spinVariant->value()) + "</p>");
    469         output.append("<p>" + tr("Task:") + "</p>");
    470         outputMatrix(matrix, output);
    471         output.append("<hr>");
    472         output.append("<p>" + tr("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
     497        connect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
     498        connect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
     499SStep *root = solver.solve(n, matrix);
     500        disconnect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
     501        disconnect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
     502        if (!root) {
     503                pd.reset();
     504                if (!solver.wasCanceled())
     505                        QMessageBox::warning(this, tr("Solution Result"), tr("Unable to find a solution.\nMaybe, this task has no solution."));
     506                return;
     507        }
     508        pb->setFormat("%p%");
     509        pd.setLabelText(tr("Generating solution output..."));
     510        pd.setValue(n + 1);
     511
     512        solutionText->clear();
     513        pd.setValue(n + 2);
     514
     515        solutionText->setDocumentTitle(tr("Solution of Variant #%1 task").arg(spinVariant->value()));
     516        solutionText->append("<p>" + tr("Variant #%1").arg(spinVariant->value()) + "</p>");
     517        solutionText->append("<p>" + tr("Task:") + "</p>");
     518        solutionText->append(outputMatrix(matrix));
     519        solutionText->append("<hr><p>" + tr("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
    473520SStep *step = root;
    474521        n = 1;
    475522        while (n <= spinCities->value()) {
     523                if (pd.wasCanceled()) {
     524                        solutionText->clear();
     525                        return;
     526                }
     527                pd.setValue(spinCities->value() + 2 + n);
     528
    476529                if (step->prNode->prNode != NULL || ((step->prNode->prNode == NULL) && (step->plNode->prNode == NULL))) {
    477530                        if (n != spinCities->value()) {
    478                                 output.append("<p>" + tr("Step #%1").arg(n++) + "</p>");
     531                                solutionText->append("<p>" + tr("Step #%1").arg(n++) + "</p>");
    479532                                if (settings->value("Output/ShowMatrix", DEF_SHOW_MATRIX).toBool() && (!settings->value("Output/UseShowMatrixLimit", DEF_USE_SHOW_MATRIX_LIMIT).toBool() || (settings->value("Output/UseShowMatrixLimit", DEF_USE_SHOW_MATRIX_LIMIT).toBool() && (spinCities->value() <= settings->value("Output/ShowMatrixLimit", DEF_SHOW_MATRIX_LIMIT).toInt())))) {
    480                                         outputMatrix(*step, output);
     533                                        solutionText->append(outputMatrix(*step));
    481534                                }
    482                                 output.append("<p>" + tr("Selected candidate for branching: %1.").arg(tr("(%1;%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1)) + "</p>");
     535                                solutionText->append("<p>" + tr("Selected candidate for branching: %1.").arg(tr("(%1;%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1)) + "</p>");
    483536                                if (!step->alts.empty()) {
    484537SCandidate cand;
     
    489542                                                alts += tr("(%1;%2)").arg(cand.nRow + 1).arg(cand.nCol + 1);
    490543                                        }
    491                                         output.append("<p class=\"hasalts\">" + tr("%n alternate candidate(s) for branching: %1.","",step->alts.count()).arg(alts) + "</p>");
     544                                        solutionText->append("<p class=\"hasalts\">" + tr("%n alternate candidate(s) for branching: %1.","",step->alts.count()).arg(alts) + "</p>");
    492545                                }
    493                                 output.append("<p>&nbsp;</p>");
     546                                solutionText->append("<p>&nbsp;</p>");
    494547                        }
    495548                }
     
    501554                        break;
    502555        }
     556        pd.setValue(spinCities->value() + 2 + n);
     557
    503558        if (solver.isOptimal())
    504                 output.append("<p>" + tr("Optimal path:") + "</p>");
     559                solutionText->append("<p>" + tr("Optimal path:") + "</p>");
    505560        else
    506                 output.append("<p>" + tr("Resulting path:") + "</p>");
    507         output.append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
     561                solutionText->append("<p>" + tr("Resulting path:") + "</p>");
     562        solutionText->append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
    508563        if (isInteger(step->price))
    509                 output.append("<p>" + tr("The price is <b>%n</b> unit(s).", "", qRound(step->price)) + "</p>");
     564                solutionText->append("<p>" + tr("The price is <b>%n</b> unit(s).", "", qRound(step->price)) + "</p>");
    510565        else
    511                 output.append("<p>" + tr("The price is <b>%1</b> units.").arg(step->price, 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()) + "</p>");
     566                solutionText->append("<p>" + tr("The price is <b>%1</b> units.").arg(step->price, 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()) + "</p>");
    512567        if (!solver.isOptimal()) {
    513                 output.append("<p>&nbsp;</p>");
    514                 output.append("<p>" + tr("<b>WARNING!!!</b><br>This result is a record, but it may not be optimal.<br>Iterations need to be continued to check whether this result is optimal or get an optimal one.") + "</p>");
    515         }
    516         output.append("<p></p>");
    517 
    518         solutionText->setHtml(output.join(""));
    519         solutionText->setDocumentTitle(tr("Solution of Variant #%1 task").arg(spinVariant->value()));
     568                solutionText->append("<p>&nbsp;</p>");
     569                solutionText->append("<p>" + tr("<b>WARNING!!!</b><br>This result is a record, but it may not be optimal.<br>Iterations need to be continued to check whether this result is optimal or get an optimal one.") + "</p>");
     570        }
    520571
    521572        if (settings->value("Output/ScrollToEnd", DEF_SCROLL_TO_END).toBool()) {
    522573                // Scrolling to the end of text.
    523574                solutionText->moveCursor(QTextCursor::End);
    524         }
    525 
     575        } else
     576                solutionText->moveCursor(QTextCursor::Start);
     577
     578        pd.setLabelText(tr("Cleaning up..."));
     579        pd.setMaximum(0);
     580        pd.setCancelButton(NULL);
     581        pd.adjustSize();
     582        QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
     583        solver.cleanup(true);
    526584        toggleSolutionActions();
    527585        tabWidget->setCurrentIndex(1);
    528         QApplication::restoreOverrideCursor();
    529586}
    530587
     
    632689}
    633690
     691bool MainWindow::hasUpdater() const
     692{
     693#ifdef Q_OS_WIN32
     694        return QFile::exists("updater/Update.exe");
     695#else // Q_OS_WIN32
     696        return false;
     697#endif // Q_OS_WIN32
     698}
     699
    634700void MainWindow::initDocStyleSheet()
    635701{
     
    719785                if (!ad) {
    720786                        settings->remove("Language");
    721                         if (QApplication::overrideCursor() != 0)
    722                                 QApplication::restoreOverrideCursor();
     787                        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
    723788                        if (isVisible())
    724789                                QMessageBox::warning(this, tr("Language Change"), tr("Unable to load the translation language.\nFalling back to autodetection."));
    725790                        else
    726791                                QMessageBox::warning(NULL, tr("Language Change"), tr("Unable to load the translation language.\nFalling back to autodetection."));
     792                        QApplication::restoreOverrideCursor();
    727793                }
    728794                return false;
     
    735801        if (!isWindowModified())
    736802                return true;
    737 int res = QMessageBox(QMessageBox::Warning,tr("Unsaved Changes"),tr("Would you like to save changes in current task?"),QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,this).exec();
     803int res = QMessageBox::warning(this, tr("Unsaved Changes"), tr("Would you like to save changes in the current task?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    738804        if (res == QMessageBox::Save)
    739                 return saveTask();
     805                return actionFileSaveTriggered();
    740806        else if (res == QMessageBox::Cancel)
    741807                return false;
     
    744810}
    745811
    746 void MainWindow::outputMatrix(const TMatrix &matrix, QStringList &output)
     812QString MainWindow::outputMatrix(const TMatrix &matrix) const
    747813{
    748814int n = spinCities->value();
    749 QString line="";
     815QString output(""), line;
    750816        output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
    751817        for (int r = 0; r < n; r++) {
     
    761827        }
    762828        output.append("</table>");
    763 }
    764 
    765 void MainWindow::outputMatrix(const SStep &step, QStringList &output)
     829        return output;
     830}
     831
     832QString MainWindow::outputMatrix(const SStep &step) const
    766833{
    767834int n = spinCities->value();
    768 QString line="";
     835QString output(""), line;
    769836        output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
    770837        for (int r = 0; r < n; r++) {
     
    789856        }
    790857        output.append("</table>");
     858        return output;
    791859}
    792860
     
    816884        actionFilePrint->setShortcut(QApplication::translate("MainWindow", "Ctrl+P", 0, QApplication::UnicodeUTF8));
    817885#endif // QT_NO_PRINTER
     886#ifdef Q_OS_WIN32
     887        actionHelpCheck4Updates->setText(tr("Check for &Updates..."));
     888#ifndef QT_NO_TOOLTIP
     889        actionHelpCheck4Updates->setToolTip(tr("Check for %1 updates").arg(QApplication::applicationName()));
     890#endif // QT_NO_TOOLTIP
     891#ifndef QT_NO_STATUSTIP
     892        actionHelpCheck4Updates->setStatusTip(tr("Check for %1 updates").arg(QApplication::applicationName()));
     893#endif // QT_NO_STATUSTIP
     894#endif // Q_OS_WIN32
    818895}
    819896
     
    901978        toolBar->insertAction(actionSettingsPreferences,actionFilePrint);
    902979#endif // QT_NO_PRINTER
     980#ifdef Q_OS_WIN32
     981        actionHelpCheck4Updates = new QAction(this);
     982        actionHelpCheck4Updates->setEnabled(hasUpdater());
     983        menuHelp->insertAction(actionHelpAboutQt, actionHelpCheck4Updates);
     984        menuHelp->insertSeparator(actionHelpAboutQt);
     985#endif // Q_OS_WIN32
    903986
    904987        groupSettingsLanguageList = new QActionGroup(this);
     
    9251008        actionFileSaveAsSolution->setEnabled(enable);
    9261009        solutionText->setEnabled(enable);
    927         if (!enable)
    928                 output.clear();
    9291010#ifndef QT_NO_PRINTER
    9301011        actionFilePrint->setEnabled(enable);
  • trunk/src/mainwindow.h

    r95 r99  
    5353        void actionFileNewTriggered();
    5454        void actionFileOpenTriggered();
    55         void actionFileSaveTriggered();
     55        bool actionFileSaveTriggered();
    5656        void actionFileSaveAsTaskTriggered();
    5757        void actionFileSaveAsSolutionTriggered();
     
    6363        void actionSettingsLanguageAutodetectTriggered(bool checked);
    6464        void groupSettingsLanguageListTriggered(QAction *action);
     65#ifdef Q_OS_WIN32
     66        void actionHelpCheck4UpdatesTriggered();
     67#endif // Q_OS_WIN32
    6568        void actionHelpAboutTriggered();
    6669// Buttons
     
    8487        QString fileName;
    8588        QActionGroup *groupSettingsLanguageList;
    86         QStringList output;
    8789#ifndef QT_NO_PRINTER
    8890        QPrinter *printer;
     
    9092        QAction *actionFilePrint;
    9193#endif // QT_NO_PRINTER
     94#ifdef Q_OS_WIN32
     95        QAction *actionHelpCheck4Updates;
     96#endif // Q_OS_WIN32
    9297        QSettings *settings;
    9398        CTSPModel *tspmodel;
     
    97102
    98103        void closeEvent(QCloseEvent *ev);
     104        bool hasUpdater() const;
    99105        void initDocStyleSheet();
    100106        void loadLangList();
    101107        bool loadLanguage(const QString &lang = QString());
    102108        bool maybeSave();
    103         void outputMatrix(const TMatrix &matrix, QStringList &output);
    104         void outputMatrix(const SStep &step, QStringList &output);
     109        QString outputMatrix(const TMatrix &matrix) const;
     110        QString outputMatrix(const SStep &step) const;
    105111        void retranslateUi(bool all = true);
    106112        bool saveTask();
  • trunk/src/os.h

    r97 r99  
    149149        #define OS "Windows"ARCH
    150150        #define OSID quint8(28)
     151#elif defined Q_OS_WINCE_WM
     152        #define OS "Windows Mobile"ARCH
     153        #define OSID quint8(29)
    151154#elif defined Q_OS_WINCE
    152155        #define OS "Windows CE"ARCH
    153         #define OSID quint8(29)
     156        #define OSID quint8(30)
    154157#else
    155158        #define OS "Unknown"ARCH
  • trunk/src/settingsdialog.cpp

    r98 r99  
    189189        cbCitiesLimit->setEnabled(cbShowMatrix->isChecked());
    190190        cbCitiesLimit->setChecked(settings->value("UseShowMatrixLimit", DEF_USE_SHOW_MATRIX_LIMIT && cbShowMatrix->isChecked()).toBool());
    191         spinCitiesLimit->setEnabled(cbShowMatrix->isChecked());
     191        spinCitiesLimit->setEnabled(cbShowMatrix->isChecked() && cbCitiesLimit->isChecked());
    192192        spinCitiesLimit->setValue(settings->value("ShowMatrixLimit", DEF_SHOW_MATRIX_LIMIT).toInt());
    193193        spinCitiesLimit->setMaximum(MAX_NUM_CITIES);
  • trunk/src/tspmodel.cpp

    r95 r99  
    8181        } else if (role == Qt::DisplayRole || role == Qt::EditRole) {
    8282                if (index.row() < nCities && index.column() < nCities)
    83                         if (table[index.row()][index.column()] == INFINITY)
     83                        if (table.at(index.row()).at(index.column()) == INFINITY)
    8484                                return tr(INFSTR);
    8585                        else
    8686//! \hack HACK: Converting to string to prevent spinbox in edit mode
    87                                 return QVariant(table[index.row()][index.column()]).toString();
     87                                return QVariant(table.at(index.row()).at(index.column())).toString();
    8888                else
    8989                        return QVariant();
     
    139139        if (!f.open(QIODevice::ReadOnly)) {
    140140                QApplication::restoreOverrideCursor();
    141                 QMessageBox(QMessageBox::Critical,tr("Task Load"),QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    142144                return false;
    143145        }
     
    163165                f.close();
    164166                QApplication::restoreOverrideCursor();
    165                 QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
     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();
    166170                return false;
    167171        }
     
    232236        if (!f.open(QIODevice::WriteOnly)) {
    233237                QApplication::restoreOverrideCursor();
    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();
     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();
    235240                return false;
    236241        }
     
    238243        ds.setVersion(QDataStream::Qt_4_4);
    239244        if (f.error() != QFile::NoError) {
    240                 f.close();
    241                 QApplication::restoreOverrideCursor();
    242                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    243249                return false;
    244250        }
     
    246252        ds << TSPT;
    247253        if (f.error() != QFile::NoError) {
    248                 f.close();
    249                 QApplication::restoreOverrideCursor();
    250                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    251258                return false;
    252259        }
     
    254261        ds << TSPT_VERSION;
    255262        if (f.error() != QFile::NoError) {
    256                 f.close();
    257                 QApplication::restoreOverrideCursor();
    258                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    259267                return false;
    260268        }
     
    262270        ds << TSPT_META_VERSION;
    263271        if (f.error() != QFile::NoError) {
    264                 f.close();
    265                 QApplication::restoreOverrideCursor();
    266                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    267276                return false;
    268277        }
     
    270279        ds << OSID;
    271280        if (f.error() != QFile::NoError) {
    272                 f.close();
    273                 QApplication::restoreOverrideCursor();
    274                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    275285                return false;
    276286        }
     
    278288        ds << nCities;
    279289        if (f.error() != QFile::NoError) {
    280                 f.close();
    281                 QApplication::restoreOverrideCursor();
    282                 QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     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();
    283294                return false;
    284295        }
     
    289300                                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
    290301                                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()));
    291304                                        f.close();
    292                                         QApplication::restoreOverrideCursor();
    293                                         QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
     305                                        f.remove();
    294306                                        return false;
    295307                                }
     
    369381                err = tr("Unknown error.");
    370382        QApplication::restoreOverrideCursor();
    371         QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
     383        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
     384        QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + err);
     385        QApplication::restoreOverrideCursor();
    372386        return true;
    373387}
     
    386400        if (version > TSPT_VERSION) {
    387401                QApplication::restoreOverrideCursor();
    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();
     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();
    389405                return false;
    390406        }
     
    400416        if ((size < 3) || (size > MAX_NUM_CITIES)) {
    401417                QApplication::restoreOverrideCursor();
    402                 QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
     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();
    403421                return false;
    404422        }
     
    438456        if (version > ZKT_VERSION) {
    439457                QApplication::restoreOverrideCursor();
    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();
     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();
    441461                return false;
    442462        }
     
    448468        if ((size < 3) || (size > 5)) {
    449469                QApplication::restoreOverrideCursor();
    450                 QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
     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();
    451473                return false;
    452474        }
  • trunk/src/tspsolver.cpp

    r98 r99  
    2424#include "tspsolver.h"
    2525
    26 //! Class constructor
    27 CTSPSolver::CTSPSolver()
    28         : nCities(0), root(NULL)
    29 {
     26//! \internal \brief A short for maximum double, used internally in the solution algorithm.
     27#define MAX_DOUBLE std::numeric_limits<double>::max()
     28
     29/*!
     30 * \brief Returns CTSPSolver's version ID.
     31 * \return A string: <b>\$Id$</b>.
     32 */
     33QString CTSPSolver::getVersionId()
     34{
     35        return QString("$Id$");
     36}
     37
     38/*!
     39 * \brief Constructs CTSPSolver object.
     40 * \param parent A parent object.
     41 */
     42CTSPSolver::CTSPSolver(QObject *parent)
     43        : QObject(parent), nCities(0), root(NULL) {}
     44
     45/*!
     46 * \brief Cleans up the object and frees up memory used by the solution tree.
     47 * \param processEvents If set to \c true then \link QCoreApplication::processEvents() QApplication::processEvents(QEventLoop::ExcludeUserInputEvents)\endlink will be called from time to time while cleaning up.
     48 * \warning After call to this function a solution tree returned by the solve() function is no longer valid.
     49 * \note It is not required to call this function manually. This function is always called by solve() at the beginning of the solution process.
     50 *
     51 * \sa solve()
     52 */
     53void CTSPSolver::cleanup(bool processEvents)
     54{
     55        if (!processEvents)
     56                QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
     57        route.clear();
     58        mayNotBeOptimal = false;
     59        if (root != NULL)
     60                deleteTree(root, processEvents);
     61        if (!processEvents)
     62                QApplication::restoreOverrideCursor();
    3063}
    3164
     
    5184
    5285/*!
    53  * \brief Returns CTSPSolver's version ID.
    54  * \return A string: <b>\$Id$</b>.
    55  */
    56 QString CTSPSolver::getVersionId()
    57 {
    58         return QString("$Id$");
    59 }
    60 
    61 /*!
    62  * \brief Returns whether or not the solution is definitely optimal.
    63  * \return \c true if solution is definitely optimal, otherwise \c false.
    64  *
    65  *  The solution may need some further interations to determine whether it is optimal.
     86 * \brief Indicates whether or not the solution is definitely optimal.
     87 * \return \c true if the solution is definitely optimal, otherwise \c false.
     88 *
     89 *  The solution may need some further iterations to determine whether or not it is optimal.
    6690 *  In such cases this function returns \c false.
    6791 */
     
    7599 * \param numCities Number of cities in the task.
    76100 * \param task The matrix of city-to-city travel costs.
    77  * \param parent The parent widget for displaying messages and dialogs.
    78101 * \return Pointer to the root of the solution tree.
    79102 *
    80103 * \todo TODO: Comment the algorithm.
    81104 */
    82 SStep *CTSPSolver::solve(int numCities, TMatrix task, QWidget *parent)
     105SStep *CTSPSolver::solve(int numCities, const TMatrix &task)
    83106{
    84107        if (numCities <= 1)
    85108                return NULL;
     109
     110QMutexLocker locker(&mutex);
    86111        cleanup();
     112        canceled = false;
     113        locker.unlock();
     114
    87115        nCities = numCities;
    88 QProgressDialog pd(parent);
    89 QProgressBar *pb = new QProgressBar(&pd);
    90         pb->setAlignment(Qt::AlignCenter);
    91         pb->setFormat(tr("%v of %m parts found"));
    92         pd.setBar(pb);
    93         pd.setMaximum(nCities);
    94         pd.setMinimumDuration(1000);
    95         pd.setLabelText(tr("Calculating optimal route..."));
    96         pd.setWindowTitle(tr("Solution Progress"));
    97         pd.setWindowModality(Qt::ApplicationModal);
    98         pd.setValue(0);
    99116
    100117SStep *step = new SStep();
    101118        step->matrix = task;
     119        // We need to distinguish the values forbidden by the user
     120        // from the values forbidden by the algorithm.
     121        // So we replace user's infinities by the maximum available double value.
     122        normalize(step->matrix);
     123#ifdef DEBUG
     124        qDebug() << step->matrix;
     125#endif // DEBUG
    102126        step->price = align(step->matrix);
    103127        root = step;
     
    108132double check = INFINITY;
    109133        while (this->route.size() < nCities) {
    110 //              forbidden.clear();
    111134                step->alts = findCandidate(step->matrix,nRow,nCol);
     135
    112136                while (hasSubCycles(nRow,nCol)) {
    113 //                      forbidden[nRow] = nCol;
     137#ifdef DEBUG
     138                        qDebug() << "Forbidden: (" << nRow << ";" << nCol << ")";
     139#endif // DEBUG
    114140                        step->matrix[nRow][nCol] = INFINITY;
    115141                        step->price += align(step->matrix);
    116142                        step->alts = findCandidate(step->matrix,nRow,nCol);
    117143                }
    118                 if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
     144
     145#ifdef DEBUG
     146                qDebug() /*<< step->matrix*/ << "Selected: (" << nRow << ";" << nCol << ")";
     147                qDebug() << "Alternate:" << step->alts;
     148                qDebug() << "Step price:" << step->price << endl;
     149#endif // DEBUG
     150
     151                locker.relock();
     152                if ((nRow == -1) || (nCol == -1) || canceled) {
    119153                        cleanup();
    120                         break;
    121                 }
     154                        return NULL;
     155                }
     156                locker.unlock();
    122157
    123158                // Route with (nRow,nCol) path
     
    132167                }
    133168                right->price = step->price + align(right->matrix);
    134                 // Forbid selected route to exclude its reuse in next steps.
     169                // Forbid the selected route to exclude its reuse in next steps.
    135170                right->matrix[nCol][nRow] = INFINITY;
    136171                right->matrix[nRow][nCol] = INFINITY;
     
    148183                step->prNode = right;
    149184
     185                // This matrix is not used anymore. Restoring infinities back.
     186                denormalize(step->matrix);
     187
    150188                if (right->price <= left->price) {
    151189                        // Route with (nRow,nCol) path is cheaper
    152190                        step = right;
    153191                        this->route[nRow] = nCol;
    154                         pd.setValue(this->route.size());
     192                        emit routePartFound(this->route.size());
    155193                        if (firstStep) {
    156194                                check = left->price;
     
    168206        }
    169207
    170         if (!root && !pd.wasCanceled()) {
    171                 pd.reset();
    172                 QMessageBox(QMessageBox::Warning,tr("Solution Result"),tr("Unable to find solution.\nMaybe, this task has no solutions."),QMessageBox::Ok,parent).exec();
    173         }
    174 
    175         qApp->processEvents();
    176 
    177         if (root) {
    178                 route = this->route;
    179                 mayNotBeOptimal = (check < step->price);
    180         }
     208        route = this->route;
     209        mayNotBeOptimal = (check < step->price);
     210
    181211        return root;
     212}
     213
     214/*!
     215 * \brief Indicates whether or not the solution process was canceled.
     216 * \return \c true if the solution process was canceled, otherwise \c false.
     217 */
     218bool CTSPSolver::wasCanceled() const
     219{
     220QMutexLocker locker(&mutex);
     221        return canceled;
     222}
     223
     224/*!
     225 * \brief Cancels the solution process.
     226 */
     227void CTSPSolver::cancel()
     228{
     229QMutexLocker locker(&mutex);
     230        canceled = true;
    182231}
    183232
     
    198247                if (min > 0) {
    199248                        r += min;
    200                         subRow(matrix,k,min);
     249                        if (min < MAX_DOUBLE)
     250                                subRow(matrix,k,min);
    201251                }
    202252        }
     
    205255                if (min > 0) {
    206256                        r += min;
    207                         subCol(matrix,k,min);
     257                        if (min < MAX_DOUBLE)
     258                                subCol(matrix,k,min);
    208259                }
    209260        }
     
    211262}
    212263
    213 void CTSPSolver::cleanup()
    214 {
    215         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    216         route.clear();
    217         mayNotBeOptimal = false;
    218         if (root != NULL)
    219                 deleteTree(root);
    220         QApplication::restoreOverrideCursor();
    221 }
    222 
    223 void CTSPSolver::deleteTree(SStep *&root)
     264void CTSPSolver::deleteTree(SStep *&root, bool processEvents)
    224265{
    225266        if (root == NULL)
     
    228269SStep *parent;
    229270        forever {
     271                if (processEvents)
     272                        QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    230273                if (step->plNode != NULL) {
    231274                        // We have left child node - going inside it
     
    252295                }
    253296        }
     297}
     298
     299void CTSPSolver::denormalize(TMatrix &matrix) const
     300{
     301        for (int r = 0; r < nCities; r++)
     302                for (int c = 0; c < nCities; c++)
     303                        if ((r != c) && (matrix.at(r).at(c) == MAX_DOUBLE))
     304                                matrix[r][c] = INFINITY;
    254305}
    255306
     
    287338                if ((k != exr) && (min > matrix.at(k).at(nCol)))
    288339                        min = matrix.at(k).at(nCol);
    289         return min == INFINITY ? 0 : min;
     340        return (min == INFINITY) ? 0 : min;
    290341}
    291342
     
    293344{
    294345double min = INFINITY;
    295         for (int k = 0; k < nCities; k++)
     346        for (int k = 0; k < nCities; k++) {
    296347                if (((k != exc)) && (min > matrix.at(nRow).at(k)))
    297348                        min = matrix.at(nRow).at(k);
    298         return min == INFINITY ? 0 : min;
     349        }
     350        return (min == INFINITY) ? 0 : min;
    299351}
    300352
     
    313365}
    314366
     367void CTSPSolver::normalize(TMatrix &matrix) const
     368{
     369        for (int r = 0; r < nCities; r++)
     370                for (int c = 0; c < nCities; c++)
     371                        if ((r != c) && (matrix.at(r).at(c) == INFINITY))
     372                                matrix[r][c] = MAX_DOUBLE;
     373}
     374
    315375void CTSPSolver::subCol(TMatrix &matrix, int nCol, double val)
    316376{
     
    327387}
    328388
     389#ifdef DEBUG
    329390QDebug operator<<(QDebug dbg, const TMatrix &matrix)
    330391{
    331392        for (int r = 0; r < matrix.count(); r++) {
    332393                for (int c = 0; c < matrix.at(r).count(); c++)
    333                         dbg.space() << matrix.at(r).at(c);
     394                        dbg.space() << QString::number(matrix.at(r).at(c)).leftJustified(5);
    334395                dbg << endl;
    335396        }
    336397        return dbg;
    337398}
     399
     400QDebug operator<<(QDebug dbg, const SCandidate &cand)
     401{
     402        dbg.nospace() << "(" << cand.nRow << ";" << cand.nCol << ")";
     403        return dbg;
     404}
     405#endif // DEBUG
  • trunk/src/tspsolver.h

    r98 r99  
    7878 * \author Copyright &copy; 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
    7979 */
    80 class CTSPSolver
     80class CTSPSolver: public QObject
    8181{
    82         Q_DECLARE_TR_FUNCTIONS(CTSPSolver)
     82        Q_OBJECT
    8383
    8484public:
    85         CTSPSolver();
     85        static QString getVersionId();
     86
     87        CTSPSolver(QObject *parent = NULL);
     88        void cleanup(bool processEvents = false);
    8689        QString getSortedPath() const;
    87         static QString getVersionId();
    8890        bool isOptimal() const;
    89         SStep *solve(int numCities, TMatrix task, QWidget *parent = 0);
     91        SStep *solve(int numCities, const TMatrix &task);
     92        bool wasCanceled() const;
    9093        ~CTSPSolver();
    9194
     95public slots:
     96        void cancel();
     97
     98signals:
     99        /*!
     100         * \brief This signal is emitted once every time a part of the route is found.
     101         * \param n Indicates the number of the route parts found.
     102         */
     103        void routePartFound(int n);
     104
    92105private:
    93         bool mayNotBeOptimal;
     106        bool mayNotBeOptimal, canceled;
    94107        int nCities;
    95108        SStep *root;
    96109        QHash<int,int> route;
    97 //      QHash<int,int> forbidden;
     110        mutable QMutex mutex;
    98111
    99112        double align(TMatrix &matrix);
    100         void cleanup();
    101         void deleteTree(SStep *&root);
     113        void deleteTree(SStep *&root, bool processEvents = false);
     114        void denormalize(TMatrix &matrix) const;
    102115        QList<SCandidate> findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const;
    103116        double findMinInCol(int nCol, const TMatrix &matrix, int exr = -1) const;
    104117        double findMinInRow(int nRow, const TMatrix &matrix, int exc = -1) const;
    105118        bool hasSubCycles(int nRow, int nCol) const;
     119        void normalize(TMatrix &matrix) const;
    106120        void subCol(TMatrix &matrix, int nCol, double val);
    107121        void subRow(TMatrix &matrix, int nRow, double val);
     
    110124#ifdef DEBUG
    111125QDebug operator<<(QDebug dbg, const TMatrix &matrix);
    112 #endif
     126QDebug operator<<(QDebug dbg, const SCandidate &candidate);
     127#endif // DEBUG
    113128
    114129#endif // TSPSOLVER_H
Note: See TracChangeset for help on using the changeset viewer.