Fix doc path
[opentx.git] / companion / src / apppreferencesdialog.cpp
blob17411667bb7413a2920cc78c651a8e43962b16bd
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program 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.
21 #include "apppreferencesdialog.h"
22 #include "ui_apppreferencesdialog.h"
23 #include "mainwindow.h"
24 #include "appdata.h"
25 #include "helpers.h"
26 #include "storage.h"
27 #if defined(JOYSTICKS)
28 #include "joystick.h"
29 #include "joystickdialog.h"
30 #endif
32 AppPreferencesDialog::AppPreferencesDialog(QWidget * parent) :
33 QDialog(parent),
34 updateLock(false),
35 mainWinHasDirtyChild(false),
36 ui(new Ui::AppPreferencesDialog)
38 ui->setupUi(this);
39 setWindowIcon(CompanionIcon("apppreferences.png"));
41 initSettings();
42 connect(ui->downloadVerCB, SIGNAL(currentIndexChanged(int)), this, SLOT(baseFirmwareChanged()));
43 connect(ui->opt_appDebugLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings);
44 connect(ui->opt_fwTraceLog, &QCheckBox::toggled, this, &AppPreferencesDialog::toggleAppLogSettings);
46 #if !defined(JOYSTICKS)
47 ui->joystickCB->hide();
48 ui->joystickCB->setDisabled(true);
49 ui->joystickcalButton->hide();
50 ui->joystickChkB->hide();
51 ui->label_11->hide();
52 #endif
54 shrink();
57 AppPreferencesDialog::~AppPreferencesDialog()
59 delete ui;
62 void AppPreferencesDialog::setMainWinHasDirtyChild(bool value)
64 mainWinHasDirtyChild = value;
67 void AppPreferencesDialog::accept()
69 g.autoCheckApp(ui->autoCheckCompanion->isChecked());
70 g.OpenTxBranch(DownloadBranchType(ui->OpenTxBranch->currentIndex()));
71 g.autoCheckFw(ui->autoCheckFirmware->isChecked());
72 g.showSplash(ui->showSplash->isChecked());
73 g.simuSW(ui->simuSW->isChecked());
74 g.removeModelSlots(ui->opt_removeBlankSlots->isChecked());
75 g.newModelAction(ui->opt_newMdl_useWizard->isChecked() ? 1 : ui->opt_newMdl_useEditor->isChecked() ? 2 : 0);
76 g.historySize(ui->historySize->value());
77 g.backLight(ui->backLightColor->currentIndex());
78 g.profile[g.id()].volumeGain(round(ui->volumeGain->value() * 10.0));
79 g.libDir(ui->libraryPath->text());
80 g.gePath(ui->ge_lineedit->text());
81 g.embedSplashes(ui->splashincludeCB->currentIndex());
82 g.enableBackup(ui->backupEnable->isChecked());
84 g.appDebugLog(ui->opt_appDebugLog->isChecked());
85 g.fwTraceLog(ui->opt_fwTraceLog->isChecked());
86 g.appLogsDir(ui->appLogsDir->text());
88 if (ui->joystickChkB ->isChecked() && ui->joystickCB->isEnabled()) {
89 g.jsSupport(ui->joystickChkB ->isChecked());
90 g.jsCtrl(ui->joystickCB ->currentIndex());
92 else {
93 g.jsSupport(false);
94 g.jsCtrl(0);
96 g.profile[g.id()].channelOrder(ui->channelorderCB->currentIndex());
97 g.profile[g.id()].defaultMode(ui->stickmodeCB->currentIndex());
98 g.profile[g.id()].renameFwFiles(ui->renameFirmware->isChecked());
99 g.profile[g.id()].burnFirmware(ui->burnFirmware->isChecked());
100 g.profile[g.id()].sdPath(ui->sdPath->text());
101 g.profile[g.id()].pBackupDir(ui->profilebackupPath->text());
102 g.profile[g.id()].penableBackup(ui->pbackupEnable->isChecked());
103 g.profile[g.id()].splashFile(ui->SplashFileName->text());
105 // The profile name may NEVER be empty
106 if (ui->profileNameLE->text().isEmpty())
107 g.profile[g.id()].name(tr("My Radio"));
108 else
109 g.profile[g.id()].name(ui->profileNameLE->text());
111 bool fwchange = false;
112 Firmware * newFw = getFirmwareVariant();
113 // If a new fw type has been choosen, several things need to reset
114 if (Firmware::getCurrentVariant()->getId() != newFw->getId()) {
115 // check if we're going to be converting to a new radio type and there are unsaved files in the main window
116 if (mainWinHasDirtyChild && !Boards::isBoardCompatible(Firmware::getCurrentVariant()->getBoard(), newFw->getBoard())) {
117 QString q = tr("<p><b>You cannot switch Radio Type or change Build Options while there are unsaved file changes. What do you wish to do?</b></p> <ul>" \
118 "<li><i>Save All</i> - Save any open file(s) before saving Settings.<li>" \
119 "<li><i>Reset</i> - Revert to the previous Radio Type and Build Options before saving Settings.</li>" \
120 "<li><i>Cancel</i> - Return to the Settings editor dialog.</li></ul>");
121 int resp = QMessageBox::question(this, windowTitle(), q, (QMessageBox::SaveAll | QMessageBox::Reset | QMessageBox::Cancel), QMessageBox::Cancel);
122 if (resp == QMessageBox::SaveAll) {
123 // signal main window to save files, need to do this before the fw actually changes
124 emit firmwareProfileAboutToChange();
126 else if (resp == QMessageBox::Reset) {
127 // bail out early before saving the radio type & firmware options
128 QDialog::accept();
129 return;
131 else {
132 // we do not accept the dialog close
133 return;
136 Firmware::setCurrentVariant(newFw);
137 g.profile[g.id()].fwName("");
138 g.profile[g.id()].initFwVariables();
139 g.profile[g.id()].fwType(newFw->getId());
140 fwchange = true;
143 QDialog::accept();
145 if (fwchange)
146 emit firmwareProfileChanged(g.id()); // important to do this after the accepted() signal
149 void AppPreferencesDialog::on_snapshotPathButton_clicked()
151 QString fileName = QFileDialog::getExistingDirectory(this, tr("Select your snapshot folder"), g.snapshotDir());
152 if (!fileName.isEmpty()) {
153 g.snapshotDir(fileName);
154 g.snapToClpbrd(false);
155 ui->snapshotPath->setText(fileName);
159 void AppPreferencesDialog::initSettings()
161 ui->snapshotClipboardCKB->setChecked(g.snapToClpbrd());
162 ui->burnFirmware->setChecked(g.profile[g.id()].burnFirmware());
163 ui->snapshotPath->setText(g.snapshotDir());
164 ui->snapshotPath->setReadOnly(true);
165 if (ui->snapshotClipboardCKB->isChecked()) {
166 ui->snapshotPath->setDisabled(true);
167 ui->snapshotPathButton->setDisabled(true);
169 #if !defined(ALLOW_NIGHTLY_BUILDS)
170 // TODO should we gray out nightly builds here?
171 #endif
172 ui->OpenTxBranch->setCurrentIndex(g.OpenTxBranch());
173 ui->autoCheckCompanion->setChecked(g.autoCheckApp());
174 ui->autoCheckFirmware->setChecked(g.autoCheckFw());
175 ui->showSplash->setChecked(g.showSplash());
176 ui->historySize->setValue(g.historySize());
177 ui->backLightColor->setCurrentIndex(g.backLight());
178 ui->volumeGain->setValue(g.profile[g.id()].volumeGain() / 10.0);
180 if (IS_TARANIS(getCurrentBoard())) {
181 ui->backLightColor->setEnabled(false);
184 ui->simuSW->setChecked(g.simuSW());
185 ui->opt_removeBlankSlots->setChecked(g.removeModelSlots());
186 ui->opt_newMdl_useNone->setChecked(g.newModelAction() == 0);
187 ui->opt_newMdl_useWizard->setChecked(g.newModelAction() == 1);
188 ui->opt_newMdl_useEditor->setChecked(g.newModelAction() == 2);
189 ui->libraryPath->setText(g.libDir());
190 ui->ge_lineedit->setText(g.gePath());
192 if (!g.backupDir().isEmpty()) {
193 if (QDir(g.backupDir()).exists()) {
194 ui->backupPath->setText(g.backupDir());
195 ui->backupEnable->setEnabled(true);
196 ui->backupEnable->setChecked(g.enableBackup());
198 else {
199 ui->backupEnable->setDisabled(true);
202 else {
203 ui->backupEnable->setDisabled(true);
205 ui->splashincludeCB->setCurrentIndex(g.embedSplashes());
207 ui->opt_appDebugLog->setChecked(g.appDebugLog());
208 ui->opt_fwTraceLog->setChecked(g.fwTraceLog());
209 ui->appLogsDir->setText(g.appLogsDir());
210 toggleAppLogSettings();
212 #if defined(JOYSTICKS)
213 ui->joystickChkB->setChecked(g.jsSupport());
214 if (ui->joystickChkB->isChecked()) {
215 QStringList joystickNames;
216 joystickNames << tr("No joysticks found");
217 joystick = new Joystick(0,false,0,0);
218 ui->joystickcalButton->setDisabled(true);
219 ui->joystickCB->setDisabled(true);
221 if ( joystick ) {
222 if ( joystick->joystickNames.count() > 0 ) {
223 joystickNames = joystick->joystickNames;
224 ui->joystickCB->setEnabled(true);
225 ui->joystickcalButton->setEnabled(true);
227 joystick->close();
229 ui->joystickCB->clear();
230 ui->joystickCB->insertItems(0, joystickNames);
231 ui->joystickCB->setCurrentIndex(g.jsCtrl());
233 else {
234 ui->joystickCB->clear();
235 ui->joystickCB->setDisabled(true);
236 ui->joystickcalButton->setDisabled(true);
238 #endif
239 // Profile Tab Inits
240 ui->channelorderCB->setCurrentIndex(g.profile[g.id()].channelOrder());
241 ui->stickmodeCB->setCurrentIndex(g.profile[g.id()].defaultMode());
242 ui->renameFirmware->setChecked(g.profile[g.id()].renameFwFiles());
243 ui->sdPath->setText(g.profile[g.id()].sdPath());
244 if (!g.profile[g.id()].pBackupDir().isEmpty()) {
245 if (QDir(g.profile[g.id()].pBackupDir()).exists()) {
246 ui->profilebackupPath->setText(g.profile[g.id()].pBackupDir());
247 ui->pbackupEnable->setEnabled(true);
248 ui->pbackupEnable->setChecked(g.profile[g.id()].penableBackup());
249 } else {
250 ui->pbackupEnable->setDisabled(true);
253 else {
254 ui->pbackupEnable->setDisabled(true);
257 ui->profileNameLE->setText(g.profile[g.id()].name());
259 QString hwSettings;
260 if (g.profile[g.id()].stickPotCalib() == "" ) {
261 hwSettings = tr("EMPTY: No radio settings stored in profile");
263 else {
264 QString str = g.profile[g.id()].timeStamp();
265 if (str.isEmpty())
266 hwSettings = tr("AVAILABLE: Radio settings of unknown age");
267 else
268 hwSettings = tr("AVAILABLE: Radio settings stored %1").arg(str);
270 ui->lblGeneralSettings->setText(hwSettings);
272 QString currType = QStringList(g.profile[g.id()].fwType().split('-').mid(0, 2)).join('-');
273 foreach(Firmware * firmware, Firmware::getRegisteredFirmwares()) {
274 ui->downloadVerCB->addItem(firmware->getName(), firmware->getId());
275 if (currType == firmware->getId()) {
276 ui->downloadVerCB->setCurrentIndex(ui->downloadVerCB->count() - 1);
280 baseFirmwareChanged();
283 void AppPreferencesDialog::on_libraryPathButton_clicked()
285 QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your library folder"), g.libDir());
286 if (!fileName.isEmpty()) {
287 g.libDir(fileName);
288 ui->libraryPath->setText(fileName);
292 void AppPreferencesDialog::on_snapshotClipboardCKB_clicked()
294 if (ui->snapshotClipboardCKB->isChecked()) {
295 ui->snapshotPath->setDisabled(true);
296 ui->snapshotPathButton->setDisabled(true);
297 g.snapToClpbrd(true);
299 else {
300 ui->snapshotPath->setEnabled(true);
301 ui->snapshotPath->setReadOnly(true);
302 ui->snapshotPathButton->setEnabled(true);
303 g.snapToClpbrd(false);
307 void AppPreferencesDialog::on_backupPathButton_clicked()
309 QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your Models and Settings backup folder"), g.backupDir());
310 if (!fileName.isEmpty()) {
311 g.backupDir(fileName);
312 ui->backupPath->setText(fileName);
313 ui->backupEnable->setEnabled(true);
317 void AppPreferencesDialog::on_ProfilebackupPathButton_clicked()
319 QString fileName = QFileDialog::getExistingDirectory(this,tr("Select your Models and Settings backup folder"), g.backupDir());
320 if (!fileName.isEmpty()) {
321 ui->profilebackupPath->setText(fileName);
322 ui->pbackupEnable->setEnabled(true);
327 void AppPreferencesDialog::on_btn_appLogsDir_clicked()
329 QString fileName = QFileDialog::getExistingDirectory(this, tr("Select a folder for application logs"), ui->appLogsDir->text());
330 if (!fileName.isEmpty()) {
331 ui->appLogsDir->setText(fileName);
335 void AppPreferencesDialog::on_ge_pathButton_clicked()
337 QString fileName = QFileDialog::getOpenFileName(this, tr("Select Google Earth executable"),ui->ge_lineedit->text());
338 if (!fileName.isEmpty()) {
339 ui->ge_lineedit->setText(fileName);
343 #if defined(JOYSTICKS)
344 void AppPreferencesDialog::on_joystickChkB_clicked() {
345 if (ui->joystickChkB->isChecked()) {
346 QStringList joystickNames;
347 joystickNames << tr("No joysticks found");
348 joystick = new Joystick(0,false,0,0);
349 ui->joystickcalButton->setDisabled(true);
350 ui->joystickCB->setDisabled(true);
352 if ( joystick ) {
353 if ( joystick->joystickNames.count() > 0 ) {
354 joystickNames = joystick->joystickNames;
355 ui->joystickCB->setEnabled(true);
356 ui->joystickcalButton->setEnabled(true);
358 joystick->close();
360 ui->joystickCB->clear();
361 ui->joystickCB->insertItems(0, joystickNames);
363 else {
364 ui->joystickCB->clear();
365 ui->joystickCB->setDisabled(true);
366 ui->joystickcalButton->setDisabled(true);
370 void AppPreferencesDialog::on_joystickcalButton_clicked() {
371 joystickDialog * jd=new joystickDialog(this, ui->joystickCB->currentIndex());
372 jd->exec();
374 #endif
376 // ******** Profile tab functions
378 void AppPreferencesDialog::on_sdPathButton_clicked()
380 QString fileName = QFileDialog::getExistingDirectory(this,tr("Select the folder replicating your SD structure"), g.profile[g.id()].sdPath());
381 if (!fileName.isEmpty()) {
382 ui->sdPath->setText(fileName);
386 bool AppPreferencesDialog::displayImage(const QString & fileName)
388 // Start by clearing the label
389 ui->imageLabel->clear();
391 if (fileName.isEmpty())
392 return false;
394 QImage image(fileName);
395 if (image.isNull())
396 return false;
398 ui->imageLabel->setPixmap(makePixMap(image));
399 ui->imageLabel->setFixedSize(getCurrentFirmware()->getCapability(LcdWidth), getCurrentFirmware()->getCapability(LcdHeight));
400 return true;
403 void AppPreferencesDialog::on_SplashSelect_clicked()
405 QString supportedImageFormats;
406 for (int formatIndex = 0; formatIndex < QImageReader::supportedImageFormats().count(); formatIndex++) {
407 supportedImageFormats += QLatin1String(" *.") + QImageReader::supportedImageFormats()[formatIndex];
410 QString fileName = QFileDialog::getOpenFileName(this,
411 tr("Open Image to load"), g.imagesDir(), tr("Images (%1)").arg(supportedImageFormats));
413 if (!fileName.isEmpty()){
414 g.imagesDir(QFileInfo(fileName).dir().absolutePath());
416 displayImage(fileName);
417 ui->SplashFileName->setText(fileName);
421 void AppPreferencesDialog::on_clearImageButton_clicked()
423 ui->imageLabel->clear();
424 ui->SplashFileName->clear();
428 void AppPreferencesDialog::showVoice(bool show)
430 ui->voiceLabel->setVisible(show);
431 ui->voiceCombo->setVisible(show);
434 void AppPreferencesDialog::baseFirmwareChanged()
436 QString selected_firmware = ui->downloadVerCB->currentData().toString();
438 foreach(Firmware * firmware, Firmware::getRegisteredFirmwares()) {
439 if (firmware->getId() == selected_firmware) {
440 populateFirmwareOptions(firmware);
441 break;
446 Firmware * AppPreferencesDialog::getFirmwareVariant()
448 QString selected_firmware = ui->downloadVerCB->currentData().toString();
450 foreach(Firmware * firmware, Firmware::getRegisteredFirmwares()) {
451 QString id = firmware->getId();
452 if (id == selected_firmware) {
453 foreach(QCheckBox *cb, optionsCheckBoxes) {
454 if (cb->isChecked()) {
455 id += "-" + cb->text();
459 if (voice && voice->isChecked()) {
460 id += "-tts" + ui->voiceCombo->currentText();
463 if (ui->langCombo->count()) {
464 id += "-" + ui->langCombo->currentText();
467 return Firmware::getFirmwareForId(id);
471 // Should never occur...
472 return Firmware::getDefaultVariant();
475 void AppPreferencesDialog::firmwareOptionChanged(bool state)
477 QCheckBox *cb = qobject_cast<QCheckBox*>(sender());
478 if (cb == voice) {
479 showVoice(voice->isChecked());
481 Firmware * firmware=NULL;
482 if (cb && state) {
483 QVariant selected_firmware = ui->downloadVerCB->currentData();
484 foreach(firmware, Firmware::getRegisteredFirmwares()) {
485 if (firmware->getId() == selected_firmware) {
486 foreach(QList<Option> opts, firmware->opts) {
487 foreach(Option opt, opts) {
488 if (cb->text() == opt.name) {
489 foreach(Option other, opts) {
490 if (other.name != opt.name) {
491 foreach(QCheckBox *ocb, optionsCheckBoxes) {
492 if (ocb->text() == other.name) {
493 ocb->setChecked(false);
498 return;
507 void AppPreferencesDialog::toggleAppLogSettings()
509 bool vis = (ui->opt_appDebugLog->isChecked() || ui->opt_fwTraceLog->isChecked());
510 ui->appLogsDir->setVisible(vis);
511 ui->lbl_appLogsDir->setVisible(vis);
512 ui->btn_appLogsDir->setVisible(vis);
515 void AppPreferencesDialog::populateFirmwareOptions(const Firmware * firmware)
517 const Firmware * parent = firmware->getFirmwareBase();
519 updateLock = true;
521 QString id = Firmware::getCurrentVariant()->getId();
522 ui->langCombo->clear();
523 foreach(const char *lang, parent->languages) {
524 ui->langCombo->addItem(lang);
525 if (id.endsWith(lang)) {
526 ui->langCombo->setCurrentIndex(ui->langCombo->count() - 1);
530 voice = NULL; // we will search for a voice checkbox
532 int index = 0;
533 QWidget * prevFocus = ui->voiceCombo;
534 foreach(QList<Option> opts, parent->opts) {
535 foreach(Option opt, opts) {
536 if (index >= optionsCheckBoxes.size()) {
537 QCheckBox * checkbox = new QCheckBox(ui->profileTab);
538 ui->optionsLayout->addWidget(checkbox, optionsCheckBoxes.count()/4, optionsCheckBoxes.count()%4, 1, 1);
539 optionsCheckBoxes.push_back(checkbox);
540 connect(checkbox, SIGNAL(toggled(bool)), this, SLOT(firmwareOptionChanged(bool)));
541 if (prevFocus) {
542 QWidget::setTabOrder(prevFocus, checkbox);
546 QCheckBox *cb = optionsCheckBoxes.at(index++);
547 if (cb) {
548 cb->show();
549 cb->setText(opt.name);
550 cb->setToolTip(opt.tooltip);
551 cb->setCheckState(id.contains(opt.name) ? Qt::Checked : Qt::Unchecked);
552 if (opt.name == QString("voice")) {
553 voice = cb;
555 prevFocus = cb;
560 for (; index<optionsCheckBoxes.size(); index++) {
561 QCheckBox *cb = optionsCheckBoxes.at(index);
562 cb->hide();
563 cb->setCheckState(Qt::Unchecked);
566 ui->voiceCombo->clear();
567 foreach(const char *lang, parent->ttslanguages) {
568 ui->voiceCombo->addItem(lang);
569 if (id.contains(QString("-tts%1").arg(lang))) {
570 ui->voiceCombo->setCurrentIndex(ui->voiceCombo->count() - 1);
574 showVoice(voice && voice->isChecked());
576 // TODO: Remove once splash replacement supported on Horus
577 // NOTE: 480x272 image causes issues on screens <800px high, needs a solution like scrolling once reinstated
578 if (IS_HORUS(parent->getBoard())) {
579 ui->widget_splashImage->hide();
580 ui->SplashFileName->setText("");
582 else {
583 ui->widget_splashImage->show();
584 ui->SplashFileName->setText(g.profile[g.id()].splashFile());
585 displayImage(g.profile[g.id()].splashFile());
588 updateLock = false;
589 QTimer::singleShot(50, this, SLOT(shrink()));
592 void AppPreferencesDialog::shrink()
594 adjustSize();