Index: trunk/src/globals.cpp
===================================================================
--- trunk/src/globals.cpp	(revision 99)
+++ trunk/src/globals.cpp	(revision 99)
@@ -0,0 +1,35 @@
+/*
+ *  TSPSG: TSP Solver and Generator
+ *  Copyright (C) 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
+ *
+ *  $Id$
+ *  $URL$
+ *
+ *  This file is part of TSPSG.
+ *
+ *  TSPSG is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  TSPSG is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with TSPSG.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "globals.h"
+
+#ifdef Q_OS_WIN32
+void toggleStyle(QWidget *widget, bool enable)
+{
+	if (enable) {
+		widget->setStyleSheet(QString("%1 {background-color: %2; border-color: %3; border-width: 1px; border-style: solid; border-radius: 3px;}").arg(widget->metaObject()->className(), widget->palette().window().color().name(), widget->palette().shadow().color().name()));
+	} else {
+		widget->setStyleSheet(QString());
+	}
+}
+#endif // Q_OS_WIN32
Index: trunk/src/main.cpp
===================================================================
--- trunk/src/main.cpp	(revision 98)
+++ trunk/src/main.cpp	(revision 99)
@@ -45,6 +45,6 @@
 	QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf8"));
 	QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
-	app.setOrganizationName("..::Lёppsville::.. Homes");
-	app.setOrganizationDomain("l-homes.org");
+	app.setOrganizationName("Oleksii \"Lёppa\" Serdiuk");
+	app.setOrganizationDomain("oleksii.name");
 	app.setApplicationName("TSPSG");
 	app.setApplicationVersion(BUILD_VERSION);
Index: trunk/src/mainwindow.cpp
===================================================================
--- trunk/src/mainwindow.cpp	(revision 98)
+++ trunk/src/mainwindow.cpp	(revision 99)
@@ -60,4 +60,7 @@
 #endif // QT_NO_PRINTER
 	connect(actionSettingsPreferences,SIGNAL(triggered()),this,SLOT(actionSettingsPreferencesTriggered()));
+#ifdef Q_OS_WIN32
+	connect(actionHelpCheck4Updates, SIGNAL(triggered()), SLOT(actionHelpCheck4UpdatesTriggered()));
+#endif // Q_OS_WIN32
 	connect(actionSettingsLanguageAutodetect,SIGNAL(triggered(bool)),this,SLOT(actionSettingsLanguageAutodetectTriggered(bool)));
 	connect(groupSettingsLanguageList,SIGNAL(triggered(QAction *)),this,SLOT(groupSettingsLanguageListTriggered(QAction *)));
@@ -134,6 +137,7 @@
 	filters.append(tr("All Files") + " (*)");
 
+QString file = QFileInfo(fileName).canonicalPath();
 QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
-QString file = QFileDialog::getOpenFileName(this, tr("Task Load"), QString(), filters.join(";;"), NULL, opts);
+	file = QFileDialog::getOpenFileName(this, tr("Task Load"), file, filters.join(";;"), NULL, opts);
 	if (file.isEmpty() || !QFileInfo(file).isFile())
 		return;
@@ -147,12 +151,14 @@
 }
 
-void MainWindow::actionFileSaveTriggered()
-{
-	qDebug() << tr("Untitled");
+bool MainWindow::actionFileSaveTriggered()
+{
 	if ((fileName == tr("Untitled") + ".tspt") || (!fileName.endsWith(".tspt", Qt::CaseInsensitive)))
-		saveTask();
+		return saveTask();
 	else
-		if (tspmodel->saveTask(fileName))
+		if (tspmodel->saveTask(fileName)) {
 			setWindowModified(false);
+			return true;
+		} else
+			return false;
 }
 
@@ -165,18 +171,22 @@
 {
 static QString selectedFile;
-	if (selectedFile.isEmpty()) {
-		if (fileName == tr("Untitled") + ".tspt") {
-#ifndef QT_NO_PRINTER
-			selectedFile = "solution.pdf";
+	if (selectedFile.isEmpty())
+		selectedFile = QFileInfo(fileName).canonicalPath();
+	else
+		selectedFile = QFileInfo(selectedFile).canonicalPath();
+	if (!selectedFile.isEmpty())
+		selectedFile += "/";
+	if (fileName == tr("Untitled") + ".tspt") {
+#ifndef QT_NO_PRINTER
+		selectedFile += "solution.pdf";
 #else
-			selectedFile = "solution.html";
+		selectedFile += "solution.html";
 #endif // QT_NO_PRINTER
-		} else {
-#ifndef QT_NO_PRINTER
-			selectedFile = QFileInfo(fileName).canonicalPath() + "/" + QFileInfo(fileName).completeBaseName() + ".pdf";
+	} else {
+#ifndef QT_NO_PRINTER
+		selectedFile += QFileInfo(fileName).completeBaseName() + ".pdf";
 #else
-			selectedFile = QFileInfo(fileName).canonicalPath() + "/" + QFileInfo(fileName).completeBaseName() + ".html";
+		selectedFile += QFileInfo(fileName).completeBaseName() + ".html";
 #endif // QT_NO_PRINTER
-		}
 	}
 
@@ -191,5 +201,5 @@
 	filters.append(tr("All Files") + " (*)");
 
-QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
+QFileDialog::Options opts(settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog);
 QString file = QFileDialog::getSaveFileName(this, QString(), selectedFile, filters.join(";;"), NULL, opts);
 	if (file.isEmpty())
@@ -211,10 +221,12 @@
 	if (!(selectedFile.endsWith(".htm",Qt::CaseInsensitive) || selectedFile.endsWith(".html",Qt::CaseInsensitive) || selectedFile.endsWith(".odt",Qt::CaseInsensitive) || selectedFile.endsWith(".txt",Qt::CaseInsensitive)))
 		dw.setFormat("plaintext");
-	dw.write(solutionText->document());
-#else
+	if (!dw.write(solutionText->document()))
+		QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(dw.device()->errorString()));
+#else // QT_VERSION >= 0x040500
 	// Qt < 4.5 has no QTextDocumentWriter class
 QFile file(selectedFile);
 	if (!file.open(QFile::WriteOnly)) {
 		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(file->errorString()));
 		return;
 	}
@@ -258,14 +270,9 @@
 	if (sd.colorChanged() || sd.fontChanged()) {
 		initDocStyleSheet();
-		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)) {
-			QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-			solutionText->clear();
-			solutionText->setHtml(output.join(""));
-			QApplication::restoreOverrideCursor();
-		}
-	}
-	if (sd.translucencyChanged() != 0) {
+		if (!solutionText->document()->isEmpty() && sd.colorChanged())
+			QMessageBox::information(this, tr("Settings Changed"), tr("You have changed color settings.\nThey will be applied to the next solution output."));
+	}
+	if (sd.translucencyChanged() != 0)
 		toggleTranclucency(sd.translucencyChanged() == 1);
-	}
 }
 
@@ -283,5 +290,5 @@
 	if (actionSettingsLanguageAutodetect->isChecked()) {
 		// We have language autodetection. It needs to be disabled to change language.
-		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) {
+		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) {
 			actionSettingsLanguageAutodetect->trigger();
 		} else
@@ -302,10 +309,24 @@
 #endif
 		QApplication::restoreOverrideCursor();
-	}
-}
+		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."));
+	}
+}
+
+#ifdef Q_OS_WIN32
+void MainWindow::actionHelpCheck4UpdatesTriggered()
+{
+	if (!hasUpdater()) {
+		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."));
+		return;
+	}
+
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+	QProcess::execute("updater/Update.exe -name=\"TSPSG: TSP Solver and Generator\" -check=\"freeupdate\"");
+	QApplication::restoreOverrideCursor();
+}
+#endif // Q_OS_WIN32
 
 void MainWindow::actionHelpAboutTriggered()
 {
-//! \todo TODO: Normal about window :-)
 QString title;
 #if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
@@ -314,7 +335,7 @@
 	title += QString("<b>TSPSG: TSP Solver and Generator</b><br>");
 #endif // Q_OS_WINCE || Q_OS_SYMBIAN
-	title += QString("%1: <b>%2</b><br>").arg(tr("Version"), BUILD_VERSION);
+	title += QString("%1: <b>%2</b><br>").arg(tr("Version"), QApplication::applicationVersion());
 #if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
-	title += QString("<b>&copy; 2007-%1 Oleksii \"Lёppa\" Serdiuk</b><br>").arg(QDate::currentDate().toString("yyyy"));
+	title += QString("<b>&copy; 2007-%1 <a href=\"http://%2/\">%3</a></b><br>").arg(QDate::currentDate().toString("yyyy"), QApplication::organizationDomain(), QApplication::organizationName());
 	title += QString("<b><a href=\"http://tspsg.sourceforge.net/\">http://tspsg.sourceforge.net/</a></b>");
 #else
@@ -410,5 +431,5 @@
 	vb->addLayout(hb2);
 
-	dlg->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint);
+	dlg->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
 	dlg->setWindowTitle(tr("About TSPSG"));
 	dlg->setLayout(vb);
@@ -453,5 +474,5 @@
 			row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
 			if (!ok) {
-				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();
+				QMessageBox::critical(this, tr("Data error"), tr("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1));
 				return;
 			}
@@ -459,26 +480,58 @@
 		matrix.append(row);
 	}
+
+QProgressDialog pd(this);
+QProgressBar *pb = new QProgressBar(&pd);
+	pb->setAlignment(Qt::AlignCenter);
+	pb->setFormat(tr("%v of %1 parts found").arg(n));
+	pd.setBar(pb);
+	pd.setMaximum(n * 2 + 3);
+	pd.setMinimumDuration(1000);
+	pd.setLabelText(tr("Calculating optimal route..."));
+	pd.setWindowTitle(tr("Solution Progress"));
+	pd.setWindowModality(Qt::ApplicationModal);
+	pd.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
+	pd.setValue(0);
+
 CTSPSolver solver;
-SStep *root = solver.solve(n,matrix,this);
-	if (!root)
-		return;
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
-	output.clear();
-	output.append("<p>" + tr("Variant #%1").arg(spinVariant->value()) + "</p>");
-	output.append("<p>" + tr("Task:") + "</p>");
-	outputMatrix(matrix, output);
-	output.append("<hr>");
-	output.append("<p>" + tr("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
+	connect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
+	connect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
+SStep *root = solver.solve(n, matrix);
+	disconnect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
+	disconnect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
+	if (!root) {
+		pd.reset();
+		if (!solver.wasCanceled())
+			QMessageBox::warning(this, tr("Solution Result"), tr("Unable to find a solution.\nMaybe, this task has no solution."));
+		return;
+	}
+	pb->setFormat("%p%");
+	pd.setLabelText(tr("Generating solution output..."));
+	pd.setValue(n + 1);
+
+	solutionText->clear();
+	pd.setValue(n + 2);
+
+	solutionText->setDocumentTitle(tr("Solution of Variant #%1 task").arg(spinVariant->value()));
+	solutionText->append("<p>" + tr("Variant #%1").arg(spinVariant->value()) + "</p>");
+	solutionText->append("<p>" + tr("Task:") + "</p>");
+	solutionText->append(outputMatrix(matrix));
+	solutionText->append("<hr><p>" + tr("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
 SStep *step = root;
 	n = 1;
 	while (n <= spinCities->value()) {
+		if (pd.wasCanceled()) {
+			solutionText->clear();
+			return;
+		}
+		pd.setValue(spinCities->value() + 2 + n);
+
 		if (step->prNode->prNode != NULL || ((step->prNode->prNode == NULL) && (step->plNode->prNode == NULL))) {
 			if (n != spinCities->value()) {
-				output.append("<p>" + tr("Step #%1").arg(n++) + "</p>");
+				solutionText->append("<p>" + tr("Step #%1").arg(n++) + "</p>");
 				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())))) {
-					outputMatrix(*step, output);
+					solutionText->append(outputMatrix(*step));
 				}
-				output.append("<p>" + tr("Selected candidate for branching: %1.").arg(tr("(%1;%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1)) + "</p>");
+				solutionText->append("<p>" + tr("Selected candidate for branching: %1.").arg(tr("(%1;%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1)) + "</p>");
 				if (!step->alts.empty()) {
 SCandidate cand;
@@ -489,7 +542,7 @@
 						alts += tr("(%1;%2)").arg(cand.nRow + 1).arg(cand.nCol + 1);
 					}
-					output.append("<p class=\"hasalts\">" + tr("%n alternate candidate(s) for branching: %1.","",step->alts.count()).arg(alts) + "</p>");
+					solutionText->append("<p class=\"hasalts\">" + tr("%n alternate candidate(s) for branching: %1.","",step->alts.count()).arg(alts) + "</p>");
 				}
-				output.append("<p>&nbsp;</p>");
+				solutionText->append("<p>&nbsp;</p>");
 			}
 		}
@@ -501,30 +554,34 @@
 			break;
 	}
+	pd.setValue(spinCities->value() + 2 + n);
+
 	if (solver.isOptimal())
-		output.append("<p>" + tr("Optimal path:") + "</p>");
+		solutionText->append("<p>" + tr("Optimal path:") + "</p>");
 	else
-		output.append("<p>" + tr("Resulting path:") + "</p>");
-	output.append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
+		solutionText->append("<p>" + tr("Resulting path:") + "</p>");
+	solutionText->append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
 	if (isInteger(step->price))
-		output.append("<p>" + tr("The price is <b>%n</b> unit(s).", "", qRound(step->price)) + "</p>");
+		solutionText->append("<p>" + tr("The price is <b>%n</b> unit(s).", "", qRound(step->price)) + "</p>");
 	else
-		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>");
+		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>");
 	if (!solver.isOptimal()) {
-		output.append("<p>&nbsp;</p>");
-		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>");
-	}
-	output.append("<p></p>");
-
-	solutionText->setHtml(output.join(""));
-	solutionText->setDocumentTitle(tr("Solution of Variant #%1 task").arg(spinVariant->value()));
+		solutionText->append("<p>&nbsp;</p>");
+		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>");
+	}
 
 	if (settings->value("Output/ScrollToEnd", DEF_SCROLL_TO_END).toBool()) {
 		// Scrolling to the end of text.
 		solutionText->moveCursor(QTextCursor::End);
-	}
-
+	} else
+		solutionText->moveCursor(QTextCursor::Start);
+
+	pd.setLabelText(tr("Cleaning up..."));
+	pd.setMaximum(0);
+	pd.setCancelButton(NULL);
+	pd.adjustSize();
+	QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+	solver.cleanup(true);
 	toggleSolutionActions();
 	tabWidget->setCurrentIndex(1);
-	QApplication::restoreOverrideCursor();
 }
 
@@ -632,4 +689,13 @@
 }
 
+bool MainWindow::hasUpdater() const
+{
+#ifdef Q_OS_WIN32
+	return QFile::exists("updater/Update.exe");
+#else // Q_OS_WIN32
+	return false;
+#endif // Q_OS_WIN32
+}
+
 void MainWindow::initDocStyleSheet()
 {
@@ -719,10 +785,10 @@
 		if (!ad) {
 			settings->remove("Language");
-			if (QApplication::overrideCursor() != 0)
-				QApplication::restoreOverrideCursor();
+			QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
 			if (isVisible())
 				QMessageBox::warning(this, tr("Language Change"), tr("Unable to load the translation language.\nFalling back to autodetection."));
 			else
 				QMessageBox::warning(NULL, tr("Language Change"), tr("Unable to load the translation language.\nFalling back to autodetection."));
+			QApplication::restoreOverrideCursor();
 		}
 		return false;
@@ -735,7 +801,7 @@
 	if (!isWindowModified())
 		return true;
-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();
+int res = QMessageBox::warning(this, tr("Unsaved Changes"), tr("Would you like to save changes in the current task?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
 	if (res == QMessageBox::Save)
-		return saveTask();
+		return actionFileSaveTriggered();
 	else if (res == QMessageBox::Cancel)
 		return false;
@@ -744,8 +810,8 @@
 }
 
-void MainWindow::outputMatrix(const TMatrix &matrix, QStringList &output)
+QString MainWindow::outputMatrix(const TMatrix &matrix) const
 {
 int n = spinCities->value();
-QString line="";
+QString output(""), line;
 	output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
 	for (int r = 0; r < n; r++) {
@@ -761,10 +827,11 @@
 	}
 	output.append("</table>");
-}
-
-void MainWindow::outputMatrix(const SStep &step, QStringList &output)
+	return output;
+}
+
+QString MainWindow::outputMatrix(const SStep &step) const
 {
 int n = spinCities->value();
-QString line="";
+QString output(""), line;
 	output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
 	for (int r = 0; r < n; r++) {
@@ -789,4 +856,5 @@
 	}
 	output.append("</table>");
+	return output;
 }
 
@@ -816,4 +884,13 @@
 	actionFilePrint->setShortcut(QApplication::translate("MainWindow", "Ctrl+P", 0, QApplication::UnicodeUTF8));
 #endif // QT_NO_PRINTER
+#ifdef Q_OS_WIN32
+	actionHelpCheck4Updates->setText(tr("Check for &Updates..."));
+#ifndef QT_NO_TOOLTIP
+	actionHelpCheck4Updates->setToolTip(tr("Check for %1 updates").arg(QApplication::applicationName()));
+#endif // QT_NO_TOOLTIP
+#ifndef QT_NO_STATUSTIP
+	actionHelpCheck4Updates->setStatusTip(tr("Check for %1 updates").arg(QApplication::applicationName()));
+#endif // QT_NO_STATUSTIP
+#endif // Q_OS_WIN32
 }
 
@@ -901,4 +978,10 @@
 	toolBar->insertAction(actionSettingsPreferences,actionFilePrint);
 #endif // QT_NO_PRINTER
+#ifdef Q_OS_WIN32
+	actionHelpCheck4Updates = new QAction(this);
+	actionHelpCheck4Updates->setEnabled(hasUpdater());
+	menuHelp->insertAction(actionHelpAboutQt, actionHelpCheck4Updates);
+	menuHelp->insertSeparator(actionHelpAboutQt);
+#endif // Q_OS_WIN32
 
 	groupSettingsLanguageList = new QActionGroup(this);
@@ -925,6 +1008,4 @@
 	actionFileSaveAsSolution->setEnabled(enable);
 	solutionText->setEnabled(enable);
-	if (!enable)
-		output.clear();
 #ifndef QT_NO_PRINTER
 	actionFilePrint->setEnabled(enable);
Index: trunk/src/mainwindow.h
===================================================================
--- trunk/src/mainwindow.h	(revision 98)
+++ trunk/src/mainwindow.h	(revision 99)
@@ -53,5 +53,5 @@
 	void actionFileNewTriggered();
 	void actionFileOpenTriggered();
-	void actionFileSaveTriggered();
+	bool actionFileSaveTriggered();
 	void actionFileSaveAsTaskTriggered();
 	void actionFileSaveAsSolutionTriggered();
@@ -63,4 +63,7 @@
 	void actionSettingsLanguageAutodetectTriggered(bool checked);
 	void groupSettingsLanguageListTriggered(QAction *action);
+#ifdef Q_OS_WIN32
+	void actionHelpCheck4UpdatesTriggered();
+#endif // Q_OS_WIN32
 	void actionHelpAboutTriggered();
 // Buttons
@@ -84,5 +87,4 @@
 	QString fileName;
 	QActionGroup *groupSettingsLanguageList;
-	QStringList output;
 #ifndef QT_NO_PRINTER
 	QPrinter *printer;
@@ -90,4 +92,7 @@
 	QAction *actionFilePrint;
 #endif // QT_NO_PRINTER
+#ifdef Q_OS_WIN32
+	QAction *actionHelpCheck4Updates;
+#endif // Q_OS_WIN32
 	QSettings *settings;
 	CTSPModel *tspmodel;
@@ -97,10 +102,11 @@
 
 	void closeEvent(QCloseEvent *ev);
+	bool hasUpdater() const;
 	void initDocStyleSheet();
 	void loadLangList();
 	bool loadLanguage(const QString &lang = QString());
 	bool maybeSave();
-	void outputMatrix(const TMatrix &matrix, QStringList &output);
-	void outputMatrix(const SStep &step, QStringList &output);
+	QString outputMatrix(const TMatrix &matrix) const;
+	QString outputMatrix(const SStep &step) const;
 	void retranslateUi(bool all = true);
 	bool saveTask();
Index: trunk/src/os.h
===================================================================
--- trunk/src/os.h	(revision 98)
+++ trunk/src/os.h	(revision 99)
@@ -149,7 +149,10 @@
 	#define OS "Windows"ARCH
 	#define OSID quint8(28)
+#elif defined Q_OS_WINCE_WM
+	#define OS "Windows Mobile"ARCH
+	#define OSID quint8(29)
 #elif defined Q_OS_WINCE
 	#define OS "Windows CE"ARCH
-	#define OSID quint8(29)
+	#define OSID quint8(30)
 #else
 	#define OS "Unknown"ARCH
Index: trunk/src/settingsdialog.cpp
===================================================================
--- trunk/src/settingsdialog.cpp	(revision 98)
+++ trunk/src/settingsdialog.cpp	(revision 99)
@@ -189,5 +189,5 @@
 	cbCitiesLimit->setEnabled(cbShowMatrix->isChecked());
 	cbCitiesLimit->setChecked(settings->value("UseShowMatrixLimit", DEF_USE_SHOW_MATRIX_LIMIT && cbShowMatrix->isChecked()).toBool());
-	spinCitiesLimit->setEnabled(cbShowMatrix->isChecked());
+	spinCitiesLimit->setEnabled(cbShowMatrix->isChecked() && cbCitiesLimit->isChecked());
 	spinCitiesLimit->setValue(settings->value("ShowMatrixLimit", DEF_SHOW_MATRIX_LIMIT).toInt());
 	spinCitiesLimit->setMaximum(MAX_NUM_CITIES);
Index: trunk/src/tspmodel.cpp
===================================================================
--- trunk/src/tspmodel.cpp	(revision 98)
+++ trunk/src/tspmodel.cpp	(revision 99)
@@ -81,9 +81,9 @@
 	} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
 		if (index.row() < nCities && index.column() < nCities)
-			if (table[index.row()][index.column()] == INFINITY)
+			if (table.at(index.row()).at(index.column()) == INFINITY)
 				return tr(INFSTR);
 			else
 //! \hack HACK: Converting to string to prevent spinbox in edit mode
-				return QVariant(table[index.row()][index.column()]).toString();
+				return QVariant(table.at(index.row()).at(index.column())).toString();
 		else
 			return QVariant();
@@ -139,5 +139,7 @@
 	if (!f.open(QIODevice::ReadOnly)) {
 		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Load"),QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), QString(tr("Unable to open task file.\nError: %1")).arg(f.errorString()));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
@@ -163,5 +165,7 @@
 		f.close();
 		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unknown file format or file is corrupted."));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
@@ -232,5 +236,6 @@
 	if (!f.open(QIODevice::WriteOnly)) {
 		QApplication::restoreOverrideCursor();
-		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();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), QString(tr("Unable to create task file.\nError: %1\nMaybe, file is read-only?")).arg(f.errorString()));
+		f.remove();
 		return false;
 	}
@@ -238,7 +243,8 @@
 	ds.setVersion(QDataStream::Qt_4_4);
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -246,7 +252,8 @@
 	ds << TSPT;
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -254,7 +261,8 @@
 	ds << TSPT_VERSION;
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -262,7 +270,8 @@
 	ds << TSPT_META_VERSION;
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -270,7 +279,8 @@
 	ds << OSID;
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -278,7 +288,8 @@
 	ds << nCities;
 	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		QApplication::restoreOverrideCursor();
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
+		f.close();
+		f.remove();
 		return false;
 	}
@@ -289,7 +300,8 @@
 				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
 				if (f.error() != QFile::NoError) {
+					QApplication::restoreOverrideCursor();
+					QMessageBox::critical(QApplication::activeWindow(), tr("Task Save"), tr("Unable to save task.\nError: %1").arg(f.errorString()));
 					f.close();
-					QApplication::restoreOverrideCursor();
-					QMessageBox(QMessageBox::Critical,tr("Task Save"),tr("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+					f.remove();
 					return false;
 				}
@@ -369,5 +381,7 @@
 		err = tr("Unknown error.");
 	QApplication::restoreOverrideCursor();
-	QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
+	QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+	QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + err);
+	QApplication::restoreOverrideCursor();
 	return true;
 }
@@ -386,5 +400,7 @@
 	if (version > TSPT_VERSION) {
 		QApplication::restoreOverrideCursor();
-		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();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		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."));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
@@ -400,5 +416,7 @@
 	if ((size < 3) || (size > MAX_NUM_CITIES)) {
 		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
@@ -438,5 +456,7 @@
 	if (version > ZKT_VERSION) {
 		QApplication::restoreOverrideCursor();
-		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();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		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."));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
@@ -448,5 +468,7 @@
 	if ((size < 3) || (size > 5)) {
 		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,tr("Task Load"),tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."),QMessageBox::Ok).exec();
+		QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
+		QMessageBox::critical(QApplication::activeWindow(), tr("Task Load"), tr("Unable to load task:") + "\n" + tr("Unexpected data read.\nFile is possibly corrupted."));
+		QApplication::restoreOverrideCursor();
 		return false;
 	}
Index: trunk/src/tspsolver.cpp
===================================================================
--- trunk/src/tspsolver.cpp	(revision 98)
+++ trunk/src/tspsolver.cpp	(revision 99)
@@ -24,8 +24,41 @@
 #include "tspsolver.h"
 
-//! Class constructor
-CTSPSolver::CTSPSolver()
-	: nCities(0), root(NULL)
-{
+//! \internal \brief A short for maximum double, used internally in the solution algorithm.
+#define MAX_DOUBLE std::numeric_limits<double>::max()
+
+/*!
+ * \brief Returns CTSPSolver's version ID.
+ * \return A string: <b>\$Id$</b>.
+ */
+QString CTSPSolver::getVersionId()
+{
+	return QString("$Id$");
+}
+
+/*!
+ * \brief Constructs CTSPSolver object.
+ * \param parent A parent object.
+ */
+CTSPSolver::CTSPSolver(QObject *parent)
+	: QObject(parent), nCities(0), root(NULL) {}
+
+/*!
+ * \brief Cleans up the object and frees up memory used by the solution tree.
+ * \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.
+ * \warning After call to this function a solution tree returned by the solve() function is no longer valid.
+ * \note It is not required to call this function manually. This function is always called by solve() at the beginning of the solution process.
+ *
+ * \sa solve()
+ */
+void CTSPSolver::cleanup(bool processEvents)
+{
+	if (!processEvents)
+		QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+	route.clear();
+	mayNotBeOptimal = false;
+	if (root != NULL)
+		deleteTree(root, processEvents);
+	if (!processEvents)
+		QApplication::restoreOverrideCursor();
 }
 
