diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 3629d51b7..ef4e11271 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -191,12 +191,9 @@ set(common_RESOURCES qt5_wrap_cpp(common_SRCS ${common_MOC_HDRS}) qt5_add_resources(common_RCC ${common_RESOURCES}) -#add_custom_target(gen_qrc DEPENDS ${common_RCC} ${companion_QM}) add_library(common ${common_SRCS} ${common_RCC}) -qt5_use_modules(common Core Xml Widgets) -target_link_libraries(common PRIVATE simulation) -#add_dependencies(common gen_qrc) +target_link_libraries(common simulation maxLibQtWidgets Qt5::Core Qt5::Xml Qt5::Widgets) set(CPN_COMMON_LIB common) @@ -307,7 +304,7 @@ qt5_wrap_cpp(companion_SRCS ${companion_MOC_HDRS}) add_executable(${COMPANION_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${companion_SRCS} ${icon_RC}) qt5_use_modules(${COMPANION_NAME} Core Widgets Network) -target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit simulation ${CPN_COMMON_LIB} maxLibQtWidgets qcustomplot shared storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES}) +target_link_libraries(${COMPANION_NAME} PRIVATE generaledit modeledit ${CPN_COMMON_LIB} qcustomplot shared storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES}) PrintTargetReport("${COMPANION_NAME}") @@ -316,7 +313,7 @@ PrintTargetReport("${COMPANION_NAME}") set(simu_SRCS simulator.cpp ) add_executable(${SIMULATOR_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${simu_SRCS} ${icon_RC}) -target_link_libraries(${SIMULATOR_NAME} PRIVATE simulation ${CPN_COMMON_LIB} storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES}) +target_link_libraries(${SIMULATOR_NAME} PRIVATE ${CPN_COMMON_LIB} storage ${PTHREAD_LIBRARY} ${SDL_LIBRARY} ${WIN_LINK_LIBRARIES}) ############# Install #################### diff --git a/companion/src/eeprominterface.cpp b/companion/src/eeprominterface.cpp index d949f99fb..7fd1906e9 100644 --- a/companion/src/eeprominterface.cpp +++ b/companion/src/eeprominterface.cpp @@ -244,11 +244,6 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting Firmware * firmware = Firmware::getCurrentVariant(); int board = firmware->getBoard(); - bool singleprec = (flags & RANGE_SINGLE_PRECISION); - - if (!singleprec && !IS_ARM(board)) { - singleprec = true; - } switch (type) { case SOURCE_TYPE_TELEMETRY: @@ -267,9 +262,7 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.unit = sensor.unitString(); } else { - if (singleprec) { - result.offset = -DBL_MAX; - } + result.offset = -DBL_MAX; switch (index) { case TELEMETRY_SOURCE_TX_BATT: @@ -279,20 +272,21 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.unit = QObject::tr("V"); break; case TELEMETRY_SOURCE_TX_TIME: - result.step = 1; - result.max = 24*60 - 1; + result.step = 60; + result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution + result.unit = QObject::tr("s"); break; case TELEMETRY_SOURCE_TIMER1: case TELEMETRY_SOURCE_TIMER2: case TELEMETRY_SOURCE_TIMER3: - result.step = singleprec ? 5 : 1; - result.max = singleprec ? 255*5 : 60*60; + result.step = 5; + result.max = 255 * result.step; result.unit = QObject::tr("s"); break; case TELEMETRY_SOURCE_RSSI_TX: case TELEMETRY_SOURCE_RSSI_RX: result.max = 100; - if (singleprec) result.offset = 128; + result.offset = 128; break; case TELEMETRY_SOURCE_A1_MIN: case TELEMETRY_SOURCE_A2_MIN: @@ -310,9 +304,9 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting case TELEMETRY_SOURCE_ALT_MIN: case TELEMETRY_SOURCE_ALT_MAX: case TELEMETRY_SOURCE_GPS_ALT: - result.step = singleprec ? 8 : 1; + result.step = 8; result.min = -500; - result.max = singleprec ? 1540 : 3000; + result.max = 1540; if (firmware->getCapability(Imperial) || settings.imperial) { result.step = (result.step * 105) / 32; result.min = (result.min * 105) / 32; @@ -332,15 +326,15 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.unit = QObject::trUtf8("°C"); break; case TELEMETRY_SOURCE_HDG: - result.step = singleprec ? 2 : 1; + result.step = 2; result.max = 360; - if (singleprec) result.offset = 256; + result.offset = 256; result.unit = QObject::trUtf8("°"); break; case TELEMETRY_SOURCE_RPM: case TELEMETRY_SOURCE_RPM_MAX: - result.step = singleprec ? 50 : 1; - result.max = singleprec ? 12750 : 30000; + result.step = 50; + result.max = 12750; break; case TELEMETRY_SOURCE_FUEL: result.max = 100; @@ -349,8 +343,8 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting case TELEMETRY_SOURCE_ASPEED: case TELEMETRY_SOURCE_ASPEED_MAX: result.decimals = 1; - result.step = singleprec ? 2.0 : 0.1; - result.max = singleprec ? (2*255) : 2000; + result.step = 2.0; + result.max = (2*255); if (firmware->getCapability(Imperial) || settings.imperial) { result.step *= 1.150779; result.max *= 1.150779; @@ -364,8 +358,8 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting break; case TELEMETRY_SOURCE_SPEED: case TELEMETRY_SOURCE_SPEED_MAX: - result.step = singleprec ? 2 : 1; - result.max = singleprec ? (2*255) : 2000; + result.step = 2; + result.max = (2*255); if (firmware->getCapability(Imperial) || settings.imperial) { result.step *= 1.150779; result.max *= 1.150779; @@ -379,8 +373,8 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting break; case TELEMETRY_SOURCE_VERTICAL_SPEED: result.step = 0.1; - result.min = singleprec ? -12.5 : -300.0; - result.max = singleprec ? 13.0 : 300.0; + result.min = -12.5; + result.max = 13.0; result.decimals = 1; result.unit = QObject::tr("m/s"); break; @@ -389,13 +383,13 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting break; case TELEMETRY_SOURCE_DIST: case TELEMETRY_SOURCE_DIST_MAX: - result.step = singleprec ? 8 : 1; - result.max = singleprec ? 2040 : 10000; + result.step = 8; + result.max = 2040; result.unit = QObject::tr("m"); break; case TELEMETRY_SOURCE_CELL: case TELEMETRY_SOURCE_CELL_MIN: - result.step = singleprec ? 0.02 : 0.01; + result.step = 0.02; result.max = 5.1; result.decimals = 2; result.unit = QObject::tr("V"); @@ -405,26 +399,26 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting case TELEMETRY_SOURCE_VFAS: case TELEMETRY_SOURCE_VFAS_MIN: result.step = 0.1; - result.max = singleprec ? 25.5 : 100.0; + result.max = 25.5; result.decimals = 1; result.unit = QObject::tr("V"); break; case TELEMETRY_SOURCE_CURRENT: case TELEMETRY_SOURCE_CURRENT_MAX: - result.step = singleprec ? 0.5 : 0.1; - result.max = singleprec ? 127.5 : 200.0; + result.step = 0.5; + result.max = 127.5; result.decimals = 1; result.unit = QObject::tr("A"); break; case TELEMETRY_SOURCE_CONSUMPTION: - result.step = singleprec ? 100 : 1; - result.max = singleprec ? 25500 : 30000; + result.step = 100; + result.max = 25500; result.unit = QObject::tr("mAh"); break; case TELEMETRY_SOURCE_POWER: case TELEMETRY_SOURCE_POWER_MAX: - result.step = singleprec ? 5 : 1; - result.max = singleprec ? 1275 : 2000; + result.step = 5; + result.max = 1275; result.unit = QObject::tr("W"); break; case TELEMETRY_SOURCE_ACCX: @@ -432,8 +426,8 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting case TELEMETRY_SOURCE_ACCZ: result.step = 0.01; result.decimals = 2; - result.max = singleprec ? 2.55 : 10.00; - result.min = singleprec ? 0 : -10.00; + result.max = 2.55; + result.min = 0; result.unit = QObject::tr("g"); break; default: @@ -441,28 +435,38 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting break; } - if (singleprec && result.offset==-DBL_MAX) { + if (result.offset == -DBL_MAX) { result.offset = result.max - (127*result.step); } - if (flags & (RANGE_DELTA_FUNCTION|RANGE_DELTA_ABS_FUNCTION)) { - if (singleprec) { - result.offset = 0; - result.min = result.step * -127; - result.max = result.step * 127; - } - else { - result.min = -result.max; - } + if (flags & (RANGE_DELTA_FUNCTION | RANGE_ABS_FUNCTION)) { + result.offset = 0; + result.min = result.step * -127; + result.max = result.step * 127; } } break; - case SOURCE_TYPE_GVAR: - result.max = 1024; + case SOURCE_TYPE_LUA_OUTPUT: + result.max = 30000; result.min = -result.max; break; + case SOURCE_TYPE_TRIM: + result.max = (model && model->extendedTrims ? firmware->getCapability(ExtendedTrimsRange) : firmware->getCapability(TrimsRange)); + result.min = -result.max; + break; + + case SOURCE_TYPE_GVAR: { + GVarData gv = model->gvarData[index]; + result.step = gv.multiplierGet(); + result.decimals = gv.prec; + result.max = gv.getMaxPrec(); + result.min = gv.getMinPrec(); + result.unit = gv.unitToString(); + break; + } + case SOURCE_TYPE_SPECIAL: if (index == 0) { //Batt result.step = 0.1; @@ -471,26 +475,30 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting result.unit = QObject::tr("V"); } else if (index == 1) { //Time - result.step = 1; - result.max = 24*60 - 1; - result.unit = QObject::tr("h:m"); + result.step = 60; + result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution + result.unit = QObject::tr("s"); } else { // Timers 1 - 3 - result.step = singleprec ? 5 : 1; - result.max = singleprec ? 255*5 : 60*60; - result.unit = singleprec ? QObject::tr("m:s") : QObject::tr("h:m:s"); + result.step = 1; + result.max = 9 * 60 * 60 - 1; // 8:59:59 (to match firmware) + result.min = -result.max; + result.unit = QObject::tr("s"); } break; + case SOURCE_TYPE_CH: + result.max = model->getChannelsMax(false); + result.min = -result.max; + break; + default: - if (model) { - result.max = model->getChannelsMax(true); - result.min = -result.max; - } + result.max = 100; + result.min = -result.max; break; } - if (flags & RANGE_DELTA_ABS_FUNCTION) { + if (flags & RANGE_ABS_FUNCTION) { result.min = 0; } @@ -936,12 +944,15 @@ CSFunctionFamily LogicalSwitchData::getFunctionFamily() const unsigned int LogicalSwitchData::getRangeFlags() const { - if (func == LS_FN_DPOS) - return RANGE_DELTA_FUNCTION; - else if (func == LS_FN_DAPOS) - return RANGE_DELTA_ABS_FUNCTION; - else - return 0; + int f = 0; + + if (func == LS_FN_DPOS || func == LS_FN_DAPOS) + f |= RANGE_DELTA_FUNCTION; + + if (func == LS_FN_DAPOS || func == LS_FN_APOS || func == LS_FN_ANEG) + f |= RANGE_ABS_FUNCTION; + + return f; } QString LogicalSwitchData::funcToString() const diff --git a/companion/src/modeledit/customfunctions.cpp b/companion/src/modeledit/customfunctions.cpp index 3c840782b..d97aa1117 100644 --- a/companion/src/modeledit/customfunctions.cpp +++ b/companion/src/modeledit/customfunctions.cpp @@ -23,6 +23,8 @@ #include "helpers.h" #include "appdata.h" +#include + RepeatComboBox::RepeatComboBox(QWidget *parent, int & repeatParam): QComboBox(parent), repeatParam(repeatParam) @@ -178,10 +180,8 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, connect(fswtchParam[i], SIGNAL(editingFinished()), this, SLOT(customFunctionEdited())); paramLayout->addWidget(fswtchParam[i]); - fswtchParamTime[i] = new QTimeEdit(this); + fswtchParamTime[i] = new TimerEdit(this); fswtchParamTime[i]->setProperty("index", i); - fswtchParamTime[i]->setAccelerated(true); - fswtchParamTime[i]->setDisplayFormat("hh:mm:ss"); connect(fswtchParamTime[i], SIGNAL(editingFinished()), this, SLOT(customFunctionEdited())); paramLayout->addWidget(fswtchParamTime[i]); @@ -450,10 +450,10 @@ void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified) } else if (func>=FuncSetTimer1 && func<=FuncSetTimer3) { if (modified) - cfn.param = QTimeS(fswtchParamTime[i]->time()).seconds(); - fswtchParamTime[i]->setMinimumTime(QTime(0, 0, 0)); - fswtchParamTime[i]->setMaximumTime(firmware->getMaxTimerStart()); - fswtchParamTime[i]->setTime(QTimeS(cfn.param)); + cfn.param = fswtchParamTime[i]->timeInSeconds(); + RawSourceRange range = RawSource(SOURCE_TYPE_SPECIAL, func - FuncSetTimer1 + 2).getRange(model, generalSettings); + fswtchParamTime[i]->setTimeRange((int)range.min, (int)range.max); + fswtchParamTime[i]->setTime(cfn.param); widgetsMask |= CUSTOM_FUNCTION_TIME_PARAM | CUSTOM_FUNCTION_ENABLE; } else if (func>=FuncSetFailsafeInternalModule && func<=FuncBindExternalModule) { diff --git a/companion/src/modeledit/customfunctions.h b/companion/src/modeledit/customfunctions.h index e66403688..1aa8b899e 100644 --- a/companion/src/modeledit/customfunctions.h +++ b/companion/src/modeledit/customfunctions.h @@ -26,6 +26,7 @@ #include "eeprominterface.h" class RawSwitchFilterItemModel; +class TimerEdit; class RepeatComboBox: public QComboBox { @@ -87,7 +88,7 @@ class CustomFunctionsPanel : public GenericPanel QComboBox * fswtchFunc[CPN_MAX_SPECIAL_FUNCTIONS]; QCheckBox * fswtchParamGV[CPN_MAX_SPECIAL_FUNCTIONS]; QDoubleSpinBox * fswtchParam[CPN_MAX_SPECIAL_FUNCTIONS]; - QTimeEdit * fswtchParamTime[CPN_MAX_SPECIAL_FUNCTIONS]; + TimerEdit * fswtchParamTime[CPN_MAX_SPECIAL_FUNCTIONS]; QPushButton * playBT[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchParamT[CPN_MAX_SPECIAL_FUNCTIONS]; QComboBox * fswtchParamArmT[CPN_MAX_SPECIAL_FUNCTIONS]; diff --git a/companion/src/modeledit/logicalswitches.cpp b/companion/src/modeledit/logicalswitches.cpp index 87a1e31d1..81386276a 100644 --- a/companion/src/modeledit/logicalswitches.cpp +++ b/companion/src/modeledit/logicalswitches.cpp @@ -22,17 +22,19 @@ #include "switchitemmodel.h" #include "helpers.h" +#include + LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), + rawSourceItemModel(NULL), selectedSwitch(0) { Stopwatch s1("LogicalSwitchesPanel"); int channelsMax = model.getChannelsMax(true); rawSwitchItemModel = new RawSwitchFilterItemModel(&generalSettings, &model, LogicalSwitchesContext); - int srcFlags = POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0); - rawSourceItemModel = Helpers::getRawSourceItemModel(&generalSettings, &model, srcFlags); - rawSourceItemModel->setParent(this); + + setDataModels(); QStringList headerLabels; headerLabels << "#" << tr("Function") << tr("V1") << tr("V2") << tr("AND Switch"); @@ -58,7 +60,8 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, // The function csw[i] = new QComboBox(this); csw[i]->setProperty("index", i); - connect(csw[i], SIGNAL(currentIndexChanged(int)), this, SLOT(edited())); + populateCSWCB(csw[i]); + connect(csw[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionChanged())); tableLayout->addWidget(i, 1, csw[i]); // V1 @@ -74,7 +77,7 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, cswitchValue[i]->setAccelerated(true); cswitchValue[i]->setDecimals(0); cswitchValue[i]->setProperty("index", i); - connect(cswitchValue[i], SIGNAL(valueChanged(double)), this, SLOT(edited())); + connect(cswitchValue[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited())); v1Layout->addWidget(cswitchValue[i]); cswitchValue[i]->setVisible(false); tableLayout->addLayout(i, 2, v1Layout); @@ -92,7 +95,7 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, cswitchOffset[i]->setMinimum(-channelsMax); cswitchOffset[i]->setAccelerated(true); cswitchOffset[i]->setDecimals(0); - connect(cswitchOffset[i], SIGNAL(valueChanged(double)), this, SLOT(edited())); + connect(cswitchOffset[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited())); cswitchOffset[i]->setVisible(false); v2Layout->addWidget(cswitchOffset[i]); cswitchOffset2[i] = new QDoubleSpinBox(this); @@ -101,13 +104,13 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, cswitchOffset2[i]->setMinimum(-channelsMax); cswitchOffset2[i]->setAccelerated(true); cswitchOffset2[i]->setDecimals(0); - connect(cswitchOffset2[i], SIGNAL(valueChanged(double)), this, SLOT(edited())); + cswitchOffset2[i]->setSpecialValueText(" " + tr("(instant)")); + connect(cswitchOffset2[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited())); cswitchOffset2[i]->setVisible(false); v2Layout->addWidget(cswitchOffset2[i]); - cswitchTOffset[i] = new QTimeEdit(this); + cswitchTOffset[i] = new TimerEdit(this); cswitchTOffset[i]->setProperty("index",i); - cswitchTOffset[i]->setAccelerated(true); - connect(cswitchTOffset[i],SIGNAL(editingFinished()),this,SLOT(edited())); + connect(cswitchTOffset[i],SIGNAL(editingFinished()),this,SLOT(offsetEdited())); v2Layout->addWidget(cswitchTOffset[i]); cswitchTOffset[i]->setVisible(false); tableLayout->addLayout(i, 3, v2Layout); @@ -115,6 +118,7 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, // AND cswitchAnd[i] = new QComboBox(this); cswitchAnd[i]->setProperty("index", i); + populateAndSwitchCB(cswitchAnd[i]); connect(cswitchAnd[i], SIGNAL(currentIndexChanged(int)), this, SLOT(andEdited(int))); tableLayout->addWidget(i, 4, cswitchAnd[i]); @@ -157,18 +161,55 @@ LogicalSwitchesPanel::~LogicalSwitchesPanel() { } +void LogicalSwitchesPanel::setDataModels() +{ + if (rawSourceItemModel) + rawSourceItemModel->deleteLater(); + + int srcFlags = POPULATE_NONE | POPULATE_SOURCES | POPULATE_SCRIPT_OUTPUTS | POPULATE_VIRTUAL_INPUTS | POPULATE_TRIMS | POPULATE_SWITCHES | POPULATE_TELEMETRY | (firmware->getCapability(GvarsInCS) ? POPULATE_GVARS : 0); + rawSourceItemModel = Helpers::getRawSourceItemModel(&generalSettings, model, srcFlags); + rawSourceItemModel->setParent(this); +} + +void LogicalSwitchesPanel::functionChanged() +{ + int i = sender()->property("index").toInt(); + unsigned newFunc = csw[i]->currentData().toUInt(); + + if (model->logicalSw[i].func == newFunc) + return; + + CSFunctionFamily oldFuncFamily = model->logicalSw[i].getFunctionFamily(); + model->logicalSw[i].func = newFunc; + CSFunctionFamily newFuncFamily = model->logicalSw[i].getFunctionFamily(); + + if (oldFuncFamily != newFuncFamily) { + model->logicalSw[i].clear(); + model->logicalSw[i].func = newFunc; + if (newFuncFamily == LS_FAMILY_TIMER) { + model->logicalSw[i].val1 = -119; + model->logicalSw[i].val2 = -119; + } + else if (newFuncFamily == LS_FAMILY_EDGE) { + model->logicalSw[i].val2 = -129; + } + } + updateLine(i); + emit modified(); +} + void LogicalSwitchesPanel::v1Edited(int value) { if (!lock) { int i = sender()->property("index").toInt(); model->logicalSw[i].val1 = cswitchSource1[i]->itemData(value).toInt(); if (model->logicalSw[i].getFunctionFamily() == LS_FAMILY_VOFS) { - RawSource source = RawSource(model->logicalSw[i].val1); - RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[i].getRangeFlags()); - model->logicalSw[i].val2 = (cswitchOffset[i]->value() - range.offset) / range.step; - setSwitchWidgetVisibility(i); + if (!offsetEditedAt(i)) + updateLine(i); + } + else { + emit modified(); } - emit modified(); } } @@ -208,75 +249,65 @@ void LogicalSwitchesPanel::delayEdited(double delay) } } -void LogicalSwitchesPanel::edited() +void LogicalSwitchesPanel::offsetEdited() { - if (!lock) { - lock = true; - int i = sender()->property("index").toInt(); - CSFunctionFamily oldFuncFamily = model->logicalSw[i].getFunctionFamily(); - model->logicalSw[i].func = csw[i]->itemData(csw[i]->currentIndex()).toInt(); - CSFunctionFamily newFuncFamily = model->logicalSw[i].getFunctionFamily(); + offsetEditedAt(sender()->property("index").toInt()); +} - if (oldFuncFamily != newFuncFamily) { - model->logicalSw[i].clear(); - model->logicalSw[i].func = csw[i]->currentData().toInt(); - if (newFuncFamily == LS_FAMILY_TIMER) { - model->logicalSw[i].val1 = -119; - model->logicalSw[i].val2 = -119; - } - else if (newFuncFamily == LS_FAMILY_EDGE) { - model->logicalSw[i].val2 = -129; - } - setSwitchWidgetVisibility(i); - } +bool LogicalSwitchesPanel::offsetEditedAt(int index) +{ + if (lock) + return false; - RawSource source; + lock = true; + bool mod = false; + int value; - switch (newFuncFamily) + switch (model->logicalSw[index].getFunctionFamily()) + { + case LS_FAMILY_VOFS: { - case LS_FAMILY_VOFS: - { - source = RawSource(model->logicalSw[i].val1); - RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[i].getRangeFlags()); - double value = source.isTimeBased() ? QTimeS(cswitchTOffset[i]->time()).seconds() : cswitchOffset[i]->value(); - model->logicalSw[i].val2 = round((value-range.offset)/range.step);; - value = model->logicalSw[i].val2*range.step + range.offset; - if (source.isTimeBased()) - cswitchTOffset[i]->setTime(QTimeS(round(value))); - else - cswitchOffset[i]->setValue(value); - break; - } - case LS_FAMILY_TIMER: - model->logicalSw[i].val1 = TimToVal(cswitchValue[i]->value()); - model->logicalSw[i].val2 = TimToVal(cswitchOffset[i]->value()); - updateTimerParam(cswitchValue[i], model->logicalSw[i].val1, 0.1); - updateTimerParam(cswitchOffset[i], model->logicalSw[i].val2, 0.1); - break; - case LS_FAMILY_EDGE: - cswitchOffset2[i]->setSpecialValueText(" " + tr("(instant)")); - if (sender() == cswitchOffset[i]) { - model->logicalSw[i].val2 = TimToVal(cswitchOffset[i]->value()); - updateTimerParam(cswitchOffset[i], model->logicalSw[i].val2, 0.0); - } - else { - model->logicalSw[i].val3 = TimToVal(cswitchOffset2[i]->value()) - model->logicalSw[i].val2; - } - updateTimerParam(cswitchOffset2[i], model->logicalSw[i].val2+model->logicalSw[i].val3, ValToTim(TimToVal(cswitchOffset[i]->value())-1)); - if (model->logicalSw[i].val3 == 0) { - cswitchOffset2[i]->setSuffix(" " + tr("(infinite)")); - } - else { - cswitchOffset2[i]->setSuffix(""); - } - break; - default: - break; + RawSource source = RawSource(model->logicalSw[index].val1); + RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[index].getRangeFlags()); + double currVal = source.isTimeBased() ? cswitchTOffset[index]->timeInSeconds() : cswitchOffset[index]->value(); + value = round((currVal - range.offset) / range.step); + mod = (mod || value != model->logicalSw[index].val2); + model->logicalSw[index].val2 = value; + break; } - emit modified(); - lock = false; - updateLine(i); + + case LS_FAMILY_TIMER: + value = TimToVal(cswitchValue[index]->value()); + mod = (mod || value != model->logicalSw[index].val1); + model->logicalSw[index].val1 = value; + value = TimToVal(cswitchOffset[index]->value()); + mod = (mod || value != model->logicalSw[index].val2); + model->logicalSw[index].val2 = value; + break; + + case LS_FAMILY_EDGE: + if (sender() == cswitchOffset[index]) { + value = TimToVal(cswitchOffset[index]->value()); + mod = (mod || value != model->logicalSw[index].val2); + model->logicalSw[index].val2 = value; + } + else { + value = TimToVal(cswitchOffset2[index]->value()) - model->logicalSw[index].val2; + mod = (mod || value != model->logicalSw[index].val3); + model->logicalSw[index].val3 = value; + } + break; + + default: + break; } + lock = false; + + if (mod) { + updateLine(index); + emit modified(); + } + return mod; } void LogicalSwitchesPanel::updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum) @@ -303,17 +334,22 @@ void LogicalSwitchesPanel::updateTimerParam(QDoubleSpinBox *sb, int timer, doubl #define VALUE_TO_VISIBLE 0x20 #define DELAY_ENABLED 0x40 #define DURATION_ENABLED 0x80 +#define LINE_ENABLED 0x100 -void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) +void LogicalSwitchesPanel::updateLine(int i) { lock = true; unsigned int mask; + csw[i]->setCurrentIndex(csw[i]->findData(model->logicalSw[i].func)); + cswitchAnd[i]->setCurrentIndex(cswitchAnd[i]->findData(RawSwitch(model->logicalSw[i].andsw).toValue())); + if (!model->logicalSw[i].func) { mask = 0; } else { - mask = DELAY_ENABLED | DURATION_ENABLED; + mask = LINE_ENABLED | DELAY_ENABLED | DURATION_ENABLED; + switch (model->logicalSw[i].getFunctionFamily()) { case LS_FAMILY_VOFS: @@ -321,20 +357,16 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) mask |= SOURCE1_VISIBLE; RawSource source = RawSource(model->logicalSw[i].val1); RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[i].getRangeFlags()); + double value = range.step * model->logicalSw[i].val2 + range.offset; /* TODO+source.getRawOffset(model)*/ cswitchSource1[i]->setModel(rawSourceItemModel); cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(source.toValue())); - cswitchOffset[i]->setDecimals(range.decimals); - cswitchOffset[i]->setSingleStep(range.step); if (source.isTimeBased()) { mask |= VALUE_TO_VISIBLE; - int maxTime = round(range.max); - int value = round(range.step*model->logicalSw[i].val2 + range.offset); - cswitchTOffset[i]->setMaximumTime(QTimeS(maxTime)); - QString format = (maxTime>=3600) ? "hh:mm:ss" : "mm:ss"; - if (!range.unit.isEmpty()) - format += QString("' [%1]'").arg(range.unit); - cswitchTOffset[i]->setDisplayFormat(format); - cswitchTOffset[i]->setTime(QTimeS(value)); + cswitchTOffset[i]->setTimeRange(range.min, range.max); + cswitchTOffset[i]->setSingleStep(range.step); + cswitchTOffset[i]->setPageStep(range.step * 60); + cswitchTOffset[i]->setShowSeconds(range.step != 60); + cswitchTOffset[i]->setTime((int)value); } else { mask |= VALUE2_VISIBLE; @@ -342,13 +374,16 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) cswitchOffset[i]->setSuffix(""); else cswitchOffset[i]->setSuffix(" " + range.unit); + cswitchOffset[i]->setDecimals(range.decimals); cswitchOffset[i]->setMinimum(range.min); cswitchOffset[i]->setMaximum(range.max); - cswitchOffset[i]->setValue(range.step*(model->logicalSw[i].val2/* TODO+source.getRawOffset(model)*/)+range.offset); + cswitchOffset[i]->setSingleStep(range.step); + cswitchOffset[i]->setValue(value); } break; } + case LS_FAMILY_STICKY: // no break case LS_FAMILY_VBOOL: mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE; @@ -357,6 +392,7 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) cswitchSource2[i]->setModel(rawSwitchItemModel); cswitchSource2[i]->setCurrentIndex(cswitchSource2[i]->findData(model->logicalSw[i].val2)); break; + case LS_FAMILY_EDGE: mask |= SOURCE1_VISIBLE | VALUE2_VISIBLE | VALUE3_VISIBLE; mask &= ~DELAY_ENABLED; @@ -364,14 +400,9 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(model->logicalSw[i].val1)); updateTimerParam(cswitchOffset[i], model->logicalSw[i].val2, 0.0); updateTimerParam(cswitchOffset2[i], model->logicalSw[i].val2+model->logicalSw[i].val3, ValToTim(TimToVal(cswitchOffset[i]->value())-1)); - cswitchOffset2[i]->setSpecialValueText(tr("(instant)")); - if (model->logicalSw[i].val3 == 0) { - cswitchOffset2[i]->setSuffix(tr("(infinite)")); - } - else { - cswitchOffset2[i]->setSuffix(""); - } + cswitchOffset2[i]->setSuffix((model->logicalSw[i].val3) ? "" : tr(" (infinite)")); break; + case LS_FAMILY_VCOMP: mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE; cswitchSource1[i]->setModel(rawSourceItemModel); @@ -379,6 +410,7 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) cswitchSource2[i]->setModel(rawSourceItemModel); cswitchSource2[i]->setCurrentIndex(cswitchSource2[i]->findData(model->logicalSw[i].val2)); break; + case LS_FAMILY_TIMER: mask |= VALUE1_VISIBLE | VALUE2_VISIBLE; updateTimerParam(cswitchValue[i], model->logicalSw[i].val1, 0.1); @@ -393,6 +425,7 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) cswitchOffset[i]->setVisible(mask & VALUE2_VISIBLE); cswitchOffset2[i]->setVisible(mask & VALUE3_VISIBLE); cswitchTOffset[i]->setVisible(mask & VALUE_TO_VISIBLE); + cswitchAnd[i]->setVisible(mask & LINE_ENABLED); if (firmware->getCapability(LogicalSwitchesExt)) { cswitchDuration[i]->setVisible(mask & DURATION_ENABLED); cswitchDelay[i]->setVisible(mask & DELAY_ENABLED); @@ -401,10 +434,12 @@ void LogicalSwitchesPanel::setSwitchWidgetVisibility(int i) if (mask & DELAY_ENABLED) cswitchDelay[i]->setValue(model->logicalSw[i].delay/10.0); } + + rawSwitchItemModel->update(); lock = false; } -void LogicalSwitchesPanel::populateCSWCB(QComboBox *b, int value) +void LogicalSwitchesPanel::populateCSWCB(QComboBox *b) { int order[] = { LS_FN_OFF, @@ -441,23 +476,14 @@ void LogicalSwitchesPanel::populateCSWCB(QComboBox *b, int value) continue; } b->addItem(LogicalSwitchData(func).funcToString(), func); - if (value == func) { - b->setCurrentIndex(b->count()-1); - } } b->setMaxVisibleItems(10); } -void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b, const RawSwitch & value, const int idx) +void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b) { - if (!model->logicalSw[idx].func) { - b->setVisible(false); - return; - } - if (IS_ARM(firmware->getBoard())) { b->setModel(rawSwitchItemModel); - b->setCurrentIndex(b->findData(value.toValue())); } else { RawSwitch item; @@ -466,36 +492,23 @@ void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b, const RawSwitch & v item = RawSwitch(SWITCH_TYPE_NONE); b->addItem(item.toString(), item.toValue()); - if (item == value) b->setCurrentIndex(b->count()-1); for (int i=1; i <= Boards::getCapability(firmware->getBoard(), Board::SwitchPositions); i++) { item = RawSwitch(SWITCH_TYPE_SWITCH, i); b->addItem(item.toString(), item.toValue()); - if (item == value) b->setCurrentIndex(b->count()-1); } for (int i=1; i<=6; i++) { item = RawSwitch(SWITCH_TYPE_VIRTUAL, i); b->addItem(item.toString(), item.toValue()); - if (item == value) b->setCurrentIndex(b->count()-1); } } b->setVisible(true); } -void LogicalSwitchesPanel::updateLine(int i) -{ - lock = true; - populateCSWCB(csw[i], model->logicalSw[i].func); - lock = true; - setSwitchWidgetVisibility(i); - lock = true; - populateAndSwitchCB(cswitchAnd[i], RawSwitch(model->logicalSw[i].andsw), i); - lock = false; -} - void LogicalSwitchesPanel::update() { + setDataModels(); for (int i=0; igetCapability(LogicalSwitches); i++) { updateLine(i); } @@ -510,7 +523,6 @@ void LogicalSwitchesPanel::cswPaste() LogicalSwitchData *csw = &model->logicalSw[selectedSwitch]; memcpy(csw, cswData.constData(), sizeof(LogicalSwitchData)); emit modified(); - rawSwitchItemModel->update(); updateLine(selectedSwitch); } } diff --git a/companion/src/modeledit/logicalswitches.h b/companion/src/modeledit/logicalswitches.h index eb9178557..0fed61bfa 100644 --- a/companion/src/modeledit/logicalswitches.h +++ b/companion/src/modeledit/logicalswitches.h @@ -25,6 +25,7 @@ #include "radiodata.h" class RawSwitchFilterItemModel; +class TimerEdit; class LogicalSwitchesPanel : public ModelPanel { @@ -37,12 +38,16 @@ class LogicalSwitchesPanel : public ModelPanel virtual void update(); private slots: - void edited(); + void setDataModels(); + void functionChanged(); void v1Edited(int value); void v2Edited(int value); void andEdited(int value); void durationEdited(double duration); void delayEdited(double delay); + void offsetEdited(); + bool offsetEditedAt(int index); + void updateLine(int index); void csw_customContextMenuRequested(QPoint pos); void cswDelete(); void cswCopy(); @@ -54,7 +59,7 @@ class LogicalSwitchesPanel : public ModelPanel QDoubleSpinBox * cswitchValue[CPN_MAX_LOGICAL_SWITCHES]; QDoubleSpinBox * cswitchOffset[CPN_MAX_LOGICAL_SWITCHES]; QDoubleSpinBox * cswitchOffset2[CPN_MAX_LOGICAL_SWITCHES]; - QTimeEdit * cswitchTOffset[CPN_MAX_LOGICAL_SWITCHES]; + TimerEdit * cswitchTOffset[CPN_MAX_LOGICAL_SWITCHES]; QComboBox * cswitchAnd[CPN_MAX_LOGICAL_SWITCHES]; QDoubleSpinBox * cswitchDuration[CPN_MAX_LOGICAL_SWITCHES]; QDoubleSpinBox * cswitchDelay[CPN_MAX_LOGICAL_SWITCHES]; @@ -62,12 +67,10 @@ class LogicalSwitchesPanel : public ModelPanel QComboBox * cswitchSource2[CPN_MAX_LOGICAL_SWITCHES]; RawSwitchFilterItemModel * rawSwitchItemModel; QStandardItemModel * rawSourceItemModel; - void setSwitchWidgetVisibility(int i); int selectedSwitch; - void populateCSWCB(QComboBox *b, int value); - void populateAndSwitchCB(QComboBox *b, const RawSwitch & value, const int idx); - void updateLine(int index); + void populateCSWCB(QComboBox *b); + void populateAndSwitchCB(QComboBox *b); void updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum=0); }; diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index b3628612e..306d24320 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -26,6 +26,8 @@ #include "helpers.h" #include "appdata.h" +#include + TelemetryAnalog::TelemetryAnalog(QWidget *parent, FrSkyChannelData & analog, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetryAnalog), @@ -324,6 +326,13 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, connect(minSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMinChanged(double))); ui->screenBarsLayout->addWidget(minSB[l], l, 1, 1, 1); + minTime[l] = new TimerEdit(this); + minTime[l]->setProperty("index", l); + minTime[l]->setProperty("type", "min"); + connect(minTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged())); + ui->screenBarsLayout->addWidget(minTime[l], l, 1, 1, 1); + minTime[l]->hide(); + QLabel * label = new QLabel(this); label->setAutoFillBackground(false); label->setStyleSheet(QString::fromUtf8("Background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 128, 255), stop:0.339795 rgba(0, 0, 128, 255), stop:0.339799 rgba(255, 255, 255, 255), stop:0.662444 rgba(255, 255, 255, 255),)\n""")); @@ -336,6 +345,13 @@ TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, maxSB[l]->setProperty("index", l); connect(maxSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMaxChanged(double))); ui->screenBarsLayout->addWidget(maxSB[l], l, 3, 1, 1); + + maxTime[l] = new TimerEdit(this); + maxTime[l]->setProperty("index", l); + maxTime[l]->setProperty("type", "max"); + connect(maxTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged())); + ui->screenBarsLayout->addWidget(maxTime[l], l, 3, 1, 1); + maxTime[l]->hide(); } disableMouseScrolling(); @@ -410,30 +426,58 @@ void TelemetryCustomScreen::updateBar(int line) lock = true; RawSource source = screen.body.bars[line].source; + + minTime[line]->setVisible(false); + maxTime[line]->setVisible(false); + if (source.type != SOURCE_TYPE_NONE) { - RawSourceRange range = source.getRange(model, generalSettings, RANGE_SINGLE_PRECISION); + RawSourceRange range = source.getRange(model, generalSettings); if (!IS_ARM(getCurrentBoard())) { int max = round((range.max - range.min) / range.step); if (int(255-screen.body.bars[line].barMax) > max) { screen.body.bars[line].barMax = 255 - max; } } - minSB[line]->setEnabled(true); - minSB[line]->setDecimals(range.decimals); - minSB[line]->setMinimum(range.min); - minSB[line]->setMaximum(range.max); - minSB[line]->setSingleStep(range.step); - minSB[line]->setValue(range.getValue(screen.body.bars[line].barMin)); - maxSB[line]->setEnabled(true); - maxSB[line]->setDecimals(range.decimals); - maxSB[line]->setMinimum(range.min); - maxSB[line]->setMaximum(range.max); - maxSB[line]->setSingleStep(range.step); - if (IS_ARM(getCurrentBoard())) { - maxSB[line]->setValue(range.getValue(screen.body.bars[line].barMax)); + float minVal = range.getValue(screen.body.bars[line].barMin); + float maxVal = IS_ARM(getCurrentBoard()) ? screen.body.bars[line].barMax : 255 - screen.body.bars[line].barMax; + maxVal = range.getValue(maxVal); + + if (source.isTimeBased()) { + minTime[line]->setVisible(true); + minTime[line]->setTimeRange(range.min, range.max); + minTime[line]->setSingleStep(range.step); + minTime[line]->setPageStep(range.step * 60); + minTime[line]->setShowSeconds(range.step != 60); + minTime[line]->setTime((int)minVal); + + maxTime[line]->setVisible(true); + maxTime[line]->setTimeRange(range.min, range.max); + maxTime[line]->setSingleStep(range.step); + maxTime[line]->setPageStep(range.step * 60); + maxTime[line]->setShowSeconds(range.step != 60); + maxTime[line]->setTime((int)maxVal); + + minSB[line]->setVisible(false); + maxSB[line]->setVisible(false); } else { - maxSB[line]->setValue(range.getValue(255 - screen.body.bars[line].barMax)); + minSB[line]->setVisible(true); + minSB[line]->setEnabled(true); + minSB[line]->setDecimals(range.decimals); + minSB[line]->setMinimum(range.min); + minSB[line]->setMaximum(range.max); + minSB[line]->setSingleStep(range.step); + minSB[line]->setSuffix(" " + range.unit); + minSB[line]->setValue(minVal); + + maxSB[line]->setVisible(true); + maxSB[line]->setEnabled(true); + maxSB[line]->setDecimals(range.decimals); + maxSB[line]->setMinimum(range.min); + maxSB[line]->setMaximum(range.max); + maxSB[line]->setSingleStep(range.step); + maxSB[line]->setSuffix(" " + range.unit); + maxSB[line]->setValue(maxVal); } } else { @@ -511,6 +555,24 @@ void TelemetryCustomScreen::barMaxChanged(double value) } } +void TelemetryCustomScreen::barTimeChanged() +{ + if (!lock) { + int line = sender()->property("index").toInt(); + int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax); + TimerEdit * te = qobject_cast(sender()); + if (!te) + return; + + if (IS_ARM(getCurrentBoard())) + valRef = round(te->timeInSeconds() / te->singleStep()); + else + valRef = round((te->timeInSeconds() - te->minimumTime()) / te->singleStep()); + + emit modified(); + } +} + TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), @@ -598,23 +660,27 @@ void TelemetrySensorPanel::update() ui->formula->hide(); isConfigurable = sensor.unit < SensorData::UNIT_FIRST_VIRTUAL; ratioFieldsDisplayed = (sensor.unit < SensorData::UNIT_FIRST_VIRTUAL); - ui->offset->setMaximum((sensor.prec > 0 ? sensor.prec == 2 ? 30000 : 3000 : 300)); - ui->offset->setMinimum((sensor.prec > 0 ? sensor.prec == 2 ? -30000 : -3000 : -300)); - if (sensor.unit == SensorData::UNIT_RPMS) { - ui->offset->setDecimals(0); - ui->offset->setSingleStep(1); - ui->ratio->setDecimals(0); - ui->ratio->setSingleStep(1); - ui->autoOffset->hide(); - ui->ratio->setMinimum(1); - ui->offset->setMinimum(1); + if (ui->ratio->decimals()) { + ui->ratio->setMaximum(30000); + ui->ratio->setMinimum(1); + ui->ratio->setDecimals(0); + ui->ratio->setSingleStep(1); + ui->offset->setMaximum(30000); + ui->offset->setMinimum(1); + ui->offset->setDecimals(0); + ui->offset->setSingleStep(1); + } } - else { - ui->offset->setDecimals(sensor.prec); - ui->offset->setSingleStep(pow(0.1, (int)sensor.prec)); + else if (ui->ratio->decimals() != 1) { + ui->ratio->setMaximum(3000); + ui->ratio->setMinimum(0); ui->ratio->setDecimals(1); ui->ratio->setSingleStep(0.1); + ui->offset->setMaximum(30000.0f / powf(10.0f, sensor.prec)); + ui->offset->setMinimum(-ui->offset->maximum()); + ui->offset->setDecimals(sensor.prec); + ui->offset->setSingleStep(pow(0.1, sensor.prec)); } } diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index 532f88698..09758d38b 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -25,6 +25,7 @@ #include "eeprominterface.h" class AutoComboBox; +class TimerEdit; namespace Ui { class TelemetryAnalog; @@ -82,6 +83,7 @@ class TelemetryCustomScreen: public ModelPanel void barSourceChanged(int index); void barMinChanged(double value); void barMaxChanged(double value); + void barTimeChanged(); protected: void populateTelemetrySourceCB(QComboBox * b, RawSource & source, bool last=false); @@ -94,6 +96,8 @@ class TelemetryCustomScreen: public ModelPanel QComboBox * barsCB[4]; QDoubleSpinBox * minSB[4]; QDoubleSpinBox * maxSB[4]; + TimerEdit * minTime[4]; + TimerEdit * maxTime[4]; }; class TelemetrySensorPanel: public ModelPanel diff --git a/companion/src/radiodata.h b/companion/src/radiodata.h index fba4becc4..bb66020b1 100644 --- a/companion/src/radiodata.h +++ b/companion/src/radiodata.h @@ -187,9 +187,8 @@ class RawSourceRange QString unit; }; -#define RANGE_SINGLE_PRECISION 1 -#define RANGE_DELTA_FUNCTION 2 -#define RANGE_DELTA_ABS_FUNCTION 4 +#define RANGE_DELTA_FUNCTION 1 +#define RANGE_ABS_FUNCTION 2 class RawSource { public: diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index 7a7ba2e46..1692c70dc 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -15,8 +15,8 @@ set(shared_HDRS genericpanel.h hexspinbox.h ) - + qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) add_library(shared ${shared_SRCS}) -qt5_use_modules(shared Widgets) \ No newline at end of file +target_link_libraries(shared PRIVATE Qt5::Widgets ${CPN_COMMON_LIB}) diff --git a/companion/src/shared/genericpanel.cpp b/companion/src/shared/genericpanel.cpp index 66628e00f..5dfad648e 100644 --- a/companion/src/shared/genericpanel.cpp +++ b/companion/src/shared/genericpanel.cpp @@ -19,6 +19,8 @@ */ #include "genericpanel.h" + +#include #include #include #include @@ -105,20 +107,23 @@ bool GenericPanel::eventFilter(QObject *object, QEvent * event) return QWidget::eventFilter(object, event); } +void GenericPanel::setFocusFilter(QWidget * w) +{ + w->installEventFilter(this); + w->setFocusPolicy(Qt::StrongFocus); +} + void GenericPanel::disableMouseScrolling() { - Q_FOREACH(QComboBox * cb, findChildren()) { - cb->installEventFilter(this); - cb->setFocusPolicy(Qt::StrongFocus); - } + foreach(QWidget * cb, findChildren()) + setFocusFilter(cb); - Q_FOREACH(QAbstractSpinBox * sb, findChildren()) { - sb->installEventFilter(this); - sb->setFocusPolicy(Qt::StrongFocus); - } + foreach(QWidget * sb, findChildren()) + setFocusFilter(sb); - Q_FOREACH(QSlider * slider, findChildren()) { - slider->installEventFilter(this); - slider->setFocusPolicy(Qt::StrongFocus); - } + foreach(QWidget * slider, findChildren()) + setFocusFilter(slider); + + foreach(QWidget * te, findChildren()) + setFocusFilter(te); } diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index 36402917b..1ce8e906e 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -61,6 +61,7 @@ class GenericPanel : public QWidget void addVSpring(QGridLayout *, int col, int row); void addDoubleSpring(QGridLayout *, int col, int row); virtual bool eventFilter(QObject *obj, QEvent *event); + void setFocusFilter(QWidget * w); void disableMouseScrolling(); }; diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 48a594c40..dcbc819ff 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -92,4 +92,4 @@ qt5_wrap_ui(simulation_SRCS ${simulation_UIS}) qt5_wrap_cpp(simulation_SRCS ${simulation_HDRS}) add_library(simulation ${simulation_SRCS} ${simulation_HDRS}) -qt5_use_modules(simulation Core Widgets Svg) +target_link_libraries(simulation PRIVATE ${CPN_COMMON_LIB} Qt5::Core Qt5::Widgets Qt5::Svg) diff --git a/companion/src/thirdparty/maxlibqt/src/widgets/CMakeLists.txt b/companion/src/thirdparty/maxlibqt/src/widgets/CMakeLists.txt index dd2ef22a4..29cd334bb 100644 --- a/companion/src/thirdparty/maxlibqt/src/widgets/CMakeLists.txt +++ b/companion/src/thirdparty/maxlibqt/src/widgets/CMakeLists.txt @@ -13,14 +13,14 @@ project(maxLibQtWidgets) set(SRCS "${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.cpp" # "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.cpp" -# "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.cpp" # "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.cpp" ) set(HDRS "${CMAKE_CURRENT_LIST_DIR}/ExportableTableView.h" # "${CMAKE_CURRENT_LIST_DIR}/ScrollableMessageBox.h" -# "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.h" + "${CMAKE_CURRENT_LIST_DIR}/TimerEdit.h" # "${CMAKE_CURRENT_LIST_DIR}/TreeComboBox.h" ) diff --git a/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit new file mode 100644 index 000000000..f1aabbabc --- /dev/null +++ b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit @@ -0,0 +1 @@ +#include "TimerEdit.h" diff --git a/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.cpp b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.cpp new file mode 100644 index 000000000..ec7fef683 --- /dev/null +++ b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.cpp @@ -0,0 +1,281 @@ +/* + TimerEdit + + COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved. + Contact: http://www.WorldDesign.com/contact + + LICENSE: + + Commercial License Usage + Licensees holding valid commercial licenses may use this file in + accordance with the terms contained in a written agreement between + you and the copyright holder. + + GNU General Public License Usage + Alternatively, this file may be used under the terms of the GNU + General Public License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + A copy of the GNU General Public License is available at . +*/ + +#include "TimerEdit.h" +#include + +TimerEdit::TimerEdit(QWidget *parent) : + QLineEdit(parent), + m_showSeconds(true), + m_minTime(0), + m_maxTime(24 * 3600 - 1), + m_singleStep(1), + m_pageStep(60), + m_hourDigits(2), + m_validator(new QRegularExpressionValidator(this)) +{ + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + setupFormat(); + setToolTip(tr("Use mouse scroll or UP/DOWN arrow keys to change time in small steps.\nCTRL + scroll or PAGE UP/DOWN keys to change time in larger steps.")); + connect(this, &TimerEdit::textEdited, this, &TimerEdit::textEditedHandler); +} + +int TimerEdit::timeInSeconds() const +{ + int ret = 0; + + ret += timepart(text(), TimeParts::Hours) * 3600; + ret += timepart(text(), TimeParts::Minutes) * 60; + ret += timepart(text(), TimeParts::Seconds); + ret *= timepart(text(), TimeParts::Polarity); + + return ret; +} + +int TimerEdit::timepart(const QString &input, TimerEdit::TimeParts part) const +{ + int ret = 0; + QRegularExpressionMatch match = m_validator->regularExpression().match(input, 0, QRegularExpression::NormalMatch); + + if (match.hasMatch()) { + switch (part) { + case Hours: + if (!match.captured("hrs").isEmpty()) + ret = match.captured("hrs").toInt(); + break; + + case Minutes: + if (!match.captured("mins").isEmpty()) + ret = match.captured("mins").toInt(); + break; + + case Seconds: + if (!match.captured("secs").isEmpty()) + ret = match.captured("secs").toInt(); + break; + + case Polarity: + if (!match.captured("pol").isEmpty() && !match.captured("pol").compare("-")) + ret = -1; + else + ret = 1; + break; + + default: + break; + } + } + + return ret; +} + + +//// Slots //// + +void TimerEdit::setTime(int seconds) +{ + if (seconds > m_maxTime) + seconds = m_maxTime; + else if (seconds < m_minTime) + seconds = m_minTime; + + div_t qrM = div(abs(seconds), 60); + div_t qrH = div(qrM.quot, 60); + + QString val = QString("%1").arg(qrH.rem, 2, 10, QChar('0')); + + if (m_hourDigits) + val.prepend(QString("%1:").arg(qrH.quot, m_hourDigits, 10, QChar('0'))); + + if (m_showSeconds) + val.append(QString(":%1").arg(qrM.rem, 2, 10, QChar('0'))); + + if (m_minTime < 0) { + if (seconds < 0) + val.prepend("-"); + else + val.prepend(" "); + } + + if (text().compare(val)) { + setText(val); + } +} + +void TimerEdit::incrDecr(int seconds) +{ + if (!seconds) + return; + + const int cs = timeInSeconds(); + int s = seconds + cs; + if (s < m_minTime) + s = m_minTime; + if (s > m_maxTime) + s = m_maxTime; + if (s != cs) { + setTime(s); + emitValueChanged(); + } +} + +void TimerEdit::setTimeRange(int minSeconds, int maxSeconds) +{ + if (minSeconds <= maxSeconds && (m_minTime != minSeconds || m_maxTime != maxSeconds)) { + int digits = 0; + if (maxSeconds >= 3600) + digits = floorf(logf(round(maxSeconds / 3600)) / logf(10.0f)) + 1; + + bool mod = (m_hourDigits != digits || std::signbit((float)m_minTime) != std::signbit((float)minSeconds)); + + m_minTime = minSeconds; + m_maxTime = maxSeconds; + m_hourDigits = digits; + + if (mod) + setupFormat(); + } +} + +void TimerEdit::setMinimumTime(int minSeconds) +{ + setTimeRange(minSeconds, (m_maxTime > minSeconds ? m_maxTime : minSeconds)); +} + +void TimerEdit::setMaximumTime(int maxSeconds) +{ + setTimeRange((m_minTime < maxSeconds ? m_minTime : maxSeconds), maxSeconds); +} + +void TimerEdit::setShowSeconds(bool showSeconds) +{ + if (m_showSeconds != showSeconds) { + m_showSeconds = showSeconds; + setupFormat(); + } +} + + +//// Protected //// + +void TimerEdit::textEditedHandler() +{ + int sec = timeInSeconds(); + if (sec < m_minTime) + setTime(m_minTime); + else if (sec > m_maxTime) + setTime(m_maxTime); + else + return; + + emitValueChanged(); +} + +void TimerEdit::setupFormat() +{ + QString inputRe = "(?[0-5][0-9])"; + QString inputMsk = "99"; + QString suffx = "\\" % tr("m") % "\\" % tr("m"); + + if (m_hourDigits) { + inputRe.prepend("(?[0-9]*[0-9]):"); + inputMsk.prepend(":"); + suffx.prepend(":"); + for (int i=0; i < m_hourDigits; ++i) { + inputMsk.prepend("9"); + suffx.prepend("\\" % tr("h")); + } + } + if (m_showSeconds) { + inputRe.append(":(?[0-5][0-9])"); + inputMsk.append(":99"); + suffx.append(":\\" % tr("s") % "\\" % tr("s")); + } + if (m_minTime < 0) { + inputRe.prepend("(?-|\\+|\\s)?"); + inputMsk.prepend("#"); + } + + inputRe.prepend("^"); + inputRe.append("\\s?\\[[^\\]]+\\]$"); + inputMsk.append(" \\[" % suffx % "\\]"); + + m_validator->setRegularExpression(QRegularExpression(inputRe)); + + setInputMask(inputMsk); + setValidator(m_validator); +} + +void TimerEdit::emitValueChanged() +{ + emit QLineEdit::textEdited(text()); +} + +void TimerEdit::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Up: + incrDecr(m_singleStep); + break; + + case Qt::Key_Down: + incrDecr(-(int)m_singleStep); + break; + + case Qt::Key_PageUp: + incrDecr(m_pageStep); + break; + + case Qt::Key_PageDown: + incrDecr(-(int)m_pageStep); + break; + + case Qt::Key_Plus: + case Qt::Key_Equal: + case Qt::Key_Minus: + if (m_minTime < 0) { + setTime(-timeInSeconds()); + emitValueChanged(); + } + break; + + default: + QLineEdit::keyPressEvent(event); + break; + } +} + +void TimerEdit::wheelEvent(QWheelEvent *event) +{ + if (event->angleDelta().isNull()) { + event->ignore(); + return; + } + int numSteps = -event->angleDelta().y() / 8 / 15 * -1; // one step per 15deg + numSteps *= (event->modifiers() & Qt::ControlModifier) ? m_pageStep : m_singleStep; + incrDecr(numSteps); + event->accept(); +} diff --git a/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.h b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.h new file mode 100644 index 000000000..abacaa20a --- /dev/null +++ b/companion/src/thirdparty/maxlibqt/src/widgets/TimerEdit.h @@ -0,0 +1,103 @@ +/* + TimerEdit + https://github.com/mpaperno/maxLibQt + + COPYRIGHT: (c)2017 Maxim Paperno; All Right Reserved. + Contact: http://www.WorldDesign.com/contact + + LICENSE: + + Commercial License Usage + Licensees holding valid commercial licenses may use this file in + accordance with the terms contained in a written agreement between + you and the copyright holder. + + GNU General Public License Usage + Alternatively, this file may be used under the terms of the GNU + General Public License as published by the Free Software Foundation, + either version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + A copy of the GNU General Public License is available at . +*/ + +#ifndef TIMEREDIT_H +#define TIMEREDIT_H + +#include +#include +#include +#include +#include + +/** + \class TimerEdit + \version 1.0.0 + + \brief A time value line editor which accepts negative and large times (> 23:59:59), suitable for a timer, etc. + + Qt's \c QTimeEdit has a limitation in that it can only accept "valid" times, in the range of 0:00:00 to 23:59:59. + The TimerEdit control aims to address that. It allows any amount of time to be entered, as well as negative times. + It provides a mask for data entry, a validator with settable min/max times, and increment/decrement via keyboard + or mouse wheel in configurable steps. + + Data I/O is in seconds only, that is you set the time value in total seconds and also read it back in seconds + (there is no conversion from/to a \e QTime object). + +*/ +class TimerEdit : public QLineEdit +{ + Q_OBJECT + Q_PROPERTY(int minimumTime READ minimumTime WRITE setMinimumTime) + Q_PROPERTY(int maximumTime READ maximumTime WRITE setMaximumTime) + Q_PROPERTY(bool showSeconds READ showSeconds WRITE setShowSeconds) + Q_PROPERTY(unsigned int singleStep READ singleStep WRITE setSingleStep) + Q_PROPERTY(unsigned int pageStep READ pageStep WRITE setPageStep) + + + public: + enum TimeParts {Whole, Polarity, Hours, Minutes, Seconds}; + + TimerEdit(QWidget *parent = Q_NULLPTR); + + int timeInSeconds() const; + int timepart(const QString &input, TimeParts part) const; + + bool showSeconds() const { return m_showSeconds; } + int minimumTime() const { return m_minTime; } + int maximumTime() const { return m_maxTime; } + unsigned int singleStep() const { return m_singleStep; } + unsigned int pageStep() const { return m_pageStep; } + + public slots: + void setTime(int seconds); + void incrDecr(int seconds); + void setTimeRange(int minSeconds, int maxSeconds); + void setMinimumTime(int minSeconds); + void setMaximumTime(int maxSeconds); + void setShowSeconds(bool showSeconds); + + void setSingleStep(unsigned int step) { m_singleStep = step; } + void setPageStep(unsigned int pageStep) { m_pageStep = pageStep; } + + protected: + void textEditedHandler(); + void setupFormat(); + void emitValueChanged(); + void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; + void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; + + bool m_showSeconds; + int m_minTime; // seconds + int m_maxTime; // seconds + unsigned m_singleStep; // seconds + unsigned m_pageStep; // seconds + short m_hourDigits; + QRegularExpressionValidator * m_validator; +}; + +#endif // TIMEREDIT_H