diff --git a/companion/src/apppreferencesdialog.cpp b/companion/src/apppreferencesdialog.cpp index 17411667b..05737fed3 100644 --- a/companion/src/apppreferencesdialog.cpp +++ b/companion/src/apppreferencesdialog.cpp @@ -66,16 +66,18 @@ void AppPreferencesDialog::setMainWinHasDirtyChild(bool value) void AppPreferencesDialog::accept() { + Profile & profile = g.currentProfile(); + g.autoCheckApp(ui->autoCheckCompanion->isChecked()); - g.OpenTxBranch(DownloadBranchType(ui->OpenTxBranch->currentIndex())); + g.OpenTxBranch(AppData::DownloadBranchType(ui->OpenTxBranch->currentIndex())); g.autoCheckFw(ui->autoCheckFirmware->isChecked()); g.showSplash(ui->showSplash->isChecked()); g.simuSW(ui->simuSW->isChecked()); g.removeModelSlots(ui->opt_removeBlankSlots->isChecked()); - g.newModelAction(ui->opt_newMdl_useWizard->isChecked() ? 1 : ui->opt_newMdl_useEditor->isChecked() ? 2 : 0); + g.newModelAction(ui->opt_newMdl_useWizard->isChecked() ? AppData::MODEL_ACT_WIZARD : ui->opt_newMdl_useEditor->isChecked() ? AppData::MODEL_ACT_EDITOR : AppData::MODEL_ACT_NONE); g.historySize(ui->historySize->value()); g.backLight(ui->backLightColor->currentIndex()); - g.profile[g.id()].volumeGain(round(ui->volumeGain->value() * 10.0)); + profile.volumeGain(round(ui->volumeGain->value() * 10.0)); g.libDir(ui->libraryPath->text()); g.gePath(ui->ge_lineedit->text()); g.embedSplashes(ui->splashincludeCB->currentIndex()); @@ -93,20 +95,20 @@ void AppPreferencesDialog::accept() g.jsSupport(false); g.jsCtrl(0); } - g.profile[g.id()].channelOrder(ui->channelorderCB->currentIndex()); - g.profile[g.id()].defaultMode(ui->stickmodeCB->currentIndex()); - g.profile[g.id()].renameFwFiles(ui->renameFirmware->isChecked()); - g.profile[g.id()].burnFirmware(ui->burnFirmware->isChecked()); - g.profile[g.id()].sdPath(ui->sdPath->text()); - g.profile[g.id()].pBackupDir(ui->profilebackupPath->text()); - g.profile[g.id()].penableBackup(ui->pbackupEnable->isChecked()); - g.profile[g.id()].splashFile(ui->SplashFileName->text()); + profile.channelOrder(ui->channelorderCB->currentIndex()); + profile.defaultMode(ui->stickmodeCB->currentIndex()); + profile.renameFwFiles(ui->renameFirmware->isChecked()); + profile.burnFirmware(ui->burnFirmware->isChecked()); + profile.sdPath(ui->sdPath->text()); + profile.pBackupDir(ui->profilebackupPath->text()); + profile.penableBackup(ui->pbackupEnable->isChecked()); + profile.splashFile(ui->SplashFileName->text()); // The profile name may NEVER be empty if (ui->profileNameLE->text().isEmpty()) - g.profile[g.id()].name(tr("My Radio")); + profile.name(tr("My Radio")); else - g.profile[g.id()].name(ui->profileNameLE->text()); + profile.name(ui->profileNameLE->text()); bool fwchange = false; Firmware * newFw = getFirmwareVariant(); @@ -134,16 +136,16 @@ void AppPreferencesDialog::accept() } } Firmware::setCurrentVariant(newFw); - g.profile[g.id()].fwName(""); - g.profile[g.id()].initFwVariables(); - g.profile[g.id()].fwType(newFw->getId()); + profile.fwName(""); + profile.resetFwVariables(); + profile.fwType(newFw->getId()); fwchange = true; } QDialog::accept(); if (fwchange) - emit firmwareProfileChanged(g.id()); // important to do this after the accepted() signal + emit firmwareProfileChanged(); // important to do this after the accepted() signal } void AppPreferencesDialog::on_snapshotPathButton_clicked() @@ -158,8 +160,10 @@ void AppPreferencesDialog::on_snapshotPathButton_clicked() void AppPreferencesDialog::initSettings() { + const Profile & profile = g.currentProfile(); + ui->snapshotClipboardCKB->setChecked(g.snapToClpbrd()); - ui->burnFirmware->setChecked(g.profile[g.id()].burnFirmware()); + ui->burnFirmware->setChecked(profile.burnFirmware()); ui->snapshotPath->setText(g.snapshotDir()); ui->snapshotPath->setReadOnly(true); if (ui->snapshotClipboardCKB->isChecked()) { @@ -175,7 +179,7 @@ void AppPreferencesDialog::initSettings() ui->showSplash->setChecked(g.showSplash()); ui->historySize->setValue(g.historySize()); ui->backLightColor->setCurrentIndex(g.backLight()); - ui->volumeGain->setValue(g.profile[g.id()].volumeGain() / 10.0); + ui->volumeGain->setValue(profile.volumeGain() / 10.0); if (IS_TARANIS(getCurrentBoard())) { ui->backLightColor->setEnabled(false); @@ -183,9 +187,9 @@ void AppPreferencesDialog::initSettings() ui->simuSW->setChecked(g.simuSW()); ui->opt_removeBlankSlots->setChecked(g.removeModelSlots()); - ui->opt_newMdl_useNone->setChecked(g.newModelAction() == 0); - ui->opt_newMdl_useWizard->setChecked(g.newModelAction() == 1); - ui->opt_newMdl_useEditor->setChecked(g.newModelAction() == 2); + ui->opt_newMdl_useNone->setChecked(g.newModelAction() == AppData::MODEL_ACT_NONE); + ui->opt_newMdl_useWizard->setChecked(g.newModelAction() == AppData::MODEL_ACT_WIZARD); + ui->opt_newMdl_useEditor->setChecked(g.newModelAction() == AppData::MODEL_ACT_EDITOR); ui->libraryPath->setText(g.libDir()); ui->ge_lineedit->setText(g.gePath()); @@ -237,15 +241,15 @@ void AppPreferencesDialog::initSettings() } #endif // Profile Tab Inits - ui->channelorderCB->setCurrentIndex(g.profile[g.id()].channelOrder()); - ui->stickmodeCB->setCurrentIndex(g.profile[g.id()].defaultMode()); - ui->renameFirmware->setChecked(g.profile[g.id()].renameFwFiles()); - ui->sdPath->setText(g.profile[g.id()].sdPath()); - if (!g.profile[g.id()].pBackupDir().isEmpty()) { - if (QDir(g.profile[g.id()].pBackupDir()).exists()) { - ui->profilebackupPath->setText(g.profile[g.id()].pBackupDir()); + ui->channelorderCB->setCurrentIndex(profile.channelOrder()); + ui->stickmodeCB->setCurrentIndex(profile.defaultMode()); + ui->renameFirmware->setChecked(profile.renameFwFiles()); + ui->sdPath->setText(profile.sdPath()); + if (!profile.pBackupDir().isEmpty()) { + if (QDir(profile.pBackupDir()).exists()) { + ui->profilebackupPath->setText(profile.pBackupDir()); ui->pbackupEnable->setEnabled(true); - ui->pbackupEnable->setChecked(g.profile[g.id()].penableBackup()); + ui->pbackupEnable->setChecked(profile.penableBackup()); } else { ui->pbackupEnable->setDisabled(true); } @@ -254,14 +258,14 @@ void AppPreferencesDialog::initSettings() ui->pbackupEnable->setDisabled(true); } - ui->profileNameLE->setText(g.profile[g.id()].name()); + ui->profileNameLE->setText(profile.name()); QString hwSettings; - if (g.profile[g.id()].stickPotCalib() == "" ) { + if (profile.stickPotCalib() == "" ) { hwSettings = tr("EMPTY: No radio settings stored in profile"); } else { - QString str = g.profile[g.id()].timeStamp(); + QString str = profile.timeStamp(); if (str.isEmpty()) hwSettings = tr("AVAILABLE: Radio settings of unknown age"); else @@ -269,7 +273,7 @@ void AppPreferencesDialog::initSettings() } ui->lblGeneralSettings->setText(hwSettings); - QString currType = QStringList(g.profile[g.id()].fwType().split('-').mid(0, 2)).join('-'); + QString currType = QStringList(profile.fwType().split('-').mid(0, 2)).join('-'); foreach(Firmware * firmware, Firmware::getRegisteredFirmwares()) { ui->downloadVerCB->addItem(firmware->getName(), firmware->getId()); if (currType == firmware->getId()) { @@ -377,7 +381,7 @@ void AppPreferencesDialog::on_joystickcalButton_clicked() { void AppPreferencesDialog::on_sdPathButton_clicked() { - QString fileName = QFileDialog::getExistingDirectory(this,tr("Select the folder replicating your SD structure"), g.profile[g.id()].sdPath()); + QString fileName = QFileDialog::getExistingDirectory(this,tr("Select the folder replicating your SD structure"), g.currentProfile().sdPath()); if (!fileName.isEmpty()) { ui->sdPath->setText(fileName); } @@ -581,8 +585,8 @@ void AppPreferencesDialog::populateFirmwareOptions(const Firmware * firmware) } else { ui->widget_splashImage->show(); - ui->SplashFileName->setText(g.profile[g.id()].splashFile()); - displayImage(g.profile[g.id()].splashFile()); + ui->SplashFileName->setText(g.currentProfile().splashFile()); + displayImage(g.currentProfile().splashFile()); } updateLock = false; diff --git a/companion/src/apppreferencesdialog.h b/companion/src/apppreferencesdialog.h index 22949e990..e1a43d174 100644 --- a/companion/src/apppreferencesdialog.h +++ b/companion/src/apppreferencesdialog.h @@ -46,7 +46,7 @@ class AppPreferencesDialog : public QDialog void setMainWinHasDirtyChild(bool value); signals: - void firmwareProfileChanged(int profId); + void firmwareProfileChanged(); void firmwareProfileAboutToChange(bool saveFiles = true); private: diff --git a/companion/src/constants.h b/companion/src/constants.h index d5672c2c6..a5a28fdb7 100644 --- a/companion/src/constants.h +++ b/companion/src/constants.h @@ -74,12 +74,4 @@ #define CPN_STR_SW_INDICATOR_NEUT QCoreApplication::translate("RawSwitch", "-") // Switch neutral (middle) position indicator. #define CPN_STR_SW_INDICATOR_REV QCoreApplication::translate("RawSwitch", "!") // Switch reversed logic (NOT) indicator. -enum DownloadBranchType { - BRANCH_RELEASE_STABLE, - BRANCH_RC_TESTING, - BRANCH_NIGHTLY_UNSTABLE -}; - -Q_DECLARE_METATYPE(DownloadBranchType) - #endif // _CONSTANTS_H_ diff --git a/companion/src/fwpreferencesdialog.cpp b/companion/src/fwpreferencesdialog.cpp index 74bb0bae1..ebfc5b9df 100644 --- a/companion/src/fwpreferencesdialog.cpp +++ b/companion/src/fwpreferencesdialog.cpp @@ -73,7 +73,7 @@ void FirmwarePreferencesDialog::on_sd_dnld_clicked() QString fwType = g.profile[g.id()].fwType(); QStringList list = fwType.split("-"); QString firmware = QString("%1-%2").arg(list[0]).arg(list[1]); - if (g.boundedOpenTxBranch() != BRANCH_NIGHTLY_UNSTABLE) { + if (g.boundedOpenTxBranch() != AppData::BRANCH_NIGHTLY_UNSTABLE) { url.append(QString("%1/").arg(firmware)); } QDesktopServices::openUrl(url); diff --git a/companion/src/macros.h b/companion/src/macros.h index 0b29f12c3..3893cfb66 100644 --- a/companion/src/macros.h +++ b/companion/src/macros.h @@ -21,10 +21,26 @@ #ifndef _MACROS_H_ #define _MACROS_H_ +#include #include // #define DIM(arr) (sizeof((arr))/sizeof((arr)[0])) // new way for c++11 #define DIM(arr__) ((size_t)(std::end((arr__)) - std::begin((arr__)))) +#ifndef CONCATENATE + #define CONCATENATE_IMPL(A, B) A ## B + #define CONCATENATE(A, B) CONCATENATE_IMPL(A, B) +#endif + +#define CREATE_ENUM_STREAM_OPS3(functype, type, datatype) \ + functype QDataStream& operator << (QDataStream &out, type &e) { return out << (datatype&)e; } \ + functype QDataStream& operator >> (QDataStream &in, type &e) { in >> (datatype&)e; return in; } + +#define CREATE_ENUM_STREAM_OPS2(type, datatype) CREATE_ENUM_STREAM_OPS3(inline, type, datatype) +#define CREATE_ENUM_STREAM_OPS(type) CREATE_ENUM_STREAM_OPS2(type, quint32) + +#define CREATE_ENUM_FRIEND_STREAM_OPS2(type, datatype) CREATE_ENUM_STREAM_OPS3(friend inline , type, datatype) +#define CREATE_ENUM_FRIEND_STREAM_OPS(type) CREATE_ENUM_FRIEND_STREAM_OPS2(type, quint32) + #endif // _MACROS_H_ diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index d99ea5a34..a0bb46b77 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -103,14 +103,11 @@ MainWindow::MainWindow(): createMenus(); createToolBars(); retranslateUi(); - updateMenus(); - setIconThemeSize(g.iconSize()); - restoreGeometry(g.mainWinGeo()); - restoreState(g.mainWinState()); - setTabbedWindows(g.tabbedMdi()); + initWindowOptions(); connect(windowsListActions, &QActionGroup::triggered, this, &MainWindow::onChangeWindowAction); + connect(&g, &AppData::currentProfileChanged, this, &MainWindow::onCurrentProfileChanged); // give time to the splash to disappear and main window to open before starting updates int updateDelay = 1000; @@ -181,6 +178,15 @@ MainWindow::~MainWindow() } } +void MainWindow::initWindowOptions() +{ + updateMenus(); + setIconThemeSize(g.iconSize()); + restoreGeometry(g.mainWinGeo()); + restoreState(g.mainWinState()); + setTabbedWindows(g.tabbedMdi()); +} + void MainWindow::displayWarnings() { using namespace AppMessages; @@ -756,9 +762,6 @@ bool MainWindow::loadProfileId(const unsigned pid) // TODO Load all variables - // Set the new profile number g.id(pid); - Firmware::setCurrentVariant(newFw); - emit firmwareChanged(); - updateMenus(); return true; } @@ -778,7 +781,7 @@ void MainWindow::appPrefs() AppPreferencesDialog * dialog = new AppPreferencesDialog(this); dialog->setMainWinHasDirtyChild(anyChildrenDirty()); connect(dialog, &AppPreferencesDialog::firmwareProfileAboutToChange, this, &MainWindow::saveAll); - connect(dialog, &AppPreferencesDialog::firmwareProfileChanged, this, &MainWindow::loadProfileId); + connect(dialog, &AppPreferencesDialog::firmwareProfileChanged, this, &MainWindow::onCurrentProfileChanged); dialog->exec(); dialog->deleteLater(); } @@ -1783,6 +1786,13 @@ void MainWindow::onChangeWindowAction(QAction * act) mdiArea->setActiveSubWindow(win); } +void MainWindow::onCurrentProfileChanged() +{ + Firmware::setCurrentVariant(Firmware::getFirmwareForId(g.currentProfile().fwType())); + emit firmwareChanged(); + updateMenus(); +} + int MainWindow::newProfile(bool loadProfile) { int i; @@ -1791,7 +1801,7 @@ int MainWindow::newProfile(bool loadProfile) if (i == MAX_PROFILES) //Failed to find free slot return -1; - g.profile[i].init(i); + g.profile[i].init(); g.profile[i].name(tr("New Radio")); if (loadProfile) { @@ -1835,7 +1845,7 @@ void MainWindow::deleteProfile(const int pid) if (ret != QMessageBox::Yes) return; - g.profile[pid].remove(); + g.getProfile(pid).resetAll(); loadProfileId(0); } diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index 9f38bbc03..ee63bdf1a 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -70,6 +70,7 @@ class MainWindow : public QMainWindow private slots: void openDocURL(); + void initWindowOptions(); void retranslateUi(bool showMsg = false); void setLanguage(const QString & langString); @@ -84,6 +85,7 @@ class MainWindow : public QMainWindow void updateWindowActionTitle(const QMdiSubWindow * win, QAction * act = NULL); void onSubwindowTitleChanged(); void onSubwindowModified(); + void onCurrentProfileChanged(); void checkForUpdates(); void checkForFirmwareUpdate(); diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index 74763794d..ba64488f8 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -894,9 +894,9 @@ int MdiChild::newModel(int modelIndex, int categoryIndex) setSelectedModel(modelIndex); //qDebug() << modelIndex << categoryIndex << isNewModel; - if (isNewModel && g.newModelAction() == 1) + if (isNewModel && g.newModelAction() == AppData::MODEL_ACT_WIZARD) openModelWizard(modelIndex); - else if (g.newModelAction() == 2) + else if (g.newModelAction() == AppData::MODEL_ACT_EDITOR) openModelEditWindow(modelIndex); return modelIndex; diff --git a/companion/src/simulator.cpp b/companion/src/simulator.cpp index a75575143..5cf10c8dd 100644 --- a/companion/src/simulator.cpp +++ b/companion/src/simulator.cpp @@ -156,7 +156,7 @@ CommandLineParseResult cliOptions(SimulatorOptions * simOptions, int * profileId } if (cliOptions.isSet(optVer)) { - showMessage(APP_SIMULATOR " v" VERSION " " __DATE__, QMessageBox::Information); + showMessage(APP_SIMULATOR % " v" VERSION " " __DATE__, QMessageBox::Information); return CommandLineExitOk; } diff --git a/companion/src/storage/CMakeLists.txt b/companion/src/storage/CMakeLists.txt index b779ca75d..ffbd39c58 100644 --- a/companion/src/storage/CMakeLists.txt +++ b/companion/src/storage/CMakeLists.txt @@ -21,10 +21,16 @@ set(storage_SRCS set(storage_HDRS ) +set(storage_MOC_HDRS + appdata.h +) + foreach(name ${storage_NAMES}) set(storage_SRCS ${storage_SRCS} ${name}.cpp) set(storage_HDRS ${storage_HDRS} ${name}.h) endforeach() -add_library(storage ${storage_SRCS}) +qt5_wrap_cpp(storage_SRCS ${storage_MOC_HDRS}) + +add_library(storage ${storage_SRCS} ${storage_HDRS}) target_link_libraries(storage PRIVATE ${CPN_COMMON_LIB}) diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index eef0fc231..f219077ad 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -20,611 +20,383 @@ #include "appdata.h" +#include +#include +#include +#include + +#define SETTINGS_VERSION_KEY QStringLiteral("settings_version") +#define SETTINGS_TIMESTAMP_KEY QStringLiteral("settingsLastSave") + // Global data and storage object AppData g; // ** CompStoreObj class******************** -void CompStoreObj::clear (const QString &tag1, const QString &tag2, const QString &tag3) + +CompStoreObj::CompStoreObj() : + QObject(), + m_settings(COMPANY, PRODUCT) +{ } + +QString CompStoreObj::pathForKey(const QString & key, const QString & group) const { - QSettings settings(COMPANY, PRODUCT); - if (tag2.isEmpty()) - { - settings.remove(tag1); + QString path = (group.isNull() ? settingsPath() : group); + if (!path.isEmpty() && !path.endsWith('/')) + path.append('/'); + return path.append(key); +} + +void CompStoreObj::load(CompStoreObj * obj, const QString & name, const QString & key, const QVariant & def, const QString & group) +{ + const int idx = obj->metaObject()->indexOfProperty(qPrintable(name)); + if (key.isEmpty() || idx < 0) + return; + const QMetaProperty & prop = obj->metaObject()->property(idx); + const QVariant currValue = prop.read(obj); + QVariant savedValue = m_settings.value(pathForKey(key, group), def); + if (savedValue.isValid() && savedValue.convert(currValue.userType()) && savedValue != currValue) + prop.write(obj, savedValue); +} + +void CompStoreObj::store(const QVariant & newValue, const QString & key, const QString & group) const +{ + m_settings.setValue(pathForKey(key, group), newValue); + m_settings.setValue(SETTINGS_TIMESTAMP_KEY, QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); +} + +void CompStoreObj::clear(const QString &key, const QString &group) const +{ + m_settings.remove(pathForKey(key, group)); + m_settings.setValue(SETTINGS_TIMESTAMP_KEY, QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); +} + +void CompStoreObj::removeGroupIfEmpty(const QString &group) const +{ + m_settings.beginGroup(group); + if (!m_settings.allKeys().size()) + m_settings.remove(""); + m_settings.endGroup(); +} + +//static void dumpMetadata(QObject *obj) { +// for (int i = obj->metaObject()->propertyOffset() /* 0 */, e = obj->metaObject()->propertyCount(); i < e; ++i) +// qDebug() << obj->metaObject()->property(i).name() << "=" << obj->metaObject()->property(i).read(obj); +// for (int i = obj->metaObject()->methodOffset() /* 0 */, e = obj->metaObject()->methodCount(); i < e; ++i) { +// QMetaMethod::Access a = obj->metaObject()->method(i).access(); QMetaMethod::MethodType t = obj->metaObject()->method(i).methodType(); +// qDebug().noquote() << (a == 0 ? " private" : a == 1 ? "protected" : " public") << (t == 1 ? "signal:" : t == 2 ? "slot: " : ": ") +// << obj->metaObject()->method(i).typeName() << obj->metaObject()->method(i).methodSignature(); +// } +//} + +// static methods + +bool CompStoreObj::initProperty(CompStoreObj * obj, const QString & name) +{ + if (obj && !name.isEmpty()) { + const int initIdx = obj->metaObject()->indexOfMethod(PROP_FSIG_INIT_STR(name)); + if (initIdx > -1) + return obj->metaObject()->method(initIdx).invoke(obj); } - else if (tag3.isEmpty()) - { - settings.beginGroup(tag1); - settings.remove(tag2); - settings.endGroup(); + return false; +} + +void CompStoreObj::initAllProperties(CompStoreObj * obj) +{ + if (!obj) + return; + for (int i = obj->metaObject()->propertyOffset(), e = obj->metaObject()->propertyCount(); i < e; ++i) + initProperty(obj, QString(obj->metaObject()->property(i).name())); +} + +bool CompStoreObj::storeProperty(CompStoreObj * obj, const QString & name) +{ + if (!obj || name.isEmpty()) + return false; + + const QMetaProperty & prop = obj->metaObject()->property(obj->metaObject()->indexOfProperty(qPrintable(name))); + const QString key = propertyKeyName(obj, name); + QVariant currValue; + if (!prop.isValid() || !prop.isStored() || key.isEmpty() || !(currValue = prop.read(obj)).isValid()) + return false; + if (currValue == propertyDefaultValue(obj, name)) { + obj->clear(key); + obj->removeGroupIfEmpty(obj->settingsPath()); } - else - { - settings.beginGroup(tag1); - settings.beginGroup(tag2); - settings.remove(tag3); - settings.endGroup(); - settings.endGroup(); + else { + obj->store(currValue, key); + } + return false; +} + +void CompStoreObj::storeAllProperties(CompStoreObj * obj) +{ + if (!obj) + return; + for (int i = obj->metaObject()->propertyOffset(), e = obj->metaObject()->propertyCount(); i < e; ++i) + storeProperty(obj, QString(obj->metaObject()->property(i).name())); +} + +bool CompStoreObj::resetProperty(CompStoreObj * obj, const QString & name) +{ + if (obj && !name.isEmpty()) { + const QMetaProperty & prop = obj->metaObject()->property(obj->metaObject()->indexOfProperty(qPrintable(name))); + if (prop.isValid() && prop.isResettable()) + return prop.reset(obj); + } + return false; +} + +void CompStoreObj::resetAllProperties(CompStoreObj * obj) +{ + if (!obj) + return; + for (int i = obj->metaObject()->propertyOffset(), e = obj->metaObject()->propertyCount(); i < e; ++i) { + const QMetaProperty & prop = obj->metaObject()->property(i); + if (prop.isValid() && prop.isResettable()) + prop.reset(obj); } } -template -void CompStoreObj::store(const OBJ_T newValue, OBJ_T & destValue, const TAG_T tag, const GP1_T group1, const GP2_T group2) +bool CompStoreObj::propertyPathIsValid(const QString & path) { - QSettings settings(COMPANY, PRODUCT); - QString g1 = group1; // these may come in as const char * or QString - QString g2 = group2; - if (!g1.isEmpty()){ - settings.beginGroup(g1); - if (!g2.isEmpty()) - settings.beginGroup(g2); - } - - settings.setValue(tag, QVariant::fromValue(newValue)); - destValue = newValue; - - if (!g1.isEmpty()){ - settings.endGroup(); - if (!g2.isEmpty()) - settings.endGroup(); + const QPair grp = splitGroupedPath(path); // {, } + return (groupHasDynamicProperties(grp.first) || propertyKeyExists(grp.first, grp.second)); +} + +bool CompStoreObj::propertyPathIsValidNonDefault(const QString & path, const QVariant & newVal) +{ + const QPair grp = splitGroupedPath(path); // {, } + return (groupHasDynamicProperties(grp.first) || (propertyKeyExists(grp.first, grp.second) && newVal != propertyKeyDefaultValue(grp.first, grp.second))); +} + +bool CompStoreObj::propertyExists(CompStoreObj * obj, const QString & name) +{ + if (!obj) + return false; + return obj->metaObject()->indexOfProperty(qPrintable(name)) > -1; +} + +const QVariant CompStoreObj::propertyDefaultValue(CompStoreObj * obj, const QString & name) +{ + QVariant ret; + if (!obj || name.isEmpty()) + return ret; + const int idx = obj->metaObject()->indexOfMethod(PROP_FSIG_DFLT_STR(name)); + if (idx > -1) + obj->metaObject()->method(idx).invoke(obj, Qt::DirectConnection, Q_RETURN_ARG(QVariant, ret)); + //qDebug() << ret; + return ret; +} + +const QString CompStoreObj::propertyKeyName(CompStoreObj * obj, const QString & name) +{ + QString ret; + if (!obj || name.isEmpty()) + return ret; + const int idx = obj->metaObject()->indexOfMethod(PROP_FSIG_KEY_STR(name)); + if (idx > -1) + obj->metaObject()->method(idx).invoke(obj, Qt::DirectConnection, Q_RETURN_ARG(QString, ret)); + //qDebug() << ret; + return ret; +} + +QPair CompStoreObj::splitGroupedPath(const QString & path) +{ + const QStringList list = path.split('/'); + const QString grp = list.size() > 1 ? list.first() : QStringLiteral("General"); // "General" is the default (no-name) group, used by AppData top-level settings. + return QPair(grp, list.last()); +} + +QHash & CompStoreObj::group2ObjMap() +{ + static QHash map; + return map; +} + +QVector &CompStoreObj::dynamicPropGroups() +{ + static QVector list; + return list; +} + +const QHash > & CompStoreObj::keyToNameMap() +{ + static QHash > map; + if (map.isEmpty()) { + QHashIterator it(group2ObjMap()); + while (it.hasNext()) { + it.next(); + QHash grpMap; + CompStoreObj * obj = it.value(); + //dumpMetadata(obj); + for (int i = obj->metaObject()->propertyOffset(), e = obj->metaObject()->propertyCount(); i < e; ++i) { + const QString name = QString(obj->metaObject()->property(i).name()); + const QString key = propertyKeyName(obj, name); + if (!key.isEmpty()) + grpMap.insert(key, name); + } + map.insert(it.key(), grpMap); + //qDebug() << it.key() << grpMap; + } } + return map; } -template -void CompStoreObj::retrieve(OBJ_T & destValue, const TAG_T tag, const DEF_T def, const GP1_T group1, const GP2_T group2) -{ - QSettings settings(COMPANY, PRODUCT); - QString g1 = group1; // these may come in as const char * or QString - QString g2 = group2; - if (!g1.isEmpty()){ - settings.beginGroup(g1); - if (!g2.isEmpty()) - settings.beginGroup(g2); - } - - QVariant val = settings.value(QString(tag), QVariant::fromValue(def)); - if (val.canConvert()) - destValue = val.value(); - - if (!g1.isEmpty()){; - settings.endGroup(); - if (!g2.isEmpty()) - settings.endGroup(); - } -} - -template -void CompStoreObj::getset(OBJ_T & value, const TAG_T tag, const DEF_T def, const GP1_T group1, const GP2_T group2 ) -{ - retrieve(value, tag, def, group1, group2); - store(value, value, tag, group1, group2); -} - -template -void CompStoreObj::getset(OBJ_T & value, const TAG_T tag, const char * def, const GP1_T group1, const GP2_T group2 ) -{ - getset(value, tag, QString(def), group1, group2); -} - - -// ** FwRevision class******************** -int FwRevision::get( const QString fwType ) -{ - QString result; - retrieve( result, fwType, QString(""), "FwRevisions" ); - return result.toInt(); -} - -void FwRevision::set( const QString fwType, const int fwRevision ) -{ - QString tempString = QString("%1").arg(fwRevision); - store( tempString, tempString, fwType, "FwRevisions" ); -} - -void FwRevision::remove( const QString tag ) -{ - clear( "FwRevisions", tag ); -} // ** JStickData class******************** -// Get declarations -int JStickData::stick_axe() { return _stickAxe; } -int JStickData::stick_min() { return _stickMin; } -int JStickData::stick_med() { return _stickMed; } -int JStickData::stick_max() { return _stickMax; } -int JStickData::stick_inv() { return _stickInv; } -// Set declarations -void JStickData::stick_axe(const int it) { store( it, _stickAxe, QString("stick%1_axe").arg(index), "JsCalibration" );} -void JStickData::stick_min(const int it) { store( it, _stickMin, QString("stick%1_min").arg(index), "JsCalibration" );} -void JStickData::stick_med(const int it) { store( it, _stickMed, QString("stick%1_med").arg(index), "JsCalibration" );} -void JStickData::stick_max(const int it) { store( it, _stickMax, QString("stick%1_max").arg(index), "JsCalibration" );} -void JStickData::stick_inv(const int it) { store( it, _stickInv, QString("stick%1_inv").arg(index), "JsCalibration" );} - -// Constructor -JStickData::JStickData() +JStickData::JStickData() : CompStoreObj(), index(-1) { - index = -1; -} - -void JStickData::remove() -{ - // Remove all JStickData values from settings file - QSettings settings(COMPANY, PRODUCT); - settings.beginGroup( "JsCalibration" ); - settings.remove( QString( "stick%1_axe").arg(index) ); - settings.remove( QString( "stick%1_min").arg(index) ); - settings.remove( QString( "stick%1_med").arg(index) ); - settings.remove( QString( "stick%1_max").arg(index) ); - settings.remove( QString( "stick%1_inv").arg(index) ); - settings.endGroup(); - - // Reset all JStickData variables to initial values - init(index); + CompStoreObj::addObjectMapping(propertyGroup(), this); } bool JStickData::existsOnDisk() { - QSettings settings(COMPANY, PRODUCT); - settings.beginGroup("JsCalibration"); - int axe = settings.value( QString("stick%1_axe").arg(index), -1 ).toInt(); - settings.endGroup(); - - return (axe > -1); + return (m_settings.value(settingsPath() % stick_axe_key(), -1).toInt() > -1); } -void JStickData::init(int newIndex) -{ - index = newIndex; - _stickAxe = -1; - _stickMin = -32767; - _stickMed = 0; - _stickMax = 0; - _stickInv = 0; - - // Do not write empty joystick calibrations to disk. - if ( !existsOnDisk() ) - return; - - flush(); -} - -void JStickData::flush() -{ - getset( _stickAxe, QString("stick%1_axe").arg(index), -1, "JsCalibration" ); - getset( _stickMin, QString("stick%1_min").arg(index), -32767, "JsCalibration" ); - getset( _stickMed, QString("stick%1_med").arg(index), 0, "JsCalibration" ); - getset( _stickMax, QString("stick%1_max").arg(index), 0, "JsCalibration" ); - getset( _stickInv, QString("stick%1_inv").arg(index), 0, "JsCalibration" ); -} // ** Profile class******************** -// Get declarations -QString Profile::fwName() const { return _fwName; } -QString Profile::fwType() const { return _fwType; } -QString Profile::name() const { return _name; } -QString Profile::sdPath() const { return _sdPath; } -int Profile::volumeGain() const { return _volumeGain; } -QString Profile::pBackupDir() const { return _pBackupDir; } -QString Profile::splashFile() const { return _splashFile; } -bool Profile::burnFirmware() const { return _burnFirmware; } -bool Profile::penableBackup() const { return _penableBackup; } -bool Profile::renameFwFiles() const { return _renameFwFiles; } -int Profile::channelOrder() const { return _channelOrder; } -int Profile::defaultMode() const { return _defaultMode; } -QString Profile::beeper() const { return _beeper; } -QString Profile::countryCode() const { return _countryCode; } -QString Profile::display() const { return _display; } -QString Profile::haptic() const { return _haptic; } -QString Profile::speaker() const { return _speaker; } -QString Profile::stickPotCalib() const { return _stickPotCalib; } -QString Profile::timeStamp() const { return _timeStamp; } -QString Profile::trainerCalib() const { return _trainerCalib; } -QString Profile::controlTypes() const { return _controlTypes; } -QString Profile::controlNames() const { return _controlNames; } -int Profile::gsStickMode() const { return _gsStickMode; } -int Profile::ppmMultiplier() const { return _ppmMultiplier; } -int Profile::vBatWarn() const { return _vBatWarn; } -int Profile::vBatMin() const { return _vBatMin; } -int Profile::vBatMax() const { return _vBatMax; } -int Profile::txCurrentCalibration() const { return _txCurrentCalibration; } -int Profile::txVoltageCalibration() const { return _txVoltageCalibration; } - -SimulatorOptions Profile::simulatorOptions() const { return _simulatorOptions; } - -// Set declarations -void Profile::name (const QString x) { store(x, _name, "Name" ,"Profiles", groupId());} -void Profile::fwName (const QString x) { store(x, _fwName, "fwName" ,"Profiles", groupId());} -void Profile::fwType (const QString x) { store(x, _fwType, "fwType" ,"Profiles", groupId());} -void Profile::sdPath (const QString x) { store(x, _sdPath, "sdPath" ,"Profiles", groupId());} -void Profile::volumeGain (const int x) { store(x, _volumeGain, "volumeGain" ,"Profiles", groupId());} -void Profile::pBackupDir (const QString x) { store(x, _pBackupDir, "pBackupDir" ,"Profiles", groupId());} -void Profile::splashFile (const QString x) { store(x, _splashFile, "SplashFileName" ,"Profiles", groupId());} -void Profile::burnFirmware (const bool x) { store(x, _burnFirmware, "burnFirmware" ,"Profiles", groupId());} -void Profile::renameFwFiles (const bool x) { store(x, _renameFwFiles, "rename_firmware_files" ,"Profiles", groupId());} -void Profile::penableBackup (const bool x) { store(x, _penableBackup, "penableBackup" ,"Profiles", groupId());} -void Profile::channelOrder (const int x) { store(x, _channelOrder, "default_channel_order" ,"Profiles", groupId());} -void Profile::defaultMode (const int x) { store(x, _defaultMode, "default_mode" ,"Profiles", groupId());} -void Profile::beeper (const QString x) { store(x, _beeper, "Beeper" ,"Profiles", groupId());} -void Profile::countryCode (const QString x) { store(x, _countryCode, "countryCode" ,"Profiles", groupId());} -void Profile::display (const QString x) { store(x, _display, "Display" ,"Profiles", groupId());} -void Profile::haptic (const QString x) { store(x, _haptic, "Haptic" ,"Profiles", groupId());} -void Profile::speaker (const QString x) { store(x, _speaker, "Speaker" ,"Profiles", groupId());} -void Profile::stickPotCalib (const QString x) { store(x, _stickPotCalib, "StickPotCalib" ,"Profiles", groupId());} -void Profile::timeStamp (const QString x) { store(x, _timeStamp, "TimeStamp" ,"Profiles", groupId());} -void Profile::trainerCalib (const QString x) { store(x, _trainerCalib, "TrainerCalib" ,"Profiles", groupId());} -void Profile::controlTypes (const QString x) { store(x, _controlTypes, "ControlTypes" ,"Profiles", groupId());} -void Profile::controlNames (const QString x) { store(x, _controlNames, "ControlNames" ,"Profiles", groupId());} -void Profile::gsStickMode (const int x) { store(x, _gsStickMode, "GSStickMode" ,"Profiles", groupId());} -void Profile::ppmMultiplier (const int x) { store(x, _ppmMultiplier, "PPM_Multiplier" ,"Profiles", groupId());} -void Profile::vBatWarn (const int x) { store(x, _vBatWarn, "vBatWarn" ,"Profiles", groupId());} -void Profile::vBatMin (const int x) { store(x, _vBatMin, "VbatMin" ,"Profiles", groupId());} -void Profile::vBatMax (const int x) { store(x, _vBatMax, "VbatMax" ,"Profiles", groupId());} -void Profile::txCurrentCalibration (const int x) { store(x, _txCurrentCalibration, "currentCalib","Profiles", groupId());} -void Profile::txVoltageCalibration (const int x) { store(x, _txVoltageCalibration, "VbatCalib" ,"Profiles", groupId());} - -void Profile::simulatorOptions(const SimulatorOptions & x) { store(x, _simulatorOptions, "simulatorOptions" ,"Profiles", groupId()); } - -// Constructor -Profile::Profile() +Profile::Profile() : CompStoreObj(), index(-1) { - index = -1; + CompStoreObj::addObjectMapping(propertyGroup(), this); } // The default copy operator can not be used since the index variable would be destroyed -Profile& Profile::operator=(const Profile& rhs) +Profile & Profile::operator= (const Profile & rhs) { - name ( rhs.name() ); - fwName ( rhs.fwName() ); - fwType ( rhs.fwType() ); - sdPath ( rhs.sdPath() ); - volumeGain ( rhs.volumeGain() ); - pBackupDir ( rhs.pBackupDir() ); - splashFile ( rhs.splashFile() ); - burnFirmware ( rhs.burnFirmware() ); - renameFwFiles( rhs.renameFwFiles() ); - penableBackup( rhs.penableBackup() ); - channelOrder ( rhs.channelOrder() ); - defaultMode ( rhs.defaultMode() ); - beeper ( rhs.beeper() ); - countryCode ( rhs.countryCode() ); - display ( rhs.display() ); - haptic ( rhs.haptic() ); - speaker ( rhs.speaker() ); - stickPotCalib( rhs.stickPotCalib() ); - trainerCalib ( rhs.trainerCalib() ); - controlTypes ( rhs.controlTypes() ); - controlNames ( rhs.controlNames() ); - gsStickMode ( rhs.gsStickMode() ); - ppmMultiplier( rhs.ppmMultiplier() ); - vBatWarn ( rhs.vBatWarn() ); - vBatMin ( rhs.vBatMin() ); - vBatMax ( rhs.vBatMax() ); - txCurrentCalibration ( rhs.txCurrentCalibration() ); - txVoltageCalibration ( rhs.txVoltageCalibration() ); - simulatorOptions ( rhs.simulatorOptions() ); - - return *this; -} - -void Profile::remove() -{ - // Remove all profile values from settings file - QSettings settings(COMPANY, PRODUCT); - settings.beginGroup("Profiles"); - settings.remove(groupId()); - settings.endGroup(); - - // Reset all profile variables to initial values - init(index); + for (int i = metaObject()->propertyOffset(), e = metaObject()->propertyCount(); i < e; ++i) { + const QMetaProperty & prop = metaObject()->property(i); + if (!prop.isValid() || !prop.isWritable()) { + qWarning() << "Could not copy property" << QString(prop.name()) << "isValid:" << prop.isValid() << "isWritable:" << prop.isWritable(); + continue; + } + prop.write(this, prop.read(&rhs)); + } + return *this; } bool Profile::existsOnDisk() { - QSettings settings(COMPANY, PRODUCT); - settings.beginGroup("Profiles"); - settings.beginGroup(groupId()); - QStringList keyList = settings.allKeys(); - settings.endGroup(); - settings.endGroup(); - - return (keyList.length() > 0); + return m_settings.contains(settingsPath() % "Name"); } -void Profile::initFwVariables() +void Profile::resetFwVariables() { - _beeper = ""; - _countryCode = ""; - _display = ""; - _haptic = ""; - _speaker = ""; - _stickPotCalib = ""; - _timeStamp = ""; - _trainerCalib = ""; - _controlTypes = ""; - _controlNames = ""; - - _txCurrentCalibration = 0; - _gsStickMode = 0; - _ppmMultiplier = 0; - _txVoltageCalibration = 0; - _vBatWarn = 0; - _vBatMin = 0; - _vBatMax = 0; -} - -void Profile::init(int newIndex) -{ - index = newIndex; - - _fwName = ""; - _fwType = ""; - _name = ""; - _sdPath = ""; - _volumeGain = 10; - _pBackupDir = ""; - _splashFile = ""; - _burnFirmware = false; - _renameFwFiles = false; - _penableBackup = false; - _channelOrder = 0; - _defaultMode = 1; - - _simulatorOptions = SimulatorOptions(); - - initFwVariables(); - - // Do not write empty profiles to disk except the default (0) profile. - if ( index > 0 && !existsOnDisk()) - return; - - flush(); -} - -void Profile::flush() -{ - // Load and store all variables. Use default values if setting values are missing - getset( _fwName, "fwName" ,"" ,"Profiles", groupId()); - getset( _fwType, "fwType" ,"" ,"Profiles", groupId()); - getset( _name, "Name" ,"" ,"Profiles", groupId()); - getset( _sdPath, "sdPath" ,"" ,"Profiles", groupId()); - getset( _volumeGain, "volumeGain" ,10 ,"Profiles", groupId()); - getset( _pBackupDir, "pBackupDir" ,"" ,"Profiles", groupId()); - getset( _splashFile, "SplashFileName" ,"" ,"Profiles", groupId()); - getset( _burnFirmware, "burnFirmware" ,false ,"Profiles", groupId()); - getset( _penableBackup, "penableBackup" ,false ,"Profiles", groupId()); - getset( _renameFwFiles, "rename_firmware_files" ,false ,"Profiles", groupId()); - getset( _channelOrder, "default_channel_order" ,0 ,"Profiles", groupId()); - getset( _defaultMode, "default_mode" ,1 ,"Profiles", groupId()); - - getset( _beeper, "Beeper" ,"" ,"Profiles", groupId()); - getset( _countryCode, "countryCode" ,"" ,"Profiles", groupId()); - getset( _display, "Display" ,"" ,"Profiles", groupId()); - getset( _haptic, "Haptic" ,"" ,"Profiles", groupId()); - getset( _speaker, "Speaker" ,"" ,"Profiles", groupId()); - getset( _stickPotCalib, "StickPotCalib" ,"" ,"Profiles", groupId()); - getset( _timeStamp, "TimeStamp" ,"" ,"Profiles", groupId()); - getset( _trainerCalib, "TrainerCalib" ,"" ,"Profiles", groupId()); - getset( _controlTypes, "ControlTypes" ,"" ,"Profiles", groupId()); - getset( _controlNames, "ControlNames" ,"" ,"Profiles", groupId()); - getset( _gsStickMode, "GSStickMode" ,0 ,"Profiles", groupId()); - getset( _ppmMultiplier, "PPM_Multiplier" ,0 ,"Profiles", groupId()); - getset( _vBatWarn, "vBatWarn" ,0 ,"Profiles", groupId()); - getset( _vBatMin, "VbatMin" ,0 ,"Profiles", groupId()); - getset( _vBatMax, "VbatMax" ,0 ,"Profiles", groupId()); - getset( _txCurrentCalibration, "currentCalib" ,0 ,"Profiles", groupId()); - getset( _txVoltageCalibration, "VbatCalib" ,0 ,"Profiles", groupId()); - - getset( _simulatorOptions, "simulatorOptions", SimulatorOptions(), "Profiles", groupId()); - -} - -QString Profile::groupId() -{ - return QString("profile%1").arg(index); + for (const QString & name : fwVarsList()) + CompStoreObj::resetProperty(this, name); } // ** AppData class******************** -// Get declarations -QStringList AppData::recentFiles() { return _recentFiles; } -QStringList AppData::simuDbgFilters() { return _simuDbgFilters; } - -QByteArray AppData::mainWinGeo() { return _mainWinGeo; } -QByteArray AppData::mainWinState() { return _mainWinState; } -QByteArray AppData::modelEditGeo() { return _modelEditGeo; } -QByteArray AppData::mdiWinGeo() { return _mdiWinGeo; } -QByteArray AppData::mdiWinState() { return _mdiWinState; } -QByteArray AppData::compareWinGeo(){ return _compareWinGeo; } - -QString AppData::armMcu() { return _armMcu; } -QString AppData::avrArguments() { return _avrArguments; } -QString AppData::avrPort() { return _avrPort; } -QString AppData::avrdudeLocation() { return _avrdudeLocation; } -QString AppData::dfuArguments() { return _dfuArguments; } -QString AppData::dfuLocation() { return _dfuLocation; } -QString AppData::locale() { return _locale; } -QString AppData::mcu() { return _mcu; } -QString AppData::programmer() { return _programmer; } -QString AppData::sambaLocation() { return _sambaLocation; } -QString AppData::sambaPort() { return _sambaPort; } - -QString AppData::backupDir() { return _backupDir; } -QString AppData::gePath() { return _gePath; } -QString AppData::eepromDir() { return _eepromDir; } -QString AppData::flashDir() { return _flashDir; } -QString AppData::imagesDir() { return _imagesDir; } -QString AppData::logDir() { return _logDir; } -QString AppData::libDir() { return _libDir; } -QString AppData::snapshotDir() { return _snapshotDir; } -QString AppData::updatesDir() { return _updatesDir; } - -int AppData::newModelAction() { return _newModelAction; } -int AppData::backLight() { return _backLight; } -int AppData::embedSplashes() { return _embedSplashes; } -int AppData::fwServerFails() { return _fwServerFails; } -int AppData::generalEditTab() { return _generalEditTab; } -int AppData::iconSize() { return _iconSize; } -int AppData::historySize() { return _historySize; } -int AppData::jsCtrl() { return _jsCtrl; } -int AppData::id() { return _id; } -int AppData::theme() { return _theme; } -int AppData::warningId() { return _warningId; } -int AppData::simuLastProfId() { return _simuLastProfId; } -int AppData::sessionId() { return _sessionId; } - -// Set declarations -void AppData::recentFiles (const QStringList x) { store(x, _recentFiles, "recentFileList" );} -void AppData::simuDbgFilters (const QStringList x) { store(x, _simuDbgFilters, "simuDbgFilters" );} - -void AppData::mainWinGeo (const QByteArray x) { store(x, _mainWinGeo, "mainWindowGeometry" );} -void AppData::mainWinState (const QByteArray x) { store(x, _mainWinState, "mainWindowState" );} -void AppData::modelEditGeo (const QByteArray x) { store(x, _modelEditGeo, "modelEditGeometry" );} -void AppData::mdiWinGeo (const QByteArray x) { store(x, _mdiWinGeo, "mdiWinGeo" );} -void AppData::mdiWinState (const QByteArray x) { store(x, _mdiWinState, "mdiWinState" );} -void AppData::compareWinGeo (const QByteArray x) { store(x, _compareWinGeo, "compareWinGeo" );} - -void AppData::armMcu (const QString x) { store(x, _armMcu, "arm_mcu" );} -void AppData::avrArguments (const QString x) { store(x, _avrArguments, "avr_arguments" );} -void AppData::avrPort (const QString x) { store(x, _avrPort, "avr_port" );} -void AppData::avrdudeLocation (const QString x) { store(x, _avrdudeLocation, "avrdudeLocation" );} -void AppData::dfuArguments (const QString x) { store(x, _dfuArguments, "dfu_arguments" );} -void AppData::dfuLocation (const QString x) { store(x, _dfuLocation, "dfu_location" );} -void AppData::locale (const QString x) { store(x, _locale, "locale" );} -void AppData::mcu (const QString x) { store(x, _mcu, "mcu" );} -void AppData::programmer (const QString x) { store(x, _programmer, "programmer" );} -void AppData::sambaLocation (const QString x) { store(x, _sambaLocation, "samba_location" );} -void AppData::sambaPort (const QString x) { store(x, _sambaPort, "samba_port" );} - -void AppData::backupDir (const QString x) { store(x, _backupDir, "backupPath" );} -void AppData::gePath (const QString x) { store(x, _gePath, "gePath" );} -void AppData::eepromDir (const QString x) { store(x, _eepromDir, "lastDir" );} -void AppData::flashDir (const QString x) { store(x, _flashDir, "lastFlashDir" );} -void AppData::imagesDir (const QString x) { store(x, _imagesDir, "lastImagesDir" );} -void AppData::logDir (const QString x) { store(x, _logDir, "lastLogDir" );} -void AppData::libDir (const QString x) { store(x, _libDir, "libraryPath" );} -void AppData::snapshotDir (const QString x) { store(x, _snapshotDir, "snapshotpath" );} -void AppData::updatesDir (const QString x) { store(x, _updatesDir, "lastUpdatesDir" );} - -void AppData::newModelAction (const int x) { store(x, _newModelAction, "newModelAction" );} -void AppData::backLight (const int x) { store(x, _backLight, "backLight" );} -void AppData::embedSplashes (const int x) { store(x, _embedSplashes, "embedded_splashes" );} -void AppData::fwServerFails (const int x) { store(x, _fwServerFails, "fwserver" );} -void AppData::generalEditTab (const int x) { store(x, _generalEditTab, "generalEditTab" );} -void AppData::iconSize (const int x) { store(x, _iconSize, "icon_size" );} -void AppData::historySize (const int x) { store(x, _historySize, "history_size" );} -void AppData::jsCtrl (const int x) { store(x, _jsCtrl, "js_ctrl" );} -void AppData::theme (const int x) { store(x, _theme, "theme" );} -void AppData::warningId (const int x) { store(x, _warningId, "warningId" );} -void AppData::simuLastProfId (const int x) { store(x, _simuLastProfId, "simuLastProfId" );} - -void AppData::id(const int x) +AppData::AppData() : + CompStoreObj(), + m_sessionId(-1) { - store(x, _id, "profileId"); - sessionId(x); + QMetaType::registerComparators(); + qRegisterMetaTypeStreamOperators("SimulatorOptions"); + qRegisterMetaTypeStreamOperators("AppData::DownloadBranchType"); + qRegisterMetaTypeStreamOperators("AppData::NewModelAction"); + + CompStoreObj::addObjectMapping(propertyGroup(), this); + + firstUse = !hasCurrentSettings(); + + // Make sure the settings backup folder exists + if (firstUse && !QDir().mkpath(CPN_SETTINGS_BACKUP_DIR)) + qWarning() << "Could not create settings backup path" << CPN_SETTINGS_BACKUP_DIR; + + // Configure the profiles + for (int i=0; i("SimulatorOptions"); + qInfo().noquote() << "Settings init with" << m_settings.organizationName() << m_settings.applicationName() + << "Saved version:" << fmtHex(m_settings.value(SETTINGS_VERSION_KEY).toUInt()) << "Current version:" << fmtHex(CPN_SETTINGS_VERSION); - firstUse = false; + // This connection doesn't work in the constructor because AppData is created before QApplication. Globals suck like that. Compensate by using Qt::UniqueConnection because init() may be called multiple times within app lifetime. + connect(this, &AppData::idChanged, this, static_cast(&AppData::sessionId), Qt::UniqueConnection); - QSettings settings(COMPANY, PRODUCT); - if (!settings.contains("settings_version")) { - firstUse = true; - } - convertSettings(settings); + // update anything that may have changed since last saved version and update the version marker if needed. + convertSettings(m_settings); - // Initialize the profiles - for (int i=0; i AppData::getActiveProfiles() +void AppData::initAll() +{ + // Initialize all variables. Use default values if no saved settings. + CompStoreObj::initAllProperties(this); + // Initialize the profiles + for (int i=0; i= MAX_PROFILES || index == m_sessionId) + return; + m_sessionId = index; + emit sessionIdChanged(index); + emit currentProfileChanged(); +} + +Profile & AppData::getProfile(int index) +{ + if (index > -1 && index < MAX_PROFILES) + return profile[index]; + return profile[0]; +} + +const Profile & AppData::getProfile(int index) const +{ + if (index > -1 && index < MAX_PROFILES) + return profile[index]; + return profile[0]; +} + +QMap AppData::getActiveProfiles() const { QMap active; for (int i=0; i AppData::getActiveProfiles() void AppData::convertSettings(QSettings & settings) { - // convert old settings to new + quint32 savedVer = settings.value(SETTINGS_VERSION_KEY, 0).toUInt(); +#if (VERSION_NUMBER < QT_VERSION_CHECK(2, 3, 0)) + // convert old 2.2.x version marker; TODO: remove once v2.3 is established (because we'll always convert and clean 2.2.x settings anyway) + if (savedVer == 220) + savedVer = 0x2020000; +#endif + + if (savedVer == CPN_SETTINGS_VERSION) + return; + if (savedVer > CPN_SETTINGS_VERSION) { + qWarning().noquote() << "Saved settings version is newer than current, skippig conversions. Saved:" << fmtHex(savedVer) << "Current:" << fmtHex(CPN_SETTINGS_VERSION); + return; + } + + // We only want to remove old/deprecated settngs if the {major}{minor} part of the settings version number has changed. + // This preserves backwards compatiblity within minor version releases (where only revision number changes). + const unsigned savedMajMin = (savedVer >> 16); + const unsigned currMajMin = (CPN_SETTINGS_VERSION >> 16); + const bool removeUnused = (savedMajMin < currMajMin); + + qInfo().noquote().nospace() << "Converting settings " << settings.applicationName() + << " from v" << fmtHex(savedVer) << " (" << fmtHex(savedMajMin) << ") to v" + << fmtHex(CPN_SETTINGS_VERSION) << " (" << fmtHex(currMajMin) << "). Removing unused: " << removeUnused; + if (settings.contains("useWizard")) { - if (!settings.contains("newModelAction")) { - newModelAction(settings.value("useWizard").toBool() ? 1 : 2); - } - settings.remove("useWizard"); + if (!settings.contains(newModelAction_key())) + settings.setValue(newModelAction_key(), (settings.value("useWizard").toBool() ? MODEL_ACT_WIZARD : MODEL_ACT_EDITOR)); + if (removeUnused) + settings.remove("useWizard"); } - if (settings.contains("warningId") && settings.value("warningId").toInt() == 7) { - // meaning of warningId changed during v2.2 development but value of "7" indicates old setting, removing it will restore defaults + + // meaning of warningId changed during v2.2 development but value of "7" indicates old setting, removing it will restore defaults + if (savedMajMin < 0x203 && settings.contains("warningId") && settings.value("warningId").toInt() == 7) settings.remove("warningId"); - } -} -bool AppData::hasCurrentSettings() -{ - QSettings settings(COMPANY, PRODUCT); - if (!settings.contains("settings_version")) { - return false; - } - return true; -} - -bool AppData::findPreviousVersionSettings(QString * version) -{ - QSettings * fromSettings = NULL; - *version = ""; - - QSettings settings21("OpenTX", "Companion 2.1"); - if (settings21.contains("settings_version")) { - fromSettings = &settings21; - *version = "2.1"; - } - else { - settings21.clear(); - } - - QSettings settings20("OpenTX", "Companion 2.0"); - if (settings20.contains("settings_version")) { - if (!fromSettings) { - fromSettings = &settings20; - *version = "2.0"; + // Convert joystick settings to new style of key names + static const QString jsCalBasePath = QStringLiteral("JsCalibration/%1"); + if (settings.contains(jsCalBasePath.arg("stick0_axe"))) { + const QStringList vars({"axe", "inv", "max", "med", "min"}); + for (int i=0; i < MAX_JOYSTICKS; ++i) { + for (const QString & var : vars) { + const QString oldPath = jsCalBasePath.arg(QString("stick%1_%2").arg(i).arg(var)); // old format "/stick#_var" + const QVariant val = settings.value(oldPath); + if (val.isValid()) + settings.setValue(jsCalBasePath.arg(QString("%1/stick_%2").arg(i).arg(var)), val); // new format "/#/stick_var" + if (removeUnused) + settings.remove(oldPath); + } } } - else { - settings20.clear(); - } - QSettings settings16("OpenTX", "OpenTX Companion"); - if (settings16.contains("settings_version")) { - if (!fromSettings) { - fromSettings = &settings16; - *version = "1.x"; - } - } - else { - settings16.clear(); - } + if (removeUnused) + clearUnusedSettings(settings); - if (!fromSettings) - return false; - - return true; + // This is a version marker, used to convert settings between versions. Do NOT remove it, ever! Also see notes on CPN_SETTINGS_VERSION macro. + settings.setValue(SETTINGS_VERSION_KEY, CPN_SETTINGS_VERSION); } -bool AppData::importSettings(QString fromVersion) +void AppData::clearUnusedSettings(QSettings & settings) { - QSettings toSettings(COMPANY, PRODUCT); + // Go through and clean up anything that doesn't exist or is set to default value. + foreach (const QString & key, settings.allKeys()) { + if (key == ".") // special Windows registry key, don't delete it + continue; + const QVariant newVal = settings.value(key); + // Remove key if property does not exist or is the default value. + if (!newVal.isValid() || !CompStoreObj::propertyPathIsValidNonDefault(key, newVal)) + settings.remove(key); + } +} - QString fromCompany; +bool AppData::findPreviousVersionSettings(QString * version) const +{ + static const QStringList versList({QStringLiteral("2.2"), QStringLiteral("2.1"), QStringLiteral("2.0")}); + + for (const QString &ver : versList) { + const QString prod("Companion " % ver); + // make sure we do not try to import from ourselves otherwise settings WILL get corrupted + if (prod == PRODUCT) + continue; + QSettings settings(COMPANY, prod); + if (settings.contains(SETTINGS_VERSION_KEY)) { + *version = ver; + return true; + } + else { + settings.clear(); + } + } + + QSettings settings(COMPANY, "OpenTX Companion"); + if (settings.contains(SETTINGS_VERSION_KEY)) { + *version = QStringLiteral("1.x"); + return true; + } + else { + settings.clear(); + } + + return false; +} + +bool AppData::importSettings(const QString & fromVersion) +{ QString fromProduct; + upgradeFromVersion.clear(); - upgradeFromVersion = ""; - - if (fromVersion == "2.1") { - fromCompany = "OpenTX"; - fromProduct = "Companion 2.1"; - } - else if (fromVersion == "2.0") { - fromCompany = "OpenTX"; - fromProduct = "Companion 2.0"; - } - else if (fromVersion == "1.x") { - fromCompany = "OpenTX"; + if (fromVersion.startsWith("2.")) + fromProduct = "Companion " % fromVersion; + else if (fromVersion == "1.x") fromProduct = "OpenTX Companion"; - } else return false; upgradeFromVersion = fromVersion; - QSettings fromSettings(fromCompany, fromProduct); + QSettings fromSettings(COMPANY, fromProduct); + return importSettings(&fromSettings); +} - // do not copy these settings - QStringList excludeKeys = QStringList() << "compilation-server"; -#ifdef WIN32 - // locations of tools which come with Companion distros - excludeKeys << "avrdude_location" << "avrdudeLocation" << "dfu_location"; - // install-specific keys; "." is the "default" key which may contain install path - excludeKeys << "Start Menu Folder" << "."; -#endif +bool AppData::importSettings(QSettings * fromSettings) +{ + if (!fromSettings) + return false; - // import settings - foreach (const QString & key, fromSettings.allKeys()) { - if (fromSettings.value(key).isValid() && !excludeKeys.contains(key)) { - toSettings.setValue(key, fromSettings.value(key)); - } + // Create temporary settings because we may modify them before import. + QSettings tempSettings(COMPANY, PRODUCT % "_import"); + for (const QString & key : fromSettings->allKeys()) + tempSettings.setValue(key, fromSettings->value(key)); + + // convert settings first to simplify import process + convertSettings(tempSettings); + + if (fromSettings->status() != QSettings::NoError || tempSettings.status() != QSettings::NoError) { + tempSettings.clear(); + return false; } + // import settings, just copy since they have already been converted/cleaned. + for (const QString & key : tempSettings.allKeys()) { + if (key != ".") // special Windows registry key, don't import it + m_settings.setValue(key, tempSettings.value(key)); + } + + tempSettings.clear(); + + if (m_settings.status() != QSettings::NoError) + return false; + + firstUse = !hasCurrentSettings(); return true; } + +bool AppData::exportSettings(QSettings * toSettings, bool clearDestination) +{ + if (!toSettings) + return false; + + m_settings.sync(); + if (clearDestination) + toSettings->clear(); + foreach (const QString & key, m_settings.allKeys()) { + const QVariant newVal = m_settings.value(key); + // Skip export if property does not exist or is the default value. + if (newVal.isValid() && CompStoreObj::propertyPathIsValidNonDefault(key, newVal)) + toSettings->setValue(key, newVal); + //else qDebug() << "SKIPPING:" << key << newVal; + } + // Write the version and timestamp to export file -- they will NOT be imported, but may be useful for converting future imports. + toSettings->setValue(SETTINGS_VERSION_KEY, m_settings.value(SETTINGS_VERSION_KEY)); + toSettings->setValue(SETTINGS_TIMESTAMP_KEY, m_settings.value(SETTINGS_TIMESTAMP_KEY)); + toSettings->sync(); + + return (toSettings->status() == QSettings::NoError); +} + +bool AppData::exportSettingsToFile(const QString & expFile, QString & resultMsg) +{ + QSettings toSettings(expFile, QSettings::IniFormat); + if (exportSettings(&toSettings)) { + resultMsg = tr("Application Settings have been saved to\n %1").arg(expFile); + return true; + } + resultMsg = tr("Could not save Application Settings to file \"%1\"").arg(expFile) % " "; + if (toSettings.status() == QSettings::AccessError) + resultMsg.append(tr("because the file could not be saved (check access permissions).")); + else + resultMsg.append(tr("for unknown reasons.")); + return false; +} diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index 7ac02bdcb..8818a416c 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -18,414 +18,474 @@ * GNU General Public License for more details. */ -// All temporary and permanent global variables are defined here to make -// initialization and storage safe and visible. -// Do not access variables in QSettings directly, it is not type safe! - +/*! \defgroup AppData Application Settings Manager + \brief All temporary and permanent global variables are defined here to make initialization and storage safe and visible. + \note Do not access variables in QSettings directly. + \sa CompStoreObj + @{ +*/ #ifndef _APPDATA_H_ #define _APPDATA_H_ -#include -#include -#include +#include "constants.h" +#include "macros.h" +#include "simulator.h" +#include "version.h" + +#include +#include #include #include -#include "constants.h" -#include "simulator.h" +//! CPN_SETTINGS_REVISION is used to track settings changes independently of OpenTX version. It should be reset to zero whenever settings are migrated to new COMPANY or PRODUCT. +//! \note !! Increment this value if properties are removed or refactored. It will trigger a conversion/cleanup of any stored settings. \sa AppData::convertSettings() +#define CPN_SETTINGS_REVISION 0 -#define COMPANY "OpenTX" -#define COMPANY_DOMAIN "open-tx.org" -#define PRODUCT "Companion 2.2" -#define APP_COMPANION "OpenTX Companion" -#define APP_SIMULATOR "OpenTX Simulator" +//! CPN_SETTINGS_VERSION is used for settings data version tracking. +#define CPN_SETTINGS_VERSION ((VERSION_NUMBER << 8) | CPN_SETTINGS_REVISION) + +#define COMPANY QStringLiteral("OpenTX") +#define COMPANY_DOMAIN QStringLiteral("open-tx.org") +#define PRODUCT QStringLiteral("Companion " QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR)) +#define APP_COMPANION QStringLiteral("OpenTX Companion") +#define APP_SIMULATOR QStringLiteral("OpenTX Simulator") + +//! Default location for OpenTX-related user documents (settigns, logs, etc) +#define CPN_DOCUMENTS_LOCATION QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" % COMPANY +//! Location for settings backup files. TODO: make option or remember last location. +#define CPN_SETTINGS_BACKUP_DIR CPN_DOCUMENTS_LOCATION % "/backup" +#define CPN_SETTINGS_INI_FILE QString(PRODUCT % " " % QCoreApplication::translate("Companion", "settings") % " %1.ini") +#define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE) #define MAX_PROFILES 15 #define MAX_JOYSTICKS 8 -class CompStoreObj -{ - public: - void clear (const QString & tag1 = "", const QString &tag2 = "", const QString &tag3 = ""); +// It important that these function names are consistent everywhere. +#define PROP_FSIG_INIT_IMPL _init() +#define PROP_FSIG_KEY_IMPL _key() +#define PROP_FSIG_DFLT_IMPL _default() +// this set is used in macros +#define PROP_FSIG_INIT(NAME) CONCATENATE(NAME, PROP_FSIG_INIT_IMPL) +#define PROP_FSIG_KEY(NAME) CONCATENATE(NAME, PROP_FSIG_KEY_IMPL) +#define PROP_FSIG_DFLT(NAME) CONCATENATE(NAME, PROP_FSIG_DFLT_IMPL) +// this set is used at runtime +#define PROP_FSIG_INIT_STR(NAME) qPrintable(QString("%1" QT_STRINGIFY(PROP_FSIG_INIT_IMPL)).arg(NAME)) +#define PROP_FSIG_KEY_STR(NAME) qPrintable(QString("%1" QT_STRINGIFY(PROP_FSIG_KEY_IMPL)).arg(NAME)) +#define PROP_FSIG_DFLT_STR(NAME) qPrintable(QString("%1" QT_STRINGIFY(PROP_FSIG_DFLT_IMPL)).arg(NAME)) - // NOTE: OBJ_T can only be a standard or registered Qt type. To use custom types, - // either Q_DECLARE_METATYPE() them or add additional logic in retrieve() to handle QMetaType::User. +//! Helper macro to cast the default value of property \a NAME to its actual \a TYPE . +#define PROP_DFLT_CAST(NAME, TYPE) PROP_FSIG_DFLT(NAME).value() - template - void store(const OBJ_T newValue, OBJ_T & destValue, const TAG_T tag, const GP1_T group1 = "", const GP2_T group2 = ""); - template - void retrieve(OBJ_T & destValue, const TAG_T tag, const DEF_T def, const GP1_T group1 = "", const GP2_T group2 = ""); +/*! + \brief The PROPERTY_META4 macro declares and defines member property meta data functions, and declares the member variable. It also sets up the Q_PROPERTY declaration, essential for introspection. - template - void getset(OBJ_T & value, const TAG_T tag, const DEF_T def, const GP1_T group1 = "", const GP2_T group2 = "" ); + It produces the following code for each invokation (where \e name is the property name and \e T is the type.): + \code + public: + static const QVariant name_default(); // Returns the default value as a QVariant. + static const QString name_key(); // Returns the settings storage key name (may or may not be same as \a name ) + Q_SIGNAL void nameChanged(const T &); // Value change notifier signal. + private: + T m_name; // Member variable. + \endcode - // this specialization is needed because QVariant::fromValue(const char *) fails - template - void getset(OBJ_T & value, const TAG_T tag, const char * def, const GP1_T group1 = "", const GP2_T group2 = "" ); + If you only use this macro (w/out a \p PROPERTY*() macro) then you need to provide the getter, setter, and reset functions yourself. The initializer functoin is optional. + Those function names must match the expected signatures as defined in the \e Q_PROPERTY() macro. -}; + \param TYPE The data type (T). + \param NAME Name of the property. + \param KEY Settings key name, a const string. Can be the same as \a NAME. + \param DFLT Default value, must be compatible with \a TYPE. -class FwRevision: protected CompStoreObj -{ - public: - int get( const QString); - void set( const QString, const int ); - void remove( const QString ); -}; + \note \a Macro should be placed in the private declarations section. + \note \a TYPE must be a standard or registered Qt type. See notes in CompStoreObj. -class JStickData: protected CompStoreObj -{ + \sa PROPERTY4() \sa PROPERTY_META() +*/ +#define PROPERTY_META4(TYPE, NAME, KEY, DFLT) \ + public: \ + Q_INVOKABLE static inline const QVariant PROP_FSIG_DFLT(NAME) \ + { return QVariant::fromValue(DFLT); } \ + Q_INVOKABLE static inline const QString PROP_FSIG_KEY(NAME) \ + { return QStringLiteral(KEY); } \ + Q_SIGNALS: void NAME##Changed(const TYPE & val); \ + private: \ + Q_PROPERTY(TYPE NAME READ NAME WRITE NAME RESET NAME##Reset NOTIFY NAME##Changed STORED true) \ + TYPE m_##NAME; + +/*! + \brief Convenience equivalent to \p PROPERTY_META4() macro when the \a NAME and \a KEY are the same (recommended). + \sa PROPERTY() \sa PROPERTY_META4() +*/ +#define PROPERTY_META(TYPE, NAME, DFLT) PROPERTY_META4(TYPE, NAME, #NAME, DFLT) + + +/*! + \brief This macro extends \p PROPERTY_META4() to add initializer, getter, setter, and reset functions. + It produces the following additional code for each invokation (where \e name is the property name and \e T is the type.): + \code + public: + inline T name() const; // Getter function. + void name(const T &, bool=true); // Sets value and optionally saves to storage. If new value == default value, and store == true, the storage is cleared. + void nameReset(bool=true); // Reset to default and optionally save. + protected: + void name_init(); // Sets value from saved settings, if any, otherwise sets default value. Calls CompStoreObj::load() + \endcode + + \note \a Macro should be placed in the private declarations section. + \sa PROPERTY(), PROPERTY_META4() +*/ +#define PROPERTY4(TYPE, NAME, KEY, DFLT) \ + PROPERTY_META4(TYPE, NAME, KEY, DFLT) \ + public: \ + inline TYPE NAME() const { return m_##NAME; } \ + public Q_SLOTS: \ + void NAME(const TYPE &val, bool store = true) { \ + if (val == m_##NAME) return; \ + m_##NAME = val; \ + emit NAME##Changed(val); \ + if (store) \ + CompStoreObj::storeProperty(this, QStringLiteral(#NAME)); \ + } \ + inline void NAME##Reset(bool store = true) \ + { NAME(PROP_DFLT_CAST(NAME, TYPE), store); } \ + protected Q_SLOTS: \ + void PROP_FSIG_INIT(NAME) { \ + load(this, QStringLiteral(#NAME), PROP_FSIG_KEY(NAME), PROP_FSIG_DFLT(NAME)); \ + } \ private: - int index; - int _stickAxe; - int _stickMin; - int _stickMed; - int _stickMax; - int _stickInv; +/*! + \brief Convenience equivalent to \p PROPERTY4() macro when the \a NAME and \a KEY are the same (recommended). + \param TYPE The data type (T). + \param NAME Name of the property, and also the settings key name. + \param DFLT Default value, must be compatible with \a TYPE. + \sa PROPERTY4() +*/ +#define PROPERTY(TYPE, NAME, DFLT) PROPERTY4(TYPE, NAME, #NAME, DFLT) - public: - // All the get definitions - int stick_axe(); - int stick_min(); - int stick_med(); - int stick_max(); - int stick_inv(); +//! Convenience macros for QString types with either null or custom default value. \sa PROPERTY() +#define PROPERTYSTR(NAME) PROPERTY(QString, NAME, QStringLiteral("")) +#define PROPERTYSTRD(NAME, DFLT) PROPERTY(QString, NAME, QString(DFLT)) +//! Convenience macros for QString types where key name != property name and either null or custom default value. \sa PROPERTY4() +#define PROPERTYSTR2(NAME, KEY) PROPERTY4(QString, NAME, KEY, QStringLiteral("")) +#define PROPERTYSTR3(NAME, KEY, DFLT) PROPERTY4(QString, NAME, KEY, QString(DFLT)) - // All the set definitions - void stick_axe(const int); - void stick_min(const int); - void stick_med(const int); - void stick_max(const int); - void stick_inv(const int); +//! Convenience macros for QByteArray types with null value. \sa PROPERTY() +#define PROPERTYQBA(NAME) PROPERTY(QByteArray, NAME, QByteArray()) +#define PROPERTYQBA2(NAME, KEY) PROPERTY4(QByteArray, NAME, KEY, QByteArray()) - JStickData(); - void remove(); + +/*! + \brief CompStoreObj manages staic meta data on member properties (extending Qt's own property system) and provides utility functions for subclasses. + + The extension is essentially used to provide some "integration" with \e QSettings by providing storage path/key mappings to properties. + It also adds ability to access property default values, auto-initialization from saved settings (with fallback to default), and provides change notification signals. + A reset slot is provided for all properties, which sets the value back to default and is invokable through the Qt property system. + + The process of managing which settings key is used to store the property is complicated by properties which were originally configured to use a different key name than propety name. + Hence we need to keep a mapping of all names to keys, even if they're identical (which should be the case with any newly/recently added properties. + + \note Property types must be a standard or registered Qt type which are compatible with \e QVariant and \e QSettings. To use custom types, you need to \e Q_DECLARE_METATYPE() + them and add \e QDataStream streaming operators and (possibly) comparison operators so that they can be handled by \e QVariant and read/written by \e QSettings. + For an example of using a custom type with stream operators see the \p SimulatorOptions class, or e.g. https://stackoverflow.com/questions/18144377/writing-and-reading-custom-class-to-qsettings . + For an example of using a custom enum type, see \p AppData::DownloadBranchType . + + \sa AppData +*/ +class CompStoreObj: public QObject +{ + Q_OBJECT + public slots: + virtual inline void init() { initAllProperties(this); } + virtual inline void resetAll() { resetAllProperties(this); } + virtual inline void storeAll() { storeAllProperties(this); } + + protected: + CompStoreObj(); + + mutable QSettings m_settings; + + //! Subclasses must reimplement this to return a unique name, typically used in settings storage as the base path. + virtual QString propertyGroup() const = 0; + + //! reimplement this function to return the default settings group (used in \p pathForKey() if the \a group is not specified). + virtual QString settingsPath() const { return propertyGroup() % "/"; } + + //! Utility function to return a fully qualified settings path for given \a key in optional \a group. If \a group is null, \p settingsPath() is used. Reimpliment to provide custom paths. + virtual QString pathForKey(const QString &key, const QString &group = QString()) const; + + //! Set property to value saved in persistent storage, if any, otherwise to \a def default value. Calls \p pathForKey() to determine full settings path from given \a key and \a group. + void load(CompStoreObj *obj, const QString & name, const QString & key, const QVariant & def, const QString & group = QString()); + + //! Save property value \a newValue to \a key in persistent storage. Calls \p pathForKey() to determine full settings path from given \a key and \a group. + void store(const QVariant & newValue, const QString & key, const QString & group = QString()) const; + + //! Deletes any saved settings in \a key, which is (optionally) a subkey of \a group. Calls \p pathForKey() to determine full settings path from given \a key and \a group. + void clear(const QString & key, const QString & group = QString()) const; + + //! Deletes settings \a group if it is empty. + void removeGroupIfEmpty(const QString & group) const; + + //// Static property meta data handling functions. This first set of functons operates on specific object instances. + + //! Calls the initialization function on a property declared with a Q_PROPERTY() macro for given \a obj object. + static bool initProperty(CompStoreObj * obj, const QString & name); + //! Calls the initialization function on all properties declared with a Q_PROPERTY() macro (and by extension with our own PROPERTY() et.al.). + static void initAllProperties(CompStoreObj * obj); + + //! Invokes the RESET function on a property declared with a Q_PROPERTY() macro for given \a obj object. + static bool resetProperty(CompStoreObj * obj, const QString & name); + //! Invokes the RESET function on all properties declared with a Q_PROPERTY() macro for given \a obj object. + static void resetAllProperties(CompStoreObj * obj); + + //! Invokes the \p store() function on a property declared with a Q_PROPERTY() macro for given \a obj object. + static bool storeProperty(CompStoreObj * obj, const QString & name); + //! Invokes the \p store() function on all properties declared with a Q_PROPERTY() macro for given \a obj object. + static void storeAllProperties(CompStoreObj * obj); + + //// Methods below here operate only on static object data, not specific object instances. They are only used to look up meta data about an obect. + + //! Each subclass should register their settings group name (root path). This way we can easily look up which object a property key belongs to. + //! The actual instance of \a obj doesn't matter, we just use static data and only store this mapping once. + static inline void addObjectMapping(const QString & groupName, CompStoreObj * obj) { group2ObjMap().insert(groupName, obj); } + //! Get the CompStoreObj associated with \a group, if any. Returns null pointer if \a group is invalid. Note that this object is only useful for static data lookup, it is not guaranteed to be any specific instance. + static inline CompStoreObj * getObjectByGroup(const QString & group) { return group2ObjMap().value(group, nullptr); } + + //! Returns true if property exists based on settings \a path. Special exception for groups with dynamic property names, eg. FwRevision. \sa propertyPathIsValidNonDefault(), propertyExists(), groupHasDynamicProperties() + static bool propertyPathIsValid(const QString & path); + //! Returns true if property exists (based on settings \a path) and \a newVal != the default property value. Special exception for groups with dynamic property names, eg. FwRevision. + static bool propertyPathIsValidNonDefault(const QString & path, const QVariant & newVal); + + //! Return true if \a name property exists in the given \a obj object. + static bool propertyExists(CompStoreObj * obj, const QString & name); + //! Return true if \a name property exists within settings \a group. Group names must be declared with \p addObjectMapping() + static inline bool propertyExists(const QString & group, const QString & name) { return propertyExists(getObjectByGroup(group), name); } + + //! Return true if a property exists by checking the given settings \a key in the given \a obj object. + static inline bool propertyKeyExists(CompStoreObj * obj, const QString & key) { return keyToNameMap().value(obj->propertyGroup()).contains(key); } + //! Return true if a property exists by checking the given settings \a key within settings \a group. + static inline bool propertyKeyExists(const QString & group, const QString & key) { return keyToNameMap().value(group).contains(key); } + + //! Get the default value for a property \a name in the given \a obj object. + static const QVariant propertyDefaultValue(CompStoreObj * obj, const QString & name); + //! Get the default value for a property \a name within \a group. + static inline const QVariant propertyDefaultValue(const QString & group, const QString & name) { return propertyDefaultValue(getObjectByGroup(group), name); } + + //! Get the default value for a property with settings key name of \a key in the given \a obj object. + static inline const QVariant propertyKeyDefaultValue(CompStoreObj * obj, const QString & key) { return propertyDefaultValue(obj, keyToNameMap().value(obj->propertyGroup()).value(key)); } + //! Get the default value for a property with settings key name of \a key within \a group. Group names must be declared with \p addObjectMapping() + static inline const QVariant propertyKeyDefaultValue(const QString & group, const QString & key) { return propertyDefaultValue(group, keyToNameMap().value(group).value(key)); } + + //! Get the settings key name for property \a name for the given \a obj object. + static const QString propertyKeyName(CompStoreObj * obj, const QString & name); + //! Get the settings key name for property \a name within the \a group. + static inline const QString propertyKeyName(const QString & group, const QString & name) { return propertyKeyName(getObjectByGroup(group), name); } + + //! Groups which have dynamic property names always return true in \p propertyPathIsValid() and \p propertyPathIsValidNonDefault(). So far only FwRevision uses this. + static void addDynamicPropertyGroup(const QString & group) { dynamicPropGroups().append(group); } + //! Check if \a group has dynamic properties. + static inline bool groupHasDynamicProperties(const QString & group) { return dynamicPropGroups().contains(group); } + + //! Utility function. Given a full settings path ([group/][.../]key), returns the primary group (or "General" if no group) and + //! the last key value in the path (which would correspond to a property in the object which owns the group). + //! \retval QPair({, }) + static QPair splitGroupedPath(const QString & path); + + private: + // static lookup tables storage + static QHash & group2ObjMap(); //! Maps propertyGroup() name to the CompStoreObj subclass which owns it. + static const QHash > & keyToNameMap(); //! Maps key names to property names for each property group. + static QVector & dynamicPropGroups(); //! \sa addDynamicPropertyGroup() +}; + +class AppData; + + +//! \brief FwRevision class stores data about downloaded firmware binaries. It uses dynamic key names and does not have any properties. +class FwRevision: public CompStoreObj +{ + Q_OBJECT + public slots: + inline int get(const QString & fwType) const { return m_settings.value(fwType, 0).toInt(); } + inline void set(const QString & fwType, const int fwRevision) const { store(QString("%1").arg(fwRevision), fwType); } + inline void remove(const QString & tag) const { clear(tag); } + inline void resetAll() override { clear(propertyGroup(), ""); } + + protected: + explicit FwRevision() : CompStoreObj() { CompStoreObj::addDynamicPropertyGroup(propertyGroup()); } + QString propertyGroup() const override { return QStringLiteral("FwRevisions"); } + friend class AppData; +}; + + +//! \brief JStickData class stores properties related to each joystick axis (calibration/assignment/direction). +class JStickData: public CompStoreObj +{ + Q_OBJECT + public slots: bool existsOnDisk(); - void init(int index); - void flush(); + + protected: + explicit JStickData(); + void setIndex(int idx) { index = idx; } + inline QString propertyGroup() const override { return QStringLiteral("JsCalibration"); } + inline QString settingsPath() const override { return QString("%1/%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + + private: + PROPERTY(int, stick_axe, -1) + PROPERTY(int, stick_min, -32768) + PROPERTY(int, stick_med, 0) + PROPERTY(int, stick_max, 32767) + PROPERTY(int, stick_inv, 0) + + int index; }; -class Profile: protected CompStoreObj + +//! \brief Profile class stores properties related to each Radio Profile. +//! \todo TODO: Remove or refactor stored radio settings system (#4583) +class Profile: public CompStoreObj { + Q_OBJECT + public: + Profile & operator=(const Profile & rhs); + + public slots: + bool existsOnDisk(); + void resetFwVariables(); + + protected: + explicit Profile(); + void setIndex(int idx) { index = idx; } + inline QString propertyGroup() const override { return QStringLiteral("Profiles"); } + inline QString settingsPath() const override { return QString("%1/profile%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + private: - // Class Internal Variable - int index; + PROPERTYSTR2(name, "Name") + PROPERTYSTR2(splashFile, "SplashFileName") + PROPERTYSTR(fwName) + PROPERTYSTR(fwType) + PROPERTYSTR(sdPath) + PROPERTYSTR(pBackupDir) - // Application Variables - QString _fwName; - QString _fwType; - QString _name; - QString _sdPath; - int _volumeGain; - QString _pBackupDir; - QString _splashFile; - bool _burnFirmware; - bool _penableBackup; - bool _renameFwFiles; - int _channelOrder; - int _defaultMode; + PROPERTY4(int, channelOrder, "default_channel_order", 0) + PROPERTY4(int, defaultMode, "default_mode", 1) + PROPERTY (int, volumeGain, 10) - // Firmware Variables - QString _beeper; - QString _countryCode; - QString _display; - QString _haptic; - QString _speaker; - QString _stickPotCalib; - QString _timeStamp; - QString _trainerCalib; - QString _controlTypes; - QString _controlNames; - int _gsStickMode; - int _ppmMultiplier; - int _vBatWarn; - int _vBatMin; - int _vBatMax; - int _txCurrentCalibration; - int _txVoltageCalibration; + PROPERTY4(bool, renameFwFiles, "rename_firmware_files", false) + PROPERTY (bool, burnFirmware, false) + PROPERTY (bool, penableBackup, false) // Simulator variables - SimulatorOptions _simulatorOptions; + PROPERTY(SimulatorOptions, simulatorOptions, SimulatorOptions()) - public: - // All the get definitions - QString fwName() const; - QString fwType() const; - QString name() const; - QString sdPath() const; - int volumeGain() const; - QString pBackupDir() const; - QString splashFile() const; - bool burnFirmware() const; - bool renameFwFiles() const; - bool penableBackup() const; - int channelOrder() const; - int defaultMode() const; + // Firmware Variables + PROPERTYSTR2(beeper, "Beeper") + PROPERTYSTR2(countryCode, "countryCode") + PROPERTYSTR2(display, "Display") + PROPERTYSTR2(haptic, "Haptic") + PROPERTYSTR2(speaker, "Speaker") + PROPERTYSTR2(stickPotCalib, "StickPotCalib") + PROPERTYSTR2(timeStamp, "TimeStamp") + PROPERTYSTR2(trainerCalib, "TrainerCalib") + PROPERTYSTR2(controlTypes, "ControlTypes") + PROPERTYSTR2(controlNames, "ControlNames") - QString beeper() const; - QString countryCode() const; - QString display() const; - QString haptic() const; - QString speaker() const; - QString stickPotCalib() const; - QString timeStamp() const; - QString trainerCalib() const; - QString controlTypes() const; - QString controlNames() const; - int txCurrentCalibration() const; - int gsStickMode() const; - int ppmMultiplier() const; - int txVoltageCalibration() const; - int vBatWarn() const; - int vBatMin() const; - int vBatMax() const; + PROPERTY4(int, gsStickMode, "GSStickMode", 0) + PROPERTY4(int, ppmMultiplier, "PPM_Multiplier", 0) + PROPERTY4(int, vBatWarn, "vBatWarn", 0) // not a typo.. vBat vs. Vbat + PROPERTY4(int, vBatMin, "VbatMin", 0) + PROPERTY4(int, vBatMax, "VbatMax", 0) + PROPERTY4(int, txCurrentCalibration, "currentCalib", 0) + PROPERTY4(int, txVoltageCalibration, "VbatCalib", 0) - SimulatorOptions simulatorOptions() const; + int index; - // All the set definitions - void name (const QString); - void fwName (const QString); - void fwType (const QString); - void volumeGain (const int); - void sdPath (const QString); - void pBackupDir (const QString); - void splashFile (const QString); - void burnFirmware (const bool); - void renameFwFiles (const bool); - void penableBackup (const bool); - void channelOrder (const int); - void defaultMode (const int); - - void beeper (const QString); - void countryCode (const QString); - void display (const QString); - void haptic (const QString); - void speaker (const QString); - void stickPotCalib (const QString); - void timeStamp (const QString); - void trainerCalib (const QString); - void controlTypes (const QString); - void controlNames (const QString); - void txCurrentCalibration (const int); - void gsStickMode (const int); - void ppmMultiplier (const int); - void txVoltageCalibration (const int); - void vBatWarn (const int); - void vBatMin (const int); - void vBatMax (const int); - - void simulatorOptions (const SimulatorOptions &); - - Profile(); - Profile& operator=(const Profile&); - void remove(); - bool existsOnDisk(); - void init(int newIndex); - void initFwVariables(); - void flush(); - QString groupId(); + static const QStringList fwVarsList() //! for resetFwVariables()... TODO: make this go away + { + static const QStringList list({ + "Beeper", "countryCode", "Display", "Haptic", "Speaker", "TimeStamp", "TrainerCalib", "StickPotCalib", + "ControlTypes", "ControlNames", "GSStickMode", "PPM_Multiplier", "vBatWarn", "VbatMin", "VbatMax", "currentCalib", "VbatCalib" + }); + return list; + } }; -#define PROPERTY4(type, name, key, dflt) \ - public: \ - inline type name() { return _ ## name; } \ - void name(const type val) { store(val, _ ## name, # key); } \ - private: \ - void name ## _init() { getset(_ ## name, # key, dflt); } \ - type _ ## name; +/*! + \brief The AppData class acts as the main interface for all application settings. + \note Only one instance of this class should ever exist. It is accessible application-wide through the glabal variable \p g . -#define PROPERTY(type, name, dflt) PROPERTY4(type, name, name, dflt) - -class AppData: protected CompStoreObj + \todo TODO: constructor should be protected/private, this class acts as a singleton. + \todo TODO: formalize access to Profile and JStickData via getter functions. + \todo TODO: better profile handling... e.g. a dynamic list instead of fixed number (see above TODO). + \todo TODO: move \p backLight to \p Profile +*/ +class AppData: public CompStoreObj { - PROPERTY(bool, enableBackup, false) - PROPERTY(bool, backupOnFlash, true) - PROPERTY(bool, outputDisplayDetails, false) - PROPERTY(bool, checkHardwareCompatibility, true) - PROPERTY(bool, removeModelSlots, true) - PROPERTY(bool, maximized, false) - PROPERTY(bool, simuSW, false) - PROPERTY(bool, tabbedMdi, false) - PROPERTY(bool, appDebugLog, false) - PROPERTY(bool, fwTraceLog, false) - - PROPERTY(unsigned, OpenTxBranch, BRANCH_RELEASE_STABLE); - - PROPERTY4(bool, jsSupport, js_support ,false) - PROPERTY4(bool, showSplash, show_splash ,true) - PROPERTY4(bool, snapToClpbrd, snapshot_to_clipboard ,false) - PROPERTY4(bool, autoCheckApp, startup_check_companion ,true) - PROPERTY4(bool, autoCheckFw, startup_check_fw ,true) - - // All the global variables + Q_OBJECT public: - Profile profile[MAX_PROFILES]; - JStickData joystick[MAX_JOYSTICKS]; - FwRevision fwRev; + enum DownloadBranchType { + BRANCH_RELEASE_STABLE, + BRANCH_RC_TESTING, + BRANCH_NIGHTLY_UNSTABLE + }; + Q_ENUM(DownloadBranchType) - private: - QStringList _recentFiles; - QStringList _simuDbgFilters; + enum NewModelAction { + MODEL_ACT_NONE, + MODEL_ACT_WIZARD, + MODEL_ACT_EDITOR + }; + Q_ENUM(NewModelAction) - QByteArray _mainWinGeo; - QByteArray _mainWinState; - QByteArray _modelEditGeo; - QByteArray _mdiWinGeo; - QByteArray _mdiWinState; - QByteArray _compareWinGeo; + explicit AppData(); + void init() override; + void initAll(); + void resetAllSettings(); + void storeAllSettings(); - QString _armMcu; - QString _avrArguments; - QString _avrPort; - QString _avrdudeLocation; - QString _dfuArguments; - QString _dfuLocation; - QString _locale; - QString _mcu; - QString _programmer; - QString _sambaLocation; - QString _sambaPort; - - QString _backupDir; - QString _gePath; - QString _eepromDir; - QString _flashDir; - QString _imagesDir; - QString _logDir; - QString _libDir; - QString _snapshotDir; - QString _updatesDir; - PROPERTY(QString, appLogsDir, QString(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" COMPANY "/DebugLogs")) - - int _newModelAction; // 0=no action; 1=model wizard; 2=model edit - int _backLight; - int _embedSplashes; - int _fwServerFails; - int _generalEditTab; - int _iconSize; - int _jsCtrl; - int _historySize; - int _id; - int _theme; - int _warningId; - int _simuLastProfId; - // currently loaded radio profile ID, NOT saved to persistent storage - int _sessionId; - - public: - // All the get definitions - QStringList recentFiles(); - QStringList simuDbgFilters(); - - QByteArray mainWinGeo(); - QByteArray mainWinState(); - QByteArray modelEditGeo(); - QByteArray mdiWinGeo(); - QByteArray mdiWinState(); - QByteArray compareWinGeo(); - - QString armMcu(); - QString avrArguments(); - QString avrPort(); - QString avrdudeLocation(); - QString dfuArguments(); - QString dfuLocation(); - QString lastFw(); - QString locale(); - QString mcu(); - QString programmer(); - QString sambaLocation(); - QString sambaPort(); - - QString backupDir(); - QString gePath(); - QString eepromDir(); - QString flashDir(); - QString imagesDir(); - QString logDir(); - QString libDir(); - QString snapshotDir(); - QString updatesDir(); - - int newModelAction(); - int backLight(); - int embedSplashes(); - int fwServerFails(); - int generalEditTab(); - int iconSize(); - int historySize(); - int jsCtrl(); - int id(); - int theme(); - int warningId(); - int simuLastProfId(); - int sessionId(); - - // All the set definitions - void recentFiles (const QStringList x); - void simuDbgFilters (const QStringList x); - - void mainWinGeo (const QByteArray); - void mainWinState (const QByteArray); - void modelEditGeo (const QByteArray); - void mdiWinGeo (const QByteArray); - void mdiWinState (const QByteArray); - void compareWinGeo (const QByteArray); - - void armMcu (const QString); - void avrArguments (const QString); - void avrPort (const QString); - void avrdudeLocation (const QString); - void dfuArguments (const QString); - void dfuLocation (const QString); - void lastFw (const QString); - void locale (const QString); - void mcu (const QString); - void programmer (const QString); - void sambaLocation (const QString); - void sambaPort (const QString); - - void backupDir (const QString); - void gePath (const QString); - void eepromDir (const QString); - void flashDir (const QString); - void imagesDir (const QString); - void logDir (const QString); - void libDir (const QString); - void snapshotDir (const QString); - void updatesDir (const QString); - - void newModelAction (const int); - void backLight (const int); - void embedSplashes (const int); - void fwServerFails (const int); - void generalEditTab (const int); - void iconSize (const int); - void historySize (const int); - void jsCtrl (const int); - void modelEditTab (const int); - void id (const int); - void theme (const int); - void warningId (const int); - void simuLastProfId (const int); - void sessionId (const int); - - // Constructor - AppData(); - void init(); - - QMap getActiveProfiles(); - inline bool isFirstUse() const { return firstUse; } + inline bool isFirstUse() const { return firstUse; } inline QString previousVersion() const { return upgradeFromVersion; } - bool hasCurrentSettings(); - bool findPreviousVersionSettings(QString * version); - bool importSettings(QString fromVersion); + bool hasCurrentSettings() const; - inline DownloadBranchType boundedOpenTxBranch() { + //! Get the currently active radio profile ID. This may or may not be the same as \p id(). \sa currentProfile() + inline int sessionId() const { return m_sessionId; } + //! Set the current profile ID, but do not save it in persisted settings. To set and save the ID, use \p id(int). + Q_SLOT void sessionId(int index); + //! Reset to stored ID + Q_SLOT inline void sessionIdReset() { sessionId(id()); } + + //! Get a modifiable (non-const) reference to the currently active Profile. \sa sessionId() + inline Profile & currentProfile() { return getProfile(m_sessionId); } + //! Get a non-modifiable (const) reference to the currently active Profile. \sa sessionId() + inline const Profile & currentProfile() const { return getProfile(m_sessionId); } + //! Get a modifiable (non-const) reference to the Profile at \a index. Returns the default profile if \a index is invalid. + Profile & getProfile(int index); + //! Get a non-modifiable (const) reference to the Profile at \a index. Returns the default profile if \a index is invalid. + const Profile & getProfile(int index) const; + //! List of all active profiles mapped by index. + QMap getActiveProfiles() const; + + //! Find the set of settings from the last previous version installed, if any. Used at virgin startup to offer import option. + bool findPreviousVersionSettings(QString * version) const; + //! Converts any old/refactored settings to new ones and removes anything stale if \a removeUnused is true. This is only important when + //! the meanings of property values change, or to do cleanup when properties are removed. + void convertSettings(QSettings & settings); + //! Removes all stored properties which either no longer exist or are set to default value. + void clearUnusedSettings(QSettings & settings); + + bool importSettings(const QString & fromVersion); + bool importSettings(QSettings * fromSettings); + bool exportSettings(QSettings * toSettings, bool clearDestination = true); + bool exportSettingsToFile(const QString & expFile, QString & resultMsg); + + inline DownloadBranchType boundedOpenTxBranch() const { #if defined(ALLOW_NIGHTLY_BUILDS) return qBound(BRANCH_RELEASE_STABLE, DownloadBranchType(OpenTxBranch()), BRANCH_NIGHTLY_UNSTABLE); #else @@ -433,13 +493,96 @@ class AppData: protected CompStoreObj #endif } + Profile profile[MAX_PROFILES]; + JStickData joystick[MAX_JOYSTICKS]; + FwRevision fwRev; + + signals: + void currentProfileChanged(); + protected: - void convertSettings(QSettings & settings); + inline QString propertyGroup() const override { return QStringLiteral("General"); } + inline QString settingsPath() const override { return QString(); } + + private: + + PROPERTY_META(int, sessionId, 0) // currently active radio profile ID, NOT saved to persistent storage -- Initialize before profileId! + PROPERTY4(int, id, "profileId", 0) // saved radio profile id, loaded at next Companion startup + + PROPERTY4(QStringList, recentFiles, "recentFileList", QStringList()) + + PROPERTYQBA2(mainWinGeo, "mainWindowGeometry") + PROPERTYQBA2(mainWinState, "mainWindowState") + PROPERTYQBA2(modelEditGeo, "modelEditGeometry") + PROPERTYQBA (mdiWinGeo) + PROPERTYQBA (mdiWinState) + PROPERTYQBA (compareWinGeo) + + PROPERTYSTR3(armMcu, "arm_mcu", QStringLiteral("at91sam3s4-9x")) + PROPERTYSTR2(avrArguments, "avr_arguments") + PROPERTYSTR2(avrPort, "avr_port") + PROPERTYSTR2(avrdudeLocation, "avrdudeLocation") + PROPERTYSTR3(dfuArguments, "dfu_arguments", QStringLiteral("-a 0")) + PROPERTYSTR2(dfuLocation, "dfu_location") + PROPERTYSTR2(sambaLocation, "samba_location") + PROPERTYSTR3(sambaPort, "samba_port", QStringLiteral("\\USBserial\\COM23")) + PROPERTYSTR2(backupDir, "backupPath") + PROPERTYSTR2(eepromDir, "lastDir") + PROPERTYSTR2(flashDir, "lastFlashDir") + PROPERTYSTR2(imagesDir, "lastImagesDir") + PROPERTYSTR2(logDir, "lastLogDir") + PROPERTYSTR2(libDir, "libraryPath") + PROPERTYSTR2(snapshotDir, "snapshotpath") + PROPERTYSTR2(updatesDir, "lastUpdatesDir") + + PROPERTYSTR (locale) + PROPERTYSTR (gePath) + PROPERTYSTRD(mcu, QStringLiteral("m64")) + PROPERTYSTRD(programmer, QStringLiteral("usbasp")) + PROPERTYSTRD(appLogsDir, CPN_DOCUMENTS_LOCATION % "/DebugLogs") + + PROPERTY(DownloadBranchType, OpenTxBranch, BRANCH_RELEASE_STABLE) + PROPERTY(NewModelAction, newModelAction, MODEL_ACT_WIZARD) + + PROPERTY4(int, embedSplashes, "embedded_splashes", 0) + PROPERTY4(int, fwServerFails, "fwserver", 0) + PROPERTY4(int, iconSize, "icon_size", 2) + PROPERTY4(int, jsCtrl, "js_ctrl", 0) + PROPERTY4(int, historySize, "history_size", 10) + PROPERTY (int, generalEditTab, 0) + PROPERTY (int, theme, 1) + PROPERTY (int, warningId, 0) + + PROPERTY4(bool, jsSupport, "js_support", false) + PROPERTY4(bool, showSplash, "show_splash", true) + PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false) + PROPERTY4(bool, autoCheckApp, "startup_check_companion", true) + PROPERTY4(bool, autoCheckFw, "startup_check_fw", true) + + PROPERTY(bool, enableBackup, false) + PROPERTY(bool, backupOnFlash, true) + PROPERTY(bool, outputDisplayDetails, false) + PROPERTY(bool, checkHardwareCompatibility, true) + PROPERTY(bool, removeModelSlots, true) + PROPERTY(bool, maximized, false) + PROPERTY(bool, tabbedMdi, false) + PROPERTY(bool, appDebugLog, false) + PROPERTY(bool, fwTraceLog, false) + + // Simulator global (non-profile) settings + PROPERTY(QStringList, simuDbgFilters, QStringList()) + PROPERTY(int, backLight, 0) + PROPERTY(int, simuLastProfId, -1) + PROPERTY(bool, simuSW, true) bool firstUse; QString upgradeFromVersion; + + CREATE_ENUM_FRIEND_STREAM_OPS(AppData::DownloadBranchType) + CREATE_ENUM_FRIEND_STREAM_OPS(AppData::NewModelAction) }; extern AppData g; +//! @} #endif // _APPDATA_H_ diff --git a/companion/src/version.h.in b/companion/src/version.h.in index 145b881d4..205d87ce7 100644 --- a/companion/src/version.h.in +++ b/companion/src/version.h.in @@ -1,2 +1,9 @@ -#define VERSION "@VERSION@" +#define VERSION "@VERSION@" +#define VERSION_MAJOR @VERSION_MAJOR@ +#define VERSION_MINOR @VERSION_MINOR@ +#define VERSION_REVISION @VERSION_REVISION@ +#define VERSION_SUFFIX "@VERSION_SUFFIX@" + +#define VERSION_NUMBER ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | VERSION_REVISION) + #define SIMULATOR_LIB_SEARCH_PATH "@SIMULATOR_LIB_PATH@"