source: tspsg/src/mainwindow.cpp @ 31694c8b58

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

Fixed wrong placement of tspsg.ini on Windows Mobile.

Also, Main Window stopped showing maximized on Windows Mobile. Made a
workaround for this.

Closes #15 - Try to fix wrong placement of tspsg.ini.

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