source: tspsg/src/mainwindow.cpp @ 243b53b608

appveyorimgbotreadme
Last change on this file since 243b53b608 was 2940c14782, checked in by Oleksii Serdiuk, 11 years ago

Relicensed TSP Solver and Generator under GPLv2 license.

Due to potential conflicts between GPLv3 and app stores.

  • Property mode set to 100644
File size: 77.9 KB
Line 
1/*
2 *  TSPSG: TSP Solver and Generator
3 *  Copyright (C) 2007-2013 Oleksii Serdiuk <contacts[at]oleksii[dot]name>
4 *
5 *  $Id: $Format:%h %ai %an$ $
6 *  $URL: http://tspsg.info/ $
7 *
8 *  This file is part of TSPSG.
9 *
10 *  TSPSG is free software: you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation, either version 2 of the License, or
13 *  (at your option) any later version.
14 *
15 *  TSPSG is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with TSPSG.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "mainwindow.h"
25#include "settingsdialog.h"
26#include "tspmodel.h"
27
28#include <QBuffer>
29#include <QCloseEvent>
30#include <QDate>
31#include <QDesktopServices>
32#include <QDesktopWidget>
33#include <QFileDialog>
34#include <QImageWriter>
35#include <QLibraryInfo>
36#include <QMessageBox>
37#include <QPageSetupDialog>
38#include <QPainter>
39#include <QProgressBar>
40#include <QProgressDialog>
41#include <QSettings>
42#include <QStatusBar>
43#include <QStyleFactory>
44#include <QTextCodec>
45#include <QTextDocumentWriter>
46#include <QTextBrowser>
47#include <QTextStream>
48#include <QTextTable>
49#include <QTranslator>
50
51#ifdef Q_OS_WINCE_WM
52#   include <QScrollArea>
53#endif
54
55#ifndef QT_NO_PRINTER
56#   include <QPrinter>
57#   include <QPrintDialog>
58#   include <QPrintPreviewDialog>
59#endif
60
61#if !defined(NOSVG)
62#   include <QSvgGenerator>
63#endif // NOSVG
64
65#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
66#   include <QtConcurrent>
67#endif
68
69#include "os.h"
70
71#ifndef HANDHELD
72#   include "qttoolbardialog.h"
73    // Eyecandy
74#   include "qtwin.h"
75#endif // HANDHELD
76
77#ifndef QT_NO_PRINTER
78    Q_DECLARE_METATYPE(QPrinter::PageSize)
79    Q_DECLARE_METATYPE(QPrinter::Orientation)
80#endif
81
82#ifdef Q_OS_WIN32
83#   include "shobjidl.h"
84#endif
85
86#ifdef _T_T_L_
87#include "_.h"
88_C_ _R_ _Y_ _P_ _T_
89#endif
90
91// BEGIN HELPER FUNCTIONS
92/*!
93 * \brief Checks whether \a x contains an integer value.
94 * \param x A value to check.
95 * \return \c true if \a x countains an integer, oherwise \c false.
96 */
97inline bool isInteger(double x)
98{
99double i;
100    return (modf(x, &i) == 0.0);
101}
102
103/*!
104 * \brief Converts \a in into Base64 format with lines wrapped at 64 characters.
105 * \param in A byte array to be converted.
106 * \return Converted byte array.
107 */
108inline QByteArray toWrappedBase64(const QByteArray &in)
109{
110    QByteArray out, base64(in.toBase64());
111    for (int i = 0; i <= base64.size() - 64; i += 64) {
112        out.append(QByteArray::fromRawData(base64.data() + i, 64)).append('\n');
113    }
114    if (int rest = base64.size() % 64)
115        out.append(QByteArray::fromRawData(base64.data() + base64.size() - rest, rest));
116    return out;
117}
118// END HELPER FUNCTIONS
119
120/*!
121 * \brief Class constructor.
122 * \param parent Main Window parent widget.
123 *
124 *  Initializes Main Window and creates its layout based on target OS.
125 *  Loads TSPSG settings and opens a task file if it was specified as a commandline parameter.
126 */
127MainWindow::MainWindow(QWidget *parent)
128    : QMainWindow(parent)
129{
130    settings = initSettings(this);
131
132    // Sanity check
133    int m = settings->value("Tweaks/MaxNumCities", MAX_NUM_CITIES).toInt();
134    if (m < 3)
135        settings->setValue("Tweaks/MaxNumCities", 3);
136
137    if (settings->contains("Style")) {
138QStyle *s = QStyleFactory::create(settings->value("Style").toString());
139        if (s != NULL)
140            QApplication::setStyle(s);
141        else
142            settings->remove("Style");
143    }
144
145    loadLanguage();
146    setupUi();
147    setAcceptDrops(true);
148
149    initDocStyleSheet();
150
151#ifndef QT_NO_PRINTER
152    printer = new QPrinter(QPrinter::HighResolution);
153    settings->beginGroup("Printer");
154QPrinter::PaperSize size = qvariant_cast<QPrinter::PaperSize>(settings->value("PaperSize", DEF_PAGE_SIZE));
155    if (size != QPrinter::Custom) {
156        printer->setPaperSize(size);
157    } else {
158        printer->setPaperSize(QSizeF(settings->value("PaperWidth").toReal(), settings->value("PaperHeight").toReal()),
159                              QPrinter::Millimeter);
160    }
161
162    printer->setOrientation(qvariant_cast<QPrinter::Orientation>(settings->value("PageOrientation", DEF_PAGE_ORIENTATION)));
163    printer->setPageMargins(
164        settings->value("MarginLeft", DEF_MARGIN_LEFT).toReal(),
165        settings->value("MarginTop", DEF_MARGIN_TOP).toReal(),
166        settings->value("MarginRight", DEF_MARGIN_RIGHT).toReal(),
167        settings->value("MarginBottom", DEF_MARGIN_BOTTOM).toReal(),
168        QPrinter::Millimeter);
169    settings->endGroup();
170#endif // QT_NO_PRINTER
171
172#ifdef Q_OS_WINCE_WM
173    currentGeometry = QApplication::desktop()->availableGeometry(0);
174    // We need to react to SIP show/hide and resize the window appropriately
175    connect(QApplication::desktop(), SIGNAL(workAreaResized(int)), SLOT(desktopResized(int)));
176#endif // Q_OS_WINCE_WM
177    connect(actionFileNew, SIGNAL(triggered()), SLOT(actionFileNewTriggered()));
178    connect(actionFileOpen, SIGNAL(triggered()), SLOT(actionFileOpenTriggered()));
179    connect(actionFileSave, SIGNAL(triggered()), SLOT(actionFileSaveTriggered()));
180    connect(actionFileSaveAsTask, SIGNAL(triggered()), SLOT(actionFileSaveAsTaskTriggered()));
181    connect(actionFileSaveAsSolution, SIGNAL(triggered()), SLOT(actionFileSaveAsSolutionTriggered()));
182#ifndef QT_NO_PRINTER
183    connect(actionFilePrintPreview, SIGNAL(triggered()), SLOT(actionFilePrintPreviewTriggered()));
184    connect(actionFilePageSetup, SIGNAL(triggered()), SLOT(actionFilePageSetupTriggered()));
185    connect(actionFilePrint, SIGNAL(triggered()), SLOT(actionFilePrintTriggered()));
186#endif // QT_NO_PRINTER
187#ifndef HANDHELD
188    connect(actionSettingsToolbarsConfigure, SIGNAL(triggered()), SLOT(actionSettingsToolbarsConfigureTriggered()));
189#endif // HANDHELD
190    connect(actionSettingsPreferences, SIGNAL(triggered()), SLOT(actionSettingsPreferencesTriggered()));
191    if (actionHelpCheck4Updates != NULL)
192        connect(actionHelpCheck4Updates, SIGNAL(triggered()), SLOT(actionHelpCheck4UpdatesTriggered()));
193    connect(actionSettingsLanguageAutodetect, SIGNAL(triggered(bool)), SLOT(actionSettingsLanguageAutodetectTriggered(bool)));
194    connect(groupSettingsLanguageList, SIGNAL(triggered(QAction *)), SLOT(groupSettingsLanguageListTriggered(QAction *)));
195    connect(actionSettingsStyleSystem, SIGNAL(triggered(bool)), SLOT(actionSettingsStyleSystemTriggered(bool)));
196    connect(groupSettingsStyleList, SIGNAL(triggered(QAction*)), SLOT(groupSettingsStyleListTriggered(QAction*)));
197    connect(actionHelpOnlineSupport, SIGNAL(triggered()), SLOT(actionHelpOnlineSupportTriggered()));
198    connect(actionHelpReportBug, SIGNAL(triggered()), SLOT(actionHelpReportBugTriggered()));
199    connect(actionHelpAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
200    connect(actionHelpAbout, SIGNAL(triggered()), SLOT(actionHelpAboutTriggered()));
201
202    connect(buttonSolve, SIGNAL(clicked()), SLOT(buttonSolveClicked()));
203    connect(buttonRandom, SIGNAL(clicked()), SLOT(buttonRandomClicked()));
204    connect(buttonBackToTask, SIGNAL(clicked()), SLOT(buttonBackToTaskClicked()));
205    connect(spinCities, SIGNAL(valueChanged(int)), SLOT(spinCitiesValueChanged(int)));
206
207#ifndef HANDHELD
208    // Centering main window
209QRect rect = geometry();
210    rect.moveCenter(QApplication::desktop()->availableGeometry(this).center());
211    setGeometry(rect);
212    if (settings->value("SavePos", DEF_SAVEPOS).toBool()) {
213        // Loading of saved window state
214        settings->beginGroup("MainWindow");
215        restoreGeometry(settings->value("Geometry").toByteArray());
216        restoreState(settings->value("State").toByteArray());
217        settings->endGroup();
218    }
219#endif // HANDHELD
220
221    tspmodel = new CTSPModel(this);
222    taskView->setModel(tspmodel);
223    connect(tspmodel, SIGNAL(numCitiesChanged(int)), SLOT(numCitiesChanged(int)));
224    connect(tspmodel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
225    connect(tspmodel, SIGNAL(layoutChanged()), SLOT(dataChanged()));
226    if ((QCoreApplication::arguments().count() > 1) && (tspmodel->loadTask(QCoreApplication::arguments().at(1))))
227        setFileName(QCoreApplication::arguments().at(1));
228    else {
229        setFileName();
230        spinCities->setValue(settings->value("NumCities",DEF_NUM_CITIES).toInt());
231        spinCitiesValueChanged(spinCities->value());
232    }
233    setWindowModified(false);
234
235    if (actionHelpCheck4Updates != NULL) {
236        if (!settings->contains("Check4Updates/Enabled")) {
237            QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
238            settings->setValue("Check4Updates/Enabled",
239                QMessageBox::question(this, QCoreApplication::applicationName(),
240                    tr("Would you like %1 to automatically check for updates every %n day(s)?", "", settings->value("Check4Updates/Interval", DEF_UPDATE_CHECK_INTERVAL).toInt()).arg(QCoreApplication::applicationName()),
241                    QMessageBox::Yes | QMessageBox::No
242                ) == QMessageBox::Yes
243            );
244            QApplication::restoreOverrideCursor();
245        }
246        if ((settings->value("Check4Updates/Enabled", DEF_CHECK_FOR_UPDATES).toBool())
247            && (QDate(qvariant_cast<QDate>(settings->value("Check4Updates/LastAttempt"))).daysTo(QDate::currentDate()) >= settings->value("Check4Updates/Interval", DEF_UPDATE_CHECK_INTERVAL).toInt())) {
248            check4Updates(true);
249        }
250    }
251}
252
253MainWindow::~MainWindow()
254{
255#ifndef QT_NO_PRINTER
256    delete printer;
257#endif
258}
259
260/* Privates **********************************************************/
261
262void MainWindow::actionFileNewTriggered()
263{
264    if (!maybeSave())
265        return;
266    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
267    tspmodel->clear();
268    setFileName();
269    setWindowModified(false);
270    tabWidget->setCurrentIndex(0);
271    solutionText->clear();
272    graph = QPicture();
273    toggleSolutionActions(false);
274    QApplication::restoreOverrideCursor();
275}
276
277void MainWindow::actionFileOpenTriggered()
278{
279    if (!maybeSave())
280        return;
281
282QStringList filters(tr("All Supported Formats") + " (*.tspt *.zkt)");
283    filters.append(tr("%1 Task Files").arg("TSPSG") + " (*.tspt)");
284    filters.append(tr("%1 Task Files").arg("ZKomModRd") + " (*.zkt)");
285    filters.append(tr("All Files") + " (*)");
286
287QString file;
288    if ((fileName == tr("Untitled") + ".tspt") && settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool())
289        file = settings->value(OS"/LastUsed/TaskLoadPath").toString();
290    else
291        file = QFileInfo(fileName).path();
292QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
293    file = QFileDialog::getOpenFileName(this, tr("Task Load"), file, filters.join(";;"), NULL, opts);
294    if (file.isEmpty() || !QFileInfo(file).isFile())
295        return;
296    if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool())
297        settings->setValue(OS"/LastUsed/TaskLoadPath", QFileInfo(file).path());
298
299    if (!tspmodel->loadTask(file))
300        return;
301    setFileName(file);
302    tabWidget->setCurrentIndex(0);
303    setWindowModified(false);
304    solutionText->clear();
305    toggleSolutionActions(false);
306}
307
308bool MainWindow::actionFileSaveTriggered()
309{
310    if ((fileName == tr("Untitled") + ".tspt") || !fileName.endsWith(".tspt", Qt::CaseInsensitive))
311        return saveTask();
312    else
313        if (tspmodel->saveTask(fileName)) {
314            setWindowModified(false);
315            return true;
316        } else
317            return false;
318}
319
320void MainWindow::actionFileSaveAsTaskTriggered()
321{
322    saveTask();
323}
324
325void MainWindow::actionFileSaveAsSolutionTriggered()
326{
327static QString selectedFile;
328    if (selectedFile.isEmpty()) {
329        if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) {
330            selectedFile = settings->value(OS"/LastUsed/SolutionSavePath").toString();
331        }
332    } else
333        selectedFile = QFileInfo(selectedFile).path();
334    if (!selectedFile.isEmpty())
335        selectedFile.append("/");
336    if (fileName == tr("Untitled") + ".tspt") {
337#ifndef QT_NO_PRINTER
338        selectedFile += "solution.pdf";
339#else
340        selectedFile += "solution.html";
341#endif // QT_NO_PRINTER
342    } else {
343#ifndef QT_NO_PRINTER
344        selectedFile += QFileInfo(fileName).completeBaseName() + ".pdf";
345#else
346        selectedFile += QFileInfo(fileName).completeBaseName() + ".html";
347#endif // QT_NO_PRINTER
348    }
349
350QStringList filters;
351#ifndef QT_NO_PRINTER
352    filters.append(tr("PDF Files") + " (*.pdf)");
353#endif
354    filters.append(tr("HTML Files") + " (*.html *.htm)");
355    filters.append(tr("Web Archive Files") + " (*.mht *.mhtml)");
356    filters.append(tr("OpenDocument Files") + " (*.odt)");
357    filters.append(tr("All Files") + " (*)");
358
359QFileDialog::Options opts(settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog);
360QString file = QFileDialog::getSaveFileName(this, QString(), selectedFile, filters.join(";;"), NULL, opts);
361    if (file.isEmpty())
362        return;
363    selectedFile = file;
364    if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool())
365        settings->setValue(OS"/LastUsed/SolutionSavePath", QFileInfo(selectedFile).path());
366    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
367#ifndef QT_NO_PRINTER
368    if (selectedFile.endsWith(".pdf", Qt::CaseInsensitive)) {
369        printer->setOutputFileName(selectedFile);
370        solutionText->document()->print(printer);
371        printer->setOutputFileName(QString());
372        QApplication::restoreOverrideCursor();
373        return;
374    }
375#endif
376    QByteArray imgdata;
377    bool mhtml = selectedFile.contains(QRegExp("\\.mht(ml)?$", Qt::CaseInsensitive));
378    bool embed = !mhtml && settings->value("Output/EmbedGraphIntoHTML", DEF_EMBED_GRAPH_INTO_HTML).toBool();
379    if (selectedFile.contains(QRegExp("\\.(html?|mht(ml)?)$", Qt::CaseInsensitive))) {
380        QFile file(selectedFile);
381        if (!file.open(QFile::WriteOnly | QFile::Text)) {
382            QApplication::restoreOverrideCursor();
383            QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(file.errorString()));
384            return;
385        }
386
387        QString html = solutionText->document()->toHtml("UTF-8");
388        html.replace(QRegExp("font-family:([^;]*);"),
389                     "font-family:\\1, 'DejaVu Sans Mono', 'Courier New', Courier, monospace;");
390        html.replace(QRegExp("<style ([^>]*)>"), QString("<style \\1>\n"
391                                                         "body { color: %1 }\n"
392                                                         "td { border-style: solid; border-width: 1px; border-color: %2; }")
393                     .arg(settings->value("Output/Colors/Font", DEF_TEXT_COLOR).toString(),
394                          settings->value("Output/Colors/TableBorder", DEF_TABLE_COLOR).toString()));
395
396        QFileInfo fi(selectedFile);
397        QString format = settings->value("Output/GraphImageFormat", DEF_GRAPH_IMAGE_FORMAT).toString();
398#if !defined(NOSVG)
399        if (!QImageWriter::supportedImageFormats().contains(format.toLatin1()) && (format != "svg")) {
400#else // NOSVG
401        if (!QImageWriter::supportedImageFormats().contains(format.toLatin1())) {
402#endif // NOSVG
403            format = DEF_GRAPH_IMAGE_FORMAT;
404            settings->remove("Output/GraphImageFormat");
405        }
406
407        if (!graph.isNull()) {
408            imgdata = generateImage(format);
409            if (imgdata.isEmpty()) {
410                QApplication::restoreOverrideCursor();
411                return;
412            }
413            if (embed) {
414                QString fmt = format;
415                if (format == "svg")
416                    fmt.append("+xml");
417                html.replace(QRegExp("<img\\s+src=\"tspsg://graph.pic\""),
418                             QString("<img src=\"data:image/%1;base64,%2\" alt=\"%3\"")
419                             .arg(fmt, toWrappedBase64(imgdata), tr("Solution Graph")));
420            } else {
421                html.replace(QRegExp("<img\\s+src=\"tspsg://graph.pic\""),
422                             QString("<img src=\"%1\" alt=\"%2\"")
423                             .arg(fi.completeBaseName() + "." + format, tr("Solution Graph")));
424            }
425        }
426
427        // Saving solution text as HTML
428QTextStream ts(&file);
429        ts.setCodec(QTextCodec::codecForName("UTF-8"));
430        QString boundary = QString("------=multipart_boundary.%1").arg(qHash(selectedFile));
431        if (mhtml) {
432            ts << "Content-Type: multipart/related; start=<[email protected]>; boundary=\""
433               << boundary << "\"; type=\"text/html\"" << endl;
434            boundary.prepend("--");
435            ts << "MIME-Version: 1.0" << endl;
436            ts << endl;
437            ts << boundary << endl;
438            ts << "Content-Disposition: inline; filename=" << fi.completeBaseName() << ".html" << endl;
439            ts << "Content-Type: text/html; name=" << fi.completeBaseName() << ".html" << endl;
440            ts << "Content-ID: <[email protected]>" << endl;
441            ts << "Content-Location: " << fi.completeBaseName() << ".html" << endl;
442            ts << "Content-Transfer-Encoding: 8bit" << endl;
443            ts << endl;
444        }
445        ts << html << endl;
446        if (mhtml) {
447            ts << endl << boundary << endl;
448            ts << "Content-Disposition: inline; filename=" << fi.completeBaseName() << "." << format << endl;
449            ts << "Content-Type: image/" << format;
450            if (format == "svg")
451                ts << "+xml";
452            ts << "; name=" << fi.completeBaseName() << "." << format  << endl;
453            ts << "Content-Location: " << fi.completeBaseName() << "." << format << endl;
454            ts << "Content-Transfer-Encoding: Base64" << endl;
455            ts << endl;
456            ts << toWrappedBase64(imgdata) << endl;
457            ts << endl << boundary << "--" << endl;
458        }
459        file.close();
460        if (!embed && !mhtml) {
461            QFile img(fi.path() + "/" + fi.completeBaseName() + "." + format);
462            if (!img.open(QFile::WriteOnly)) {
463                QApplication::restoreOverrideCursor();
464                QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution graph.\nError: %1").arg(img.errorString()));
465                return;
466            }
467            if (img.write(imgdata) != imgdata.size()) {
468                QApplication::restoreOverrideCursor();
469                QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution graph.\nError: %1").arg(img.errorString()));
470            }
471            img.close();
472        }
473    } else {
474QTextDocumentWriter dw(selectedFile);
475        if (!selectedFile.endsWith(".odt",Qt::CaseInsensitive))
476            dw.setFormat("plaintext");
477        if (!dw.write(solutionText->document()))
478            QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(dw.device()->errorString()));
479    }
480    QApplication::restoreOverrideCursor();
481}
482
483#ifndef QT_NO_PRINTER
484void MainWindow::actionFilePrintPreviewTriggered()
485{
486QPrintPreviewDialog ppd(printer, this);
487    connect(&ppd,SIGNAL(paintRequested(QPrinter *)),SLOT(printPreview(QPrinter *)));
488    ppd.exec();
489
490qreal l, t, r, b;
491    printer->getPageMargins(&l, &t, &r, &b, QPrinter::Millimeter);
492
493    settings->beginGroup("Printer");
494    settings->setValue("PaperSize", printer->paperSize());
495    if (printer->paperSize() == QPrinter::Custom) {
496QSizeF size(printer->paperSize(QPrinter::Millimeter));
497        settings->setValue("PaperWidth", size.width());
498        settings->setValue("PaperHeight", size.height());
499    }
500    settings->setValue("PageOrientation", printer->orientation());
501    settings->setValue("MarginLeft", l);
502    settings->setValue("MarginTop", t);
503    settings->setValue("MarginRight", r);
504    settings->setValue("MarginBottom", b);
505    settings->endGroup();
506}
507
508void MainWindow::actionFilePageSetupTriggered()
509{
510QPageSetupDialog psd(printer, this);
511    if (psd.exec() != QDialog::Accepted)
512        return;
513
514qreal l, t, r ,b;
515    printer->getPageMargins(&l, &t, &r, &b, QPrinter::Millimeter);
516
517    settings->beginGroup("Printer");
518    settings->setValue("PaperSize", printer->paperSize());
519    if (printer->paperSize() == QPrinter::Custom) {
520QSizeF size(printer->paperSize(QPrinter::Millimeter));
521        settings->setValue("PaperWidth", size.width());
522        settings->setValue("PaperHeight", size.height());
523    }
524    settings->setValue("PageOrientation", printer->orientation());
525    settings->setValue("MarginLeft", l);
526    settings->setValue("MarginTop", t);
527    settings->setValue("MarginRight", r);
528    settings->setValue("MarginBottom", b);
529    settings->endGroup();
530}
531
532void MainWindow::actionFilePrintTriggered()
533{
534QPrintDialog pd(printer,this);
535    if (pd.exec() != QDialog::Accepted)
536        return;
537    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
538    solutionText->print(printer);
539    QApplication::restoreOverrideCursor();
540}
541#endif // QT_NO_PRINTER
542
543void MainWindow::actionSettingsPreferencesTriggered()
544{
545SettingsDialog sd(this);
546#ifdef Q_OS_SYMBIAN
547    sd.setWindowState(Qt::WindowMaximized);
548#endif
549    if (sd.exec() != QDialog::Accepted)
550        return;
551    if (sd.colorChanged() || sd.fontChanged()) {
552        if (!solutionText->document()->isEmpty() && sd.colorChanged())
553            QMessageBox::information(this, tr("Settings Changed"), tr("You have changed color settings.\nThey will be applied to the next solution output."));
554        initDocStyleSheet();
555    }
556    if (sd.translucencyChanged() != 0)
557        toggleTranclucency(sd.translucencyChanged() == 1);
558}
559
560void MainWindow::actionSettingsLanguageAutodetectTriggered(bool checked)
561{
562    if (checked) {
563        settings->remove("Language");
564        QMessageBox::information(this, tr("Language change"), tr("Language will be autodetected on the next %1 start.").arg(QCoreApplication::applicationName()));
565    } else
566        settings->setValue("Language", groupSettingsLanguageList->checkedAction()->data().toString());
567}
568
569void MainWindow::groupSettingsLanguageListTriggered(QAction *action)
570{
571#ifndef Q_WS_MAEMO_5
572    if (actionSettingsLanguageAutodetect->isChecked())
573        actionSettingsLanguageAutodetect->trigger();
574#endif
575bool untitled = (fileName == tr("Untitled") + ".tspt");
576    if (loadLanguage(action->data().toString())) {
577        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
578        settings->setValue("Language",action->data().toString());
579        retranslateUi();
580        if (untitled)
581            setFileName();
582#ifndef HANDHELD
583        if (QtWin::isCompositionEnabled() && settings->value("UseTranslucency", DEF_USE_TRANSLUCENCY).toBool())  {
584            toggleStyle(labelVariant, true);
585            toggleStyle(labelCities, true);
586        }
587#endif
588        QApplication::restoreOverrideCursor();
589        if (!solutionText->document()->isEmpty())
590            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."));
591    }
592}
593
594void MainWindow::actionSettingsStyleSystemTriggered(bool checked)
595{
596    if (checked) {
597        settings->remove("Style");
598        QMessageBox::information(this, tr("Style Change"), tr("To apply the default style you need to restart %1.").arg(QCoreApplication::applicationName()));
599    } else {
600        settings->setValue("Style", groupSettingsStyleList->checkedAction()->text());
601    }
602}
603
604void MainWindow::groupSettingsStyleListTriggered(QAction *action)
605{
606QStyle *s = QStyleFactory::create(action->text());
607    if (s != NULL) {
608        QApplication::setStyle(s);
609        settings->setValue("Style", action->text());
610        actionSettingsStyleSystem->setChecked(false);
611    }
612}
613
614#ifndef HANDHELD
615void MainWindow::actionSettingsToolbarsConfigureTriggered()
616{
617QtToolBarDialog dlg(this);
618    dlg.setToolBarManager(toolBarManager);
619    dlg.exec();
620QToolButton *tb = static_cast<QToolButton *>(toolBarMain->widgetForAction(actionFileSave));
621    if (tb != NULL) {
622        tb->setMenu(menuFileSaveAs);
623        tb->setPopupMode(QToolButton::MenuButtonPopup);
624        tb->resize(tb->sizeHint());
625    }
626
627    loadToolbarList();
628}
629#endif // HANDHELD
630
631void MainWindow::actionHelpCheck4UpdatesTriggered()
632{
633    if (!hasUpdater()) {
634        QMessageBox::warning(this, tr("Unsupported Feature"), tr("Sorry, but this feature is not supported on your\nplatform or support for it was not installed."));
635        return;
636    }
637
638    check4Updates();
639}
640
641void MainWindow::actionHelpAboutTriggered()
642{
643    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
644
645QString title;
646    title += QString("<b>%1</b><br>").arg(QCoreApplication::applicationName());
647    title += QString("%1: <b>%2</b><br>").arg(tr("Version"), QCoreApplication::applicationVersion());
648#ifndef HANDHELD
649    title += QString("<b>&copy; 2007-%1 <a href=\"http://%2/\">%3</a></b><br>").arg(QDate::currentDate().toString("yyyy"), QCoreApplication::organizationDomain(), QCoreApplication::organizationName());
650#endif // HANDHELD
651    title += QString("<b><a href=\"http://tspsg.info/\">http://tspsg.info/</a></b>");
652
653QString about;
654    about += QString("%1: <b>%2</b><br>").arg(tr("Target OS (ARCH)"), PLATFROM);
655    about += QString("%1:<br>").arg(tr("Qt library"));
656    about += QString("&nbsp;&nbsp;&nbsp;&nbsp;%1: <b>%2</b><br>").arg(tr("Build time"), QT_VERSION_STR);
657    about += QString("&nbsp;&nbsp;&nbsp;&nbsp;%1: <b>%2</b><br>").arg(tr("Runtime"), qVersion());
658    about.append(QString("%1: <b>%2x%3</b><br>").arg(tr("Logical screen DPI")).arg(logicalDpiX()).arg(logicalDpiY()));
659QString tag;
660#ifdef REVISION_STR
661    tag = tr(" from git commit <b>%1</b>").arg(QString(REVISION_STR).left(10));
662#endif
663    about += tr("Build <b>%1</b>, built on <b>%2</b> at <b>%3</b>%5 with <b>%4</b> compiler.").arg(BUILD_NUMBER).arg(__DATE__).arg(__TIME__).arg(COMPILER).arg(tag) + "<br>";
664    about += QString("%1: <b>%2</b><br>").arg(tr("Algorithm"), TSPSolver::CTSPSolver::getVersionId());
665    about += "<br>";
666    about += tr("This program is free software: you can redistribute it and/or modify<br>\n"
667        "it under the terms of the GNU General Public License as published by<br>\n"
668        "the Free Software Foundation, either version 2 of the License, or<br>\n"
669        "(at your option) any later version.<br>\n"
670        "<br>\n"
671        "This program is distributed in the hope that it will be useful,<br>\n"
672        "but WITHOUT ANY WARRANTY; without even the implied warranty of<br>\n"
673        "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>\n"
674        "GNU General Public License for more details.<br>\n"
675        "<br>\n"
676        "You should have received a copy of the GNU General Public License<br>\n"
677        "along with TSPSG.  If not, see <a href=\"http://www.gnu.org/licenses/\">www.gnu.org/licenses/</a>.");
678
679QString credits;
680    credits += tr("%1 was created using <b>Qt&nbsp;framework</b> licensed "
681        "under the terms of the GNU Lesser General Public License,<br>\n"
682        "see <a href=\"http://qt.nokia.com/\">qt.nokia.com</a><br>\n"
683        "<br>\n"
684        "Most icons used in %1 are part of <b>Oxygen&nbsp;Icons</b> project "
685        "licensed according to the GNU Lesser General Public License,<br>\n"
686        "see <a href=\"http://www.oxygen-icons.org/\">www.oxygen-icons.org</a><br>\n"
687        "<br>\n"
688        "Country flag icons used in %1 are part of the free "
689        "<b>Flag&nbsp;Icons</b> collection created by <b>IconDrawer</b>,<br>\n"
690        "see <a href=\"http://www.icondrawer.com/\">www.icondrawer.com</a><br>\n"
691        "<br>\n"
692        "%1 comes with the default \"embedded\" font <b>DejaVu&nbsp;LGC&nbsp;Sans&nbsp;"
693        "Mono</b> from the <b>DejaVu fonts</b> licensed under a Free license</a>,<br>\n"
694        "see <a href=\"http://dejavu-fonts.org/\">dejavu-fonts.org</a>")
695            .arg("TSPSG");
696
697QFile f(":/files/COPYING");
698    f.open(QIODevice::ReadOnly);
699
700QString translation = QCoreApplication::translate("--------", "AUTHORS %1", "Please, provide translator credits here. %1 will be replaced with VERSION");
701    if ((translation != "AUTHORS %1") && (translation.contains("%1"))) {
702QString about = QCoreApplication::translate("--------", "VERSION", "Please, provide your translation version here.");
703        if (about != "VERSION")
704            translation = translation.arg(about);
705    }
706
707QDialog *dlg = new QDialog(this);
708QLabel *lblIcon = new QLabel(dlg),
709    *lblTitle = new QLabel(dlg);
710#ifdef HANDHELD
711QLabel *lblSubTitle = new QLabel(QString("<b>&copy; 2007-%1 <a href=\"http://%2/\">%3</a></b>").arg(QDate::currentDate().toString("yyyy"), QCoreApplication::organizationDomain(), QCoreApplication::organizationName()), dlg);
712#endif // HANDHELD
713QTabWidget *tabs = new QTabWidget(dlg);
714QTextBrowser *txtAbout = new QTextBrowser(dlg);
715QTextBrowser *txtLicense = new QTextBrowser(dlg);
716QTextBrowser *txtCredits = new QTextBrowser(dlg);
717QVBoxLayout *vb = new QVBoxLayout();
718QHBoxLayout *hb1 = new QHBoxLayout(),
719    *hb2 = new QHBoxLayout();
720QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, dlg);
721
722    lblTitle->setOpenExternalLinks(true);
723    lblTitle->setText(title);
724    lblTitle->setAlignment(Qt::AlignTop);
725    lblTitle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
726#ifndef HANDHELD
727    lblTitle->setStyleSheet(QString("QLabel {background-color: %1; border-color: %2; border-width: 1px; border-style: solid; border-radius: 4px; padding: 1px;}").arg(palette().alternateBase().color().name(), palette().shadow().color().name()));
728#endif // HANDHELD
729
730    lblIcon->setPixmap(QPixmap(":/images/tspsg.png").scaledToHeight(lblTitle->sizeHint().height(), Qt::SmoothTransformation));
731    lblIcon->setAlignment(Qt::AlignVCenter);
732#ifndef HANDHELD
733    lblIcon->setStyleSheet(QString("QLabel {background-color: white; border-color: %1; border-width: 1px; border-style: solid; border-radius: 4px; padding: 1px;}").arg(palette().windowText().color().name()));
734#endif // HANDHELD
735
736    hb1->addWidget(lblIcon);
737    hb1->addWidget(lblTitle);
738
739    txtAbout->setWordWrapMode(QTextOption::NoWrap);
740    txtAbout->setOpenExternalLinks(true);
741    txtAbout->setHtml(about);
742    txtAbout->moveCursor(QTextCursor::Start);
743    txtAbout->setFrameShape(QFrame::NoFrame);
744
745//      txtCredits->setWordWrapMode(QTextOption::NoWrap);
746    txtCredits->setOpenExternalLinks(true);
747    txtCredits->setHtml(credits);
748    txtCredits->moveCursor(QTextCursor::Start);
749    txtCredits->setFrameShape(QFrame::NoFrame);
750
751    txtLicense->setWordWrapMode(QTextOption::NoWrap);
752    txtLicense->setOpenExternalLinks(true);
753    txtLicense->setText(f.readAll());
754    txtLicense->moveCursor(QTextCursor::Start);
755    txtLicense->setFrameShape(QFrame::NoFrame);
756
757    bb->button(QDialogButtonBox::Ok)->setCursor(QCursor(Qt::PointingHandCursor));
758    bb->button(QDialogButtonBox::Ok)->setIcon(GET_ICON("dialog-ok"));
759
760    hb2->addWidget(bb);
761
762#ifdef Q_OS_WINCE_WM
763    vb->setMargin(3);
764#endif // Q_OS_WINCE_WM
765    vb->addLayout(hb1);
766#ifdef HANDHELD
767    vb->addWidget(lblSubTitle);
768#endif // HANDHELD
769
770    tabs->addTab(txtAbout, tr("About"));
771    tabs->addTab(txtLicense, tr("License"));
772    tabs->addTab(txtCredits, tr("Credits"));
773    if (translation != "AUTHORS %1") {
774QTextBrowser *txtTranslation = new QTextBrowser(dlg);
775//              txtTranslation->setWordWrapMode(QTextOption::NoWrap);
776        txtTranslation->setOpenExternalLinks(true);
777        txtTranslation->setText(translation);
778        txtTranslation->moveCursor(QTextCursor::Start);
779        txtTranslation->setFrameShape(QFrame::NoFrame);
780
781        tabs->addTab(txtTranslation, tr("Translation"));
782    }
783#ifndef HANDHELD
784    tabs->setStyleSheet(QString("QTabWidget::pane {background-color: %1; border-color: %3; border-width: 1px; border-style: solid; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; padding: 1px;} QTabBar::tab {background-color: %2; border-color: %3; border-width: 1px; border-style: solid; border-bottom: none; border-top-left-radius: 4px; border-top-right-radius: 4px; padding: 2px 6px;} QTabBar::tab:selected {background-color: %4;} QTabBar::tab:!selected {margin-top: 1px;}").arg(palette().base().color().name(), palette().button().color().name(), palette().shadow().color().name(), palette().light().color().name()));
785#endif // HANDHELD
786
787    vb->addWidget(tabs);
788    vb->addLayout(hb2);
789
790    dlg->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
791    dlg->setWindowTitle(tr("About %1").arg(QCoreApplication::applicationName()));
792    dlg->setWindowIcon(GET_ICON("help-about"));
793
794    dlg->setLayout(vb);
795
796    connect(bb, SIGNAL(accepted()), dlg, SLOT(accept()));
797
798#ifndef HANDHELD
799    // Adding some eyecandy
800    if (QtWin::isCompositionEnabled())  {
801        QtWin::enableBlurBehindWindow(dlg, true);
802    }
803#endif // HANDHELD
804
805#ifndef HANDHELD
806    dlg->resize(450, 350);
807#elif defined(Q_OS_SYMBIAN)
808    dlg->setWindowState(Qt::WindowMaximized);
809#endif
810    QApplication::restoreOverrideCursor();
811
812    dlg->exec();
813
814    delete dlg;
815}
816
817void MainWindow::buttonBackToTaskClicked()
818{
819    tabWidget->setCurrentIndex(0);
820}
821
822void MainWindow::buttonRandomClicked()
823{
824    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
825    tspmodel->randomize();
826    QApplication::restoreOverrideCursor();
827}
828
829void MainWindow::buttonSolveClicked()
830{
831    TSPSolver::TMatrix matrix;
832    QList<double> row;
833    int n = spinCities->value();
834    bool ok;
835    for (int r = 0; r < n; r++) {
836        row.clear();
837        for (int c = 0; c < n; c++) {
838            row.append(tspmodel->index(r,c).data(Qt::UserRole).toDouble(&ok));
839            if (!ok) {
840                QMessageBox::critical(this, tr("Data error"), tr("Error in cell [Row %1; Column %2]: Invalid data format.").arg(r + 1).arg(c + 1));
841                return;
842            }
843        }
844        matrix.append(row);
845    }
846
847QProgressDialog pd(this);
848QProgressBar *pb = new QProgressBar(&pd);
849    pb->setAlignment(Qt::AlignCenter);
850    pb->setFormat(tr("%v of %1 parts found").arg(n));
851    pd.setBar(pb);
852QPushButton *cancel = new QPushButton(&pd);
853    cancel->setIcon(GET_ICON("dialog-cancel"));
854    cancel->setText(QCoreApplication::translate("QDialogButtonBox", "Cancel", "No need to translate this. The translation will be taken from Qt translation files."));
855    pd.setCancelButton(cancel);
856    pd.setMaximum(n);
857    pd.setAutoReset(false);
858    pd.setLabelText(tr("Calculating optimal route..."));
859    pd.setWindowTitle(tr("Solution Progress"));
860    pd.setWindowModality(Qt::ApplicationModal);
861    pd.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
862    pd.show();
863
864#ifdef Q_OS_WIN32
865HRESULT hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID*)&tl);
866    if (SUCCEEDED(hr)) {
867        hr = tl->HrInit();
868        if (FAILED(hr)) {
869            tl->Release();
870            tl = NULL;
871        } else {
872            tl->SetProgressValue(HWND(winId()), 0, n * 2);
873        }
874    }
875#endif
876
877    TSPSolver::CTSPSolver solver;
878    solver.setCleanupOnCancel(false);
879    connect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
880    connect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
881#ifdef Q_OS_WIN32
882    if (tl != NULL)
883        connect(&solver, SIGNAL(routePartFound(int)), SLOT(solverRoutePartFound(int)));
884#endif
885    TSPSolver::SStep *root = solver.solve(n, matrix);
886#ifdef Q_OS_WIN32
887    if (tl != NULL)
888        disconnect(&solver, SIGNAL(routePartFound(int)), this, SLOT(solverRoutePartFound(int)));
889#endif
890    disconnect(&solver, SIGNAL(routePartFound(int)), &pd, SLOT(setValue(int)));
891    disconnect(&pd, SIGNAL(canceled()), &solver, SLOT(cancel()));
892    if (!root) {
893        pd.reset();
894        if (!solver.wasCanceled()) {
895#ifdef Q_OS_WIN32
896            if (tl != NULL) {
897                tl->SetProgressState(HWND(winId()), TBPF_ERROR);
898            }
899#endif
900            QApplication::alert(this);
901            QMessageBox::warning(this, tr("Solution Result"), tr("Unable to find a solution.\nMaybe, this task has no solution."));
902        }
903        pd.setLabelText(tr("Memory cleanup..."));
904        pd.setMaximum(0);
905        pd.setCancelButton(NULL);
906        pd.show();
907#ifdef Q_OS_WIN32
908        if (tl != NULL)
909            tl->SetProgressState(HWND(winId()), TBPF_INDETERMINATE);
910#endif
911        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
912
913#ifndef QT_NO_CONCURRENT
914        QFuture<void> f = QtConcurrent::run(&solver, &TSPSolver::CTSPSolver::cleanup, false);
915        while (!f.isFinished()) {
916            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
917        }
918#else
919        solver.cleanup(true);
920#endif
921        pd.reset();
922#ifdef Q_OS_WIN32
923        if (tl != NULL) {
924            tl->SetProgressState(HWND(winId()), TBPF_NOPROGRESS);
925            tl->Release();
926            tl = NULL;
927        }
928#endif
929        return;
930    }
931    pb->setFormat(tr("Generating header"));
932    pd.setLabelText(tr("Generating solution output..."));
933    pd.setMaximum(solver.getTotalSteps() + 1);
934    pd.setValue(0);
935
936#ifdef Q_OS_WIN32
937    if (tl != NULL)
938        tl->SetProgressValue(HWND(winId()), spinCities->value(), spinCities->value() + solver.getTotalSteps() + 1);
939#endif
940
941    solutionText->clear();
942    solutionText->setDocumentTitle(tr("Solution of Variant #%1 Task").arg(spinVariant->value()));
943
944QPainter pic;
945bool dograph = settings->value("Output/GenerateGraph", DEF_GENERATE_GRAPH).toBool();
946    if (dograph) {
947        pic.begin(&graph);
948        pic.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
949QFont font = qvariant_cast<QFont>(settings->value("Output/Font", QFont(DEF_FONT_FACE)));
950        font.setStyleHint(QFont::TypeWriter);
951        // Font size in pixels = graph node radius / 2.75.
952        // See MainWindow::drawNode() for graph node radius calcualtion description.
953#ifndef Q_OS_SYMBIAN
954        font.setPixelSize(logicalDpiX() * (settings->value("Output/GraphWidth", DEF_GRAPH_WIDTH).toReal() / CM_IN_INCH) / 4.5 / 2.75);
955#else
956        // Also, see again MainWindow::drawNode() for why is additional 1.3 divider added in Symbian.
957        font.setPixelSize(logicalDpiX() * (settings->value("Output/GraphWidth", DEF_GRAPH_WIDTH).toReal() / CM_IN_INCH) / 4.5 / 2.75 / 1.3);
958#endif
959        if (settings->value("Output/HQGraph", DEF_HQ_GRAPH).toBool()) {
960            font.setWeight(QFont::DemiBold);
961            font.setPixelSize(font.pixelSize() * HQ_FACTOR);
962        }
963        pic.setFont(font);
964        pic.setBrush(QBrush(QColor(Qt::white)));
965        if (settings->value("Output/HQGraph", DEF_HQ_GRAPH).toBool()) {
966QPen pen = pic.pen();
967            pen.setWidth(HQ_FACTOR);
968            pic.setPen(pen);
969        }
970        pic.setBackgroundMode(Qt::OpaqueMode);
971    } else {
972        graph = QPicture();
973    }
974
975QTextDocument *doc = solutionText->document();
976QTextCursor cur(doc);
977
978    cur.beginEditBlock();
979    cur.setBlockFormat(fmt_paragraph);
980    cur.insertText(tr("Variant #%1 Task").arg(spinVariant->value()), fmt_default);
981    cur.insertBlock(fmt_paragraph);
982    cur.insertText(tr("Task:"), fmt_default);
983    outputMatrix(cur, matrix);
984    if (dograph) {
985#ifdef _T_T_L_
986        _b_ _i_ _z_ _a_ _r_ _r_ _e_
987#endif
988        drawNode(pic, 0);
989    }
990    cur.insertHtml("<hr>");
991    cur.insertBlock(fmt_paragraph);
992    int imgpos = cur.position();
993    cur.insertText(tr("Variant #%1 Solution").arg(spinVariant->value()), fmt_default);
994    cur.endEditBlock();
995
996    TSPSolver::SStep *step = root;
997    int c = n = 1;
998    pb->setFormat(tr("Generating step %v"));
999    while ((step->next != TSPSolver::SStep::NoNextStep) && (c < spinCities->value())) {
1000        if (pd.wasCanceled()) {
1001            pd.setLabelText(tr("Memory cleanup..."));
1002            pd.setMaximum(0);
1003            pd.setCancelButton(NULL);
1004            pd.show();
1005            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1006#ifdef Q_OS_WIN32
1007            if (tl != NULL)
1008                tl->SetProgressState(HWND(winId()), TBPF_INDETERMINATE);
1009#endif
1010#ifndef QT_NO_CONCURRENT
1011            QFuture<void> f = QtConcurrent::run(&solver, &TSPSolver::CTSPSolver::cleanup, false);
1012            while (!f.isFinished()) {
1013                QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1014            }
1015#else
1016            solver.cleanup(true);
1017#endif
1018            solutionText->clear();
1019            toggleSolutionActions(false);
1020#ifdef Q_OS_WIN32
1021            if (tl != NULL) {
1022                tl->SetProgressState(HWND(winId()), TBPF_NOPROGRESS);
1023                tl->Release();
1024                tl = NULL;
1025            }
1026#endif
1027            return;
1028        }
1029        pd.setValue(n);
1030#ifdef Q_OS_WIN32
1031        if (tl != NULL)
1032            tl->SetProgressValue(HWND(winId()), spinCities->value() + n, spinCities->value() + solver.getTotalSteps() + 1);
1033#endif
1034
1035        cur.beginEditBlock();
1036        cur.insertBlock(fmt_paragraph);
1037        cur.insertText(tr("Step #%1").arg(n), fmt_default);
1038        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())))) {
1039            outputMatrix(cur, *step);
1040        }
1041        if (step->alts.empty())
1042            cur.insertBlock(fmt_lastparagraph);
1043        else
1044            cur.insertBlock(fmt_paragraph);
1045        cur.insertText(tr("Selected route %1 %2 part.").arg((step->next == TSPSolver::SStep::RightBranch) ? tr("with") : tr("without")).arg(tr("(%1;%2)").arg(step->candidate.nRow + 1).arg(step->candidate.nCol + 1)), fmt_default);
1046        if (!step->alts.empty()) {
1047            TSPSolver::SStep::SCandidate cand;
1048            QString alts;
1049            foreach(cand, step->alts) {
1050                if (!alts.isEmpty())
1051                    alts += ", ";
1052                alts += tr("(%1;%2)").arg(cand.nRow + 1).arg(cand.nCol + 1);
1053            }
1054            cur.insertBlock(fmt_lastparagraph);
1055            cur.insertText(tr("%n alternate candidate(s) for branching: %1.", "", step->alts.count()).arg(alts), fmt_altlist);
1056        }
1057        cur.endEditBlock();
1058
1059        if (dograph) {
1060            if (step->prNode != NULL)
1061                drawNode(pic, n, false, step->prNode);
1062            if (step->plNode != NULL)
1063                drawNode(pic, n, true, step->plNode);
1064        }
1065        n++;
1066
1067        if (step->next == TSPSolver::SStep::RightBranch) {
1068            c++;
1069            step = step->prNode;
1070        } else if (step->next == TSPSolver::SStep::LeftBranch) {
1071            step = step->plNode;
1072        } else
1073            break;
1074    }
1075    pb->setFormat(tr("Generating footer"));
1076    pd.setValue(n);
1077#ifdef Q_OS_WIN32
1078    if (tl != NULL)
1079        tl->SetProgressValue(HWND(winId()), spinCities->value() + n, spinCities->value() + solver.getTotalSteps() + 1);
1080#endif
1081
1082    cur.beginEditBlock();
1083    cur.insertBlock(fmt_paragraph);
1084    if (solver.isOptimal())
1085        cur.insertText(tr("Optimal path:"), fmt_default);
1086    else
1087        cur.insertText(tr("Resulting path:"), fmt_default);
1088
1089    cur.insertBlock(fmt_paragraph);
1090    cur.insertText("  " + solver.getSortedPath(tr("City %1")));
1091
1092    if (solver.isOptimal())
1093        cur.insertBlock(fmt_paragraph);
1094    else
1095        cur.insertBlock(fmt_lastparagraph);
1096    if (isInteger(step->price))
1097        cur.insertHtml("<p>" + tr("The price is <b>%n</b> unit(s).", "", qRound(step->price)) + "</p>");
1098    else
1099        cur.insertHtml("<p>" + tr("The price is <b>%1</b> units.").arg(step->price, 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()) + "</p>");
1100    if (!solver.isOptimal()) {
1101        cur.insertBlock(fmt_paragraph);
1102        cur.insertHtml("<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>");
1103    }
1104    cur.endEditBlock();
1105
1106    if (dograph) {
1107        pic.end();
1108
1109        QImage i(graph.width() + 2, graph.height() + 2, QImage::Format_ARGB32);
1110        i.fill(QColor(255, 255, 255, 0).rgba());
1111        pic.begin(&i);
1112        pic.drawPicture(1, 1, graph);
1113        pic.end();
1114        doc->addResource(QTextDocument::ImageResource, QUrl("tspsg://graph.pic"), i);
1115
1116QTextImageFormat img;
1117        img.setName("tspsg://graph.pic");
1118        if (settings->value("Output/HQGraph", DEF_HQ_GRAPH).toBool()) {
1119            img.setWidth(i.width() / HQ_FACTOR);
1120            img.setHeight(i.height() / HQ_FACTOR);
1121        } else {
1122            img.setWidth(i.width());
1123            img.setHeight(i.height());
1124        }
1125
1126        cur.setPosition(imgpos);
1127        cur.insertImage(img, QTextFrameFormat::FloatRight);
1128    }
1129
1130    if (settings->value("Output/ScrollToEnd", DEF_SCROLL_TO_END).toBool()) {
1131        // Scrolling to the end of the text.
1132        solutionText->moveCursor(QTextCursor::End);
1133    } else
1134        solutionText->moveCursor(QTextCursor::Start);
1135
1136    pd.setLabelText(tr("Memory cleanup..."));
1137    pd.setMaximum(0);
1138    pd.setCancelButton(NULL);
1139    QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1140#ifdef Q_OS_WIN32
1141    if (tl != NULL)
1142        tl->SetProgressState(HWND(winId()), TBPF_INDETERMINATE);
1143#endif
1144#ifndef QT_NO_CONCURRENT
1145    QFuture<void> f = QtConcurrent::run(&solver, &TSPSolver::CTSPSolver::cleanup, false);
1146    while (!f.isFinished()) {
1147        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1148    }
1149#else
1150    solver.cleanup(true);
1151#endif
1152    toggleSolutionActions();
1153    tabWidget->setCurrentIndex(1);
1154#ifdef Q_OS_WIN32
1155    if (tl != NULL) {
1156        tl->SetProgressState(HWND(winId()), TBPF_NOPROGRESS);
1157        tl->Release();
1158        tl = NULL;
1159    }
1160#endif
1161
1162    pd.reset();
1163    QApplication::alert(this, 3000);
1164}
1165
1166void MainWindow::dataChanged()
1167{
1168    setWindowModified(true);
1169}
1170
1171void MainWindow::dataChanged(const QModelIndex &tl, const QModelIndex &br)
1172{
1173    setWindowModified(true);
1174    if (settings->value("Autosize", DEF_AUTOSIZE).toBool()) {
1175        for (int k = tl.row(); k <= br.row(); k++)
1176            taskView->resizeRowToContents(k);
1177        for (int k = tl.column(); k <= br.column(); k++)
1178            taskView->resizeColumnToContents(k);
1179    }
1180}
1181
1182#ifdef Q_OS_WINCE_WM
1183void MainWindow::changeEvent(QEvent *ev)
1184{
1185    if ((ev->type() == QEvent::ActivationChange) && isActiveWindow())
1186        desktopResized(0);
1187
1188    QWidget::changeEvent(ev);
1189}
1190
1191void MainWindow::desktopResized(int screen)
1192{
1193    if ((screen != 0) || !isActiveWindow())
1194        return;
1195
1196QRect availableGeometry = QApplication::desktop()->availableGeometry(0);
1197    if (currentGeometry != availableGeometry) {
1198        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1199        /*!
1200         * \hack HACK: This hack checks whether \link QDesktopWidget::availableGeometry() availableGeometry()\endlink's \c top + \c hegiht = \link QDesktopWidget::screenGeometry() screenGeometry()\endlink's \c height.
1201         *  If \c true, the window gets maximized. If we used \c setGeometry() in this case, the bottom of the
1202         *  window would end up being behind the soft buttons. Is this a bug in Qt or Windows Mobile?
1203         */
1204        if ((availableGeometry.top() + availableGeometry.height()) == QApplication::desktop()->screenGeometry().height()) {
1205            setWindowState(windowState() | Qt::WindowMaximized);
1206        } else {
1207            if (windowState() & Qt::WindowMaximized)
1208                setWindowState(windowState() ^ Qt::WindowMaximized);
1209            setGeometry(availableGeometry);
1210        }
1211        currentGeometry = availableGeometry;
1212        QApplication::restoreOverrideCursor();
1213    }
1214}
1215#endif // Q_OS_WINCE_WM
1216
1217void MainWindow::numCitiesChanged(int nCities)
1218{
1219    blockSignals(true);
1220    spinCities->setValue(nCities);
1221    blockSignals(false);
1222}
1223
1224#ifndef QT_NO_PRINTER
1225void MainWindow::printPreview(QPrinter *printer)
1226{
1227    solutionText->print(printer);
1228}
1229#endif // QT_NO_PRINTER
1230
1231#ifdef Q_OS_WIN32
1232void MainWindow::solverRoutePartFound(int n)
1233{
1234    tl->SetProgressValue(HWND(winId()), n, spinCities->value() * 2);
1235}
1236#endif // Q_OS_WIN32
1237
1238void MainWindow::spinCitiesValueChanged(int n)
1239{
1240    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1241int count = tspmodel->numCities();
1242    tspmodel->setNumCities(n);
1243    if ((n > count) && settings->value("Autosize", DEF_AUTOSIZE).toBool())
1244        for (int k = count; k < n; k++) {
1245            taskView->resizeColumnToContents(k);
1246            taskView->resizeRowToContents(k);
1247        }
1248    QApplication::restoreOverrideCursor();
1249}
1250
1251void MainWindow::check4Updates(bool silent)
1252{
1253#ifdef Q_OS_WIN32
1254    if (silent)
1255        QProcess::startDetached("updater/Update.exe -name=\"TSPSG: TSP Solver and Generator\" -check=\"freeupdate\" -silentcheck");
1256    else {
1257        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1258        QProcess::execute("updater/Update.exe -name=\"TSPSG: TSP Solver and Generator\" -check=\"freeupdate\"");
1259        QApplication::restoreOverrideCursor();
1260    }
1261#else
1262    Q_UNUSED(silent)
1263#endif
1264    settings->setValue("Check4Updates/LastAttempt", QDate::currentDate().toString(Qt::ISODate));
1265}
1266
1267void MainWindow::closeEvent(QCloseEvent *ev)
1268{
1269    if (!maybeSave()) {
1270        ev->ignore();
1271        return;
1272    }
1273    if (!settings->value("SettingsReset", false).toBool()) {
1274        settings->setValue("NumCities", spinCities->value());
1275
1276        // Saving Main Window state
1277#ifndef HANDHELD
1278        if (settings->value("SavePos", DEF_SAVEPOS).toBool()) {
1279            settings->beginGroup("MainWindow");
1280            settings->setValue("Geometry", saveGeometry());
1281            settings->setValue("State", saveState());
1282            settings->setValue("Toolbars", toolBarManager->saveState());
1283            settings->endGroup();
1284        }
1285#else
1286        settings->setValue("MainWindow/ToolbarVisible", toolBarMain->isVisible());
1287#endif // HANDHELD
1288    } else {
1289        settings->remove("SettingsReset");
1290    }
1291
1292    QMainWindow::closeEvent(ev);
1293}
1294
1295void MainWindow::dragEnterEvent(QDragEnterEvent *ev)
1296{
1297    if (ev->mimeData()->hasUrls() && (ev->mimeData()->urls().count() == 1)) {
1298QFileInfo fi(ev->mimeData()->urls().first().toLocalFile());
1299        if ((fi.suffix() == "tspt") || (fi.suffix() == "zkt"))
1300            ev->acceptProposedAction();
1301    }
1302}
1303
1304void MainWindow::drawNode(QPainter &pic, int nstep, bool left, TSPSolver::SStep *step)
1305{
1306qreal r; // Radius of graph node
1307    // We calculate r from the full graph width in centimeters:
1308    //   r = width in pixels / 4.5.
1309    //   width in pixels = DPI * width in inches.
1310    //   width in inches = width in cm / cm in inch.
1311    r = logicalDpiX() * (settings->value("Output/GraphWidth", DEF_GRAPH_WIDTH).toReal() / CM_IN_INCH) / 4.5;
1312    if (settings->value("Output/HQGraph", DEF_HQ_GRAPH).toBool())
1313        r *= HQ_FACTOR;
1314#ifdef Q_OS_SYMBIAN
1315    /*! \hack HACK: Solution graph on Symbian is visually larger than on
1316     *   Windows Mobile. This coefficient makes it about the same size.
1317     */
1318    r /= 1.3;
1319#endif
1320
1321qreal x, y;
1322    if (step != NULL)
1323        x = left ? r : r * 3.5;
1324    else
1325        x = r * 2.25;
1326    y = r * (3 * nstep + 1);
1327
1328#ifdef _T_T_L_
1329    if (nstep == -481124) {
1330        _t_t_l_(pic, r, x);
1331        return;
1332    }
1333#endif
1334
1335    pic.drawEllipse(QPointF(x, y), r, r);
1336
1337    if (step != NULL) {
1338QFont font;
1339        if (left) {
1340            font = pic.font();
1341            font.setStrikeOut(true);
1342            pic.setFont(font);
1343        }
1344        pic.drawText(QRectF(x - r, y - r, r * 2, r * 2), Qt::AlignCenter, tr("(%1;%2)").arg(step->pNode->candidate.nRow + 1).arg(step->pNode->candidate.nCol + 1) + "\n");
1345        if (left) {
1346            font.setStrikeOut(false);
1347            pic.setFont(font);
1348        }
1349        if (step->price != INFINITY) {
1350            pic.drawText(QRectF(x - r, y - r, r * 2, r * 2), Qt::AlignCenter, isInteger(step->price) ? QString("\n%1").arg(step->price) : QString("\n%1").arg(step->price, 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()));
1351        } else {
1352            pic.drawText(QRectF(x - r, y - r, r * 2, r * 2), Qt::AlignCenter, "\n"INFSTR);
1353        }
1354    } else {
1355        pic.drawText(QRectF(x - r, y - r, r * 2, r * 2), Qt::AlignCenter, tr("Root"));
1356    }
1357
1358    if (nstep == 1) {
1359        pic.drawLine(QPointF(x, y - r), QPointF(r * 2.25, y - 2 * r));
1360    } else if (nstep > 1) {
1361        pic.drawLine(QPointF(x, y - r), QPointF((step->pNode->pNode->next == TSPSolver::SStep::RightBranch) ? r * 3.5 : r, y - 2 * r));
1362    }
1363
1364}
1365
1366void MainWindow::dropEvent(QDropEvent *ev)
1367{
1368    if (maybeSave() && tspmodel->loadTask(ev->mimeData()->urls().first().toLocalFile())) {
1369        setFileName(ev->mimeData()->urls().first().toLocalFile());
1370        tabWidget->setCurrentIndex(0);
1371        setWindowModified(false);
1372        solutionText->clear();
1373        toggleSolutionActions(false);
1374
1375        ev->setDropAction(Qt::CopyAction);
1376        ev->accept();
1377    }
1378}
1379
1380QByteArray MainWindow::generateImage(const QString &format)
1381{
1382    if (graph.isNull())
1383        return QByteArray();
1384
1385    QByteArray data;
1386    QBuffer buf(&data);
1387    // Saving solution graph in SVG or supported raster format (depending on settings and SVG support)
1388#if !defined(NOSVG)
1389    if (format == "svg") {
1390        QSvgGenerator svg;
1391        svg.setSize(QSize(graph.width() + 2, graph.height() + 2));
1392        svg.setResolution(graph.logicalDpiX());
1393        svg.setOutputDevice(&buf);
1394        svg.setTitle(tr("Solution Graph"));
1395        svg.setDescription(tr("Generated with %1").arg(QCoreApplication::applicationName()));
1396        QPainter p;
1397        p.begin(&svg);
1398        p.drawPicture(1, 1, graph);
1399        p.end();
1400    } else {
1401#endif // NOSVG
1402        QImage i(graph.width() + 2, graph.height() + 2, QImage::Format_ARGB32);
1403        i.fill(0x00FFFFFF);
1404        QPainter p;
1405        p.begin(&i);
1406        p.drawPicture(1, 1, graph);
1407        p.end();
1408        QImageWriter pic;
1409        pic.setDevice(&buf);
1410        pic.setFormat(format.toLatin1());
1411        if (pic.supportsOption(QImageIOHandler::Description)) {
1412            pic.setText("Title", "Solution Graph");
1413            pic.setText("Software", QCoreApplication::applicationName());
1414        }
1415        if (format == "png")
1416            pic.setQuality(5);
1417        else if (format == "jpeg")
1418            pic.setQuality(80);
1419        if (!pic.write(i)) {
1420            QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1421            QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution graph.\nError: %1").arg(pic.errorString()));
1422            QApplication::restoreOverrideCursor();
1423            return QByteArray();
1424        }
1425#if !defined(NOSVG)
1426    }
1427#endif // NOSVG
1428    return data;
1429}
1430
1431void MainWindow::initDocStyleSheet()
1432{
1433    solutionText->document()->setDefaultFont(qvariant_cast<QFont>(settings->value("Output/Font", QFont(DEF_FONT_FACE, DEF_FONT_SIZE))));
1434
1435    fmt_paragraph.setTopMargin(5);
1436    fmt_paragraph.setRightMargin(10);
1437    fmt_paragraph.setBottomMargin(0);
1438    fmt_paragraph.setLeftMargin(10);
1439
1440    fmt_lastparagraph.setTopMargin(5);
1441    fmt_lastparagraph.setRightMargin(10);
1442    fmt_lastparagraph.setBottomMargin(15);
1443    fmt_lastparagraph.setLeftMargin(10);
1444
1445    settings->beginGroup("Output/Colors");
1446
1447    fmt_table.setTopMargin(5);
1448    fmt_table.setRightMargin(10);
1449    fmt_table.setBottomMargin(0);
1450    fmt_table.setLeftMargin(10);
1451    fmt_table.setBorder(1);
1452    fmt_table.setBorderBrush(QBrush(QColor(settings->value("TableBorder", DEF_TABLE_COLOR).toString())));
1453    fmt_table.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
1454    fmt_table.setCellSpacing(0);
1455    fmt_table.setCellPadding(2);
1456
1457    fmt_cell.setAlignment(Qt::AlignHCenter);
1458
1459QColor color = QColor(settings->value("Text", DEF_TEXT_COLOR).toString());
1460QColor hilight;
1461    if (color.value() < 192)
1462        hilight.setHsv(color.hue(), color.saturation(), 127 + (color.value() / 2));
1463    else
1464        hilight.setHsv(color.hue(), color.saturation(), color.value() / 2);
1465
1466#ifdef Q_OS_SYMBIAN
1467    /*!
1468     * \hack HACK: Fixing some weird behavior with default Symbian theme
1469     *  when text and background have the same color.
1470     */
1471    if (color != DEF_TEXT_COLOR) {
1472#endif
1473    solutionText->document()->setDefaultStyleSheet(QString("* {color: %1;}").arg(color.name()));
1474    fmt_default.setForeground(QBrush(color));
1475#ifdef Q_OS_SYMBIAN
1476    }
1477#endif
1478
1479    fmt_selected.setForeground(QBrush(QColor(settings->value("Selected", DEF_SELECTED_COLOR).toString())));
1480    fmt_selected.setFontWeight(QFont::Bold);
1481
1482    fmt_alternate.setForeground(QBrush(QColor(settings->value("Alternate", DEF_ALTERNATE_COLOR).toString())));
1483    fmt_alternate.setFontWeight(QFont::Bold);
1484    fmt_altlist.setForeground(QBrush(hilight));
1485
1486    settings->endGroup();
1487}
1488
1489void MainWindow::loadLangList()
1490{
1491QMap<QString, QStringList> langlist;
1492QFileInfoList langs;
1493QFileInfo lang;
1494QStringList language, dirs;
1495QTranslator t;
1496QDir dir;
1497    dir.setFilter(QDir::Files);
1498    dir.setNameFilters(QStringList("tspsg_*.qm"));
1499    dir.setSorting(QDir::NoSort);
1500
1501    dirs << PATH_L10N << ":/l10n";
1502    foreach (QString dirname, dirs) {
1503        dir.setPath(dirname);
1504        if (dir.exists()) {
1505            langs = dir.entryInfoList();
1506            for (int k = 0; k < langs.size(); k++) {
1507                lang = langs.at(k);
1508                if (lang.completeBaseName().compare("tspsg_en", Qt::CaseInsensitive) && !langlist.contains(lang.completeBaseName().mid(6)) && t.load(lang.completeBaseName(), dirname)) {
1509
1510                    language.clear();
1511                    language.append(lang.completeBaseName().mid(6));
1512                    language.append(t.translate("--------", "COUNTRY", "Please, provide an ISO 3166-1 alpha-2 country code for this translation language here (eg., UA).").toLower());
1513                    language.append(t.translate("--------", "LANGNAME", "Please, provide a native name of your translation language here."));
1514                    language.append(t.translate("MainWindow", "Set application language to %1", "").arg(language.at(2)));
1515
1516                    langlist.insert(language.at(0), language);
1517                }
1518            }
1519        }
1520    }
1521
1522QAction *a;
1523    foreach (language, langlist) {
1524        a = menuSettingsLanguage->addAction(language.at(2));
1525#ifndef QT_NO_STATUSTIP
1526        a->setStatusTip(language.at(3));
1527#endif
1528#if QT_VERSION >= QT_VERSION_CHECK(4,6,0)
1529        a->setIcon(QIcon::fromTheme(QString("flag-%1").arg(language.at(1)), QIcon(QString(":/images/icons/l10n/flag-%1.png").arg(language.at(1)))));
1530#else
1531        a->setIcon(QIcon(QString(":/images/icons/l10n/flag-%1.png").arg(language.at(1))));
1532#endif
1533        a->setData(language.at(0));
1534        a->setCheckable(true);
1535        a->setActionGroup(groupSettingsLanguageList);
1536        if (settings->value("Language", QLocale::system().name()).toString().startsWith(language.at(0)))
1537            a->setChecked(true);
1538    }
1539}
1540
1541bool MainWindow::loadLanguage(const QString &lang)
1542{
1543// i18n
1544bool ad = false;
1545QString lng = lang;
1546    if (lng.isEmpty()) {
1547        ad = settings->value("Language").toString().isEmpty();
1548        lng = settings->value("Language", QLocale::system().name()).toString();
1549    }
1550static QTranslator *qtTranslator; // Qt library translator
1551    if (qtTranslator) {
1552        qApp->removeTranslator(qtTranslator);
1553        delete qtTranslator;
1554        qtTranslator = NULL;
1555    }
1556static QTranslator *translator; // Application translator
1557    if (translator) {
1558        qApp->removeTranslator(translator);
1559        delete translator;
1560        translator = NULL;
1561    }
1562
1563    if (lng == "en")
1564        return true;
1565
1566    // Trying to load system Qt library translation...
1567    qtTranslator = new QTranslator(this);
1568    if (qtTranslator->load("qt_" + lng, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1569        // Trying from QT_INSTALL_TRANSLATIONS directory
1570        qApp->installTranslator(qtTranslator);
1571    } else if (qtTranslator->load("qt_" + lng, PATH_L10N)) {
1572        // Than from l10n directory bundled with TSPSG
1573        qApp->installTranslator(qtTranslator);
1574    } else {
1575        // Qt library translation unavailable for this language.
1576        delete qtTranslator;
1577        qtTranslator = NULL;
1578    }
1579
1580    // Now let's load application translation.
1581    translator = new QTranslator(this);
1582    if (translator->load("tspsg_" + lng, PATH_L10N)) {
1583        // We have a translation in the localization directory.
1584        qApp->installTranslator(translator);
1585    } else if (translator->load("tspsg_" + lng, ":/l10n")) {
1586        // We have a translation "built-in" into application resources.
1587        qApp->installTranslator(translator);
1588    } else {
1589        delete translator;
1590        translator = NULL;
1591        if (!ad) {
1592            settings->remove("Language");
1593            QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
1594            QMessageBox::warning(isVisible() ? this : NULL, tr("Language Change"), tr("Unable to load the translation language.\nFalling back to autodetection."));
1595            QApplication::restoreOverrideCursor();
1596        }
1597        return false;
1598    }
1599    return true;
1600}
1601
1602void MainWindow::loadStyleList()
1603{
1604    menuSettingsStyle->clear();
1605QStringList styles = QStyleFactory::keys();
1606    menuSettingsStyle->insertAction(NULL, actionSettingsStyleSystem);
1607    actionSettingsStyleSystem->setChecked(!settings->contains("Style"));
1608    menuSettingsStyle->addSeparator();
1609QAction *a;
1610    foreach (QString style, styles) {
1611        a = menuSettingsStyle->addAction(style);
1612        a->setData(false);
1613#ifndef QT_NO_STATUSTIP
1614        a->setStatusTip(tr("Set application style to %1").arg(style));
1615#endif
1616        a->setCheckable(true);
1617        a->setActionGroup(groupSettingsStyleList);
1618        QRegExp rx(QString("^Q?%1(::(Style)?)?$").arg(QRegExp::escape(style)), Qt::CaseInsensitive);
1619        if ((style == settings->value("Style").toString()) || QApplication::style()->objectName().contains(rx)
1620#ifndef Q_WS_MAEMO_5
1621            || QString(QApplication::style()->metaObject()->className()).contains(rx)
1622#endif
1623        ) {
1624            a->setChecked(true);
1625        }
1626    }
1627}
1628
1629void MainWindow::loadToolbarList()
1630{
1631    menuSettingsToolbars->clear();
1632#ifndef HANDHELD
1633    menuSettingsToolbars->insertAction(NULL, actionSettingsToolbarsConfigure);
1634    menuSettingsToolbars->addSeparator();
1635QList<QToolBar *> list = toolBarManager->toolBars();
1636    foreach (QToolBar *t, list) {
1637        menuSettingsToolbars->insertAction(NULL, t->toggleViewAction());
1638    }
1639#else // HANDHELD
1640    menuSettingsToolbars->insertAction(NULL, toolBarMain->toggleViewAction());
1641#endif // HANDHELD
1642}
1643
1644bool MainWindow::maybeSave()
1645{
1646    if (!isWindowModified())
1647        return true;
1648#ifdef Q_OS_SYMBIAN
1649    int res = QSMessageBox(this).exec();
1650#else
1651    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);
1652#endif
1653    if (res == QMessageBox::Save)
1654        return actionFileSaveTriggered();
1655    else if (res == QMessageBox::Cancel)
1656        return false;
1657    else
1658        return true;
1659}
1660
1661void MainWindow::outputMatrix(QTextCursor &cur, const TSPSolver::TMatrix &matrix)
1662{
1663int n = spinCities->value();
1664QTextTable *table = cur.insertTable(n, n, fmt_table);
1665
1666    for (int r = 0; r < n; r++) {
1667        for (int c = 0; c < n; c++) {
1668            cur = table->cellAt(r, c).firstCursorPosition();
1669            cur.setBlockFormat(fmt_cell);
1670            cur.setBlockCharFormat(fmt_default);
1671            if (matrix.at(r).at(c) == INFINITY)
1672                cur.insertText(INFSTR);
1673            else
1674                cur.insertText(isInteger(matrix.at(r).at(c)) ? QString("%1").arg(matrix.at(r).at(c)) : QString("%1").arg(matrix.at(r).at(c), 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()));
1675        }
1676        QCoreApplication::processEvents();
1677    }
1678    cur.movePosition(QTextCursor::End);
1679}
1680
1681void MainWindow::outputMatrix(QTextCursor &cur, const TSPSolver::SStep &step)
1682{
1683int n = spinCities->value();
1684QTextTable *table = cur.insertTable(n, n, fmt_table);
1685
1686    for (int r = 0; r < n; r++) {
1687        for (int c = 0; c < n; c++) {
1688            cur = table->cellAt(r, c).firstCursorPosition();
1689            cur.setBlockFormat(fmt_cell);
1690            if (step.matrix.at(r).at(c) == INFINITY)
1691                cur.insertText(INFSTR, fmt_default);
1692            else if ((r == step.candidate.nRow) && (c == step.candidate.nCol))
1693                cur.insertText(isInteger(step.matrix.at(r).at(c)) ? QString("%1").arg(step.matrix.at(r).at(c)) : QString("%1").arg(step.matrix.at(r).at(c), 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()), fmt_selected);
1694            else {
1695    TSPSolver::SStep::SCandidate cand;
1696                cand.nRow = r;
1697                cand.nCol = c;
1698                if (step.alts.contains(cand))
1699                    cur.insertText(isInteger(step.matrix.at(r).at(c)) ? QString("%1").arg(step.matrix.at(r).at(c)) : QString("%1").arg(step.matrix.at(r).at(c), 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()), fmt_alternate);
1700                else
1701                    cur.insertText(isInteger(step.matrix.at(r).at(c)) ? QString("%1").arg(step.matrix.at(r).at(c)) : QString("%1").arg(step.matrix.at(r).at(c), 0, 'f', settings->value("Task/FractionalAccuracy", DEF_FRACTIONAL_ACCURACY).toInt()), fmt_default);
1702            }
1703        }
1704        QCoreApplication::processEvents();
1705    }
1706
1707    cur.movePosition(QTextCursor::End);
1708}
1709
1710#ifdef Q_OS_SYMBIAN
1711void MainWindow::resizeEvent(QResizeEvent *ev)
1712{
1713    static bool tb = toolBarMain->isVisible();
1714    if ((ev->size().width() < ev->size().height())
1715            && (ev->oldSize().width() > ev->oldSize().height())) {
1716        // From landscape to portrait
1717        if (tb)
1718            toolBarMain->show();
1719        setWindowState(Qt::WindowMaximized);
1720    } else if ((ev->size().width() > ev->size().height())
1721               && (ev->oldSize().width() < ev->oldSize().height())) {
1722        // From portrait to landscape
1723        if (tb = toolBarMain->isVisible())
1724            toolBarMain->hide();
1725        setWindowState(Qt::WindowFullScreen);
1726    }
1727
1728    QWidget::resizeEvent(ev);
1729}
1730#endif // Q_OS_SYMBIAN
1731
1732void MainWindow::retranslateUi(bool all)
1733{
1734    if (all)
1735        Ui_MainWindow::retranslateUi(this);
1736
1737    loadStyleList();
1738    loadToolbarList();
1739
1740#ifndef QT_NO_PRINTER
1741    actionFilePrintPreview->setText(tr("P&rint Preview..."));
1742#ifndef QT_NO_TOOLTIP
1743    actionFilePrintPreview->setToolTip(tr("Preview solution results"));
1744#endif // QT_NO_TOOLTIP
1745#ifndef QT_NO_STATUSTIP
1746    actionFilePrintPreview->setStatusTip(tr("Preview current solution results before printing"));
1747#endif // QT_NO_STATUSTIP
1748
1749    actionFilePageSetup->setText(tr("Pa&ge Setup..."));
1750#ifndef QT_NO_TOOLTIP
1751    actionFilePageSetup->setToolTip(tr("Setup print options"));
1752#endif // QT_NO_TOOLTIP
1753#ifndef QT_NO_STATUSTIP
1754    actionFilePageSetup->setStatusTip(tr("Setup page-related options for printing"));
1755#endif // QT_NO_STATUSTIP
1756
1757    actionFilePrint->setText(tr("&Print..."));
1758#ifndef QT_NO_TOOLTIP
1759    actionFilePrint->setToolTip(tr("Print solution"));
1760#endif // QT_NO_TOOLTIP
1761#ifndef QT_NO_STATUSTIP
1762    actionFilePrint->setStatusTip(tr("Print current solution results"));
1763#endif // QT_NO_STATUSTIP
1764    actionFilePrint->setShortcut(tr("Ctrl+P"));
1765#endif // QT_NO_PRINTER
1766
1767#ifndef QT_NO_STATUSTIP
1768    actionFileExit->setStatusTip(tr("Exit %1").arg(QCoreApplication::applicationName()));
1769#endif // QT_NO_STATUSTIP
1770
1771#ifndef HANDHELD
1772    actionSettingsToolbarsConfigure->setText(tr("Configure..."));
1773#ifndef QT_NO_STATUSTIP
1774    actionSettingsToolbarsConfigure->setStatusTip(tr("Customize toolbars"));
1775#endif // QT_NO_STATUSTIP
1776#endif // HANDHELD
1777
1778#ifndef QT_NO_STATUSTIP
1779    actionHelpReportBug->setStatusTip(tr("Report about a bug in %1").arg(QCoreApplication::applicationName()));
1780#endif // QT_NO_STATUSTIP
1781    if (actionHelpCheck4Updates != NULL) {
1782        actionHelpCheck4Updates->setText(tr("Check for &Updates..."));
1783#ifndef QT_NO_STATUSTIP
1784        actionHelpCheck4Updates->setStatusTip(tr("Check for %1 updates").arg(QCoreApplication::applicationName()));
1785#endif // QT_NO_STATUSTIP
1786    }
1787#ifndef QT_NO_STATUSTIP
1788    actionHelpAbout->setStatusTip(tr("About %1").arg(QCoreApplication::applicationName()));
1789#endif // QT_NO_STATUSTIP
1790
1791#ifdef Q_OS_SYMBIAN
1792    actionRightSoftKey->setText(tr("E&xit"));
1793#endif
1794}
1795
1796bool MainWindow::saveTask() {
1797QStringList filters(tr("%1 Task File").arg("TSPSG") + " (*.tspt)");
1798    filters.append(tr("All Files") + " (*)");
1799QString file;
1800    if ((fileName == tr("Untitled") + ".tspt") && settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) {
1801        file = settings->value(OS"/LastUsed/TaskSavePath").toString();
1802        if (!file.isEmpty())
1803            file.append("/");
1804        file.append(fileName);
1805    } else if (fileName.endsWith(".tspt", Qt::CaseInsensitive))
1806        file = fileName;
1807    else
1808        file = QFileInfo(fileName).path() + "/" + QFileInfo(fileName).completeBaseName() + ".tspt";
1809
1810QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog;
1811    file = QFileDialog::getSaveFileName(this, tr("Task Save"), file, filters.join(";;"), NULL, opts);
1812    if (file.isEmpty())
1813        return false;
1814    else if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool())
1815        settings->setValue(OS"/LastUsed/TaskSavePath", QFileInfo(file).path());
1816    if (QFileInfo(file).suffix().isEmpty()) {
1817        file.append(".tspt");
1818    }
1819
1820    if (tspmodel->saveTask(file)) {
1821        setFileName(file);
1822        setWindowModified(false);
1823        return true;
1824    }
1825    return false;
1826}
1827
1828void MainWindow::setFileName(const QString &fileName)
1829{
1830    this->fileName = fileName;
1831    setWindowTitle(QString("%1[*] - %2").arg(QFileInfo(fileName).completeBaseName()).arg(QCoreApplication::applicationName()));
1832}
1833
1834void MainWindow::setupUi()
1835{
1836    Ui_MainWindow::setupUi(this);
1837
1838#ifdef Q_OS_SYMBIAN
1839    setWindowFlags(windowFlags() | Qt::WindowSoftkeysVisibleHint);
1840#endif // Q_OS_SYMBIAN
1841
1842    // File Menu
1843    actionFileNew->setIcon(GET_ICON("document-new"));
1844    actionFileOpen->setIcon(GET_ICON("document-open"));
1845    actionFileSave->setIcon(GET_ICON("document-save"));
1846#ifndef HANDHELD
1847    menuFileSaveAs->setIcon(GET_ICON("document-save-as"));
1848#endif
1849    actionFileExit->setIcon(GET_ICON("application-exit"));
1850    // Settings Menu
1851#ifndef HANDHELD
1852    menuSettingsLanguage->setIcon(GET_ICON("preferences-desktop-locale"));
1853#if QT_VERSION >= QT_VERSION_CHECK(4,6,0)
1854    actionSettingsLanguageEnglish->setIcon(QIcon::fromTheme("flag-gb", QIcon(":/images/icons/l10n/flag-gb.png")));
1855#else
1856    actionSettingsLanguageEnglish->setIcon(QIcon(":/images/icons/l10n/flag-gb.png"));
1857#endif // QT_VERSION >= QT_VERSION_CHECK(4,6,0)
1858    menuSettingsStyle->setIcon(GET_ICON("preferences-desktop-theme"));
1859#endif // HANDHELD
1860    actionSettingsPreferences->setIcon(GET_ICON("preferences-system"));
1861    // Help Menu
1862#ifndef HANDHELD
1863    actionHelpContents->setIcon(GET_ICON("help-contents"));
1864    actionHelpContextual->setIcon(GET_ICON("help-contextual"));
1865    actionHelpOnlineSupport->setIcon(GET_ICON("applications-internet"));
1866    actionHelpReportBug->setIcon(GET_ICON("tools-report-bug"));
1867    actionHelpAbout->setIcon(GET_ICON("help-about"));
1868#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
1869    actionHelpAboutQt->setIcon(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"));
1870#else
1871    actionHelpAboutQt->setIcon(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"));
1872#endif // QT_VERSION < QT_VERSION_CHECK(5,0,0)
1873#endif // HANDHELD
1874    // Buttons
1875    buttonRandom->setIcon(GET_ICON("roll"));
1876    buttonSolve->setIcon(GET_ICON("dialog-ok"));
1877    buttonSaveSolution->setIcon(GET_ICON("document-save-as"));
1878    buttonBackToTask->setIcon(GET_ICON("go-previous"));
1879
1880//      action->setIcon(GET_ICON(""));
1881
1882#if QT_VERSION >= QT_VERSION_CHECK(4,6,0)
1883    setToolButtonStyle(Qt::ToolButtonFollowStyle);
1884#endif
1885
1886#ifndef HANDHELD
1887QStatusBar *statusbar = new QStatusBar(this);
1888    statusbar->setObjectName("statusbar");
1889    setStatusBar(statusbar);
1890#endif // HANDHELD
1891
1892#ifdef Q_OS_WINCE_WM
1893    menuBar()->setDefaultAction(menuFile->menuAction());
1894
1895QScrollArea *scrollArea = new QScrollArea(this);
1896    scrollArea->setFrameShape(QFrame::NoFrame);
1897    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1898    scrollArea->setWidgetResizable(true);
1899    scrollArea->setWidget(tabWidget);
1900    setCentralWidget(scrollArea);
1901#else
1902    setCentralWidget(tabWidget);
1903#endif // Q_OS_WINCE_WM
1904
1905    //! \hack HACK: A little hack for toolbar icons to have a sane size.
1906#if defined(HANDHELD) && !defined(Q_WS_MAEMO_5)
1907#ifdef Q_OS_SYMBIAN
1908    toolBarMain->setIconSize(QSize(logicalDpiX() / 5.2, logicalDpiY() / 5.2));
1909#else
1910    toolBarMain->setIconSize(QSize(logicalDpiX() / 4, logicalDpiY() / 4));
1911#endif // Q_OS_SYMBIAN
1912#endif // HANDHELD && !Q_WS_MAEMO_5
1913QToolButton *tb = static_cast<QToolButton *>(toolBarMain->widgetForAction(actionFileSave));
1914    if (tb != NULL) {
1915        tb->setMenu(menuFileSaveAs);
1916        tb->setPopupMode(QToolButton::MenuButtonPopup);
1917    }
1918
1919//      solutionText->document()->setDefaultFont(settings->value("Output/Font", QFont(DEF_FONT_FAMILY, DEF_FONT_SIZE)).value<QFont>());
1920    solutionText->setWordWrapMode(QTextOption::WordWrap);
1921
1922#ifndef QT_NO_PRINTER
1923    actionFilePrintPreview = new QAction(this);
1924    actionFilePrintPreview->setObjectName("actionFilePrintPreview");
1925    actionFilePrintPreview->setEnabled(false);
1926    actionFilePrintPreview->setIcon(GET_ICON("document-print-preview"));
1927
1928    actionFilePageSetup = new QAction(this);
1929    actionFilePageSetup->setObjectName("actionFilePrintSetup");
1930//    actionFilePageSetup->setEnabled(false);
1931#if QT_VERSION >= QT_VERSION_CHECK(4,6,0)
1932    actionFilePageSetup->setIcon(QIcon::fromTheme("document-page-setup", QIcon(":/trolltech/dialogs/qprintpreviewdialog/images/page-setup-32.png")));
1933#else
1934    actionFilePageSetup->setIcon(QIcon(":/trolltech/dialogs/qprintpreviewdialog/images/page-setup-32.png"));
1935#endif
1936
1937    actionFilePrint = new QAction(this);
1938    actionFilePrint->setObjectName("actionFilePrint");
1939    actionFilePrint->setEnabled(false);
1940    actionFilePrint->setIcon(GET_ICON("document-print"));
1941
1942    menuFile->insertAction(actionFileExit, actionFilePrintPreview);
1943    menuFile->insertAction(actionFileExit, actionFilePageSetup);
1944    menuFile->insertAction(actionFileExit, actionFilePrint);
1945    menuFile->insertSeparator(actionFileExit);
1946
1947    toolBarMain->insertAction(actionSettingsPreferences, actionFilePrint);
1948#endif // QT_NO_PRINTER
1949
1950    groupSettingsLanguageList = new QActionGroup(this);
1951#ifdef Q_WS_MAEMO_5
1952    groupSettingsLanguageList->addAction(actionSettingsLanguageAutodetect);
1953#endif
1954    actionSettingsLanguageEnglish->setData("en");
1955    actionSettingsLanguageEnglish->setActionGroup(groupSettingsLanguageList);
1956    loadLangList();
1957    actionSettingsLanguageAutodetect->setChecked(settings->value("Language", "").toString().isEmpty());
1958
1959    actionSettingsStyleSystem->setData(true);
1960    groupSettingsStyleList = new QActionGroup(this);
1961#ifdef Q_WS_MAEMO_5
1962    groupSettingsStyleList->addAction(actionSettingsStyleSystem);
1963#endif
1964
1965#ifndef HANDHELD
1966    actionSettingsToolbarsConfigure = new QAction(this);
1967    actionSettingsToolbarsConfigure->setIcon(GET_ICON("configure-toolbars"));
1968#endif // HANDHELD
1969
1970    if (hasUpdater()) {
1971        actionHelpCheck4Updates = new QAction(this);
1972        actionHelpCheck4Updates->setIcon(GET_ICON("system-software-update"));
1973        actionHelpCheck4Updates->setEnabled(hasUpdater());
1974        menuHelp->insertAction(actionHelpAboutQt, actionHelpCheck4Updates);
1975        menuHelp->insertSeparator(actionHelpAboutQt);
1976    } else
1977        actionHelpCheck4Updates = NULL;
1978
1979    spinCities->setMaximum(settings->value("Tweaks/MaxNumCities", MAX_NUM_CITIES).toInt());
1980
1981#ifndef HANDHELD
1982    toolBarManager = new QtToolBarManager(this);
1983    toolBarManager->setMainWindow(this);
1984QString cat = toolBarMain->windowTitle();
1985    toolBarManager->addToolBar(toolBarMain, cat);
1986#ifndef QT_NO_PRINTER
1987    toolBarManager->addAction(actionFilePrintPreview, cat);
1988    toolBarManager->addAction(actionFilePageSetup, cat);
1989#endif // QT_NO_PRINTER
1990    toolBarManager->addAction(actionHelpContents, cat);
1991    toolBarManager->addAction(actionHelpContextual, cat);
1992    toolBarManager->restoreState(settings->value("MainWindow/Toolbars").toByteArray());
1993#else
1994    toolBarMain->setVisible(settings->value("MainWindow/ToolbarVisible", true).toBool());
1995#endif // HANDHELD
1996
1997#ifdef Q_OS_SYMBIAN
1998    // Replace Exit on the right soft key with our own exit action.
1999    // This makes it translatable.
2000    actionRightSoftKey = new QAction(this);
2001    actionRightSoftKey->setSoftKeyRole(QAction::NegativeSoftKey);
2002    connect(actionRightSoftKey, SIGNAL(triggered()), SLOT(close()));
2003    addAction(actionRightSoftKey);
2004#endif
2005
2006    retranslateUi(false);
2007
2008#ifndef HANDHELD
2009    // Adding some eyecandy
2010    if (QtWin::isCompositionEnabled() && settings->value("UseTranslucency", DEF_USE_TRANSLUCENCY).toBool())  {
2011        toggleTranclucency(true);
2012    }
2013#endif // HANDHELD
2014}
2015
2016void MainWindow::toggleSolutionActions(bool enable)
2017{
2018    buttonSaveSolution->setEnabled(enable);
2019    actionFileSaveAsSolution->setEnabled(enable);
2020    solutionText->setEnabled(enable);
2021#ifndef QT_NO_PRINTER
2022    actionFilePrint->setEnabled(enable);
2023    actionFilePrintPreview->setEnabled(enable);
2024#endif // QT_NO_PRINTER
2025}
2026
2027void MainWindow::toggleTranclucency(bool enable)
2028{
2029#ifndef HANDHELD
2030    toggleStyle(labelVariant, enable);
2031    toggleStyle(labelCities, enable);
2032    toggleStyle(statusBar(), enable);
2033    tabWidget->setDocumentMode(enable);
2034    QtWin::enableBlurBehindWindow(this, enable);
2035#else
2036    Q_UNUSED(enable);
2037#endif // HANDHELD
2038}
2039
2040void MainWindow::actionHelpOnlineSupportTriggered()
2041{
2042    QDesktopServices::openUrl(QUrl("http://tspsg.info/goto/support"));
2043}
2044
2045void MainWindow::actionHelpReportBugTriggered()
2046{
2047    QDesktopServices::openUrl(QUrl("http://tspsg.info/goto/bugtracker"));
2048}
2049
2050#ifdef Q_OS_SYMBIAN
2051QSMessageBox::QSMessageBox(QWidget *parent)
2052    : QMessageBox(parent)
2053{
2054    setIcon(QMessageBox::Warning);
2055    setWindowTitle(QApplication::translate("MainWindow", "Unsaved Changes"));
2056    setText(QApplication::translate("MainWindow", "Would you like to save changes in the current task?"));
2057    setStandardButtons(QMessageBox::Save);
2058
2059    QMenu *m = new QMenu(this);
2060    m->addAction(QApplication::translate("QDialogButtonBox", "Discard", "No need to translate this. The translation will be taken from Qt translation files."),
2061                 this, SLOT(discard()));
2062    m->addAction(QApplication::translate("QDialogButtonBox", "Cancel", "No need to translate this. The translation will be taken from Qt translation files."),
2063                 this, SLOT(cancel()));
2064
2065    QAction *o = new QAction(QApplication::translate("QtToolBarDialog", "Actions"), this);
2066    o->setSoftKeyRole(QAction::NegativeSoftKey);
2067    o->setMenu(m);
2068    addAction(o);
2069}
2070
2071void QSMessageBox::cancel(){
2072    done(QMessageBox::Cancel);
2073}
2074
2075void QSMessageBox::discard() {
2076    done(QMessageBox::Discard);
2077}
2078#endif // Q_OS_SYMBIAN
Note: See TracBrowser for help on using the repository browser.