@@ -51,17 +84,8 @@
 
 /*!
- * \brief Returns CTSPSolver's version ID.
- * \return A string: <b>\$Id$</b>.
- */
-QString CTSPSolver::getVersionId()
-{
-	return QString("$Id$");
-}
-
-/*!
- * \brief Returns whether or not the solution is definitely optimal.
- * \return \c true if solution is definitely optimal, otherwise \c false.
- *
- *  The solution may need some further interations to determine whether it is optimal.
+ * \brief Indicates whether or not the solution is definitely optimal.
+ * \return \c true if the solution is definitely optimal, otherwise \c false.
+ *
+ *  The solution may need some further iterations to determine whether or not it is optimal.
  *  In such cases this function returns \c false.
  */
@@ -75,29 +99,29 @@
  * \param numCities Number of cities in the task.
  * \param task The matrix of city-to-city travel costs.
- * \param parent The parent widget for displaying messages and dialogs.
  * \return Pointer to the root of the solution tree.
  *
  * \todo TODO: Comment the algorithm.
  */
-SStep *CTSPSolver::solve(int numCities, TMatrix task, QWidget *parent)
+SStep *CTSPSolver::solve(int numCities, const TMatrix &task)
 {
 	if (numCities <= 1)
 		return NULL;
+
+QMutexLocker locker(&mutex);
 	cleanup();
+	canceled = false;
+	locker.unlock();
+
 	nCities = numCities;
-QProgressDialog pd(parent);
-QProgressBar *pb = new QProgressBar(&pd);
-	pb->setAlignment(Qt::AlignCenter);
-	pb->setFormat(tr("%v of %m parts found"));
-	pd.setBar(pb);
-	pd.setMaximum(nCities);
-	pd.setMinimumDuration(1000);
-	pd.setLabelText(tr("Calculating optimal route..."));
-	pd.setWindowTitle(tr("Solution Progress"));
-	pd.setWindowModality(Qt::ApplicationModal);
-	pd.setValue(0);
 
 SStep *step = new SStep();
 	step->matrix = task;
+	// We need to distinguish the values forbidden by the user
+	// from the values forbidden by the algorithm.
+	// So we replace user's infinities by the maximum available double value.
+	normalize(step->matrix);
+#ifdef DEBUG
+	qDebug() << step->matrix;
+#endif // DEBUG
 	step->price = align(step->matrix);
 	root = step;
@@ -108,16 +132,27 @@
 double check = INFINITY;
 	while (this->route.size() < nCities) {
-//		forbidden.clear();
 		step->alts = findCandidate(step->matrix,nRow,nCol);
+
 		while (hasSubCycles(nRow,nCol)) {
-//			forbidden[nRow] = nCol;
+#ifdef DEBUG
+			qDebug() << "Forbidden: (" << nRow << ";" << nCol << ")";
+#endif // DEBUG
 			step->matrix[nRow][nCol] = INFINITY;
 			step->price += align(step->matrix);
 			step->alts = findCandidate(step->matrix,nRow,nCol);
 		}
-		if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
+
+#ifdef DEBUG
+		qDebug() /*<< step->matrix*/ << "Selected: (" << nRow << ";" << nCol << ")";
+		qDebug() << "Alternate:" << step->alts;
+		qDebug() << "Step price:" << step->price << endl;
+#endif // DEBUG
+
+		locker.relock();
+		if ((nRow == -1) || (nCol == -1) || canceled) {
 			cleanup();
-			break;
-		}
+			return NULL;
+		}
+		locker.unlock();
 
 		// Route with (nRow,nCol) path
@@ -132,5 +167,5 @@
 		}
 		right->price = step->price + align(right->matrix);
