source: tspsg/src/mainwindow.cpp @ 7ed8b57eea

appveyorimgbotreadme
Last change on this file since 7ed8b57eea was 7ed8b57eea, checked in by Oleksii Serdiuk, 11 years ago

[BB10] UI fixes:

  • added proxy style and stylesheet to fix some UI issues on BlackBerry?;
  • removed style selection option because most styles have UI issues and removed About Qt menu item because About Qt dialog is too big;
  • load bigger (128x128) icons on BlackBerry? 10;
  • removed printing related items from menu as there's no printer;
  • made dialogs to be shown maximized;
  • made solution graph smaller;
  • made font smaller;
  • made output text color black;
  • start editing table cells on single tap;
  • don't show keyboard in about dialog.

Cherry-picks from
aa64ad40827cde0a444cd8198b0f3457aa191f8f
95b0ef73dcb4517ac2ae08867494204bbe8d7ea6
ccdffe3a5f84f12fba2e2f19ab249c31f1fcf35d
628500a5d687889823197e476953859d529af4f0

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