1 // -*- C++ -*-
2 // $Id: mainwindow.cpp,v 1.27 2010-11-01 03:30:42 robertl Exp $
3 //------------------------------------------------------------------------
4 //
5 //  Copyright (C) 2009  S. Khai Mong <khai@mangrai.com>.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License as
9 //  published by the Free Software Foundation; either version 2 of the
10 //  License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20 //  USA.
21 //
22 #include <QtCore/QByteArray>           // for QByteArray
23 #include <QtCore/QDate>                // for QDate
24 #include <QtCore/QDateTime>            // for QDateTime
25 #include <QtCore/QDir>                 // for QDir
26 #include <QtCore/QEvent>               // for QEvent (& QEvent::LanguageChange, QEvent::LocaleChange)
27 #include <QtCore/QFile>                // for QFile
28 #include <QtCore/QFileInfo>            // for QFileInfo
29 #include <QtCore/QLocale>              // for QLocale
30 #include <QtCore/QMimeData>            // for QMimeData
31 #include <QtCore/QProcess>             // for QProcess, QProcess::NotRunning
32 #include <QtCore/QRegExp>              // for QRegExp
33 #include <QtCore/QSettings>            // for QSettings
34 #include <QtCore/QTemporaryFile>       // for QTemporaryFile
35 #include <QtCore/QTime>                // for QTime
36 #include <QtCore/QUrl>                 // for QUrl
37 #include <QtCore/QVariant>             // for QVariant
38 #include <QtCore/Qt>                   // for SmoothTransformation, WaitCursor
39 #include <QtCore/QtGlobal>             // for foreach
40 #include <QtGui/QCursor>               // for QCursor
41 #include <QtGui/QDesktopServices>      // for QDesktopServices
42 #include <QtGui/QIcon>                 // for QIcon
43 #include <QtGui/QImage>                // for QImage
44 #include <QtWidgets/QApplication>      // for QApplication, qApp
45 #include <QtWidgets/QCheckBox>         // for QCheckBox
46 #include <QtWidgets/QDialogButtonBox>  // for QDialogButtonBox
47 #include <QtWidgets/QFileDialog>       // for QFileDialog
48 #include <QtWidgets/QMessageBox>       // for QMessageBox, operator|, QMessageBox::Yes, QMessageBox::No
49 #include <QtWidgets/QPlainTextEdit>    // for QPlainTextEdit
50 #include <QtWidgets/QPushButton>       // for QPushButton
51 #include <QtWidgets/QRadioButton>      // for QRadioButton
52 #include <QtWidgets/QStackedWidget>    // for QStackedWidget
53 
54 #include <cstdlib>                     // for exit
55 
56 #include "mainwindow.h"
57 #include "../gbversion.h"              // for VERSION
58 #include "aboutdlg.h"                  // for AboutDlg
59 #include "advdlg.h"                    // for AdvDlg
60 #include "appname.h"                   // for appName
61 #include "babeldata.h"                 // for BabelData
62 #include "donate.h"                    // for Donate
63 #include "filterdlg.h"                 // for FilterDialog
64 #include "formatload.h"                // for FormatLoad
65 #include "gmapdlg.h"                   // for GMapDialog
66 #include "help.h"                      // for ShowHelp
67 #include "optionsdlg.h"                // for OptionsDlg
68 #include "preferences.h"               // for Preferences
69 #include "processwait.h"               // for ProcessWaitDialog
70 #include "upgrade.h"                   // for UpgradeCheck
71 #include "version_mismatch.h"          // for VersionMismatch
72 
73 
74 
75 const int BabelData::noType_ = -1;
76 const int BabelData::fileType_ = 0;
77 const int BabelData::deviceType_ = 1;
78 
79 #define FAKE_LANGUAGE_MENU 0
80 
81 //------------------------------------------------------------------------
findBabelVersion()82 QString MainWindow::findBabelVersion()
83 {
84   QProcess babel;
85   babel.start(QApplication::applicationDirPath() + "/gpsbabel", QStringList() << "-V");
86   if (!babel.waitForStarted()) {
87     return QString();
88   }
89   babel.closeWriteChannel();
90   if (!babel.waitForFinished()) {
91     return QString();
92   }
93 
94   QString str = babel.readAll();
95   isBeta_ = str.contains("-beta");
96   str.replace("Version",  "");
97   str.replace("GPSBabel",  "");
98   str.replace(QRegExp("^[\\s]*"),  "");
99   str.replace(QRegExp("[\\s]+$"),  "");
100   str = str.simplified();
101   return str;
102 }
103 
104 //------------------------------------------------------------------------
105 // Decides whether available beta upgrades are suggested to user for download.
allowBetaUpgrades()106 bool MainWindow::allowBetaUpgrades()
107 {
108   // If this is a beta version (which means the user consciously downloaded
109   // it and decided to be on the beta track or the user has ticked the
110   // 'suggest beta upgrade' box, allow betas to be suggested for installation.
111   return isBeta_ || babelData_.allowBetaUpgrades_;
112 }
113 
114 //------------------------------------------------------------------------
MakeOptions(const QList<FormatOption> & options)115 static QString MakeOptions(const QList<FormatOption>& options)
116 {
117   QString str;
118   for (int i = 0; i< options.size(); i++) {
119     FormatOption option = options[i];
120     QVariant default_value = option.getDefaultValue();
121     if (option.getSelected()) {
122       // For OPTbool, 'selected' is the key, not value.
123       if (option.getType() == FormatOption::OPTbool) {
124         // Only write "foo=1" if that's not already the default.
125         if (default_value != "1") {
126           str += "," + option.getName() + "=1";
127         }
128       } else {
129         str += "," + option.getName() + "=" + option.getValue().toString();
130       }
131     } else {
132       // For every boolean option not selected, explicitly
133       // turn it off here, but only if the default isn't zero
134       // or given.
135       if (option.getType() == FormatOption::OPTbool &&
136           default_value != "0" &&
137           default_value != "") {
138         str += "," + option.getName() + "=0";
139       }
140     }
141   }
142   return str;
143 }
144 
145 //------------------------------------------------------------------------
MakeOptionsNoLeadingComma(const QList<FormatOption> & options)146 static QString MakeOptionsNoLeadingComma(const QList<FormatOption>& options)
147 {
148   QString str = MakeOptions(options);
149   return (str.length()) != 0 ? str.mid(1) : str;
150 
151 }
152 
153 //------------------------------------------------------------------------
MainWindow(QWidget * parent)154 MainWindow::MainWindow(QWidget* parent): QMainWindow(parent)
155 {
156   ui_.setupUi(this);
157   setWindowTitle(appName);
158   babelVersion_ = findBabelVersion();
159   fmtChgInterlock_ = false;
160   loadDeviceNameCombos();
161 
162   connect(ui_.inputFileOptBtn,        SIGNAL(clicked()), this, SLOT(inputFileOptBtnClicked()));
163   connect(ui_.inputDeviceOptBtn,      SIGNAL(clicked()), this, SLOT(inputDeviceOptBtnClicked()));
164   connect(ui_.inputFileNameBrowseBtn, SIGNAL(clicked()), this, SLOT(browseInputFile()));
165 
166   ui_.outputFileOptBtn->setAutoExclusive(false);
167   ui_.outputDeviceOptBtn->setAutoExclusive(false);
168   connect(ui_.outputFileOptBtn,        SIGNAL(clicked()), this, SLOT(outputFileOptBtnClicked()));
169   connect(ui_.outputDeviceOptBtn,      SIGNAL(clicked()), this, SLOT(outputDeviceOptBtnClicked()));
170   connect(ui_.outputFileNameBrowseBtn, SIGNAL(clicked()), this, SLOT(browseOutputFile()));
171 
172   connect(ui_.actionQuit, SIGNAL(triggered()), this, SLOT(closeActionX()));
173   connect(ui_.actionHelp, SIGNAL(triggered()), this, SLOT(helpActionX()));
174   connect(ui_.actionAbout, SIGNAL(triggered()), this, SLOT(aboutActionX()));
175   connect(ui_.actionVisit_Website, SIGNAL(triggered()), this, SLOT(visitWebsiteActionX()));
176   connect(ui_.actionMake_a_Donation, SIGNAL(triggered()), this, SLOT(donateActionX()));
177   connect(ui_.actionUpgradeCheck, SIGNAL(triggered()), this, SLOT(upgradeCheckActionX()));
178   connect(ui_.actionPreferences, SIGNAL(triggered()), this, SLOT(preferencesActionX()));
179 
180   connect(ui_.inputFormatCombo,  SIGNAL(currentIndexChanged(int)),
181           this,                 SLOT(inputFormatChanged(int)));
182   connect(ui_.outputFormatCombo, SIGNAL(currentIndexChanged(int)),
183           this,                 SLOT(outputFormatChanged(int)));
184   connect(ui_.inputOptionsBtn,   SIGNAL(clicked()),
185           this,                 SLOT(inputOptionButtonClicked()));
186   connect(ui_.outputOptionsBtn, SIGNAL(clicked()),
187           this,                 SLOT(outputOptionButtonClicked()));
188   connect(ui_.moreOptionButton, SIGNAL(clicked()),
189           this,                 SLOT(moreOptionButtonClicked()));
190 
191   connect(ui_.buttonBox, SIGNAL(accepted()), this, SLOT(applyActionX()));
192   connect(ui_.buttonBox, SIGNAL(rejected()), this, SLOT(closeActionX()));
193   connect(ui_.buttonBox, SIGNAL(helpRequested()), this, SLOT(helpActionX()));
194 
195   connect(ui_.xlateFiltersBtn, SIGNAL(clicked()), this, SLOT(filtersClicked()));
196 
197   connect(ui_.inputFileNameText, SIGNAL(textEdited(QString)), this, SLOT(inputFileNameEdited()));
198   connect(ui_.outputFileNameText, SIGNAL(textEdited(QString)), this, SLOT(outputFileNameEdited()));
199 
200 #if defined (Q_OS_WIN)
201   // Windows users like the colored buttons.  They look out of place elsewhere.
202   ui_.buttonBox->button(QDialogButtonBox::Ok)->setIcon(QIcon(":/images/runit.png"));
203   ui_.buttonBox->button(QDialogButtonBox::Close)->setIcon(QIcon(":/images/exit.png"));
204 #endif
205 
206   ui_.inputOptionsText->setReadOnly(true);
207   ui_.outputOptionsText->setReadOnly(true);
208 #if 0
209   // 02/28/10  - let's try letting people edit these outside the browse.
210   ui.inputFileNameText->setReadOnly(true);
211   ui.outputFileNameText->setReadOnly(true);
212 #else
213   setAcceptDrops(true);
214 #endif
215   lights_[0] = QPixmap::fromImage(QImage(":/images/00.png").scaledToHeight(20, Qt::SmoothTransformation));
216   lights_[1] = QPixmap::fromImage(QImage(":/images/01.png").scaledToHeight(20, Qt::SmoothTransformation));
217   lights_[2] = QPixmap::fromImage(QImage(":/images/10.png").scaledToHeight(20, Qt::SmoothTransformation));
218   lights_[3] = QPixmap::fromImage(QImage(":/images/11.png").scaledToHeight(20, Qt::SmoothTransformation));
219 
220   ui_.outputWindow->setReadOnly(true);
221 
222   langPath_ = "/usr/local/share/gpsbabel";
223   langPath_.append("/translations/");
224 
225   // Start up in the current system language.
226   loadLanguage(QLocale::system().name());
227   loadFormats();
228 #if FAKE_LANGUAGE_MENU
229   createLanguageMenu();
230 #endif
231 
232   //--- Restore from registry
233   restoreSettings();
234 
235   upgrade = new UpgradeCheck(parent, formatList_, babelData_);
236   if (babelData_.startupVersionCheck_) {
237     upgrade->checkForUpgrade(babelVersion_, babelData_.upgradeCheckTime_,
238                              allowBetaUpgrades());
239   }
240 
241   if (!babelData_.ignoreVersionMismatch_ && babelVersion_ != VERSION) {
242     VersionMismatch vm(nullptr, babelVersion_, QString(VERSION));
243 
244     vm.exec();
245     babelData_.ignoreVersionMismatch_ = vm.neverAgain();
246   }
247 }
248 
249 //------------------------------------------------------------------------
~MainWindow()250 MainWindow::~MainWindow()
251 {
252 
253   delete upgrade;
254 
255 }
256 //------------------------------------------------------------------------
257 // Dynamic language switching courtesy of
258 // http://developer.qt.nokia.com/wiki/How_to_create_a_multi_language_application
259 // We create the menu entries dynamically, dependant on the existing
260 // translations.
261 #if FAKE_LANGUAGE_MENU
createLanguageMenu(void)262 void MainWindow::createLanguageMenu(void)
263 {
264   QActionGroup* langGroup = new QActionGroup(ui.menuHelp);
265   langGroup->setExclusive(true);
266   connect(langGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotLanguageChanged(QAction*)));
267 
268   // format systems language
269   QString defaultLocale = QLocale::system().name();       // e.g. "de_DE"
270   defaultLocale.truncate(defaultLocale.lastIndexOf('_')); // e.g. "de"
271 
272   QDir dir(langPath);
273   QStringList fileNames = dir.entryList(QStringList("GPSBabelFE*.qm"));
274 
275   for (int i = 0; i < fileNames.size(); ++i) {
276     // get locale extracted by filename
277     QString locale;
278     locale = fileNames[i];                  // "TranslationExample_de.qm"
279     locale.truncate(locale.lastIndexOf('.'));   // "TranslationExample_de"
280     locale.remove(0, locale.indexOf('_') + 1);   // "de"
281 
282     QString lang = QLocale::languageToString(QLocale(locale).language());
283 
284     QAction* action = new QAction(lang, this);
285     action->setCheckable(true);
286     action->setData(locale);
287 
288     ui.menuHelp->addAction(action);
289     langGroup->addAction(action);
290 
291     // set default translators and language checked
292     if (defaultLocale == locale) {
293       action->setChecked(true);
294     }
295   }
296 }
297 #endif //  FAKE_LANGUAGE_MENU
298 
299 //------------------------------------------------------------------------
300 // Called every time, when a menu entry of the language menu is called
slotLanguageChanged(QAction * action)301 void MainWindow::slotLanguageChanged(QAction* action)
302 {
303   if (nullptr != action) {
304     // load the language dependant on the action content.
305     loadLanguage(action->data().toString());
306   }
307 }
308 
switchTranslator(QTranslator & translator,const QString & filename)309 void MainWindow::switchTranslator(QTranslator& translator, const QString& filename)
310 {
311   // remove the old translator
312   qApp->removeTranslator(&translator);
313 
314   // load the new translator
315   if (translator.load(filename, langPath_)) {
316     qApp->installTranslator(&translator);
317   }
318 }
319 
loadLanguage(const QString & rLanguage)320 void MainWindow::loadLanguage(const QString& rLanguage)
321 {
322   if (currLang_ != rLanguage) {
323     currLang_ = rLanguage;
324     QLocale locale = QLocale(currLang_);
325     QLocale::setDefault(locale);
326 
327     switchTranslator(translator_, QString("gpsbabelfe_%1.qm").arg(rLanguage));
328     switchTranslator(translatorCore_, QString("gpsbabel_%1.qm").arg(rLanguage));
329     switchTranslator(translatorQt_, QString("qt_%1.qm").arg(rLanguage));
330   }
331 }
332 
changeEvent(QEvent * event)333 void MainWindow::changeEvent(QEvent* event)
334 {
335   if (nullptr != event) {
336     switch (event->type()) {
337     // This event is sent if a translator is loaded.
338     case QEvent::LanguageChange:
339       ui_.retranslateUi(this);
340       break;
341     // This event is sent if the system language changes.
342     case QEvent::LocaleChange: {
343       QString locale = QLocale::system().name();
344       locale.truncate(locale.lastIndexOf('_'));
345       loadLanguage(locale);
346     }
347     break;
348     default:
349       break;
350     }
351   }
352 
353   QMainWindow::changeEvent(event);
354 }
355 
356 //------------------------------------------------------------------------
loadInputDeviceNameCombo(const QString & format)357 void MainWindow::loadInputDeviceNameCombo(const QString& format)
358 {
359   ui_.inputDeviceNameCombo->clear();
360   // Later, we can probe the system for multiple USB devices and populate
361   // here.
362   if (formatSupportsUSB(format)) {
363     ui_.inputDeviceNameCombo->addItem("usb:");
364   }
365   if (formatSupportsSerial(format)) {
366     osLoadDeviceNameCombos(ui_.inputDeviceNameCombo);
367   }
368   // If only one choice, just disable it.
369   ui_.inputDeviceNameCombo->setEnabled(ui_.inputDeviceNameCombo->count() > 1);
370 }
371 
372 //------------------------------------------------------------------------
loadOutputDeviceNameCombo(const QString & format)373 void MainWindow::loadOutputDeviceNameCombo(const QString& format)
374 {
375   ui_.outputDeviceNameCombo->clear();
376   // Later, we can probe the system for multiple USB devices and populate
377   // here.
378   if (formatSupportsUSB(format)) {
379     ui_.outputDeviceNameCombo->addItem("usb:");
380   }
381   if (formatSupportsSerial(format)) {
382     osLoadDeviceNameCombos(ui_.outputDeviceNameCombo);
383   }
384   // If only one choice, just disable it.
385   ui_.outputDeviceNameCombo->setEnabled(ui_.outputDeviceNameCombo->count() > 1);
386 }
387 
388 //------------------------------------------------------------------------
loadDeviceNameCombos()389 void MainWindow::loadDeviceNameCombos()
390 {
391   loadInputDeviceNameCombo("");
392   loadOutputDeviceNameCombo("");
393 }
394 //------------------------------------------------------------------------
inputFileOptBtnClicked()395 void MainWindow::inputFileOptBtnClicked()
396 {
397   fmtChgInterlock_ = true;
398   QString fmt = babelData_.inputFileFormat_;
399   ui_.inputStackedWidget->setCurrentWidget(ui_.inputFilePage);
400   QList<int>indices = inputFileFormatIndices();
401   ui_.inputFormatCombo->clear();
402   for (int i=0; i<indices.size(); i++) {
403     int k = indices[i];
404     if (!formatList_[k].isHidden()) {
405       ui_.inputFormatCombo->addItem(formatList_[k].getDescription(), QVariant(k));
406     }
407   }
408   setComboToFormat(ui_.inputFormatCombo, fmt, true);
409   fmtChgInterlock_ = false;
410 }
411 
412 //------------------------------------------------------------------------
inputDeviceOptBtnClicked()413 void MainWindow::inputDeviceOptBtnClicked()
414 {
415   fmtChgInterlock_ = true;
416   QString fmt = babelData_.inputDeviceFormat_;
417   ui_.inputStackedWidget->setCurrentWidget(ui_.inputDevicePage);
418   QList<int>indices = inputDeviceFormatIndices();
419   ui_.inputFormatCombo->clear();
420   for (int i=0; i<indices.size(); i++) {
421     int k = indices[i];
422     if (!formatList_[k].isHidden()) {
423       ui_.inputFormatCombo->addItem(formatList_[k].getDescription(), QVariant(k));
424     }
425   }
426   setComboToFormat(ui_.inputFormatCombo, fmt, false);
427   fmtChgInterlock_ = false;
428 }
429 
430 //------------------------------------------------------------------------
outputFileOptBtnClicked()431 void MainWindow:: outputFileOptBtnClicked()
432 {
433   fmtChgInterlock_ = true;
434   if (ui_.outputFileOptBtn->isChecked()) {
435     ui_.outputFilePage->setEnabled(true);
436     ui_.outputDeviceOptBtn->setChecked(false);
437     QString fmt = babelData_.outputFileFormat_;
438     ui_.outputStackedWidget->setCurrentWidget(ui_.outputFilePage);
439     QList<int>indices = outputFileFormatIndices();
440     ui_.outputFormatCombo->clear();
441     for (int i=0; i<indices.size(); i++) {
442       int k = indices[i];
443       if (!formatList_[k].isHidden()) {
444         ui_.outputFormatCombo->addItem(formatList_[k].getDescription(), QVariant(k));
445       }
446     }
447     setComboToFormat(ui_.outputFormatCombo, fmt, true);
448   } else {
449     ui_.outputStackedWidget->setCurrentWidget(ui_.outputFilePage);
450     ui_.outputFilePage->setEnabled(false);
451   }
452   fmtChgInterlock_ = false;
453 }
454 
455 //------------------------------------------------------------------------
outputDeviceOptBtnClicked()456 void MainWindow:: outputDeviceOptBtnClicked()
457 {
458   fmtChgInterlock_ = true;
459   if (ui_.outputDeviceOptBtn->isChecked()) {
460     ui_.outputDevicePage->setEnabled(true);
461     ui_.outputFileOptBtn->setChecked(false);
462     QString fmt = babelData_.outputDeviceFormat_;
463     ui_.outputStackedWidget->setCurrentWidget(ui_.outputDevicePage);
464     QList<int>indices = outputDeviceFormatIndices();
465     ui_.outputFormatCombo->clear();
466     for (int i=0; i<indices.size(); i++) {
467       int k = indices[i];
468       if (!formatList_[k].isHidden()) {
469         ui_.outputFormatCombo->addItem(formatList_[k].getDescription(), QVariant(k));
470       }
471     }
472     setComboToFormat(ui_.outputFormatCombo, fmt, false);
473   } else {
474     ui_.outputStackedWidget->setCurrentWidget(ui_.outputDevicePage);
475     ui_.outputDevicePage->setEnabled(false);
476   }
477   fmtChgInterlock_ = false;
478 }
inputFileNameEdited()479 void MainWindow::inputFileNameEdited()
480 {
481   babelData_.inputFileNames_.clear();
482   babelData_.inputFileNames_ << ui_.inputFileNameText->text();
483 }
484 
outputFileNameEdited()485 void MainWindow::outputFileNameEdited()
486 {
487   babelData_.outputFileName_ = ui_.outputFileNameText->text();
488 
489 }
490 
491 //------------------------------------------------------------------------
filterForFormat(int idx)492 QString MainWindow::filterForFormat(int idx)
493 {
494   QString str = formatList_[idx].getDescription();
495   str.replace(QRegExp("\\("), "[");
496   str.replace(QRegExp("\\)"), "]");
497   QStringList extensions = formatList_[idx].getExtensions();
498 
499   // If we don't have any meaningful extensions available for this format,
500   // don't be clever here; just fall through to "All files" case.
501   if (!extensions.empty() && !extensions[0].isEmpty()) {
502     str += " (";
503     for (int i=0; i<extensions.size(); i++) {
504       if (i!= 0) {
505         str += " ";
506       }
507       str += "*." + extensions[i];
508     }
509     str += ");;";
510   }
511   str += "All Files (*.*)";
512   return str;
513 }
514 //------------------------------------------------------------------------
ensureExtensionPresent(const QString & name,int idx)515 QString MainWindow::ensureExtensionPresent(const QString& name, int idx)
516 {
517   QString outname = name;
518   if (QFileInfo(name).suffix().length() == 0) {
519     QStringList extensions = formatList_[idx].getExtensions();
520     if (!extensions.empty() && !extensions[0].isEmpty()) {
521       outname += "." + extensions[0];
522     }
523   }
524   return outname;
525 }
526 
527 //------------------------------------------------------------------------
filterForFormatIncludes(int idx,const QString & fmt)528 bool MainWindow::filterForFormatIncludes(int idx, const QString& fmt)
529 {
530   QStringList extensions = formatList_[idx].getExtensions();
531   for (int i=0; i<extensions.size(); i++) {
532     if (fmt == extensions[i]) {
533       return true;
534     }
535   }
536   return false;
537 }
538 
539 //------------------------------------------------------------------------
currentComboFormatIndex(QComboBox * comboBox)540 int MainWindow::currentComboFormatIndex(QComboBox* comboBox)
541 {
542   int idx = comboBox->currentIndex();
543   if (idx<0 || idx >= comboBox->count()) {
544     //    QMessageBox::critical(0, appName, "*** Internal Error -- current combo index is invalid!");
545     return 0;
546   }
547   return comboBox->itemData(idx).toInt();
548 }
549 //------------------------------------------------------------------------
browseInputFile()550 void MainWindow::browseInputFile()
551 {
552   QString startFile = !babelData_.inputFileNames_.empty() ? babelData_.inputFileNames_[0] : babelData_.inputBrowse_;
553   int idx = currentComboFormatIndex(ui_.inputFormatCombo);
554   QFileInfo finfo(startFile);
555   if (!finfo.isDir() && (!filterForFormatIncludes(idx, finfo.suffix()))) {
556     startFile = finfo.dir().absolutePath();
557   }
558 
559   QStringList userList =
560     QFileDialog::getOpenFileNames(nullptr, tr("Select one or more input files"),
561                                   startFile,
562                                   filterForFormat(idx));
563   if (!userList.empty()) {
564     babelData_.inputBrowse_ = userList[0];
565     babelData_.inputFileNames_ = userList;
566     QString str;
567     for (int i=0; i<babelData_.inputFileNames_.size(); i++) {
568       if (i != 0) {
569         str += ", ";
570       }
571       str += "\"" + babelData_.inputFileNames_[i] + "\"";
572     }
573     ui_.inputFileNameText->setText(str);
574   }
575 }
576 
577 //------------------------------------------------------------------------
browseOutputFile()578 void MainWindow::browseOutputFile()
579 {
580   int idx = currentComboFormatIndex(ui_.outputFormatCombo);
581   QString startFile = babelData_.outputFileName_.length() == 0 ? babelData_.outputBrowse_ : babelData_.outputFileName_;
582   QFileInfo finfo(startFile);
583   if (!finfo.isDir() && (!filterForFormatIncludes(idx, finfo.suffix()))) {
584     startFile = finfo.dir().absolutePath();
585   }
586 
587   QString str =
588     QFileDialog::getSaveFileName(nullptr, tr("Output File Name"),
589                                  startFile,
590                                  filterForFormat(idx));
591   if (str.length() != 0) {
592     str = ensureExtensionPresent(str, idx);
593     babelData_.outputBrowse_ = str;
594     babelData_.outputFileName_ = str;
595     ui_.outputFileNameText->setText(str);
596   }
597 }
598 
599 //------------------------------------------------------------------------
inputFileFormatIndices()600 QList<int> MainWindow::inputFileFormatIndices()
601 {
602   QList<int>indices;
603   for (int i=0; i<formatList_.size(); i++) {
604     if (formatList_[i].isReadSomething() && formatList_[i].isFileFormat()) {
605       indices<<i;
606     }
607   }
608   return indices;
609 }
610 
611 //------------------------------------------------------------------------
inputDeviceFormatIndices()612 QList<int> MainWindow::inputDeviceFormatIndices()
613 {
614   QList<int>indices;
615   for (int i=0; i<formatList_.size(); i++) {
616     if (formatList_[i].isReadSomething() && formatList_[i].isDeviceFormat()) {
617       indices<<i;
618     }
619   }
620   return indices;
621 }
622 
623 //------------------------------------------------------------------------
outputFileFormatIndices()624 QList<int> MainWindow::outputFileFormatIndices()
625 {
626   QList<int>indices;
627   for (int i=0; i<formatList_.size(); i++) {
628     if (formatList_[i].isWriteSomething() && formatList_[i].isFileFormat()) {
629       indices<<i;
630     }
631   }
632   return indices;
633 }
634 
635 //------------------------------------------------------------------------
outputDeviceFormatIndices()636 QList<int> MainWindow::outputDeviceFormatIndices()
637 {
638   QList<int>indices;
639   for (int i=0; i<formatList_.size(); i++) {
640     if (formatList_[i].isWriteSomething() && formatList_[i].isDeviceFormat()) {
641       indices<<i;
642     }
643   }
644   return indices;
645 }
646 
647 //------------------------------------------------------------------------
loadFormats()648 void MainWindow::loadFormats()
649 {
650   if (!FormatLoad().getFormats(formatList_)) {
651     QMessageBox::information(nullptr, QString(appName),
652                              tr("Error reading format configuration.  "
653                                 "Check that the backend program \"gpsbabel\" is properly installed "
654                                 "and is in the current PATH\n\n"
655                                 "This program cannot continue."));
656     exit(1);
657   }
658   if (inputFileFormatIndices().empty() ||
659       inputDeviceFormatIndices().empty() ||
660       outputFileFormatIndices().empty() ||
661       outputDeviceFormatIndices().empty()) {
662     QMessageBox::information(nullptr, QString(appName),
663                              tr("Some file/device formats were not found during initialization.  "
664                                 "Check that the backend program \"gpsbabel\" is properly installed "
665                                 "and is in the current PATH\n\n"
666                                 "This program cannot continue."));
667     exit(1);
668   }
669 }
670 //------------------------------------------------------------------------
iconIndex(bool a,bool b)671 static int iconIndex(bool a, bool b)
672 {
673   return ((a?1:0)*2) + (b?1:0);
674 }
675 
676 //------------------------------------------------------------------------
setIndicatorLights(QLabel * label,const QString & type,int code)677 void MainWindow::setIndicatorLights(QLabel* label, const QString& type, int code)
678 {
679   label->setPixmap(lights_[code]);
680   QString s;
681   switch (code) {
682   default:
683   case 0:
684     s = tr("Input and output formats do not support %1").arg(type);
685     break;
686   case 1:
687     s = tr("Input does not support %1; output format supports %1").arg(type);
688     break;
689   case 2:
690     s = tr("Input format supports %1; output format does not support %1").arg(type);
691     break;
692   case 3:
693     s = tr("Both input and output formats support %1").arg(type);
694     break;
695   }
696   label->setToolTip(s);
697 }
698 
699 //------------------------------------------------------------------------
crossCheckInOutFormats()700 void MainWindow::crossCheckInOutFormats()
701 {
702   if (ui_.inputFormatCombo->count() == 0 ||
703       ui_.outputFormatCombo->count() == 0) {
704     // During format/device switch this is true
705     return;
706   }
707   Format ifmt = formatList_[currentComboFormatIndex(ui_.inputFormatCombo)];
708   Format ofmt = formatList_[currentComboFormatIndex(ui_.outputFormatCombo)];
709 
710   ui_.xlateWayPtsCk->setEnabled(ifmt.isReadWaypoints() && ofmt.isWriteWaypoints());
711   ui_.xlateTracksCk->setEnabled(ifmt.isReadTracks()    && ofmt.isWriteTracks());
712   ui_.xlateRoutesCk->setEnabled(ifmt.isReadRoutes()    && ofmt.isWriteRoutes());
713 
714   setIndicatorLights(ui_.wayPtLabel, tr("waypoints"), iconIndex(ifmt.isReadWaypoints(), ofmt.isWriteWaypoints()));
715   setIndicatorLights(ui_.trackLabel, tr("tracks"), iconIndex(ifmt.isReadTracks(), ofmt.isWriteTracks()));
716   setIndicatorLights(ui_.routeLabel, tr("routes"), iconIndex(ifmt.isReadRoutes(), ofmt.isWriteRoutes()));
717 }
718 
719 //------------------------------------------------------------------------
displayOptionsText(QLineEdit * le,QComboBox * combo,bool isInput)720 void MainWindow::displayOptionsText(QLineEdit* le, QComboBox* combo, bool isInput)
721 {
722   int fidx = combo->itemData(combo->currentIndex()).toInt();
723   if (isInput) {
724     le->setText(MakeOptionsNoLeadingComma(formatList_[fidx].getInputOptions()));
725   } else {
726     le->setText(MakeOptionsNoLeadingComma(formatList_[fidx].getOutputOptions()));
727   }
728 
729 }
730 
731 //------------------------------------------------------------------------
setComboToFormat(QComboBox * comboBox,const QString & name,bool isFile)732 void MainWindow::setComboToFormat(QComboBox* comboBox, const QString& name, bool isFile)
733 {
734   int fidx = -1;
735   for (int i=0; i<formatList_.size(); i++) {
736     if (formatList_[i].getName() == name &&
737         formatList_[i].isFileFormat() == isFile) {
738       fidx = i;
739       break;
740     }
741   }
742   if (fidx >=0) {
743     for (int i=0; i<comboBox->count(); i++) {
744       if (comboBox->itemData(i).toInt() == fidx) {
745         comboBox->setCurrentIndex(i);
746         break;
747       }
748     }
749   }
750 }
751 
752 //------------------------------------------------------------------------
formatSupportsUSB(const QString & format)753 bool MainWindow::formatSupportsUSB(const QString& format)
754 {
755   return (format == "garmin" || format == "delbin");
756 }
757 
758 //------------------------------------------------------------------------
formatSupportsSerial(const QString & format)759 bool MainWindow::formatSupportsSerial(const QString& format)
760 {
761   return (format != "delbin");
762 }
763 
764 //------------------------------------------------------------------------
inputFormatChanged(int comboIdx)765 void MainWindow::inputFormatChanged(int comboIdx)
766 {
767   if (fmtChgInterlock_) {
768     return;
769   }
770   int fidx = ui_.inputFormatCombo->itemData(comboIdx).toInt();
771   ui_.inputOptionsBtn->setEnabled(!formatList_[fidx].getInputOptions().empty());
772   displayOptionsText(ui_.inputOptionsText,  ui_.inputFormatCombo, true);
773   crossCheckInOutFormats();
774 
775   if (ui_.inputFileOptBtn->isChecked()) {
776     babelData_.inputFileFormat_ =formatList_[fidx].getName();
777   } else {
778     babelData_.inputDeviceFormat_ = formatList_[fidx].getName();
779   }
780 
781   loadInputDeviceNameCombo(formatList_[fidx].getName());
782 }
783 
784 //------------------------------------------------------------------------
outputFormatChanged(int comboIdx)785 void MainWindow::outputFormatChanged(int comboIdx)
786 {
787   if (fmtChgInterlock_) {
788     return;
789   }
790   int fidx = ui_.outputFormatCombo->itemData(comboIdx).toInt();
791   ui_.outputOptionsBtn->setEnabled(!formatList_[fidx].getOutputOptions().empty());
792   displayOptionsText(ui_.outputOptionsText,  ui_.outputFormatCombo, false);
793   crossCheckInOutFormats();
794 
795   if (ui_.outputFileOptBtn->isChecked()) {
796     babelData_.outputFileFormat_ =formatList_[fidx].getName();
797   } else if (ui_.outputDeviceOptBtn->isChecked()) {
798     babelData_.outputDeviceFormat_ = formatList_[fidx].getName();
799   }
800 
801   loadOutputDeviceNameCombo(formatList_[fidx].getName());
802 }
803 
804 //------------------------------------------------------------------------
inputOptionButtonClicked()805 void MainWindow::inputOptionButtonClicked()
806 {
807   int fidx = currentComboFormatIndex(ui_.inputFormatCombo);
808   if (formatList_[fidx].getInputOptionsRef()->empty()) {
809     QMessageBox::information
810     (nullptr, appName,
811      tr("There are no input options for format \"%1\"").arg(formatList_[fidx].getDescription()));
812   } else {
813     OptionsDlg optionDlg(nullptr,
814                          formatList_[fidx].getName(),
815                          formatList_[fidx].getInputOptionsRef(),
816                          formatList_[fidx].getHtml());
817     optionDlg.setWindowTitle(QString(appName) + " - " + tr("Options for %1").arg(formatList_[fidx].getName()));
818     optionDlg.exec();
819     displayOptionsText(ui_.inputOptionsText,  ui_.inputFormatCombo, true);
820   }
821 }
822 
823 //------------------------------------------------------------------------
outputOptionButtonClicked()824 void MainWindow::outputOptionButtonClicked()
825 {
826   int fidx = currentComboFormatIndex(ui_.outputFormatCombo);
827   if (formatList_[fidx].getOutputOptionsRef()->empty()) {
828     QMessageBox::information
829     (nullptr, appName,
830      tr("There are no output options for format \"%1\"").arg(formatList_[fidx].getDescription()));
831   } else {
832     OptionsDlg optionDlg(nullptr,
833                          formatList_[fidx].getName(),
834                          formatList_[fidx].getOutputOptionsRef(),
835                          formatList_[fidx].getHtml());
836     optionDlg.setWindowTitle(QString(appName) + " - " + tr("Options for %1").arg(formatList_[fidx].getName()));
837     optionDlg.exec();
838     displayOptionsText(ui_.outputOptionsText,  ui_.outputFormatCombo, false);
839   }
840 }
841 
842 
843 
844 //------------------------------------------------------------------------
isOkToGo()845 bool MainWindow::isOkToGo()
846 {
847   if (!((ui_.xlateWayPtsCk->isChecked() && ui_.xlateWayPtsCk->isEnabled()) ||
848         (ui_.xlateRoutesCk->isChecked() && ui_.xlateRoutesCk->isEnabled()) ||
849         (ui_.xlateTracksCk->isChecked() && ui_.xlateTracksCk->isEnabled()))) {
850     QMessageBox::information(nullptr, QString(appName), tr("No valid waypoints/routes/tracks translation specified"));
851     return false;
852   }
853 
854   // Paper over what didn't happen in inputBrowse() if the user edited
855   // the filename fields directly.
856   if ((babelData_.inputType_ == BabelData::fileType_) &&
857       (babelData_.inputFileNames_.empty()) &&
858       (!ui_.inputFileNameText->text().isEmpty())) {
859     babelData_.inputFileNames_ << ui_.inputFileNameText->text();
860   }
861   if ((babelData_.outputType_ == BabelData::fileType_) &&
862       (babelData_.outputFileName_.size() == 0) &&
863       (!ui_.outputFileNameText->text().isEmpty())) {
864     babelData_.outputFileName_ = ui_.outputFileNameText->text();
865   }
866 
867   if ((babelData_.inputType_ == BabelData::fileType_) &&
868       (babelData_.inputFileNames_.empty())) {
869     QMessageBox::information(nullptr, QString(appName), tr("No input file specified"));
870     return false;
871   }
872 
873   if (babelData_.outputType_ == BabelData::noType_ && babelData_.previewGmap_) {
874   }
875   if (babelData_.outputType_ == BabelData::noType_ && !babelData_.previewGmap_) {
876     QMessageBox::information(nullptr, QString(appName), tr("No valid output specified"));
877     return false;
878   }
879   if (babelData_.outputType_ == BabelData::fileType_ &&
880       babelData_.outputFileName_.length() == 0) {
881     QMessageBox::information(nullptr, QString(appName), tr("No output file specified"));
882     return false;
883   }
884   return true;
885 }
886 
887 //------------------------------------------------------------------------
runGpsbabel(const QStringList & args,QString & errorString,QString & outputString)888 bool MainWindow::runGpsbabel(const QStringList& args, QString& errorString,
889                              QString& outputString)
890 {
891   auto* proc = new QProcess(nullptr);
892   QString name = "gpsbabel";
893   proc->start(QApplication::applicationDirPath() + '/' + name, args);
894   auto* waitDlg = new ProcessWaitDialog(nullptr, proc);
895 
896   if (proc->state() == QProcess::NotRunning) {
897     errorString = QString(tr("Process \"%1\" did not start")).arg(name);
898     return false;
899   }
900 
901   waitDlg->show();
902   waitDlg->exec();
903   int exitCode = -1;
904   bool retStatus = false;
905   if (waitDlg->getExitedNormally()) {
906     exitCode = waitDlg->getExitCode();
907     if (exitCode == 0) {
908       retStatus = true;
909     } else  {
910       errorString =
911         QString(tr("Process exited unsuccessfully with code %1"))
912         .arg(exitCode);
913       retStatus = false;
914     }
915   } else {
916     retStatus = false;
917     errorString = waitDlg->getErrorString();
918   }
919   outputString = waitDlg->getOutputString();
920   delete proc;
921   delete waitDlg;
922   return retStatus;
923 }
924 
925 //------------------------------------------------------------------------
formatIndexFromName(bool isFile,const QString & nm)926 int MainWindow::formatIndexFromName(bool isFile, const QString& nm)
927 {
928   for (int i= 0; i<formatList_.size(); i++) {
929     if (nm == formatList_[i].getName() && formatList_[i].isFileFormat() == isFile) {
930       return i;
931     }
932   }
933   return 0;
934 }
935 
936 //------------------------------------------------------------------------
charSetFromCombo(QComboBox * combo)937 QString MainWindow::charSetFromCombo(QComboBox* combo)
938 {
939   int i = combo->itemData((combo->currentIndex())).toInt();
940   return (i >=0) ? charSets_[i] : QString();
941 }
942 
943 //------------------------------------------------------------------------
setComboToCharSet(QComboBox * combo,const QString & cset)944 void MainWindow::setComboToCharSet(QComboBox* combo, const QString& cset)
945 {
946   for (int i=0; i<charSets_.size(); i++) {
947     if (charSets_[i] == cset) {
948       combo->setCurrentIndex(i+1); // first index is default;
949     }
950   }
951 }
952 //------------------------------------------------------------------------
applyActionX()953 void MainWindow::applyActionX()
954 {
955   getWidgetValues();
956   if (!isOkToGo()) {
957     return;
958   }
959 
960   QStringList args;
961 
962   if (babelData_.debugLevel_ >=0) {
963     args << QString("-D%1").arg(babelData_.debugLevel_);
964   }
965   if (babelData_.synthShortNames_) {
966     args << "-s";
967   }
968 
969   Format ifmt = formatList_[currentComboFormatIndex(ui_.inputFormatCombo)];
970   Format ofmt = formatList_[currentComboFormatIndex(ui_.outputFormatCombo)];
971 
972   if (babelData_.xlateWayPts_ && ifmt.isReadWaypoints() && ofmt.isWriteWaypoints()) {
973     args << "-w";
974   }
975   if (babelData_.xlateRoutes_ && ifmt.isReadRoutes()    && ofmt.isWriteRoutes()) {
976     args << "-r";
977   }
978   if (babelData_.xlateTracks_ && ifmt.isReadTracks()    && ofmt.isWriteTracks()) {
979     args << "-t";
980   }
981 
982   // Input type, with options
983   bool iisFile = (babelData_.inputType_ == BabelData::fileType_);
984   int fidx = formatIndexFromName(iisFile, iisFile ?
985                                  babelData_.inputFileFormat_ : babelData_.inputDeviceFormat_);
986   args << "-i";
987   args << (formatList_[fidx].getName() + MakeOptions(formatList_[fidx].getInputOptions()));
988 
989   // Input file(s) or device
990   int read_use_count = 0;
991   if (babelData_.inputType_ == BabelData::fileType_) {
992     for (int i=0; i<babelData_.inputFileNames_.size(); i++) {
993       args << "-f" << babelData_.inputFileNames_[i];
994       read_use_count++;
995     }
996   } else {
997     args << "-f" << babelData_.inputDeviceName_;
998     read_use_count++;
999   }
1000   formatList_[fidx].bumpReadUseCount(read_use_count);
1001 
1002   // --- Filters!
1003   args << filterData_.getAllFilterStrings();
1004 
1005   // Output type, with options
1006   if (babelData_.outputType_ != BabelData::noType_) {
1007     bool outIsFile = (babelData_.outputType_ == BabelData::fileType_);
1008     fidx = formatIndexFromName(outIsFile, (outIsFile ?
1009                                            babelData_.outputFileFormat_ : babelData_.outputDeviceFormat_));
1010     args << "-o";
1011     args << (formatList_[fidx].getName() + MakeOptions(formatList_[fidx].getOutputOptions()));
1012 
1013     // output file or device option
1014     if (outIsFile) {
1015       if (babelData_.outputFileName_ != "") {
1016         args << "-F" << babelData_.outputFileName_;
1017       }
1018     } else if (babelData_.outputType_ == BabelData::deviceType_) {
1019       args << "-F" << babelData_.outputDeviceName_;
1020     }
1021     // GUI only ever writes a single file at a time.
1022     formatList_[fidx].bumpWriteUseCount(1);
1023   }
1024 
1025   // Now output for preview in google maps
1026   QString tempName;
1027   if (babelData_.previewGmap_) {
1028     QTemporaryFile ftemp;
1029     ftemp.open();
1030     tempName = ftemp.fileName();
1031     ftemp.close();
1032 
1033     // Ideally, expost this in the UI.  For now, just split the track
1034     // if we've no recorded fixes for > 5 mins and we've moved > 300 meters.
1035     //args << "-x";
1036     //args << "track,pack,sdistance=0.3k,split=5m";
1037 
1038     args << "-o";
1039     args << "gpx";
1040     args << "-F" << tempName;
1041   }
1042 
1043   ui_.outputWindow->clear();
1044   ui_.outputWindow->appendPlainText("gpsbabel " + args.join(" "));
1045 
1046   QString errorString;
1047   QString outputString;
1048   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1049   bool x = runGpsbabel(args, errorString, outputString);
1050   QApplication::restoreOverrideCursor();
1051 
1052   ui_.outputWindow->appendPlainText(outputString);
1053   if (x) {
1054     ui_.outputWindow->appendPlainText(tr("Translation successful"));
1055     if (babelData_.previewGmap_) {
1056       this->hide();
1057       GMapDialog dlg(nullptr, tempName, babelData_.debugLevel_ >=1 ? ui_.outputWindow : nullptr);
1058       dlg.show();
1059       dlg.exec();
1060       QFile(tempName).remove();
1061       this->show();
1062     }
1063   } else {
1064     ui_.outputWindow->appendPlainText(tr("Error running gpsbabel: %1\n").arg(errorString));
1065   }
1066 }
1067 
1068 //------------------------------------------------------------------------
closeActionX()1069 void MainWindow::closeActionX()
1070 {
1071   QDateTime wt= upgrade->getUpgradeWarningTime();
1072   if (wt.isValid()) {
1073     babelData_.upgradeCheckTime_ = wt;
1074   }
1075   babelData_.runCount_++;
1076 
1077   QDateTime now = QDateTime::currentDateTime();
1078   if ((babelData_.runCount_ == 1) ||
1079       ((babelData_.runCount_ > 5) && (babelData_.donateSplashed_.daysTo(now) > 30))) {
1080     Donate donate(nullptr);
1081     if (babelData_.donateSplashed_.date() == QDate(2010,1,1)) {
1082       donate.showNever(false);
1083     }
1084     donate.exec();
1085     babelData_.donateSplashed_ = now;
1086   }
1087   saveSettings();
1088   delete upgrade;
1089   upgrade = nullptr;
1090   qApp->exit(0);
1091 }
1092 
1093 //------------------------------------------------------------------------
closeEvent(QCloseEvent *)1094 void MainWindow::closeEvent(QCloseEvent* /*event*/)
1095 {
1096   closeActionX();
1097 }
1098 
1099 //------------------------------------------------------------------------
donateActionX()1100 void MainWindow::donateActionX()
1101 {
1102   QDesktopServices::openUrl(QString("https://www.gpsbabel.org/contribute.html?gbversion=" VERSION));
1103 }
1104 
1105 //------------------------------------------------------------------------
visitWebsiteActionX()1106 void MainWindow::visitWebsiteActionX()
1107 {
1108   QDesktopServices::openUrl(QString("https://www.gpsbabel.org"));
1109 }
1110 
1111 //------------------------------------------------------------------------
dragEnterEvent(QDragEnterEvent * event)1112 void MainWindow::dragEnterEvent(QDragEnterEvent* event)
1113 {
1114   event->acceptProposedAction();
1115 }
1116 
dropEvent(QDropEvent * event)1117 void MainWindow::dropEvent(QDropEvent* event)
1118 {
1119   foreach (QString format, event->mimeData()->formats()) {
1120     if (format == "text/uri-list") {
1121       QList<QUrl> urlList = event->mimeData()->urls();
1122       babelData_.inputFileNames_.clear();
1123       for (int i = 0; i < urlList.size(); ++i) {
1124         QFileInfo file_info(urlList.at(i).toLocalFile());
1125         QString name = file_info.filePath();
1126         QString ext = file_info.suffix();
1127 
1128         QString fmt = getFormatNameForExtension(ext);
1129         setComboToFormat(ui_.inputFormatCombo, fmt, true);
1130         ui_.inputFileNameText->setText(name);
1131         babelData_.inputFileNames_ << ui_.inputFileNameText->text();
1132         event->acceptProposedAction();
1133       }
1134     }
1135   }
1136 }
1137 //------------------------------------------------------------------------
setComboToDevice(QComboBox * comboBox,const QString & name)1138 void MainWindow::setComboToDevice(QComboBox* comboBox, const QString& name)
1139 {
1140   for (int i=0; i<comboBox->count(); i++) {
1141     if (comboBox->itemText(i) == name) {
1142       comboBox->setCurrentIndex(i);
1143       break;
1144     }
1145   }
1146 }
1147 
1148 //------------------------------------------------------------------------
saveSettings()1149 void MainWindow::saveSettings()
1150 {
1151   getWidgetValues();
1152 
1153   QSettings settings;
1154   babelData_.saveSettings(settings);
1155   for (int i=0; i<formatList_.size(); i++) {
1156     formatList_[i].saveSettings(settings);
1157   }
1158   for (int i=0; i<filterData_.filters.size(); i++) {
1159     filterData_.filters[i]->saveSettings(settings);
1160   }
1161 }
1162 
1163 //------------------------------------------------------------------------
restoreSettings()1164 void MainWindow::restoreSettings()
1165 {
1166   QSettings settings;
1167   babelData_.restoreSettings(settings);
1168   for (int i=0; i<formatList_.size(); i++) {
1169     formatList_[i].restoreSettings(settings);
1170   }
1171 
1172   for (int i=0; i<filterData_.filters.size(); i++) {
1173     filterData_.filters[i]->restoreSettings(settings);
1174   }
1175 
1176   setWidgetValues();
1177 }
1178 
1179 //------------------------------------------------------------------------
resetFormatDefaults()1180 void MainWindow::resetFormatDefaults()
1181 {
1182   int ret = QMessageBox::warning
1183             (this, QString(appName),
1184              tr("Are you sure you want to reset all format options to default values?"),
1185              QMessageBox::Yes | QMessageBox::No);
1186   if (ret == QMessageBox::Yes) {
1187     for (int i=0; i<formatList_.size(); i++) {
1188       formatList_[i].setToDefault();
1189     }
1190     displayOptionsText(ui_.inputOptionsText,  ui_.inputFormatCombo, true);
1191     displayOptionsText(ui_.outputOptionsText,  ui_.outputFormatCombo, false);
1192   }
1193 }
1194 
1195 //------------------------------------------------------------------------
moreOptionButtonClicked()1196 void MainWindow::moreOptionButtonClicked()
1197 {
1198   AdvDlg advDlg(nullptr, babelData_.synthShortNames_,
1199                 babelData_.previewGmap_, babelData_.debugLevel_);
1200   connect(advDlg.formatButton(), SIGNAL(clicked()),
1201           this, SLOT(resetFormatDefaults()));
1202   advDlg.exec();
1203 }
1204 //------------------------------------------------------------------------
aboutActionX()1205 void MainWindow::aboutActionX()
1206 {
1207   AboutDlg aboutDlg(nullptr, babelVersion_, QString(appName) + QString(" " VERSION), babelData_.installationUuid_);
1208   aboutDlg.setWindowTitle(tr("About %1").arg(appName));
1209   aboutDlg.exec();
1210 }
1211 
1212 //------------------------------------------------------------------------
upgradeCheckActionX()1213 void MainWindow::upgradeCheckActionX()
1214 {
1215   upgrade->checkForUpgrade(babelVersion_,
1216                            QDateTime(QDate(2000, 1, 1), QTime(0, 0)),
1217                            allowBetaUpgrades());
1218 }
1219 
1220 //------------------------------------------------------------------------
preferencesActionX()1221 void MainWindow::preferencesActionX()
1222 {
1223   Preferences preferences(nullptr, formatList_, babelData_);
1224   preferences.exec();
1225 
1226   // We may have changed the list of displayed formats.  Resynchronize.
1227   setWidgetValues();
1228 }
1229 
1230 
1231 //------------------------------------------------------------------------
helpActionX()1232 void MainWindow::helpActionX()
1233 {
1234   ShowHelp("index.html");
1235 }
1236 //------------------------------------------------------------------------
filtersClicked()1237 void MainWindow::filtersClicked()
1238 {
1239   FilterDialog dlg(nullptr, filterData_);
1240   dlg.runDialog();
1241   updateFilterStatus();
1242 }
1243 
1244 
1245 //------------------------------------------------------------------------
updateFilterStatus()1246 void MainWindow::updateFilterStatus()
1247 {
1248   bool filterActive = !filterData_.getAllFilterStrings().empty();
1249   ui_.filterStatus->setEnabled(filterActive);
1250   if (filterActive) {
1251     ui_.filterStatus->setToolTip(tr("One or more data filters are active"));
1252   } else {
1253     ui_.filterStatus->setToolTip(tr("No data filters are active"));
1254   }
1255 }
1256 //------------------------------------------------------------------------
setWidgetValues()1257 void MainWindow::setWidgetValues()
1258 {
1259   if (babelData_.inputType_ == BabelData::fileType_) {
1260     ui_.inputFileOptBtn->setChecked(true);
1261     inputFileOptBtnClicked();
1262     setComboToFormat(ui_.inputFormatCombo, babelData_.inputFileFormat_, true);
1263     ui_.inputStackedWidget->setCurrentWidget(ui_.inputFilePage);
1264   } else {
1265     ui_.inputDeviceOptBtn->setChecked(true);
1266     inputDeviceOptBtnClicked();
1267     setComboToFormat(ui_.inputFormatCombo, babelData_.inputDeviceFormat_, false);
1268     loadInputDeviceNameCombo(babelData_.inputDeviceFormat_);
1269     ui_.inputStackedWidget->setCurrentWidget(ui_.inputDevicePage);
1270   }
1271   setComboToDevice(ui_.inputDeviceNameCombo, babelData_.inputDeviceName_);
1272 
1273   if (babelData_.outputType_ == BabelData::fileType_) {
1274     ui_.outputFileOptBtn->setChecked(true);
1275     outputFileOptBtnClicked();
1276     setComboToFormat(ui_.outputFormatCombo, babelData_.outputFileFormat_, true);
1277     ui_.outputStackedWidget->setCurrentWidget(ui_.outputFilePage);
1278   } else if (babelData_.outputType_ == BabelData::deviceType_) {
1279     ui_.outputDeviceOptBtn->setChecked(true);
1280     outputDeviceOptBtnClicked();
1281     setComboToFormat(ui_.outputFormatCombo, babelData_.outputDeviceFormat_, false);
1282     loadOutputDeviceNameCombo(babelData_.outputDeviceFormat_);
1283     ui_.outputStackedWidget->setCurrentWidget(ui_.outputDevicePage);
1284   } else {
1285     ui_.outputFileOptBtn->setChecked(false);
1286     ui_.outputDeviceOptBtn->setChecked(false);
1287     setComboToFormat(ui_.outputFormatCombo, babelData_.outputFileFormat_, true);
1288     ui_.outputStackedWidget->setCurrentWidget(ui_.outputFilePage);
1289     ui_.outputFilePage->setDisabled(true);
1290   }
1291 
1292   setComboToDevice(ui_.outputDeviceNameCombo, babelData_.outputDeviceName_);
1293 
1294   ui_.xlateWayPtsCk->setChecked(babelData_.xlateWayPts_);
1295   ui_.xlateTracksCk->setChecked(babelData_.xlateTracks_);
1296   ui_.xlateRoutesCk->setChecked(babelData_.xlateRoutes_);
1297 
1298   crossCheckInOutFormats();
1299   displayOptionsText(ui_.inputOptionsText,  ui_.inputFormatCombo, true);
1300   displayOptionsText(ui_.outputOptionsText,  ui_.outputFormatCombo, false);
1301   updateFilterStatus();
1302 }
1303 
1304 //------------------------------------------------------------------------
getWidgetValues()1305 void MainWindow::getWidgetValues()
1306 {
1307   int comboIdx = ui_.inputFormatCombo->currentIndex();
1308   int fidx = ui_.inputFormatCombo->itemData(comboIdx).toInt();
1309   if (ui_.inputFileOptBtn->isChecked()) {
1310     babelData_.inputType_ = BabelData::fileType_;
1311     babelData_.inputFileFormat_ =formatList_[fidx].getName();
1312   } else {
1313     babelData_.inputType_ = BabelData::deviceType_;
1314     babelData_.inputDeviceFormat_ =formatList_[fidx].getName();
1315   }
1316   babelData_.inputDeviceName_ = ui_.inputDeviceNameCombo->currentText();
1317 
1318   comboIdx = ui_.outputFormatCombo->currentIndex();
1319   fidx = ui_.outputFormatCombo->itemData(comboIdx).toInt();
1320   if (ui_.outputFileOptBtn->isChecked()) {
1321     babelData_.outputType_ = BabelData::fileType_;
1322     babelData_.outputFileFormat_ =formatList_[fidx].getName();
1323   } else if (ui_.outputDeviceOptBtn->isChecked()) {
1324     babelData_.outputType_ = BabelData::deviceType_;
1325     babelData_.outputDeviceFormat_ =formatList_[fidx].getName();
1326   } else {
1327     babelData_.outputType_ = BabelData::noType_;
1328   }
1329   babelData_.outputDeviceName_ = ui_.outputDeviceNameCombo->currentText();
1330 
1331   babelData_.xlateWayPts_ = ui_.xlateWayPtsCk->isChecked();
1332   babelData_.xlateTracks_ = ui_.xlateTracksCk->isChecked();
1333   babelData_.xlateRoutes_ = ui_.xlateRoutesCk->isChecked();
1334 }
1335 
1336 // This could be made faster, but any attempt to do so would have to be
1337 // careful about disabled formats.  As it was written to be handled by a
1338 // drag response, performance is hardly critical.
1339 // It's also kind of dumb to return the name which SetCombo then looks up,
1340 // but there's not a 1:1 correlation between offsets in the combo box and
1341 // in the list of formats.
getFormatNameForExtension(const QString & ext)1342 QString MainWindow::getFormatNameForExtension(const QString& ext)
1343 {
1344   for (int i = 0; i < formatList_.size(); i++) {
1345     QStringList extensions = formatList_[i].getExtensions();
1346     for (int j = 0; j < extensions.size(); ++j) {
1347       if (extensions[j] == ext) {
1348         return formatList_[i].getName();
1349       }
1350     }
1351   }
1352   return nullptr;
1353 }
1354