/* * TSPSG: TSP Solver and Generator * Copyright (C) 2007-2016 Oleksii Serdiuk * * $Id: $Format:%h %ai %an$ $ * $URL: http://tspsg.info/ $ * * This file is part of TSPSG. * * TSPSG is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * TSPSG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with TSPSG. If not, see . */ #include "mainwindow.h" #include "settingsdialog.h" #include "tspmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WINCE_WM # include #endif #ifndef QT_NO_PRINTER # include # include # include #endif #if !defined(NOSVG) # include #endif // NOSVG #include #include "os.h" #ifndef HANDHELD # include "qttoolbardialog.h" // Eyecandy # include "qtwin.h" #endif // HANDHELD #ifndef QT_NO_PRINTER Q_DECLARE_METATYPE(QPrinter::PageSize) Q_DECLARE_METATYPE(QPrinter::Orientation) #endif #ifdef Q_OS_WIN32 # include # include "shobjidl.h" #endif #ifdef Q_OS_BLACKBERRY # include # include using namespace bb::cascades::pickers; #endif #ifdef _T_T_L_ #include "_.h" _C_ _R_ _Y_ _P_ _T_ #endif // BEGIN HELPER FUNCTIONS /*! * \brief Checks whether \a x contains an integer value. * \param x A value to check. * \return \c true if \a x countains an integer, oherwise \c false. */ inline bool isInteger(double x) { double i; #ifdef Q_OS_BLACKBERRY return (std::modf(x, &i) == 0.0); #else return (modf(x, &i) == 0.0); #endif } /*! * \brief Converts \a in into Base64 format with lines wrapped at 64 characters. * \param in A byte array to be converted. * \return Converted byte array. */ inline QByteArray toWrappedBase64(const QByteArray &in) { QByteArray out, base64(in.toBase64()); for (int i = 0; i <= base64.size() - 64; i += 64) { out.append(QByteArray::fromRawData(base64.data() + i, 64)).append('\n'); } if (int rest = base64.size() % 64) out.append(QByteArray::fromRawData(base64.data() + base64.size() - rest, rest)); return out; } // END HELPER FUNCTIONS /*! * \brief Class constructor. * \param parent Main Window parent widget. * * Initializes Main Window and creates its layout based on target OS. * Loads TSPSG settings and opens a task file if it was specified as a commandline parameter. */ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { settings = initSettings(this); // Sanity check int m = settings->value("Tweaks/MaxNumCities", MAX_NUM_CITIES).toInt(); if (m < 3) settings->setValue("Tweaks/MaxNumCities", 3); if (settings->contains("Style")) { QStyle *s = QStyleFactory::create(settings->value("Style").toString()); if (s != NULL) QApplication::setStyle(s); else settings->remove("Style"); } loadLanguage(); setupUi(); setAcceptDrops(true); #ifdef Q_OS_BLACKBERRY taskView->setEditTriggers(QAbstractItemView::AllEditTriggers); #endif initDocStyleSheet(); #ifndef QT_NO_PRINTER printer = new QPrinter(QPrinter::HighResolution); settings->beginGroup("Printer"); QPrinter::PaperSize size = qvariant_cast(settings->value("PaperSize", DEF_PAGE_SIZE)); if (size != QPrinter::Custom) { printer->setPaperSize(size); } else { printer->setPaperSize(QSizeF(settings->value("PaperWidth").toReal(), settings->value("PaperHeight").toReal()), QPrinter::Millimeter); } printer->setOrientation(qvariant_cast(settings->value("PageOrientation", DEF_PAGE_ORIENTATION))); printer->setPageMargins( settings->value("MarginLeft", DEF_MARGIN_LEFT).toReal(), settings->value("MarginTop", DEF_MARGIN_TOP).toReal(), settings->value("MarginRight", DEF_MARGIN_RIGHT).toReal(), settings->value("MarginBottom", DEF_MARGIN_BOTTOM).toReal(), QPrinter::Millimeter); settings->endGroup(); #endif // QT_NO_PRINTER #ifdef Q_OS_WINCE_WM currentGeometry = QApplication::desktop()->availableGeometry(0); // We need to react to SIP show/hide and resize the window appropriately connect(QApplication::desktop(), SIGNAL(workAreaResized(int)), SLOT(desktopResized(int))); #endif // Q_OS_WINCE_WM connect(actionFileNew, SIGNAL(triggered()), SLOT(actionFileNewTriggered())); connect(actionFileOpen, SIGNAL(triggered()), SLOT(actionFileOpenTriggered())); connect(actionFileSave, SIGNAL(triggered()), SLOT(actionFileSaveTriggered())); connect(actionFileSaveAsTask, SIGNAL(triggered()), SLOT(actionFileSaveAsTaskTriggered())); connect(actionFileSaveAsSolution, SIGNAL(triggered()), SLOT(actionFileSaveAsSolutionTriggered())); #ifndef QT_NO_PRINTDIALOG connect(actionFilePrintPreview, SIGNAL(triggered()), SLOT(actionFilePrintPreviewTriggered())); connect(actionFilePageSetup, SIGNAL(triggered()), SLOT(actionFilePageSetupTriggered())); connect(actionFilePrint, SIGNAL(triggered()), SLOT(actionFilePrintTriggered())); #endif // QT_NO_PRINTER #ifndef HANDHELD connect(actionSettingsToolbarsConfigure, SIGNAL(triggered()), SLOT(actionSettingsToolbarsConfigureTriggered())); #endif // HANDHELD connect(actionSettingsPreferences, SIGNAL(triggered()), SLOT(actionSettingsPreferencesTriggered())); if (actionHelpCheck4Updates != NULL) connect(actionHelpCheck4Updates, SIGNAL(triggered()), SLOT(actionHelpCheck4UpdatesTriggered())); connect(actionSettingsLanguageAutodetect, SIGNAL(triggered(bool)), SLOT(actionSettingsLanguageAutodetectTriggered(bool))); connect(groupSettingsLanguageList, SIGNAL(triggered(QAction *)), SLOT(groupSettingsLanguageListTriggered(QAction *))); connect(actionSettingsStyleSystem, SIGNAL(triggered(bool)), SLOT(actionSettingsStyleSystemTriggered(bool))); connect(groupSettingsStyleList, SIGNAL(triggered(QAction*)), SLOT(groupSettingsStyleListTriggered(QAction*))); connect(actionHelpOnlineSupport, SIGNAL(triggered()), SLOT(actionHelpOnlineSupportTriggered())); connect(actionHelpReportBug, SIGNAL(triggered()), SLOT(actionHelpReportBugTriggered())); connect(actionHelpAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt())); connect(actionHelpAbout, SIGNAL(triggered()), SLOT(actionHelpAboutTriggered())); connect(buttonSolve, SIGNAL(clicked()), SLOT(buttonSolveClicked())); connect(buttonRandom, SIGNAL(clicked()), SLOT(buttonRandomClicked())); connect(buttonBackToTask, SIGNAL(clicked()), SLOT(buttonBackToTaskClicked())); connect(spinCities, SIGNAL(valueChanged(int)), SLOT(spinCitiesValueChanged(int))); #ifndef HANDHELD // Centering main window QRect rect = geometry(); rect.moveCenter(QApplication::desktop()->availableGeometry(this).center()); setGeometry(rect); if (settings->value("SavePos", DEF_SAVEPOS).toBool()) { // Loading of saved window state settings->beginGroup("MainWindow"); restoreGeometry(settings->value("Geometry").toByteArray()); restoreState(settings->value("State").toByteArray()); settings->endGroup(); } #endif // HANDHELD tspmodel = new CTSPModel(this); taskView->setModel(tspmodel); connect(tspmodel, SIGNAL(numCitiesChanged(int)), SLOT(numCitiesChanged(int))); connect(tspmodel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); connect(tspmodel, SIGNAL(layoutChanged()), SLOT(dataChanged())); if ((QCoreApplication::arguments().count() > 1) && (tspmodel->loadTask(QCoreApplication::arguments().at(1)))) setFileName(QCoreApplication::arguments().at(1)); else { setFileName(); spinCities->setValue(settings->value("NumCities",DEF_NUM_CITIES).toInt()); spinCitiesValueChanged(spinCities->value()); } setWindowModified(false); if (actionHelpCheck4Updates != NULL) { if (!settings->contains("Check4Updates/Enabled")) { QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); settings->setValue("Check4Updates/Enabled", QMessageBox::question(this, QCoreApplication::applicationName(), 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()), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes ); QApplication::restoreOverrideCursor(); } if ((settings->value("Check4Updates/Enabled", DEF_CHECK_FOR_UPDATES).toBool()) && (QDate(qvariant_cast(settings->value("Check4Updates/LastAttempt"))).daysTo(QDate::currentDate()) >= settings->value("Check4Updates/Interval", DEF_UPDATE_CHECK_INTERVAL).toInt())) { check4Updates(true); } } } MainWindow::~MainWindow() { #ifndef QT_NO_PRINTER delete printer; #endif } #ifdef Q_OS_BLACKBERRY void MainWindow::setWindowModified(bool modified) { QMainWindow::setWindowModified(modified); bb::ApplicationSupport app; if (modified) app.setClosePrompt(tr("Unsaved Changes"), tr("The task has unsaved changes. Would you really like to close the application?")); else app.clearClosePrompt(); } #endif /* Privates **********************************************************/ void MainWindow::actionFileNewTriggered() { if (!maybeSave()) return; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); tspmodel->clear(); setFileName(); setWindowModified(false); tabWidget->setCurrentIndex(0); solutionText->clear(); graph = QPicture(); toggleSolutionActions(false); QApplication::restoreOverrideCursor(); } void MainWindow::actionFileOpenTriggered() { if (!maybeSave()) return; QStringList filters; #ifdef Q_OS_BLACKBERRY filters << "*.tspt" << "*.zkt"; #else filters.append(tr("All Supported Formats") + " (*.tspt *.zkt)"); filters.append(tr("%1 Task Files").arg("TSPSG") + " (*.tspt)"); filters.append(tr("%1 Task Files").arg("ZKomModRd") + " (*.zkt)"); filters.append(tr("All Files") + " (*)"); #endif QString file; if ((fileName == tr("Untitled") + ".tspt") && settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) #ifdef Q_OS_BLACKBERRY file = settings->value(OS"/LastUsed/TaskLoadPath", "/accounts/1000/shared/documents").toString(); #else file = settings->value(OS"/LastUsed/TaskLoadPath").toString(); #endif else file = QFileInfo(fileName).path(); #ifdef Q_OS_BLACKBERRY FilePicker fd; fd.setType(FileType::Document | FileType::Other); fd.setDefaultType(FileType::Document); fd.setTitle(tr("Task Load")); fd.setFilter(filters); fd.setDirectories(QStringList(file)); fd.open(); QEventLoop loop; connect(&fd, SIGNAL(pickerClosed()), &loop, SLOT(quit())); loop.exec(); if (fd.selectedFiles().count() < 1) return; file = fd.selectedFiles().at(0); if (!QFileInfo(file).isFile()) return; #else QFileDialog::Options opts = settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog; file = QFileDialog::getOpenFileName(this, tr("Task Load"), file, filters.join(";;"), NULL, opts); if (file.isEmpty() || !QFileInfo(file).isFile()) return; #endif if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) settings->setValue(OS"/LastUsed/TaskLoadPath", QFileInfo(file).path()); if (!tspmodel->loadTask(file)) return; setFileName(file); tabWidget->setCurrentIndex(0); setWindowModified(false); solutionText->clear(); toggleSolutionActions(false); } bool MainWindow::actionFileSaveTriggered() { if ((fileName == tr("Untitled") + ".tspt") || !fileName.endsWith(".tspt", Qt::CaseInsensitive)) return saveTask(); else if (tspmodel->saveTask(fileName)) { setWindowModified(false); return true; } else return false; } void MainWindow::actionFileSaveAsTaskTriggered() { saveTask(); } void MainWindow::actionFileSaveAsSolutionTriggered() { static QString selectedFile; if (selectedFile.isEmpty()) { if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) { #ifdef Q_OS_BLACKBERRY selectedFile = settings->value(OS"/LastUsed/SolutionSavePath", "/accounts/1000/shared/documents").toString(); #else selectedFile = settings->value(OS"/LastUsed/SolutionSavePath").toString(); #endif } } else selectedFile = QFileInfo(selectedFile).path(); if (!selectedFile.isEmpty()) selectedFile.append("/"); if (fileName == tr("Untitled") + ".tspt") { #ifndef QT_NO_PRINTER selectedFile += "solution.pdf"; #else selectedFile += "solution.html"; #endif // QT_NO_PRINTER } else { #ifndef QT_NO_PRINTER selectedFile += QFileInfo(fileName).completeBaseName() + ".pdf"; #else selectedFile += QFileInfo(fileName).completeBaseName() + ".html"; #endif // QT_NO_PRINTER } QStringList filters; #ifdef Q_OS_BLACKBERRY filters << "*.pdf" << "*.html" << "*.htm" << "*.odf"; #else #ifndef QT_NO_PRINTER filters.append(tr("PDF Files") + " (*.pdf)"); #endif filters.append(tr("HTML Files") + " (*.html *.htm)"); filters.append(tr("Web Archive Files") + " (*.mht *.mhtml)"); filters.append(tr("OpenDocument Files") + " (*.odt)"); filters.append(tr("All Files") + " (*)"); #endif #ifdef Q_OS_BLACKBERRY FilePicker fd; fd.setMode(FilePickerMode::Saver); fd.setType(FileType::Document | FileType::Other); fd.setDefaultType(FileType::Document); fd.setAllowOverwrite(true); fd.setTitle(tr("Solution Save")); // fd.setDirectories(QStringList(QFileInfo(selectedFile).path())); fd.setDefaultSaveFileNames(QStringList(selectedFile)); fd.setFilter(filters); fd.open(); QEventLoop loop; connect(&fd, SIGNAL(pickerClosed()), &loop, SLOT(quit())); loop.exec(); if (fd.selectedFiles().count() < 1) return; selectedFile = fd.selectedFiles().at(0); #else QFileDialog::Options opts(settings->value("UseNativeDialogs", DEF_USE_NATIVE_DIALOGS).toBool() ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog); QString file = QFileDialog::getSaveFileName(this, QString(), selectedFile, filters.join(";;"), NULL, opts); if (file.isEmpty()) return; selectedFile = file; #endif if (settings->value("SaveLastUsed", DEF_SAVE_LAST_USED).toBool()) settings->setValue(OS"/LastUsed/SolutionSavePath", QFileInfo(selectedFile).path()); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); #ifndef QT_NO_PRINTER if (selectedFile.endsWith(".pdf", Qt::CaseInsensitive)) { printer->setOutputFileName(selectedFile); solutionText->document()->print(printer); printer->setOutputFileName(QString()); QApplication::restoreOverrideCursor(); return; } #endif QByteArray imgdata; bool mhtml = selectedFile.contains(QRegExp("\\.mht(ml)?$", Qt::CaseInsensitive)); bool embed = !mhtml && settings->value("Output/EmbedGraphIntoHTML", DEF_EMBED_GRAPH_INTO_HTML).toBool(); if (selectedFile.contains(QRegExp("\\.(html?|mht(ml)?)$", Qt::CaseInsensitive))) { QFile file(selectedFile); if (!file.open(QFile::WriteOnly | QFile::Text)) { QApplication::restoreOverrideCursor(); QMessageBox::critical(this, tr("Solution Save"), tr("Unable to save the solution.\nError: %1").arg(file.errorString())); return; } QString html = solutionText->document()->toHtml("UTF-8"); html.replace(QRegExp("font-family:([^;]*);"), "font-family:\\1, 'DejaVu Sans Mono', 'Courier New', Courier, monospace;"); html.replace(QRegExp("