-		// Forbid selected route to exclude its reuse in next steps.
+		// Forbid the selected route to exclude its reuse in next steps.
 		right->matrix[nCol][nRow] = INFINITY;
 		right->matrix[nRow][nCol] = INFINITY;
@@ -148,9 +183,12 @@
 		step->prNode = right;
 
+		// This matrix is not used anymore. Restoring infinities back.
+		denormalize(step->matrix);
+
 		if (right->price <= left->price) {
 			// Route with (nRow,nCol) path is cheaper
 			step = right;
 			this->route[nRow] = nCol;
-			pd.setValue(this->route.size());
+			emit routePartFound(this->route.size());
 			if (firstStep) {
 				check = left->price;
@@ -168,16 +206,27 @@
 	}
 
-	if (!root && !pd.wasCanceled()) {
-		pd.reset();
-		QMessageBox(QMessageBox::Warning,tr("Solution Result"),tr("Unable to find solution.\nMaybe, this task has no solutions."),QMessageBox::Ok,parent).exec();
-	}
-
-	qApp->processEvents();
-
-	if (root) {
-		route = this->route;
-		mayNotBeOptimal = (check < step->price);
-	}
+	route = this->route;
+	mayNotBeOptimal = (check < step->price);
+
 	return root;
+}
+
+/*!
+ * \brief Indicates whether or not the solution process was canceled.
+ * \return \c true if the solution process was canceled, otherwise \c false.
+ */
+bool CTSPSolver::wasCanceled() const
+{
+QMutexLocker locker(&mutex);
+	return canceled;
+}
+
+/*!
+ * \brief Cancels the solution process.
+ */
+void CTSPSolver::cancel()
+{
+QMutexLocker locker(&mutex);
+	canceled = true;
 }
 
@@ -198,5 +247,6 @@
 		if (min > 0) {
 			r += min;
-			subRow(matrix,k,min);
+			if (min < MAX_DOUBLE)
+				subRow(matrix,k,min);
 		}
 	}
@@ -205,5 +255,6 @@
 		if (min > 0) {
 			r += min;
-			subCol(matrix,k,min);
+			if (min < MAX_DOUBLE)
+				subCol(matrix,k,min);
 		}
 	}
@@ -211,15 +262,5 @@
 }
 
-void CTSPSolver::cleanup()
-{
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-	route.clear();
-	mayNotBeOptimal = false;
-	if (root != NULL)
-		deleteTree(root);
-	QApplication::restoreOverrideCursor();
-}
-
-void CTSPSolver::deleteTree(SStep *&root)
+void CTSPSolver::deleteTree(SStep *&root, bool processEvents)
 {
 	if (root == NULL)
@@ -228,4 +269,6 @@
 SStep *parent;
 	forever {
+		if (processEvents)
+			QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
 		if (step->plNode != NULL) {
 			// We have left child node - going inside it
@@ -252,4 +295,12 @@
 		}
 	}
+}
+
+void CTSPSolver::denormalize(TMatrix &matrix) const
+{
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if ((r != c) && (matrix.at(r).at(c) == MAX_DOUBLE))
+				matrix[r][c] = INFINITY;
 }
 
@@ -287,5 +338,5 @@
 		if ((k != exr) && (min > matrix.at(k).at(nCol)))
 			min = matrix.at(k).at(nCol);
-	return min == INFINITY ? 0 : min;
+	return (min == INFINITY) ? 0 : min;
 }
 
@@ -293,8 +344,9 @@
 {
 double min = INFINITY;
-	for (int k = 0; k < nCities; k++)
+	for (int k = 0; k < nCities; k++) {
 		if (((k != exc)) && (min > matrix.at(nRow).at(k)))
 			min = matrix.at(nRow).at(k);
-	return min == INFINITY ? 0 : min;
+	}
+	return (min == INFINITY) ? 0 : min;
 }
 
@@ -313,4 +365,12 @@
 }
 
+void CTSPSolver::normalize(TMatrix &matrix) const
+{
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if ((r != c) && (matrix.at(r).at(c) == INFINITY))
+				matrix[r][c] = MAX_DOUBLE;
+}
+
 void CTSPSolver::subCol(TMatrix &matrix, int nCol, double val)
 {
@@ -327,11 +387,19 @@
 }
 
+#ifdef DEBUG
 QDebug operator<<(QDebug dbg, const TMatrix &matrix)
 {
 	for (int r = 0; r < matrix.count(); r++) {
 		for (int c = 0; c < matrix.at(r).count(); c++)
-			dbg.space() << matrix.at(r).at(c);
+			dbg.space() << QString::number(matrix.at(r).at(c)).leftJustified(5);
 		dbg << endl;
 	}
 	return dbg;
 }
