source: tspsg/src/mainwindow.cpp @ f48433245d

appveyorimgbotreadme
Last change on this file since f48433245d was f48433245d, checked in by Oleksii Serdiuk, 12 years ago

Added support for saving solution in MHTML format.

MIME HTML, also known as Web Archive.

Closes #10: Add support for MHTML format

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