Index: /trunk/src/globals.h
===================================================================
--- /trunk/src/globals.h	(revision 66)
+++ /trunk/src/globals.h	(revision 67)
@@ -1,8 +1,12 @@
-/*
- *  TSPSG: TSP Solver and Generator
- *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+/*!
+ * \file globals.h
+ * \author Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
  *  $Id$
  *  $URL$
+ *
+ * \brief Contains TSPSG global defines.
+ *
+ *  <b>TSPSG: TSP Solver and Generator</b>
  *
  *  This file is part of TSPSG.
@@ -24,9 +28,4 @@
 #ifndef GLOBALS_H
 #define GLOBALS_H
-
-/*!
- * \file globals.h
- * \brief This file contains TSPSG global defines.
- */
 
 // INCLUDES
Index: /trunk/src/mainwindow.cpp
===================================================================
--- /trunk/src/mainwindow.cpp	(revision 66)
+++ /trunk/src/mainwindow.cpp	(revision 67)
@@ -115,4 +115,358 @@
 }
 
+/*!
+ * \brief Handles Main Window close event.
+ * \param event Close event.
+ *
+ *  Checks whether or not a current task was saved and asks for saving if not.
+ *  Saves TSPSG settings.
+ */
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+	if (!maybeSave()) {
+		event->ignore();
+		return;
+	}
+	settings->setValue("NumCities",spinCities->value());
+#ifndef Q_OS_WINCE
+	// Saving windows state
+	if (settings->value("SavePos",false).toBool()) {
+		settings->beginGroup("MainWindow");
+		settings->setValue("Maximized",isMaximized());
+		if (!isMaximized()) {
+			settings->setValue("Size",size());
+			settings->setValue("Position",pos());
+		}
+		settings->endGroup();
+	}
+#endif // Q_OS_WINCE
+	QMainWindow::closeEvent(event);
+}
+
+/* Privates **********************************************************/
+
+void MainWindow::actionFileNewTriggered()
+{
+	if (!maybeSave())
+		return;
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+	tspmodel->clear();
+	setFileName();
+	setWindowModified(false);
+	tabWidget->setCurrentIndex(0);
+	solutionText->clear();
+	enableSolutionActions(false);
+	QApplication::restoreOverrideCursor();
+}
+
+void MainWindow::actionFileOpenTriggered()
+{
+	if (!maybeSave())
+		return;
+QFileDialog od(this);
+	od.setAcceptMode(QFileDialog::AcceptOpen);
+	od.setFileMode(QFileDialog::ExistingFile);
+QStringList filters(trUtf8("All Supported Formats") + " (*.tspt *.zkt)");
+	filters.append(trUtf8("%1 Task Files").arg("TSPSG") + " (*.tspt)");
+	filters.append(trUtf8("%1 Task Files").arg("ZKomModRd") + " (*.zkt)");
+	filters.append(trUtf8("All Files") + " (*)");
+	od.setNameFilters(filters);
+	if (od.exec() != QDialog::Accepted)
+		return;
+QStringList files = od.selectedFiles();
+	if (files.empty())
+		return;
+	if (!tspmodel->loadTask(files.first()))
+		return;
+	setFileName(files.first());
+	tabWidget->setCurrentIndex(0);
+	setWindowModified(false);
+	solutionText->clear();
+	enableSolutionActions(false);
+}
+
+void MainWindow::actionFileSaveTriggered()
+{
+	if ((fileName == trUtf8("Untitled") + ".tspt") || (!fileName.endsWith(".tspt",Qt::CaseInsensitive)))
+		saveTask();
+	else
+		if (tspmodel->saveTask(fileName))
+			setWindowModified(false);
+}
+
+void MainWindow::actionFileSaveAsTaskTriggered()
+{
+	saveTask();
+}
+
+void MainWindow::actionFileSaveAsSolutionTriggered()
+{
+static QString selectedFile;
+	if (selectedFile.isEmpty())
+#ifndef QT_NO_PRINTER
+		selectedFile = "solution.pdf";
+#else
+		selectedFile = "solution.html";
+#endif // QT_NO_PRINTER
+QFileDialog sd(this);
+	sd.setAcceptMode(QFileDialog::AcceptSave);
+QStringList filters;
+#ifndef QT_NO_PRINTER
+	filters.append(trUtf8("PDF Files") + "(*.pdf)");
+#endif
+	filters.append(trUtf8("HTML Files") + " (*.html *.htm)");
+#if QT_VERSION >= 0x040500
+	filters.append(trUtf8("OpenDocument Files") + " (*.odt)");
+#endif // QT_VERSION >= 0x040500
+	filters.append(trUtf8("All Files") + " (*)");
+	sd.setNameFilters(filters);
+	sd.selectFile(selectedFile);
+	if (sd.exec() != QDialog::Accepted)
+		return;
+QStringList files = sd.selectedFiles();
+	if (files.empty())
+		return;
+	selectedFile = files.first();
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+#ifndef QT_NO_PRINTER
+	if (selectedFile.endsWith(".pdf",Qt::CaseInsensitive)) {
+QPrinter printer(QPrinter::HighResolution);
+		printer.setOutputFormat(QPrinter::PdfFormat);
+		printer.setOutputFileName(selectedFile);
+		solutionText->document()->print(&printer);
+		QApplication::restoreOverrideCursor();
+		return;
+	}
+#endif
+#if QT_VERSION >= 0x040500
+QTextDocumentWriter dw(selectedFile);
+	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
+	// Qt < 4.5 has no QTextDocumentWriter class
+QFile file(selectedFile);
+	if (!file.open(QFile::WriteOnly)) {
+		QApplication::restoreOverrideCursor();
+		return;
+	}
+QTextStream ts(&file);
+	ts.setCodec(QTextCodec::codecForName("UTF-8"));
+	ts << solutionText->document()->toHtml("UTF-8");
+	file.close();
+#endif // QT_VERSION >= 0x040500
+	QApplication::restoreOverrideCursor();
+}
+
+#ifndef QT_NO_PRINTER
+void MainWindow::actionFilePrintPreviewTriggered()
+{
+QPrintPreviewDialog ppd(printer, this);
+    connect(&ppd,SIGNAL(paintRequested(QPrinter *)),SLOT(printPreview(QPrinter *)));
+    ppd.exec();
+}
+
+void MainWindow::actionFilePrintTriggered()
+{
+QPrintDialog pd(printer,this);
+#if QT_VERSION >= 0x040500
+	// No such methods in Qt < 4.5
+	pd.setOption(QAbstractPrintDialog::PrintSelection,false);
+	pd.setOption(QAbstractPrintDialog::PrintPageRange,false);
+#endif
+	if (pd.exec() != QDialog::Accepted)
+		return;
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+	solutionText->document()->print(printer);
+	QApplication::restoreOverrideCursor();
+}
+#endif // QT_NO_PRINTER
+
+void MainWindow::actionSettingsPreferencesTriggered()
+{
+SettingsDialog sd(this);
+	if (sd.exec() != QDialog::Accepted)
+		return;
+	if (sd.colorChanged() || sd.fontChanged()) {
+		initDocStyleSheet();
+		if (!output.isEmpty() && sd.colorChanged() && (QMessageBox(QMessageBox::Question,trUtf8("Settings Changed"),trUtf8("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();
+		}
+	}
+}
+
+void MainWindow::actionSettingsLanguageAutodetectTriggered(bool checked)
+{
+	if (checked) {
+		settings->remove("Language");
+		QMessageBox(QMessageBox::Information,trUtf8("Language change"),trUtf8("Language will be autodetected on next application start."),QMessageBox::Ok,this).exec();
+	} else
+		settings->setValue("Language",groupSettingsLanguageList->checkedAction()->data().toString());
+}
+
+void MainWindow::groupSettingsLanguageListTriggered(QAction *action)
+{
+	if (actionSettingsLanguageAutodetect->isChecked()) {
+		// We have language autodetection. It needs to be disabled to change language.
+		if (QMessageBox(QMessageBox::Question,trUtf8("Language change"),trUtf8("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) {
+			actionSettingsLanguageAutodetect->trigger();
+		} else
+			return;
+	}
+bool untitled = (fileName == trUtf8("Untitled") + ".tspt");
+	if (loadLanguage(action->data().toString())) {
+		settings->setValue("Language",action->data().toString());
+		retranslateUi(this);
+		if (untitled)
+			setFileName();
+	}
+}
+
+void MainWindow::actionHelpAboutTriggered()
+{
+//! \todo TODO: Normal about window :-)
+QString about = QString::fromUtf8("TSPSG: TSP Solver and Generator\n");
+	about += QString::fromUtf8("    Version: "BUILD_VERSION"\n");
+	about += QString::fromUtf8("    Copyright (C) 2007-%1 Lёppa <contacts[at]oleksii[dot]name>\n").arg(QDate::currentDate().toString("yyyy"));
+	about += QString::fromUtf8("Target OS: %1\n").arg(OS);
+	about += "Qt library:\n";
+	about += QString::fromUtf8("    Compile time: %1\n").arg(QT_VERSION_STR);
+	about += QString::fromUtf8("    Runtime: %1\n").arg(qVersion());
+	about += QString::fromUtf8("Built on %1 at %2\n").arg(__DATE__).arg(__TIME__);
+	about += QString::fromUtf8(VERSIONID"\n\n");
+	about += QString::fromUtf8("Algorithm: %1\n").arg(CTSPSolver::getVersionId());
+	about += "\n";
+	about += "TSPSG is licensed under the terms of the GNU General Public License. You should have received a copy of the GNU General Public License along with TSPSG.";
+	QMessageBox(QMessageBox::Information,"About",about,QMessageBox::Ok,this).exec();
+}
+
+void MainWindow::buttonBackToTaskClicked()
+{
+	tabWidget->setCurrentIndex(0);
+}
+
+void MainWindow::buttonRandomClicked()
+{
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+	tspmodel->randomize();
+	QApplication::restoreOverrideCursor();
+}
+
+void MainWindow::buttonSolveClicked()
+{
+tMatrix matrix;
+QList<double> row;
+int n = spinCities->value();
+bool ok;
+	for (int r = 0; r < n; r++) {
+		row.clear();
+		for (int c = 0; c < n; c++) {
+			row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
+			if (!ok) {
+				QMessageBox(QMessageBox::Critical,trUtf8("Data error"),trUtf8("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1),QMessageBox::Ok,this).exec();
+				return;
+			}
+		}
+		matrix.append(row);
+	}
+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>" + trUtf8("Variant #%1").arg(spinVariant->value()) + "</p>");
+	output.append("<p>" + trUtf8("Task:") + "</p>");
+	outputMatrix(matrix,output);
+	output.append("<hr>");
+	output.append("<p>" + trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
+sStep *step = root;
+	n = 1;
+	while (n <= spinCities->value()) {
+		if (step->prNode->prNode != NULL || (step->prNode->prNode == NULL && step->plNode->prNode == NULL)) {
+			if (n != spinCities->value()) {
+				output.append("<p>" + trUtf8("Step #%1").arg(n++) + "</p>");
+				outputMatrix(step->matrix,output,step->candidate.nRow,step->candidate.nCol);
+				if (step->alts)
+					output.append("<p class=\"hasalts\">" + trUtf8("This step has alternate candidates for branching.") + "</p>");
+				output.append("<p>&nbsp;</p>");
+			}
+		}
+		if (step->prNode->prNode != NULL)
+			step = step->prNode;
+		else if (step->plNode->prNode != NULL)
+			step = step->plNode;
+		else
+			break;
+	}
+	if (solver.isOptimal())
+		output.append("<p>" + trUtf8("Optimal path:") + "</p>");
+	else
+		output.append("<p>" + trUtf8("Resulting path:") + "</p>");
+	output.append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
+	output.append("<p>" + trUtf8("The price is <b>%1</b> units.").arg(step->price) + "</p>");
+	if (!solver.isOptimal()) {
+		output.append("<p>&nbsp;</p>");
+		output.append("<p>" + trUtf8("<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(trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()));
+
+	// Scrolling to the end of text.
+QTextCursor cursor(solutionText->textCursor());
+	cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
+	solutionText->setTextCursor(cursor);
+
+	enableSolutionActions();
+	tabWidget->setCurrentIndex(1);
+	QApplication::restoreOverrideCursor();
+}
+
+void MainWindow::dataChanged()
+{
+	setWindowModified(true);
+}
+
+void MainWindow::dataChanged(const QModelIndex &tl, const QModelIndex &br)
+{
+	setWindowModified(true);
+	if (settings->value("Autosize",true).toBool()) {
+		for (int k = tl.row(); k <= br.row(); k++)
+			taskView->resizeRowToContents(k);
+		for (int k = tl.column(); k <= br.column(); k++)
+			taskView->resizeColumnToContents(k);
+	}
+}
+
+void MainWindow::numCitiesChanged(int nCities)
+{
+	blockSignals(true);
+	spinCities->setValue(nCities);
+	blockSignals(false);
+}
+
+#ifndef QT_NO_PRINTER
+void MainWindow::printPreview(QPrinter *printer)
+{
+	solutionText->print(printer);
+}
+#endif // QT_NO_PRINTER
+
+void MainWindow::spinCitiesValueChanged(int n)
+{
+int count = tspmodel->numCities();
+	tspmodel->setNumCities(n);
+	if ((n > count) && settings->value("Autosize",true).toBool())
+		for (int k = count; k < n; k++) {
+			taskView->resizeColumnToContents(k);
+			taskView->resizeRowToContents(k);
+		}
+}
+
 void MainWindow::enableSolutionActions(bool enable)
 {
@@ -126,4 +480,49 @@
 	actionFilePrintPreview->setEnabled(enable);
 #endif // QT_NO_PRINTER
+}
+
+void MainWindow::initDocStyleSheet()
+{
+QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
+QColor hilight;
+	if (color.value() < 192)
+		hilight.setHsv(color.hue(),color.saturation(),127 + qRound(color.value() / 2));
+	else
+		hilight.setHsv(color.hue(),color.saturation(),color.value() / 2);
+	solutionText->document()->setDefaultStyleSheet("* {color: " + color.name() +";} p {margin: 0px 10px;} table {margin: 5px;} td {padding: 1px 5px;} .hasalts {color: " + hilight.name() + ";} .selected {color: #A00000; font-weight: bold;} .alternate {color: #008000; font-weight: bold;}");
+	solutionText->document()->setDefaultFont(settings->value("Output/Font",QFont(DEF_FONT_FAMILY,DEF_FONT_SIZE)).value<QFont>());
+}
+
+void MainWindow::loadLangList()
+{
+QSettings langinfo(PATH_I18N"/languages.ini",QSettings::IniFormat);
+#if QT_VERSION >= 0x040500
+	// In Qt < 4.5 QSettings doesn't have method setIniCodec.
+	langinfo.setIniCodec("UTF-8");
+#endif
+QDir dir(PATH_I18N,"*.qm",QDir::Name | QDir::IgnoreCase,QDir::Files);
+	if (!dir.exists())
+		return;
+QFileInfoList langs = dir.entryInfoList();
+	if (langs.size() <= 0)
+		return;
+QAction *a;
+	for (int k = 0; k < langs.size(); k++) {
+		QFileInfo lang = langs.at(k);
+		if (!lang.completeBaseName().startsWith("qt_") && lang.completeBaseName().compare("en")) {
+#if QT_VERSION >= 0x040500
+			a = menuSettingsLanguage->addAction(langinfo.value(lang.completeBaseName() + "/NativeName",lang.completeBaseName()).toString());
+#else
+			// We use Name if Qt < 4.5 because NativeName is in UTF-8, QSettings
+			// reads .ini file as ASCII and there is no way to set file encoding.
+			a = menuSettingsLanguage->addAction(langinfo.value(lang.completeBaseName() + "/Name",lang.completeBaseName()).toString());
+#endif
+			a->setData(lang.completeBaseName());
+			a->setCheckable(true);
+			a->setActionGroup(groupSettingsLanguageList);
+			if (settings->value("Language",QLocale::system().name()).toString().startsWith(lang.completeBaseName()))
+				a->setChecked(true);
+		}
+	}
 }
 
@@ -176,33 +575,4 @@
 }
 
-void MainWindow::initDocStyleSheet()
-{
-QColor color = settings->value("Output/Color",DEF_FONT_COLOR).value<QColor>();
-QColor hilight;
-	if (color.value() < 192)
-		hilight.setHsv(color.hue(),color.saturation(),127 + qRound(color.value() / 2));
-	else
-		hilight.setHsv(color.hue(),color.saturation(),color.value() / 2);
-	solutionText->document()->setDefaultStyleSheet("* {color: " + color.name() +";} p {margin: 0px 10px;} table {margin: 5px;} td {padding: 1px 5px;} .hasalts {color: " + hilight.name() + ";} .selected {color: #A00000; font-weight: bold;} .alternate {color: #008000; font-weight: bold;}");
-	solutionText->document()->setDefaultFont(settings->value("Output/Font",QFont(DEF_FONT_FAMILY,DEF_FONT_SIZE)).value<QFont>());
-}
-
-void MainWindow::setFileName(QString fileName)
-{
-	this->fileName = fileName;
-	setWindowTitle(QString("%1[*] - %2").arg(QFileInfo(fileName).completeBaseName()).arg(trUtf8("Travelling Salesman Problem")));
-}
-
-void MainWindow::spinCitiesValueChanged(int n)
-{
-int count = tspmodel->numCities();
-	tspmodel->setNumCities(n);
-	if ((n > count) && settings->value("Autosize",true).toBool())
-		for (int k = count; k < n; k++) {
-			taskView->resizeColumnToContents(k);
-			taskView->resizeRowToContents(k);
-		}
-}
-
 bool MainWindow::maybeSave()
 {
@@ -218,115 +588,23 @@
 }
 
-void MainWindow::actionFileNewTriggered()
-{
-	if (!maybeSave())
-		return;
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-	tspmodel->clear();
-	setFileName();
-	setWindowModified(false);
-	tabWidget->setCurrentIndex(0);
-	solutionText->clear();
-	enableSolutionActions(false);
-	QApplication::restoreOverrideCursor();
-}
-
-void MainWindow::actionFileOpenTriggered()
-{
-	if (!maybeSave())
-		return;
-QFileDialog od(this);
-	od.setAcceptMode(QFileDialog::AcceptOpen);
-	od.setFileMode(QFileDialog::ExistingFile);
-QStringList filters(trUtf8("All Supported Formats") + " (*.tspt *.zkt)");
-	filters.append(trUtf8("%1 Task Files").arg("TSPSG") + " (*.tspt)");
-	filters.append(trUtf8("%1 Task Files").arg("ZKomModRd") + " (*.zkt)");
-	filters.append(trUtf8("All Files") + " (*)");
-	od.setNameFilters(filters);
-	if (od.exec() != QDialog::Accepted)
-		return;
-QStringList files = od.selectedFiles();
-	if (files.empty())
-		return;
-	if (!tspmodel->loadTask(files.first()))
-		return;
-	setFileName(files.first());
-	tabWidget->setCurrentIndex(0);
-	setWindowModified(false);
-	solutionText->clear();
-	enableSolutionActions(false);
-}
-
-void MainWindow::actionFileSaveTriggered()
-{
-	if ((fileName == trUtf8("Untitled") + ".tspt") || (!fileName.endsWith(".tspt",Qt::CaseInsensitive)))
-		saveTask();
-	else
-		if (tspmodel->saveTask(fileName))
-			setWindowModified(false);
-}
-
-void MainWindow::actionFileSaveAsTaskTriggered()
-{
-	saveTask();
-}
-
-void MainWindow::actionFileSaveAsSolutionTriggered()
-{
-static QString selectedFile;
-	if (selectedFile.isEmpty())
-#ifndef QT_NO_PRINTER
-		selectedFile = "solution.pdf";
-#else
-		selectedFile = "solution.html";
-#endif // QT_NO_PRINTER
-QFileDialog sd(this);
-	sd.setAcceptMode(QFileDialog::AcceptSave);
-QStringList filters;
-#ifndef QT_NO_PRINTER
-	filters.append(trUtf8("PDF Files") + "(*.pdf)");
-#endif
-	filters.append(trUtf8("HTML Files") + " (*.html *.htm)");
-#if QT_VERSION >= 0x040500
-	filters.append(trUtf8("OpenDocument Files") + " (*.odt)");
-#endif // QT_VERSION >= 0x040500
-	filters.append(trUtf8("All Files") + " (*)");
-	sd.setNameFilters(filters);
-	sd.selectFile(selectedFile);
-	if (sd.exec() != QDialog::Accepted)
-		return;
-QStringList files = sd.selectedFiles();
-	if (files.empty())
-		return;
-	selectedFile = files.first();
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-#ifndef QT_NO_PRINTER
-	if (selectedFile.endsWith(".pdf",Qt::CaseInsensitive)) {
-QPrinter printer(QPrinter::HighResolution);
-		printer.setOutputFormat(QPrinter::PdfFormat);
-		printer.setOutputFileName(selectedFile);
-		solutionText->document()->print(&printer);
-		QApplication::restoreOverrideCursor();
-		return;
-	}
-#endif
-#if QT_VERSION >= 0x040500
-QTextDocumentWriter dw(selectedFile);
-	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
-	// Qt < 4.5 has no QTextDocumentWriter class
-QFile file(selectedFile);
-	if (!file.open(QFile::WriteOnly)) {
-		QApplication::restoreOverrideCursor();
-		return;
-	}
-QTextStream ts(&file);
-	ts.setCodec(QTextCodec::codecForName("UTF-8"));
-	ts << solutionText->document()->toHtml("UTF-8");
-	file.close();
-#endif // QT_VERSION >= 0x040500
-	QApplication::restoreOverrideCursor();
+void MainWindow::outputMatrix(tMatrix matrix, QStringList &output, int nRow, int nCol)
+{
+int n = spinCities->value();
+QString line="";
+	output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
+	for (int r = 0; r < n; r++) {
+		line = "<tr>";
+		for (int c = 0; c < n; c++) {
+			if (matrix[r][c] == INFINITY)
+				line += "<td align=\"center\">"INFSTR"</td>";
+			else if ((r == nRow) && (c == nCol))
+				line += "<td align=\"center\" class=\"selected\">" + QVariant(matrix[r][c]).toString() + "</td>";
+			else
+				line += "<td align=\"center\">" + QVariant(matrix[r][c]).toString() + "</td>";
+		}
+		line += "</tr>";
+		output.append(line);
+	}
+	output.append("</table>");
 }
 
@@ -355,281 +633,7 @@
 }
 
-void MainWindow::actionSettingsPreferencesTriggered()
-{
-SettingsDialog sd(this);
-	if (sd.exec() != QDialog::Accepted)
-		return;
-	if (sd.colorChanged() || sd.fontChanged()) {
-		initDocStyleSheet();
-		if (!output.isEmpty() && sd.colorChanged() && (QMessageBox(QMessageBox::Question,trUtf8("Settings Changed"),trUtf8("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();
-		}
-	}
-}
-
-#ifndef QT_NO_PRINTER
-void MainWindow::printPreview(QPrinter *printer)
-{
-	solutionText->print(printer);
-}
-
-void MainWindow::actionFilePrintPreviewTriggered()
-{
-QPrintPreviewDialog ppd(printer, this);
-    connect(&ppd,SIGNAL(paintRequested(QPrinter *)),SLOT(printPreview(QPrinter *)));
-    ppd.exec();
-}
-
-void MainWindow::actionFilePrintTriggered()
-{
-QPrintDialog pd(printer,this);
-#if QT_VERSION >= 0x040500
-	// No such methods in Qt < 4.5
-	pd.setOption(QAbstractPrintDialog::PrintSelection,false);
-	pd.setOption(QAbstractPrintDialog::PrintPageRange,false);
-#endif
-	if (pd.exec() != QDialog::Accepted)
-		return;
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-	solutionText->document()->print(printer);
-	QApplication::restoreOverrideCursor();
-}
-#endif // QT_NO_PRINTER
-
-void MainWindow::buttonRandomClicked()
-{
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-	tspmodel->randomize();
-	QApplication::restoreOverrideCursor();
-}
-
-void MainWindow::buttonBackToTaskClicked()
-{
-	tabWidget->setCurrentIndex(0);
-}
-
-void MainWindow::outputMatrix(tMatrix matrix, QStringList &output, int nRow, int nCol)
-{
-int n = spinCities->value();
-QString line="";
-	output.append("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">");
-	for (int r = 0; r < n; r++) {
-		line = "<tr>";
-		for (int c = 0; c < n; c++) {
-			if (matrix[r][c] == INFINITY)
-				line += "<td align=\"center\">"INFSTR"</td>";
-			else if ((r == nRow) && (c == nCol))
-				line += "<td align=\"center\" class=\"selected\">" + QVariant(matrix[r][c]).toString() + "</td>";
-			else
-				line += "<td align=\"center\">" + QVariant(matrix[r][c]).toString() + "</td>";
-		}
-		line += "</tr>";
-		output.append(line);
-	}
-	output.append("</table>");
-}
-
-void MainWindow::buttonSolveClicked()
-{
-tMatrix matrix;
-QList<double> row;
-int n = spinCities->value();
-bool ok;
-	for (int r = 0; r < n; r++) {
-		row.clear();
-		for (int c = 0; c < n; c++) {
-			row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
-			if (!ok) {
-				QMessageBox(QMessageBox::Critical,trUtf8("Data error"),trUtf8("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1),QMessageBox::Ok,this).exec();
-				return;
-			}
-		}
-		matrix.append(row);
-	}
-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>" + trUtf8("Variant #%1").arg(spinVariant->value()) + "</p>");
-	output.append("<p>" + trUtf8("Task:") + "</p>");
-	outputMatrix(matrix,output);
-	output.append("<hr>");
-	output.append("<p>" + trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()) + "</p>");
-sStep *step = root;
-	n = 1;
-	while (n <= spinCities->value()) {
-		if (step->prNode->prNode != NULL || (step->prNode->prNode == NULL && step->plNode->prNode == NULL)) {
-			if (n != spinCities->value()) {
-				output.append("<p>" + trUtf8("Step #%1").arg(n++) + "</p>");
-				outputMatrix(step->matrix,output,step->candidate.nRow,step->candidate.nCol);
-				if (step->alts)
-					output.append("<p class=\"hasalts\">" + trUtf8("This step has alternate candidates for branching.") + "</p>");
-				output.append("<p>&nbsp;</p>");
-			}
-		}
-		if (step->prNode->prNode != NULL)
-			step = step->prNode;
-		else if (step->plNode->prNode != NULL)
-			step = step->plNode;
-		else
-			break;
-	}
-	if (solver.isOptimal())
-		output.append("<p>" + trUtf8("Optimal path:") + "</p>");
-	else
-		output.append("<p>" + trUtf8("Resulting path:") + "</p>");
-	output.append("<p>&nbsp;&nbsp;" + solver.getSortedPath() + "</p>");
-	output.append("<p>" + trUtf8("The price is <b>%1</b> units.").arg(step->price) + "</p>");
-	if (!solver.isOptimal()) {
-		output.append("<p>&nbsp;</p>");
-		output.append("<p>" + trUtf8("<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(trUtf8("Solution of Variant #%1 task").arg(spinVariant->value()));
-
-	// Scrolling to the end of text.
-QTextCursor cursor(solutionText->textCursor());
-	cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
-	solutionText->setTextCursor(cursor);
-
-	enableSolutionActions();
-	tabWidget->setCurrentIndex(1);
-	QApplication::restoreOverrideCursor();
-}
-
-void MainWindow::actionHelpAboutTriggered()
-{
-//! \todo TODO: Normal about window :-)
-QString about = QString::fromUtf8("TSPSG: TSP Solver and Generator\n");
-	about += QString::fromUtf8("    Version: "BUILD_VERSION"\n");
-	about += QString::fromUtf8("    Copyright (C) 2007-%1 Lёppa <contacts[at]oleksii[dot]name>\n").arg(QDate::currentDate().toString("yyyy"));
-	about += QString::fromUtf8("Target OS: %1\n").arg(OS);
-	about += "Qt library:\n";
-	about += QString::fromUtf8("    Compile time: %1\n").arg(QT_VERSION_STR);
-	about += QString::fromUtf8("    Runtime: %1\n").arg(qVersion());
-	about += QString::fromUtf8("Built on %1 at %2\n").arg(__DATE__).arg(__TIME__);
-	about += QString::fromUtf8(VERSIONID"\n\n");
-	about += QString::fromUtf8("Algorithm: %1\n").arg(CTSPSolver::getVersionId());
-	about += "\n";
-	about += "TSPSG is licensed under the terms of the GNU General Public License. You should have received a copy of the GNU General Public License along with TSPSG.";
-	QMessageBox(QMessageBox::Information,"About",about,QMessageBox::Ok,this).exec();
-}
-
-void MainWindow::loadLangList()
-{
-QSettings langinfo(PATH_I18N"/languages.ini",QSettings::IniFormat);
-#if QT_VERSION >= 0x040500
-	// In Qt < 4.5 QSettings doesn't have method setIniCodec.
-	langinfo.setIniCodec("UTF-8");
-#endif
-QDir dir(PATH_I18N,"*.qm",QDir::Name | QDir::IgnoreCase,QDir::Files);
-	if (!dir.exists())
-		return;
-QFileInfoList langs = dir.entryInfoList();
-	if (langs.size() <= 0)
-		return;
-QAction *a;
-	for (int k = 0; k < langs.size(); k++) {
-		QFileInfo lang = langs.at(k);
-		if (!lang.completeBaseName().startsWith("qt_") && lang.completeBaseName().compare("en")) {
-#if QT_VERSION >= 0x040500
-			a = menuSettingsLanguage->addAction(langinfo.value(lang.completeBaseName() + "/NativeName",lang.completeBaseName()).toString());
-#else
-			// We use Name if Qt < 4.5 because NativeName is in UTF-8, QSettings
-			// reads .ini file as ASCII and there is no way to set file encoding.
-			a = menuSettingsLanguage->addAction(langinfo.value(lang.completeBaseName() + "/Name",lang.completeBaseName()).toString());
-#endif
-			a->setData(lang.completeBaseName());
-			a->setCheckable(true);
-			a->setActionGroup(groupSettingsLanguageList);
-			if (settings->value("Language",QLocale::system().name()).toString().startsWith(lang.completeBaseName()))
-				a->setChecked(true);
-		}
-	}
-}
-
-void MainWindow::actionSettingsLanguageAutodetectTriggered(bool checked)
-{
-	if (checked) {
-		settings->remove("Language");
-		QMessageBox(QMessageBox::Information,trUtf8("Language change"),trUtf8("Language will be autodetected on next application start."),QMessageBox::Ok,this).exec();
-	} else
-		settings->setValue("Language",groupSettingsLanguageList->checkedAction()->data().toString());
-}
-
-void MainWindow::groupSettingsLanguageListTriggered(QAction *action)
-{
-	if (actionSettingsLanguageAutodetect->isChecked()) {
-		// We have language autodetection. It needs to be disabled to change language.
-		if (QMessageBox(QMessageBox::Question,trUtf8("Language change"),trUtf8("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) {
-			actionSettingsLanguageAutodetect->trigger();
-		} else
-			return;
-	}
-bool untitled = (fileName == trUtf8("Untitled") + ".tspt");
-	if (loadLanguage(action->data().toString())) {
-		settings->setValue("Language",action->data().toString());
-		retranslateUi(this);
-		if (untitled)
-			setFileName();
-	}
-}
-
-/*!
- * \brief Handles Main Window close event.
- * \param event Close event.
- *
- *  Checks whether or not a current task was saved and asks for saving if not.
- *  Saves TSPSG settings.
- */
-void MainWindow::closeEvent(QCloseEvent *event)
-{
-	if (!maybeSave()) {
-		event->ignore();
-		return;
-	}
-	settings->setValue("NumCities",spinCities->value());
-#ifndef Q_OS_WINCE
-	// Saving windows state
-	if (settings->value("SavePos",false).toBool()) {
-		settings->beginGroup("MainWindow");
-		settings->setValue("Maximized",isMaximized());
-		if (!isMaximized()) {
-			settings->setValue("Size",size());
-			settings->setValue("Position",pos());
-		}
-		settings->endGroup();
-	}
-#endif // Q_OS_WINCE
-	QMainWindow::closeEvent(event);
-}
-
-void MainWindow::dataChanged()
-{
-	setWindowModified(true);
-}
-
-void MainWindow::dataChanged(const QModelIndex &tl, const QModelIndex &br)
-{
-	setWindowModified(true);
-	if (settings->value("Autosize",true).toBool()) {
-		for (int k = tl.row(); k <= br.row(); k++)
-			taskView->resizeRowToContents(k);
-		for (int k = tl.column(); k <= br.column(); k++)
-			taskView->resizeColumnToContents(k);
-	}
-}
-
-void MainWindow::numCitiesChanged(int nCities)
-{
-	blockSignals(true);
-	spinCities->setValue(nCities);
-	blockSignals(false);
-}
+void MainWindow::setFileName(QString fileName)
+{
+	this->fileName = fileName;
+	setWindowTitle(QString("%1[*] - %2").arg(QFileInfo(fileName).completeBaseName()).arg(trUtf8("Travelling Salesman Problem")));
+}
Index: /trunk/src/mainwindow.h
===================================================================
--- /trunk/src/mainwindow.h	(revision 66)
+++ /trunk/src/mainwindow.h	(revision 67)
@@ -1,9 +1,10 @@
 /*!
- * \class MainWindow
- * \brief Class for handling Main Window UI and logic.
+ * \file mainwindow.h
  * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
  *  $Id$
  *  $URL$
+ *
+ * \brief Defines MainWindow class.
  *
  *  <b>TSPSG: TSP Solver and Generator</b>
@@ -28,9 +29,4 @@
 #define MAINWINDOW_H
 
-/*!
- * \file mainwindow.h
- * \brief Defines MainWindow class.
- */
-
 #include "globals.h"
 
@@ -41,4 +37,8 @@
 #include "tspmodel.h"
 
+/*!
+ * \brief Class for handling Main Window UI and logic.
+ * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+ */
 class MainWindow: public QMainWindow, Ui::MainWindow
 {
@@ -50,4 +50,5 @@
 
 private slots:
+// Actions
 	void actionFileNewTriggered();
 	void actionFileOpenTriggered();
@@ -55,38 +56,43 @@
 	void actionFileSaveAsTaskTriggered();
 	void actionFileSaveAsSolutionTriggered();
+#ifndef QT_NO_PRINTER
+	void actionFilePrintPreviewTriggered();
+	void actionFilePrintTriggered();
+#endif // QT_NO_PRINTER
 	void actionSettingsPreferencesTriggered();
 	void actionSettingsLanguageAutodetectTriggered(bool);
 	void groupSettingsLanguageListTriggered(QAction *);
 	void actionHelpAboutTriggered();
+// Buttons
+	void buttonBackToTaskClicked();
+	void buttonRandomClicked();
+	void buttonSolveClicked();
+
 	void dataChanged();
 	void dataChanged(const QModelIndex &, const QModelIndex &);
+	void numCitiesChanged(int);
 #ifndef QT_NO_PRINTER
 	void printPreview(QPrinter *printer);
-	void actionFilePrintPreviewTriggered();
-	void actionFilePrintTriggered();
 #endif // QT_NO_PRINTER
-	void buttonSolveClicked();
-	void buttonRandomClicked();
-	void buttonBackToTaskClicked();
 	void spinCitiesValueChanged(int);
-	void numCitiesChanged(int);
 
 private:
-	QSettings *settings;
+	QString fileName;
+	QActionGroup *groupSettingsLanguageList;
+	QStringList output;
 #ifndef QT_NO_PRINTER
 	QPrinter *printer;
 #endif // QT_NO_PRINTER
+	QSettings *settings;
 	CTSPModel *tspmodel;
-	QString fileName;
-	QActionGroup *groupSettingsLanguageList;
-	QStringList output;
+
 	void enableSolutionActions(bool enable = true);
 	void initDocStyleSheet();
+	void loadLangList();
 	bool loadLanguage(QString lang = "");
-	void loadLangList();
 	bool maybeSave();
 	void outputMatrix(tMatrix, QStringList &, int nRow = -1, int nCol = -1);
+	bool saveTask();
 	void setFileName(QString fileName = trUtf8("Untitled") + ".tspt");
-	bool saveTask();
 };
 
Index: /trunk/src/os.h
===================================================================
--- /trunk/src/os.h	(revision 66)
+++ /trunk/src/os.h	(revision 67)
@@ -1,8 +1,15 @@
-/*
- *  TSPSG: TSP Solver and Generator
- *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+/*!
+ * \file os.h
+ * \author Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
  *  $Id$
  *  $URL$
+ *
+ * \warning Arch detection works only for <b>GNU C</b>, <b>Visual Studio</b>, <b>Intel C/C++</b> and <b>MinGW32</b> compilers.
+ *  OS detection should work for any compiler.
+ *
+ * \brief Contains TSPSG target CPU architecture and OS detection.
+ *
+ *  <b>TSPSG: TSP Solver and Generator</b>
  *
  *  This file is part of TSPSG.
@@ -24,13 +31,4 @@
 #ifndef OS_H
 #define OS_H
-
-/*!
- * \file os.h
- * \brief This file contains TSPSG target CPU architecture and OS detection.
- *
- *  Arch detection works only for <b>GNU C</b>, <b>Visual Studio</b>, <b>Intel C/C++</b> and <b>MinGW32</b> compilers.
- *
- *  OS detection should work for any compiler.
- */
 
 // Some target arch detection.
Index: /trunk/src/resource.h
===================================================================
--- /trunk/src/resource.h	(revision 66)
+++ /trunk/src/resource.h	(revision 67)
@@ -1,9 +1,13 @@
-/*
- *  TSPSG: TSP Solver and Generator
- *  Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+/*!
+ * \file resource.h
+ * \author Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
  *  $Id$
  *  $URL$
  *
+ * \brief Contains windows resource IDs.
+ *
+ *  <b>TSPSG: TSP Solver and Generator</b>
+ *  
  *  This file is part of TSPSG.
  *
@@ -24,9 +28,4 @@
 #include "version.h"
 
-/*!
- * \file resource.h
- * \brief This file contains windows resource IDs.
- */
-
 //! Version information resource ID
 #define VS_VERSION_INFO 1
Index: /trunk/src/settingsdialog.cpp
===================================================================
--- /trunk/src/settingsdialog.cpp	(revision 66)
+++ /trunk/src/settingsdialog.cpp	(revision 67)
@@ -188,4 +188,24 @@
 }
 
+/*!
+ * \brief Indicates whether or not the font color has been changed.
+ * \return \c true if font color changed, otherwise \c false.
+ */
+bool SettingsDialog::colorChanged() const
+{
+	return newColor;
+}
+
+/*!
+ * \brief Indicates whether or not the font properties have been changed.
+ * \return \c true if font properties changed, otherwise \c false.
+ */
+bool SettingsDialog::fontChanged() const
+{
+	return newFont;
+}
+
+/* Privates **********************************************************/
+
 void SettingsDialog::accept()
 {
@@ -205,4 +225,13 @@
 }
 
+void SettingsDialog::buttonColorClicked()
+{
+QColor color = QColorDialog::getColor(this->color,this);
+	if (color.isValid() && (this->color != color)) {
+		this->color = color;
+		newColor = true;
+	}
+}
+
 void SettingsDialog::buttonFontClicked()
 {
@@ -213,31 +242,4 @@
 		newFont = true;
 	}
-}
-
-void SettingsDialog::buttonColorClicked()
-{
-QColor color = QColorDialog::getColor(this->color,this);
-	if (color.isValid() && (this->color != color)) {
-		this->color = color;
-		newColor = true;
-	}
-}
-
-/*!
- * \brief Indicates whether or not the font color has been changed.
- * \return \c true if font color changed, otherwise \c false.
- */
-bool SettingsDialog::colorChanged() const
-{
-	return newColor;
-}
-
-/*!
- * \brief Indicates whether or not the font properties have been changed.
- * \return \c true if font properties changed, otherwise \c false.
- */
-bool SettingsDialog::fontChanged() const
-{
-	return newFont;
 }
 
Index: /trunk/src/settingsdialog.h
===================================================================
--- /trunk/src/settingsdialog.h	(revision 66)
+++ /trunk/src/settingsdialog.h	(revision 67)
@@ -1,9 +1,10 @@
 /*!
- * \class SettingsDialog
- * \brief Class for handling Settings Dialog UI and logic.
+ * \file settingsdialog.h
  * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
  *  $Id$
  *  $URL$
+ *
+ * \brief Defines SettingsDialog class.
  *
  *  <b>TSPSG: TSP Solver and Generator</b>
@@ -28,13 +29,12 @@
 #define SETTINGSDIALOG_H
 
-/*!
- * \file settingsdialog.h
- * \brief Defines SettingsDialog class.
- */
-
 #include "globals.h"
 
 #include "ui_settingsdialog.h"
 
+/*!
+ * \brief Class for handling Settings Dialog UI and logic.
+ * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+ */
 class SettingsDialog: public QDialog, Ui::SettingsDialog
 {
@@ -52,10 +52,10 @@
 	QSettings *settings;
 #ifndef Q_OS_WINCE
-	bool event(QEvent *);
-
 	QCheckBox *cbSaveState;
 	QLabel *imgIcon;
 	QLabel *labelHint;
 	QFrame *lineVertical;
+
+	bool event(QEvent *);
 #endif // Q_OS_WINCE
 
Index: /trunk/src/tspmodel.cpp
===================================================================
--- /trunk/src/tspmodel.cpp	(revision 66)
+++ /trunk/src/tspmodel.cpp	(revision 67)
@@ -24,4 +24,8 @@
 #include "tspmodel.h"
 
+/*!
+ * \brief Class constructor.
+ * \param parent The parent of the table model.
+ */
 CTSPModel::CTSPModel(QObject *parent)
 	: QAbstractTableModel(parent), nCities(0)
@@ -30,30 +34,39 @@
 }
 
-inline int CTSPModel::rand(int min, int max) const
-{
-	return min + (int)(((float)qrand() / RAND_MAX) * max);
-}
-
-int CTSPModel::rowCount(const QModelIndex &) const
+/*!
+ * \brief Resets the table, setting all its elements to 0.
+ *
+ * \sa randomize()
+ */
+void CTSPModel::clear()
+{
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if (r != c)
+				table[r][c] = 0;
+	emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
+}
+
+/*!
+ * \brief Returns the column count in the table.
+ * \return Number of columns in the table.
+ *
+ *  Actually, this function returns the number of cities in the current task.
+ *
+ * \sa numCities(), rowCount()
+ */
+int CTSPModel::columnCount(const QModelIndex &) const
 {
 	return nCities;
 }
 
-int CTSPModel::columnCount(const QModelIndex &) const
-{
-	return nCities;
-}
-
-QVariant CTSPModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-	if (role == Qt::DisplayRole) {
-		if (orientation == Qt::Vertical)
-			return trUtf8("City %1").arg(section + 1);
-		else
-			return trUtf8("%1").arg(section + 1);
-	}
-	return QVariant();
-}
-
+/*!
+ * \brief Returns the data stored under the given \a role for the item referred to by the \a index.
+ * \param index An item index to get data from.
+ * \param role The role to get data for.
+ * \return Corresponding data.
+ *
+ * \sa setData(), headerData()
+ */
 QVariant CTSPModel::data(const QModelIndex &index, int role) const
 {
@@ -71,5 +84,5 @@
 				return trUtf8(INFSTR);
 			else
-				// HACK: Converting to string to prevent spinbox in edit mode
+//! \hack HACK: Converting to string to prevent spinbox in edit mode
 				return QVariant(table[index.row()][index.column()]).toString();
 		else
@@ -80,4 +93,214 @@
 }
 
+/*!
+ * \brief Returns the item flags for the given \a index.
+ * \param index An item index to get flags from.
+ * \return Corresponding item flags.
+ */
+Qt::ItemFlags CTSPModel::flags(const QModelIndex &index) const
+{
+Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+	if (index.row() != index.column())
+		flags |= Qt::ItemIsEditable;
+	return flags;
+}
+
+/*!
+ * \brief Returns the data for the given \a role and \a section in the header with the specified \a orientation.
+ * \param section The section to get header data for.
+ * \param orientation The orientation to get header data for.
+ * \param role The role to get header data for.
+ * \return Corresponding header data.
+ *
+ *  For horizontal headers, the section number corresponds to the column number of items shown beneath it. For vertical headers, the section number typically to the row number of items shown alongside it.
+ */
+QVariant CTSPModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+	if (role == Qt::DisplayRole) {
+		if (orientation == Qt::Vertical)
+			return trUtf8("City %1").arg(section + 1);
+		else
+			return trUtf8("%1").arg(section + 1);
+	}
+	return QVariant();
+}
+
+/*!
+ * \brief Loads a task from \a fname.
+ * \param fname The name of the file to be loaded.
+ * \return \c true on success, otherwise \c false.
+ *
+ * \sa saveTask()
+ */
+bool CTSPModel::loadTask(QString fname)
+{
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+QFile f(fname);
+	if (!f.open(QIODevice::ReadOnly)) {
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),QString(trUtf8("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+QDataStream ds(&f);
+	ds.setVersion(QDataStream::Qt_4_4);
+quint32 sig;
+	ds >> sig;
+	if (loadError(ds.status())) {
+		return false;
+	}
+	ds.device()->reset();
+	if (sig == TSPT) {
+		if (!loadTSPT(&ds)) {
+			f.close();
+			return false;
+		}
+	} else if ((sig >> 16) == ZKT) {
+		if (!loadZKT(&ds)) {
+			f.close();
+			return false;
+		}
+	} else {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
+		return false;
+	}
+	f.close();
+	QApplication::restoreOverrideCursor();
+	return true;
+}
+
+/*!
+ * \brief Returns the number of cities.
+ * \return Number of cities in the current task.
+ *
+ * \sa columnCount(), rowCount(), setNumCities()
+ */
+quint16 CTSPModel::numCities() const
+{
+	return nCities;
+}
+
+/*!
+ * \brief Randomizes the table by setting all its values to random ones.
+ *
+ *  Uses TSPSG settings to determine random values range.
+ *
+ * \sa clear()
+ */
+void CTSPModel::randomize()
+{
+int randMin = settings->value("MinCost",DEF_RAND_MIN).toInt();
+int randMax = settings->value("MaxCost",DEF_RAND_MAX).toInt();
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if (r != c)
+				table[r][c] = rand(randMin,randMax);
+	emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
+}
+
+/*!
+ * \brief Returns the row count in the table.
+ * \return Number of rows in the table.
+ *
+ *  Actually, this function returns the number of cities in the current task.
+ *
+ * \sa columnCount(), numCities()
+ */
+int CTSPModel::rowCount(const QModelIndex &) const
+{
+	return nCities;
+}
+
+/*!
+ * \brief Saves current task to \a fname.
+ * \param fname The name of the file to seve to.
+ * \return \c true on success, otherwise \c false.
+ *
+ * \sa loadTask()
+ */
+bool CTSPModel::saveTask(QString fname)
+{
+	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+QFile f(fname);
+	if (!f.open(QIODevice::WriteOnly)) {
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),QString(trUtf8("Unable to create task file.\nError: %1\nMaybe, file is read-only?")).arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+QDataStream ds(&f);
+	ds.setVersion(QDataStream::Qt_4_4);
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// File signature
+	ds << TSPT;
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// File version
+	ds << TSPT_VERSION;
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// File metadata version
+	ds << TSPT_META_VERSION;
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// Metadata
+	ds << OSID;
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// Number of cities
+	ds << nCities;
+	if (f.error() != QFile::NoError) {
+		f.close();
+		QApplication::restoreOverrideCursor();
+		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+		return false;
+	}
+	// Costs
+	for (int r = 0; r < nCities; r++)
+		for (int c = 0; c < nCities; c++)
+			if (r != c) {
+				ds << table[r][c];
+				if (f.error() != QFile::NoError) {
+					f.close();
+					QApplication::restoreOverrideCursor();
+					QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
+					return false;
+				}
+			}
+	f.close();
+	QApplication::restoreOverrideCursor();
+	return true;
+}
+
+/*!
+ * \brief Sets the \a role data for the item at \a index to \a value.
+ * \param index The index of the item to set data at.
+ * \param value The value of the item data to be set.
+ * \param role The role of the item to set data for.
+ * \return \c true on success, otherwise \c false.
+ *
+ * \sa data()
+ */
 bool CTSPModel::setData(const QModelIndex &index, const QVariant &value, int role)
 {
@@ -101,17 +324,10 @@
 }
 
-Qt::ItemFlags CTSPModel::flags(const QModelIndex &index) const
-{
-Qt::ItemFlags flags = QAbstractItemModel::flags(index);
-	if (index.row() != index.column())
-		flags |= Qt::ItemIsEditable;
-	return flags;
-}
-
-quint16 CTSPModel::numCities() const
-{
-	return nCities;
-}
-
+/*!
+ * \brief Sets number of cities in the current task to \a n.
+ * \param n Number of cities to set to.
+ *
+ * \sa numCities()
+ */
 void CTSPModel::setNumCities(int n)
 {
@@ -130,12 +346,5 @@
 }
 
-void CTSPModel::clear()
-{
-	for (int r = 0; r < nCities; r++)
-		for (int c = 0; c < nCities; c++)
-			if (r != c)
-				table[r][c] = 0;
-	emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
-}
+/* Privates **********************************************************/
 
 inline bool CTSPModel::loadError(QDataStream::Status status)
@@ -152,42 +361,4 @@
 	QApplication::restoreOverrideCursor();
 	QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + err,QMessageBox::Ok).exec();
-	return true;
-}
-
-bool CTSPModel::loadTask(QString fname)
-{
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-QFile f(fname);
-	if (!f.open(QIODevice::ReadOnly)) {
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),QString(trUtf8("Unable to open task file.\nError: %1")).arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-QDataStream ds(&f);
-	ds.setVersion(QDataStream::Qt_4_4);
-quint32 sig;
-	ds >> sig;
-	if (loadError(ds.status())) {
-		return false;
-	}
-	ds.device()->reset();
-	if (sig == TSPT) {
-		if (!loadTSPT(&ds)) {
-			f.close();
-			return false;
-		}
-	} else if ((sig >> 16) == ZKT) {
-		if (!loadZKT(&ds)) {
-			f.close();
-			return false;
-		}
-	} else {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Load"),trUtf8("Unable to load task:") + "\n" + trUtf8("Unknown file format or file is corrupted."),QMessageBox::Ok).exec();
-		return false;
-	}
-	f.close();
-	QApplication::restoreOverrideCursor();
 	return true;
 }
@@ -295,86 +466,6 @@
 }
 
-bool CTSPModel::saveTask(QString fname)
-{
-	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-QFile f(fname);
-	if (!f.open(QIODevice::WriteOnly)) {
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),QString(trUtf8("Unable to create task file.\nError: %1\nMaybe, file is read-only?")).arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-QDataStream ds(&f);
-	ds.setVersion(QDataStream::Qt_4_4);
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// File signature
-	ds << TSPT;
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// File version
-	ds << TSPT_VERSION;
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// File metadata version
-	ds << TSPT_META_VERSION;
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// Metadata
-	ds << OSID;
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// Number of cities
-	ds << nCities;
-	if (f.error() != QFile::NoError) {
-		f.close();
-		QApplication::restoreOverrideCursor();
-		QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-		return false;
-	}
-	// Costs
-	for (int r = 0; r < nCities; r++)
-		for (int c = 0; c < nCities; c++)
-			if (r != c) {
-				ds << table[r][c];
-				if (f.error() != QFile::NoError) {
-					f.close();
-					QApplication::restoreOverrideCursor();
-					QMessageBox(QMessageBox::Critical,trUtf8("Task Save"),trUtf8("Unable to save task.\nError: %1").arg(f.errorString()),QMessageBox::Ok).exec();
-					return false;
-				}
-			}
-	f.close();
-	QApplication::restoreOverrideCursor();
-	return true;
-}
-
-void CTSPModel::randomize()
-{
-int randMin = settings->value("MinCost",DEF_RAND_MIN).toInt();
-int randMax = settings->value("MaxCost",DEF_RAND_MAX).toInt();
-	for (int r = 0; r < nCities; r++)
-		for (int c = 0; c < nCities; c++)
-			if (r != c)
-				table[r][c] = rand(randMin,randMax);
-	emit dataChanged(index(0,0),index(nCities - 1,nCities - 1));
-}
+inline int CTSPModel::rand(int min, int max) const
+{
+	return min + (int)(((float)qrand() / RAND_MAX) * max);
+}
Index: /trunk/src/tspmodel.h
===================================================================
--- /trunk/src/tspmodel.h	(revision 66)
+++ /trunk/src/tspmodel.h	(revision 67)
@@ -1,9 +1,10 @@
 /*!
- * \class CTSPModel
+ * \file tspmodel.h
  * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
- * \brief This class implements table model for manipulating a task.
  *
  *  $Id$
  *  $URL$
+ *
+ * \brief Defines CTSPModel class.
  *
  *  <b>TSPSG: TSP Solver and Generator</b>
@@ -28,11 +29,10 @@
 #define TSPMODEL_H
 
-/*!
- * \file tspmodel.h
- * \brief Defines CTSPModel class.
- */
-
 #include "globals.h"
 
+/*!
+ * \brief This class implements table model for manipulating a task.
+ * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+ */
 class CTSPModel: public QAbstractTableModel
 {
@@ -55,4 +55,9 @@
 
 signals:
+	/*!
+	 * \brief This signal is emitted whenever the number of cities in the task changes.
+	 *
+	 * \sa setNumCities()
+	 */
 	void numCitiesChanged(int);
 
Index: /trunk/src/tspsolver.cpp
===================================================================
--- /trunk/src/tspsolver.cpp	(revision 66)
+++ /trunk/src/tspsolver.cpp	(revision 67)
@@ -30,41 +30,155 @@
 }
 
-void CTSPSolver::cleanup()
-{
-	route.clear();
-	mayNotBeOptimal = false;
-}
-
-double CTSPSolver::findMinInRow(int nRow, tMatrix matrix, int exc)
-{
-double min = INFINITY;
-	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;
-}
-
-double CTSPSolver::findMinInCol(int nCol, tMatrix matrix, int exr)
-{
-double min = INFINITY;
-	for (int k = 0; k < nCities; k++)
-		if ((k != exr) && (min > matrix.at(k).at(nCol)))
-			min = matrix.at(k).at(nCol);
-	return min == INFINITY ? 0 : min;
-}
-
-void CTSPSolver::subRow(tMatrix &matrix, int nRow, double val)
-{
-	for (int k = 0; k < nCities; k++)
-		if (k != nRow)
-			matrix[nRow][k] -= val;
-}
-
-void CTSPSolver::subCol(tMatrix &matrix, int nCol, double val)
-{
-	for (int k = 0; k < nCities; k++)
-		if (k != nCol)
-			matrix[k][nCol] -= val;
-}
+/*!
+ * \brief Returns the sorted optimal path, starting from City 1.
+ * \return A string, containing sorted optimal path.
+ */
+QString CTSPSolver::getSortedPath() const
+{
+	if (!root || route.isEmpty() || (route.size() != nCities))
+		return QString();
+
+int i = 0; // We start from City 1
+QString path = trUtf8("City %1").arg(1) + " -> ";
+	while ((i = route[i]) != 0) {
+		path += trUtf8("City %1").arg(i + 1) + " -> ";
+	}
+	// And finish in City 1, too
+	path += trUtf8("City %1").arg(1);
+
+	return path;
+}
+
+/*!
+ * \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.
+ *  In such cases this function returns \c false.
+ */
+bool CTSPSolver::isOptimal() const
+{
+	return !mayNotBeOptimal;
+}
+
+/*!
+ * \brief Solves the given task.
+ * \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)
+{
+	if (numCities <= 1)
+		return NULL;
+	cleanup();
+	nCities = numCities;
+QProgressDialog pd(parent);
+QProgressBar *pb = new QProgressBar(&pd);
+	pb->setAlignment(Qt::AlignCenter);
+	pb->setFormat(trUtf8("%v of %m parts found"));
+	pd.setBar(pb);
+	pd.setMaximum(nCities);
+	pd.setMinimumDuration(1000);
+	pd.setLabelText(trUtf8("Calculating optimal route..."));
+	pd.setWindowTitle(trUtf8("Solution Progress"));
+	pd.setWindowModality(Qt::ApplicationModal);
+	pd.setValue(0);
+
+sStep *step = new sStep();
+	step->matrix = task;
+	step->price = align(step->matrix);
+	root = step;
+
+sStep *left, *right;
+int nRow, nCol;
+bool firstStep = true;
+double check;
+	while (this->route.size() < nCities) {
+//		forbidden.clear();
+		step->alts = findCandidate(step->matrix,nRow,nCol);
+		while (hasSubCycles(nRow,nCol)) {
+//			forbidden[nRow] = nCol;
+			step->matrix[nRow][nCol] = INFINITY;
+			step->price += align(step->matrix);
+			step->alts = findCandidate(step->matrix,nRow,nCol);
+		}
+		if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
+			root = NULL;
+			break;
+		}
+
+		// Route with (nRow,nCol) path
+		right = new sStep();
+		right->matrix = step->matrix;
+		for (int k = 0; k < nCities; k++) {
+			if (k != nCol)
+				right->matrix[nRow][k] = INFINITY;
+			if (k != nRow)
+				right->matrix[k][nCol] = INFINITY;
+		}
+		right->price = step->price + align(right->matrix);
+		// Forbid selected route to exclude its reuse in next steps.
+		right->matrix[nCol][nRow] = INFINITY;
+		right->matrix[nRow][nCol] = INFINITY;
+
+		// Route without (nRow,nCol) path
+		left = new sStep();
+		left->matrix = step->matrix;
+		left->matrix[nRow][nCol] = INFINITY;
+ 		left->price = step->price + align(left->matrix);
+
+		step->candidate.nRow = nRow;
+		step->candidate.nCol = nCol;
+		step->plNode = left;
+		step->prNode = right;
+
+		if (right->price <= left->price) {
+			// Route with (nRow,nCol) path is cheaper
+			step = right;
+			this->route[nRow] = nCol;
+			pd.setValue(this->route.size());
+			if (firstStep) {
+				check = left->price;
+				firstStep = false;
+			}
+		} else {
+			// Route without (nRow,nCol) path is cheaper
+			step = left;
+			qApp->processEvents();
+			if (firstStep) {
+				check = right->price;
+				firstStep = false;
+			}
+		}
+	}
+
+	if (!root && !pd.wasCanceled()) {
+		pd.reset();
+		QMessageBox(QMessageBox::Warning,trUtf8("Solution Result"),trUtf8("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);
+	}
+	return root;
+}
+
+/* Privates **********************************************************/
 
 double CTSPSolver::align(tMatrix &matrix)
@@ -87,4 +201,10 @@
 	}
 	return r;
+}
+
+void CTSPSolver::cleanup()
+{
+	route.clear();
+	mayNotBeOptimal = false;
 }
 
@@ -112,4 +232,22 @@
 }
 
+double CTSPSolver::findMinInCol(int nCol, tMatrix matrix, int exr)
+{
+double min = INFINITY;
+	for (int k = 0; k < nCities; k++)
+		if ((k != exr) && (min > matrix.at(k).at(nCol)))
+			min = matrix.at(k).at(nCol);
+	return min == INFINITY ? 0 : min;
+}
+
+double CTSPSolver::findMinInRow(int nRow, tMatrix matrix, int exc)
+{
+double min = INFINITY;
+	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;
+}
+
 bool CTSPSolver::hasSubCycles(int nRow, int nCol)
 {
@@ -126,151 +264,15 @@
 }
 
-/*!
- * \brief Solves the given task.
- * \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)
-{
-	if (numCities <= 1)
-		return NULL;
-	cleanup();
-	nCities = numCities;
-QProgressDialog pd(parent);
-QProgressBar *pb = new QProgressBar(&pd);
-	pb->setAlignment(Qt::AlignCenter);
-	pb->setFormat(trUtf8("%v of %m parts found"));
-	pd.setBar(pb);
-	pd.setMaximum(nCities);
-	pd.setMinimumDuration(1000);
-	pd.setLabelText(trUtf8("Calculating optimal route..."));
-	pd.setWindowTitle(trUtf8("Solution Progress"));
-	pd.setWindowModality(Qt::ApplicationModal);
-	pd.setValue(0);
-
-sStep *step = new sStep();
-	step->matrix = task;
-	step->price = align(step->matrix);
-	root = step;
-
-sStep *left, *right;
-int nRow, nCol;
-bool firstStep = true;
-double check;
-	while (this->route.size() < nCities) {
-//		forbidden.clear();
-		step->alts = findCandidate(step->matrix,nRow,nCol);
-		while (hasSubCycles(nRow,nCol)) {
-//			forbidden[nRow] = nCol;
-			step->matrix[nRow][nCol] = INFINITY;
-			step->price += align(step->matrix);
-			step->alts = findCandidate(step->matrix,nRow,nCol);
-		}
-		if ((nRow == -1) || (nCol == -1) || pd.wasCanceled()) {
-			root = NULL;
-			break;
-		}
-
-		// Route with (nRow,nCol) path
-		right = new sStep();
-		right->matrix = step->matrix;
-		for (int k = 0; k < nCities; k++) {
-			if (k != nCol)
-				right->matrix[nRow][k] = INFINITY;
-			if (k != nRow)
-				right->matrix[k][nCol] = INFINITY;
-		}
-		right->price = step->price + align(right->matrix);
-		// Forbid selected route to exclude its reuse in next steps.
-		right->matrix[nCol][nRow] = INFINITY;
-		right->matrix[nRow][nCol] = INFINITY;
-
-		// Route without (nRow,nCol) path
-		left = new sStep();
-		left->matrix = step->matrix;
-		left->matrix[nRow][nCol] = INFINITY;
- 		left->price = step->price + align(left->matrix);
-
-		step->candidate.nRow = nRow;
-		step->candidate.nCol = nCol;
-		step->plNode = left;
-		step->prNode = right;
-
-		if (right->price <= left->price) {
-			// Route with (nRow,nCol) path is cheaper
-			step = right;
-			this->route[nRow] = nCol;
-			pd.setValue(this->route.size());
-			if (firstStep) {
-				check = left->price;
-				firstStep = false;
-			}
-		} else {
-			// Route without (nRow,nCol) path is cheaper
-			step = left;
-			qApp->processEvents();
-			if (firstStep) {
-				check = right->price;
-				firstStep = false;
-			}
-		}
-	}
-
-	if (!root && !pd.wasCanceled()) {
-		pd.reset();
-		QMessageBox(QMessageBox::Warning,trUtf8("Solution Result"),trUtf8("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);
-	}
-	return root;
-}
-
-/*!
- * \brief Returns the sorted optimal path, starting from City 1.
- * \return A string, containing sorted optimal path.
- */
-QString CTSPSolver::getSortedPath() const
-{
-	if (!root || route.isEmpty() || (route.size() != nCities))
-		return QString();
-
-int i = 0; // We start from City 1
-QString path = trUtf8("City %1").arg(1) + " -> ";
-	while ((i = route[i]) != 0) {
-		path += trUtf8("City %1").arg(i + 1) + " -> ";
-	}
-	// And finish in City 1, too
-	path += trUtf8("City %1").arg(1);
-
-	return path;
-}
-
-/*!
- * \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.
- *  In such cases this function returns \c false.
- */
-bool CTSPSolver::isOptimal() const
-{
-	return !mayNotBeOptimal;
-}
+void CTSPSolver::subCol(tMatrix &matrix, int nCol, double val)
+{
+	for (int k = 0; k < nCities; k++)
+		if (k != nCol)
+			matrix[k][nCol] -= val;
+}
+
+void CTSPSolver::subRow(tMatrix &matrix, int nRow, double val)
+{
+	for (int k = 0; k < nCities; k++)
+		if (k != nRow)
+			matrix[nRow][k] -= val;
+}
Index: /trunk/src/tspsolver.h
===================================================================
--- /trunk/src/tspsolver.h	(revision 66)
+++ /trunk/src/tspsolver.h	(revision 67)
@@ -1,9 +1,10 @@
 /*!
- * \class CTSPSolver
+ * \file tspsolver.h
  * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
- * \brief This class solves Travelling Salesman Problem task.
  *
  *  $Id$
  *  $URL$
+ *
+ * \brief Defines #tMatrix typedef, sStep struct and CTSPSolver class.
  *
  *  <b>TSPSG: TSP Solver and Generator</b>
@@ -23,15 +24,8 @@
  *  You should have received a copy of the GNU General Public License
  *  along with TSPSG.  If not, see <http://www.gnu.org/licenses/>.
- *
- * \todo TODO: Deletion of solution tree on destroy and cleanup.
  */
 
 #ifndef TSPSOLVER_H
 #define TSPSOLVER_H
-
-/*!
- * \file tspsolver.h
- * \brief Defines #tMatrix typedef, sStep struct and CTSPSolver class.
- */
 
 #include "globals.h"
@@ -67,4 +61,10 @@
 };
 
+/*!
+ * \brief This class solves Travelling Salesman Problem task.
+ * \author Copyright &copy; 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
+ *
+ * \todo TODO: Deletion of solution tree on destroy and cleanup.
+ */
 class CTSPSolver
 {
@@ -74,7 +74,7 @@
 	CTSPSolver();
 	QString getSortedPath() const;
+	static QString getVersionId();
 	bool isOptimal() const;
 	sStep *solve(int, tMatrix, QWidget *parent = 0);
-	static QString getVersionId();
 
 private:
@@ -84,9 +84,10 @@
 	QHash<int,int> route;
 //	QHash<int,int> forbidden;
+
 	double align(tMatrix &);
 	void cleanup();
 	bool findCandidate(tMatrix, int &, int &);
+	double findMinInCol(int, tMatrix, int exr = -1);
 	double findMinInRow(int, tMatrix, int exc = -1);
-	double findMinInCol(int, tMatrix, int exr = -1);
 	bool hasSubCycles(int, int);
 	void subCol(tMatrix &, int, double);
Index: /trunk/src/version.h
===================================================================
--- /trunk/src/version.h	(revision 66)
+++ /trunk/src/version.h	(revision 67)
@@ -1,4 +1,30 @@
-/*! \mainpage
+/*!
+ * \file version.h
+ * \author Copyright (C) 2007-2009 Lёppa <contacts[at]oleksii[dot]name>
  *
+ *  $Id$
+ *  $URL$
+ *
+ * \brief Contains TSPSG version information defines.
+ *
+ *  <b>TSPSG: TSP Solver and Generator</b>
+ *
+ *  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/>.
+ */
+/*!
+ * \mainpage
  * \image html tspsg.png
  *  <b>TSPSG: TSP Solver and Generator</b>
@@ -7,6 +33,4 @@
  *  $Id$
  *  $URL$
- *
- *  This file is part of TSPSG.
  *
  *  TSPSG is free software: you can redistribute it and/or modify
@@ -26,9 +50,4 @@
 #ifndef VERSION_H
 #define VERSION_H
-
-/*!
- * \file version.h
- * \brief This file contains TSPSG version information defines.
- */
 
 //! TSPSG version ID
