source: tspsg/src/mainwindow.cpp @ 2940c14782

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

Relicensed TSP Solver and Generator under GPLv2 license.

Due to potential conflicts between GPLv3 and app stores.

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