source: tspsg/src/mainwindow.cpp @ 1299ea5b49

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

Updated version info to be set based on git tags.

'git describe' is now used for version definition. Major, minor and
release are taken from the latest git tag. Build is defined as number of
commits since the latest tag. If we're out of source, then .tag file
should contain output of 'git describe --abbrev=40' command.

Also, moved version related code from tspsg.pro to version.pri.

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