From 5cf4e9be4e68d62dbde8887331252903d9265d3e Mon Sep 17 00:00:00 2001 From: Neil Horne Date: Thu, 9 Apr 2020 18:32:57 +1000 Subject: [PATCH] Add basic context menu to telemetry sensors in companion model edit (#7500) --- companion/src/modeledit/telemetry.cpp | 133 +++++++++- companion/src/modeledit/telemetry.h | 19 +- companion/src/modeledit/telemetry_sensor.ui | 44 ++-- companion/src/shared/CMakeLists.txt | 1 + companion/src/shared/autoprecisioncombobox.h | 257 +++++++++++++++++++ companion/src/shared/genericpanel.h | 1 + 6 files changed, 423 insertions(+), 32 deletions(-) create mode 100644 companion/src/shared/autoprecisioncombobox.h diff --git a/companion/src/modeledit/telemetry.cpp b/companion/src/modeledit/telemetry.cpp index 68be10638..83565c03e 100644 --- a/companion/src/modeledit/telemetry.cpp +++ b/companion/src/modeledit/telemetry.cpp @@ -592,13 +592,28 @@ void TelemetryCustomScreen::barTimeChanged() } } -TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): +/******************************************************/ + +TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): ModelPanel(parent, model, generalSettings, firmware), ui(new Ui::TelemetrySensor), sensor(sensor), - lock(false) + lock(false), + sensorIndex(sensorIndex), + selectedIndex(0) { ui->setupUi(this); + ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1)); + ui->numLabel->setProperty("index", sensorIndex); + ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QFontMetrics *f = new QFontMetrics(QFont()); + QSize sz; + sz = f->size(Qt::TextSingleLine, "TELE00"); + ui->numLabel->setMinimumWidth(sz.width()); + ui->numLabel->setContextMenuPolicy(Qt::CustomContextMenu); + ui->numLabel->setToolTip(tr("Popup menu available")); + ui->numLabel->setMouseTracking(true); + connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint))); ui->id->setField(sensor.id, this); ui->instance->setField(sensor.instance, this); ui->ratio->setField(sensor.ratio, this); @@ -613,7 +628,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->ampsSensor->setField(sensor.amps, this); ui->cellsSensor->setField(sensor.source, this); ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); - for (int i=1; i<=6; i++) + for (int i = 1; i <= 6; i++) ui->cellsIndex->addItem(tr("Cell %1").arg(i), i); ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); @@ -622,6 +637,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ui->source2->setField(sensor.sources[1], this); ui->source3->setField(sensor.sources[2], this); ui->source4->setField(sensor.sources[3], this); + ui->prec->setField(sensor.prec, 0, 2, false, "", this); update(); } @@ -645,7 +661,21 @@ void TelemetrySensorPanel::update() ui->name->setText(sensor.label); ui->type->setCurrentIndex(sensor.type); ui->unit->setCurrentIndex(sensor.unit); - ui->prec->setValue(sensor.prec); + ui->id->updateValue(); + ui->instance->updateValue(); + ui->ratio->updateValue(); + ui->offset->updateValue(); + ui->autoOffset->updateValue(); + ui->filter->updateValue(); + ui->logs->updateValue(); + ui->persistent->updateValue(); + ui->onlyPositive->updateValue(); + ui->gpsSensor->updateValue(); + ui->altSensor->updateValue(); + ui->ampsSensor->updateValue(); + ui->cellsSensor->updateValue(); + ui->cellsIndex->updateValue(); + ui->prec->updateValue(); if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) { sensor.updateUnit(); @@ -825,15 +855,85 @@ void TelemetrySensorPanel::on_unit_currentIndexChanged(int index) } } -void TelemetrySensorPanel::on_prec_valueChanged(double value) +void TelemetrySensorPanel::on_prec_valueChanged() { if (!lock) { - sensor.prec = value; + update(); + } +} + +void TelemetrySensorPanel::on_customContextMenuRequested(QPoint pos) +{ + QLabel *label = (QLabel *)sender(); + selectedIndex = label->property("index").toInt(); + QPoint globalPos = label->mapToGlobal(pos); + + QMenu contextMenu; + contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy())); + contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut())); + contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData()); + contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear())); + contextMenu.addSeparator(); + contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll())); + + contextMenu.exec(globalPos); +} + +bool TelemetrySensorPanel::hasClipboardData(QByteArray * data) const +{ + const QClipboard * clipboard = QApplication::clipboard(); + const QMimeData * mimeData = clipboard->mimeData(); + if (mimeData->hasFormat(MIMETYPE_TELE_SENSOR)) { + if (data) + data->append(mimeData->data(MIMETYPE_TELE_SENSOR)); + return true; + } + return false; +} + +void TelemetrySensorPanel::cmCopy() +{ + QByteArray data; + data.append((char*)&sensor, sizeof(SensorData)); + QMimeData *mimeData = new QMimeData; + mimeData->setData(MIMETYPE_TELE_SENSOR, data); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); +} + +void TelemetrySensorPanel::cmCut() +{ + cmCopy(); + cmClear(); +} + +void TelemetrySensorPanel::cmPaste() +{ + QByteArray data; + if (hasClipboardData(&data)) { + memcpy(&sensor, data.constData(), sizeof(SensorData)); emit dataModified(); emit modified(); } } +void TelemetrySensorPanel::cmClear() +{ + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Telemetry Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + + sensor.clear(); + emit dataModified(); + emit modified(); +} + +void TelemetrySensorPanel::cmClearAll() +{ + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Telemetry Sensors. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + return; + + emit clearAllSensors(); +} + /******************************************************/ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware): @@ -851,12 +951,13 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->A1GB->hide(); ui->A2GB->hide(); - for (unsigned i=0; isensorsLayout->addWidget(panel); sensorPanels[i] = panel; connect(panel, SIGNAL(dataModified()), this, SLOT(update())); connect(panel, SIGNAL(modified()), this, SLOT(onModified())); + connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors())); } } else { @@ -869,7 +970,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin ui->A2Layout->addWidget(analogs[1]); connect(analogs[1], SIGNAL(modified()), this, SLOT(onModified())); } - + if (IS_TARANIS_X9(firmware->getBoard())) { ui->voltsSource->setField(model.frsky.voltsSource, this); ui->altitudeSource->setField(model.frsky.altitudeSource, this); @@ -881,7 +982,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin RawSourceFilterItemModel * srcModel = (new RawSourceFilterItemModel(&generalSettings, &model, this)); connect(this, &TelemetryPanel::updated, srcModel, &RawSourceFilterItemModel::update); - for (int i=0; igetCapability(TelemetryCustomScreens); i++) { + for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) { TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, srcModel); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i+1)); telemetryCustomScreens[i] = tab; @@ -963,7 +1064,7 @@ void TelemetryPanel::setup() ui->rssiSourceCB->setField(model->rssiSource, this); ui->rssiSourceCB->show(); populateTelemetrySourcesComboBox(ui->rssiSourceCB, model, false); - + ui->rssiAlarmWarningCB->hide(); ui->rssiAlarmCriticalCB->hide(); ui->rssiAlarmWarningLabel->setText(tr("Low Alarm")); @@ -1198,3 +1299,13 @@ void TelemetryPanel::on_mahCount_ChkB_toggled(bool checked) ui->mahCount_SB->setDisabled(!checked); emit modified(); } + +void TelemetryPanel::on_clearAllSensors() +{ + for (int i = 0; i < CPN_MAX_SENSORS; i++) { + model->sensorData[i].clear(); + } + + update(); + emit modified(); +} diff --git a/companion/src/modeledit/telemetry.h b/companion/src/modeledit/telemetry.h index b23214927..39f3a42ad 100644 --- a/companion/src/modeledit/telemetry.h +++ b/companion/src/modeledit/telemetry.h @@ -24,6 +24,8 @@ #include "modeledit.h" #include "eeprominterface.h" +constexpr char MIMETYPE_TELE_SENSOR[] = "application/x-companion-tele-sensor"; + class AutoComboBox; class RawSourceFilterItemModel; class TimerEdit; @@ -103,19 +105,27 @@ class TelemetrySensorPanel: public ModelPanel Q_OBJECT public: - TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); + TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware); ~TelemetrySensorPanel(); void update(); signals: void dataModified(); + void clearAllSensors(); protected slots: void on_name_editingFinished(); void on_type_currentIndexChanged(int index); void on_formula_currentIndexChanged(int index); void on_unit_currentIndexChanged(int index); - void on_prec_valueChanged(double value); + void on_prec_valueChanged(); + void on_customContextMenuRequested(QPoint pos); + bool hasClipboardData(QByteArray * data = nullptr) const; + void cmCopy(); + void cmCut(); + void cmPaste(); + void cmClear(); + void cmClearAll(); protected: void updateSourcesComboBox(AutoComboBox * cb, bool negative); @@ -123,7 +133,9 @@ class TelemetrySensorPanel: public ModelPanel private: Ui::TelemetrySensor * ui; SensorData & sensor; - bool lock; + bool lock = false; + int sensorIndex = 0; + int selectedIndex = 0; }; class TelemetryPanel : public ModelPanel @@ -154,6 +166,7 @@ class TelemetryPanel : public ModelPanel void on_fasOffset_DSB_editingFinished(); void on_mahCount_SB_editingFinished(); void on_mahCount_ChkB_toggled(bool checked); + void on_clearAllSensors(); private: Ui::Telemetry *ui; diff --git a/companion/src/modeledit/telemetry_sensor.ui b/companion/src/modeledit/telemetry_sensor.ui index 7d08243e0..52660b4f0 100644 --- a/companion/src/modeledit/telemetry_sensor.ui +++ b/companion/src/modeledit/telemetry_sensor.ui @@ -13,13 +13,32 @@ Form - + 0 0 + + + + + 0 + 0 + + + + + 0 + 0 + + + + TELE## + + + @@ -536,23 +555,7 @@ - - - true - - - - 0 - 0 - - - - 0 - - - 2.000000000000000 - - + @@ -711,6 +714,11 @@ QSpinBox
autohexspinbox.h
+ + AutoPrecisionComboBox + QComboBox +
autoprecisioncombobox.h
+
diff --git a/companion/src/shared/CMakeLists.txt b/companion/src/shared/CMakeLists.txt index 81df9dd67..2fbbafbf9 100644 --- a/companion/src/shared/CMakeLists.txt +++ b/companion/src/shared/CMakeLists.txt @@ -15,6 +15,7 @@ set(shared_HDRS autolineedit.h genericpanel.h hexspinbox.h + autoprecisioncombobox.h ) qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) diff --git a/companion/src/shared/autoprecisioncombobox.h b/companion/src/shared/autoprecisioncombobox.h new file mode 100644 index 000000000..4e143f286 --- /dev/null +++ b/companion/src/shared/autoprecisioncombobox.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _AUTOPRECISIONCOMBOBOX_H_ +#define _AUTOPRECISIONCOMBOBOX_H_ + +#include "genericpanel.h" + +#include +#include + +class AutoPrecisionComboBox: public QComboBox +{ + Q_OBJECT + //! \property minDecimals Determines the first list item + Q_PROPERTY(unsigned int minDecimals READ minDecimals WRITE setMinDecimals) + //! \property maxDecimals Determines the last list item + Q_PROPERTY(unsigned int maxDecimals READ maxDecimals WRITE setMaxDecimals) + //! \property padding If true, add trailing underscore placeholders to each list item + Q_PROPERTY(bool padding READ padding WRITE setPadding) + //! \property suffix Optional text suffix to each list value + Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) + + public: + explicit AutoPrecisionComboBox(QWidget * parent = nullptr): + QComboBox(parent) + { + init(); + } + + explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): + QComboBox(parent) + { + setField(field, minDecimals, maxDecimals, padding, suffix, panel); + init(); + } + + explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr): + QComboBox(parent) + { + setField(field, minDecimals, maxDecimals, padding, suffix, panel); + init(); + } + + inline int minDecimals() const { return m_minDecimals; } + inline int maxDecimals() const { return m_maxDecimals; } + inline bool padding() const { return m_padding; } + inline QString suffix() const { return m_suffix; } + + public slots: + void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) + { + m_field = reinterpret_cast(&field); + initField(minDecimals, maxDecimals, padding, suffix); + setPanel(panel); + } + + void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr) + { + m_field = &field; + initField(minDecimals, maxDecimals, padding, suffix); + setPanel(panel); + } + + void setMinDecimals(unsigned int minDecimals) + { + m_minDecimals = rangecheckDecimals(minDecimals); + if (m_minDecimals > m_maxDecimals) + m_minDecimals = m_maxDecimals; + if (!m_lock) + updateList(); + } + + void setMaxDecimals(unsigned int maxDecimals) + { + m_maxDecimals = rangecheckDecimals(maxDecimals); + if (m_maxDecimals < m_minDecimals) + m_maxDecimals = m_minDecimals; + if (!m_lock) + updateList(); + } + + void setPadding(bool padding) + { + m_padding = padding; + if (!m_lock) + updateList(); + } + + void setSuffix(QString suffix) + { + m_suffix = suffix.trimmed(); + if (!m_lock) + updateList(); + } + + void setPanel(GenericPanel * panel) + { + m_panel = panel; + } + + void setValue(int value) + { + if (!m_field) + return; + const unsigned int val = (unsigned int)value; + if (*m_field != val) { + *m_field = rangecheckDecimals(val); + updateValue(); + emit valueChanged(); + } + } + + void setValue(unsigned int value) + { + if (!m_field) + return; + if (*m_field != value) { + *m_field = rangecheckDecimals(value); + updateValue(); + emit valueChanged(); + } + } + + void updateValue() + { + if (!m_field) + return; + if (!isValidDecimals(*m_field)) + return; + if (*m_field > m_maxDecimals) { + setMaxDecimals(*m_field); + } + const bool savedlock = setLocked(true); + setCurrentIndex(findData(*m_field)); + setLocked(savedlock); + } + + void setAutoIndexes() + { + for (int i = 0; i < count(); ++i) + setItemData(i, i); + updateValue(); + } + + signals: + void valueChanged(); + + protected slots: + void init() + { + connect(this, QOverload::of(&AutoPrecisionComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged); + } + + void initField(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix) + { + const bool savedlock = setLocked(true); + setMinDecimals(minDecimals); + setMaxDecimals(maxDecimals); + setPadding(padding); + setSuffix(suffix); + setLocked(savedlock); + updateList(); + } + + void updateList() + { + const bool savedlock = setLocked(true); + unsigned int i; + int j; + clear(); + + for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) { + if (i >= m_minDecimals) { + QString str = QString("0.%1").arg(QString(i, '0')); + if (m_padding) + str.append(QString(j, '_')); + if (m_suffix != "") + str.append(QString(" %1").arg(m_suffix)); + addItem(str, i); + } + } + + setSizeAdjustPolicy(QComboBox::AdjustToContents); + setLocked(savedlock); + } + + void onCurrentIndexChanged(int index) + { + if (index < 0) + return; + if (m_field && !m_lock) { + *m_field = itemData(index).toUInt(); + if (m_panel) + emit m_panel->modified(); + emit valueChanged(); + } + } + + unsigned int rangecheckDecimals(unsigned int decimals) + { + unsigned int ret; + if (decimals < APCB_DECIMALS_MIN) + ret = APCB_DECIMALS_MIN; + else if (decimals > APCB_DECIMALS_MAX) + ret = APCB_DECIMALS_MAX; + else + ret = decimals; + return ret; + } + + bool isValidDecimals(unsigned int value) + { + if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX) + return true; + else + return false; + } + + bool setLocked(bool lock) + { + const bool ret = m_lock; + m_lock = lock; + return ret; + } + + protected: + constexpr static unsigned int APCB_DECIMALS_MIN = {0}; + constexpr static unsigned int APCB_DECIMALS_MAX = {6}; + + unsigned int * m_field = nullptr; + GenericPanel * m_panel = nullptr; + unsigned int m_minDecimals = 0; + unsigned int m_maxDecimals = 0; + bool m_padding = false; + QString m_suffix = ""; + bool m_lock = false; +}; + +#endif // _AUTOPRECISIONCOMBOBOX_H_ diff --git a/companion/src/shared/genericpanel.h b/companion/src/shared/genericpanel.h index 1ce8e906e..5351355b6 100644 --- a/companion/src/shared/genericpanel.h +++ b/companion/src/shared/genericpanel.h @@ -39,6 +39,7 @@ class GenericPanel : public QWidget friend class AutoHexSpinBox; friend class AutoLineEdit; friend class GVarGroup; + friend class AutoPrecisionComboBox; public: GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware);