Index: src/mainwindow.cpp
===================================================================
--- src/mainwindow.cpp	(revision e2abfd326fbc0376081d9298a0ae92600057d629)
+++ src/mainwindow.cpp	(revision 394216e4680e854a3aa3a56decff8d1cccb5f94c)
@@ -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);