+
+QDebug operator<<(QDebug dbg, const SCandidate &cand)
+{
+	dbg.nospace() << "(" << cand.nRow << ";" << cand.nCol << ")";
+	return dbg;
+}
+#endif // DEBUG
Index: trunk/src/tspsolver.h
===================================================================
--- trunk/src/tspsolver.h	(revision 98)
+++ trunk/src/tspsolver.h	(revision 99)
@@ -78,30 +78,44 @@
  * \author Copyright &copy; 2007-2010 Lёppa <contacts[at]oleksii[dot]name>
  */
-class CTSPSolver
+class CTSPSolver: public QObject
 {
-	Q_DECLARE_TR_FUNCTIONS(CTSPSolver)
+	Q_OBJECT
 
 public:
-	CTSPSolver();
+	static QString getVersionId();
+
+	CTSPSolver(QObject *parent = NULL);
+	void cleanup(bool processEvents = false);
 	QString getSortedPath() const;
-	static QString getVersionId();
 	bool isOptimal() const;
-	SStep *solve(int numCities, TMatrix task, QWidget *parent = 0);
+	SStep *solve(int numCities, const TMatrix &task);
+	bool wasCanceled() const;
 	~CTSPSolver();
 
+public slots:
+	void cancel();
+
+signals:
+	/*!
+	 * \brief This signal is emitted once every time a part of the route is found.
+	 * \param n Indicates the number of the route parts found.
+	 */
+	void routePartFound(int n);
+
 private:
-	bool mayNotBeOptimal;
+	bool mayNotBeOptimal, canceled;
 	int nCities;
 	SStep *root;
 	QHash<int,int> route;
-//	QHash<int,int> forbidden;
+	mutable QMutex mutex;
 
 	double align(TMatrix &matrix);
-	void cleanup();
-	void deleteTree(SStep *&root);
+	void deleteTree(SStep *&root, bool processEvents = false);
+	void denormalize(TMatrix &matrix) const;
 	QList<SCandidate> findCandidate(const TMatrix &matrix, int &nRow, int &nCol) const;
 	double findMinInCol(int nCol, const TMatrix &matrix, int exr = -1) const;
 	double findMinInRow(int nRow, const TMatrix &matrix, int exc = -1) const;
 	bool hasSubCycles(int nRow, int nCol) const;
+	void normalize(TMatrix &matrix) const;
 	void subCol(TMatrix &matrix, int nCol, double val);
 	void subRow(TMatrix &matrix, int nRow, double val);
@@ -110,5 +124,6 @@
 #ifdef DEBUG
 QDebug operator<<(QDebug dbg, const TMatrix &matrix);
-#endif
+QDebug operator<<(QDebug dbg, const SCandidate &candidate);
+#endif // DEBUG
 
 #endif // TSPSOLVER_H
