diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 870d14cde..7791eeb4c 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -66,7 +66,7 @@ qt5_wrap_cpp(firmwares_SRCS ${firmwares_QT}) add_library(firmwares ${firmwares_SRCS} ${firmwares_HDRS}) -target_link_libraries(firmwares ${CPN_COMMON_LIB} datamodels Qt5::Core Qt5::Xml Qt5::Widgets) +target_link_libraries(firmwares ${CPN_COMMON_LIB} datamodels shared Qt5::Core Qt5::Xml Qt5::Widgets) target_link_libraries(firmwares yaml-cpp) target_include_directories(firmwares diff --git a/companion/src/firmwares/curvedata.cpp b/companion/src/firmwares/curvedata.cpp index 904863a3d..006ce9f46 100644 --- a/companion/src/firmwares/curvedata.cpp +++ b/companion/src/firmwares/curvedata.cpp @@ -20,6 +20,8 @@ #include "curvedata.h" #include "datahelpers.h" +#include "compounditemmodels.h" +#include "eeprominterface.h" CurveData::CurveData() { @@ -32,6 +34,22 @@ void CurveData::clear(int count) this->count = count; } +void CurveData::init() +{ + float incr = 200.0 / ((float)count - 1.0); + + // points[0].x must be -100 + + for (int i = 0; i < count; i++) { + if (i < count - 1) + points[i].x = -100 + (int)(incr * i); + else + points[i].x = 100; // points[count - 1].x must be +100 do not risk a rounding issue using incr + + points[i].y = points[i].x; + } +} + bool CurveData::isEmpty() const { CurveData tmp; @@ -42,3 +60,72 @@ QString CurveData::nameToString(const int idx) const { return DataHelpers::getElementName(tr("CV"), idx + 1, name); } + +QString CurveData::typeToString() const +{ + return typeToString(type); +} + +// static +QString CurveData::typeToString(int value) +{ + switch (value) { + case CURVE_TYPE_STANDARD: + return tr("Standard"); + case CURVE_TYPE_CUSTOM: + return tr("Custom"); + default: + return CPN_STR_UNKNOWN_ITEM; + } +} + +QString CurveData::pointsToString() const +{ + QString result = "["; + + if (type == CURVE_TYPE_CUSTOM) { + for (int j = 0; j < count; j++) { + if (j != 0) + result.append(", "); + result.append(QString("(%1, %2)").arg(points[j].x).arg(points[j].y)); + } + } + else { + for (int j = 0; j < count; j++) { + if (j != 0) + result.append(", "); + result.append(QString("%1").arg(points[j].y)); + } + } + + result.append("]"); + return result; +} + +// static +AbstractStaticItemModel * CurveData::typeItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("curvedata.type"); + + for (int i = 0; i <= CURVE_TYPE_LAST; i++) { + mdl->appendToItemList(typeToString(i), i); + } + + mdl->loadItemList(); + return mdl; +} + +// static +AbstractStaticItemModel * CurveData::pointsItemModel() +{ + AbstractStaticItemModel * mdl = new AbstractStaticItemModel(); + mdl->setName("curvedata.points"); + + for (int i = 2; i <= CPN_MAX_POINTS; i++) { + mdl->appendToItemList(tr("%1 points").arg(i), i); + } + + mdl->loadItemList(); + return mdl; +} diff --git a/companion/src/firmwares/curvedata.h b/companion/src/firmwares/curvedata.h index cb083375e..1363c65c2 100644 --- a/companion/src/firmwares/curvedata.h +++ b/companion/src/firmwares/curvedata.h @@ -24,6 +24,8 @@ #include +class AbstractStaticItemModel; + class CurvePoint { public: int8_t x; @@ -51,6 +53,12 @@ class CurveData { char name[CURVEDATA_NAME_LEN + 1]; void clear(int count = 5); + void init(); bool isEmpty() const; QString nameToString(const int idx) const; + QString typeToString() const; + QString pointsToString() const; + static QString typeToString(int value); + static AbstractStaticItemModel * typeItemModel(); + static AbstractStaticItemModel * pointsItemModel(); }; diff --git a/companion/src/firmwares/curvereference.cpp b/companion/src/firmwares/curvereference.cpp index 926c2d2ae..315fb5269 100644 --- a/companion/src/firmwares/curvereference.cpp +++ b/companion/src/firmwares/curvereference.cpp @@ -23,6 +23,8 @@ #include "helpers.h" #include "modeldata.h" #include "filtereditemmodels.h" +#include "curveimagewidget.h" +#include "curvedialog.h" const QString CurveReference::toString(const ModelData * model, bool verbose) const { @@ -82,7 +84,7 @@ int CurveReference::getDefaultValue(const CurveRefType type, const bool isGVar) // static QString CurveReference::typeToString(const CurveRefType type) { - const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Curve") }; + const QStringList strl = { tr("Diff"), tr("Expo") , tr("Func"), tr("Custom") }; int idx = (int)type; if (idx < 0 || idx >= strl.count()) @@ -145,7 +147,8 @@ int CurveReference::functionCount() */ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheckBox * curveGVarCB, QSpinBox * curveValueSB, - QComboBox * curveValueCB, CurveReference & curveRef, const ModelData & model, + QComboBox * curveValueCB, QLabel * curveLabelImage, + CurveReference & curveRef, ModelData & model, CompoundItemModelFactory * sharedItemModels, CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent) : QObject(parent), curveTypeCB(curveTypeCB), @@ -154,9 +157,13 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveValueCB(curveValueCB), curveRef(curveRef), model(model), + sharedItemModels(sharedItemModels), + filteredModelFactory(curveRefFilteredFactory), lock(false), - filteredModelFactory(curveRefFilteredFactory) + curveImageWidget(nullptr) { + connectItemModelEvents(filteredModelFactory->getItemModel(CurveRefFilteredFactory::CRFIM_CURVE)); + hasCapabilityGvars = getCurrentFirmware()->getCapability(Gvars); if (curveTypeCB) { @@ -178,6 +185,15 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck curveValueCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); curveValueCB->setMaxVisibleItems(10); connect(curveValueCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valueCBChanged())); + + if (curveLabelImage) { + curveImageWidget = qobject_cast(curveLabelImage); + if (curveImageWidget) { + curveImageWidget->set(&model, getCurrentFirmware(), sharedItemModels, abs(curveRef.value) - 1, Qt::black, 3); + curveImageWidget->setGrid(Qt::gray, 2); + connect(curveImageWidget, &CurveImageWidget::doubleClicked, this, &CurveReferenceUIManager::curveImageDoubleClicked); + } + } } update(); @@ -185,12 +201,13 @@ CurveReferenceUIManager::CurveReferenceUIManager(QComboBox * curveTypeCB, QCheck CurveReferenceUIManager::~CurveReferenceUIManager() { - delete filteredModelFactory; + } -#define CURVE_REF_UI_GVAR_SHOW (1<<0) -#define CURVE_REF_UI_VALUE_SHOW (1<<1) -#define CURVE_REF_UI_REF_SHOW (1<<2) +#define CURVE_REF_UI_GVAR_SHOW (1 << 0) +#define CURVE_REF_UI_VALUE_SHOW (1 << 1) +#define CURVE_REF_UI_REF_SHOW (1 << 2) +#define CURVE_REF_UI_CURVE_SHOW (1 << 3) void CurveReferenceUIManager::update() { @@ -214,20 +231,34 @@ void CurveReferenceUIManager::update() widgetsMask |= CURVE_REF_UI_REF_SHOW; } - if(curveTypeCB) { + if (curveRef.type == CurveReference::CURVE_REF_CUSTOM) + widgetsMask |= CURVE_REF_UI_CURVE_SHOW; + + + if (curveTypeCB) { curveTypeCB->setCurrentIndex(curveTypeCB->findData(curveRef.type)); curveTypeCB->show(); } - if(curveGVarCB) + + if (curveGVarCB) curveGVarCB->setVisible(widgetsMask & CURVE_REF_UI_GVAR_SHOW); - if(curveValueSB) + + if (curveValueSB) curveValueSB->setVisible(widgetsMask & CURVE_REF_UI_VALUE_SHOW); - if(curveValueCB) { + + if (curveValueCB) { if (curveRef.isValueReference()) populateValueCB(curveValueCB); curveValueCB->setVisible(widgetsMask & CURVE_REF_UI_REF_SHOW); } + if (curveValueCB && curveImageWidget) { + curveImageWidget->draw(); + curveImageWidget->setVisible(widgetsMask & CURVE_REF_UI_CURVE_SHOW); + } + + emit resized(); + lock = false; } @@ -244,6 +275,8 @@ void CurveReferenceUIManager::typeChanged(int value) if (!lock) { CurveReference::CurveRefType type = (CurveReference::CurveRefType)curveTypeCB->itemData(curveTypeCB->currentIndex()).toInt(); curveRef = CurveReference(type, CurveReference::getDefaultValue(type)); + if (curveImageWidget && curveRef.type == CurveReference::CURVE_REF_CUSTOM) + curveImageWidget->setIndex(abs(curveRef.value) - 1); update(); } } @@ -260,6 +293,8 @@ void CurveReferenceUIManager::valueCBChanged() { if (!lock) { curveRef.value = curveValueCB->itemData(curveValueCB->currentIndex()).toInt(); + if (curveImageWidget && curveRef.type == CurveReference::CURVE_REF_CUSTOM) + curveImageWidget->setIndex(abs(curveRef.value) - 1); update(); } } @@ -285,3 +320,25 @@ void CurveReferenceUIManager::populateValueCB(QComboBox * cb) cb->setCurrentIndex(cb->findData(curveRef.value)); } } + +void CurveReferenceUIManager::curveImageDoubleClicked() +{ + if (curveRef.type == CurveReference::CURVE_REF_CUSTOM && abs(curveRef.value) > 0) + curveImageWidget->edit(); +} + +void CurveReferenceUIManager::connectItemModelEvents(const FilteredItemModel * itemModel) +{ + connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CurveReferenceUIManager::onItemModelAboutToBeUpdated); + connect(itemModel, &FilteredItemModel::updateComplete, this, &CurveReferenceUIManager::onItemModelUpdateComplete); +} + +void CurveReferenceUIManager::onItemModelAboutToBeUpdated() +{ + lock = true; +} + +void CurveReferenceUIManager::onItemModelUpdateComplete() +{ + update(); +} diff --git a/companion/src/firmwares/curvereference.h b/companion/src/firmwares/curvereference.h index c4482c742..5bdad30dc 100644 --- a/companion/src/firmwares/curvereference.h +++ b/companion/src/firmwares/curvereference.h @@ -18,18 +18,20 @@ * GNU General Public License for more details. */ -#ifndef CURVEREFERENCE_H -#define CURVEREFERENCE_H +#pragma once #include #include #include +#include #include #include - class ModelData; class CurveRefFilteredFactory; +class CompoundItemModelFactory; +class FilteredItemModel; +class CurveImageWidget; class CurveReference { @@ -90,21 +92,27 @@ class CurveReferenceUIManager : public QObject { public: explicit CurveReferenceUIManager(QComboBox *curveTypeCB, QCheckBox *curveGVarCB, QSpinBox *curveValueSB, QComboBox *curveValueCB, - CurveReference & curve, const ModelData & model, CurveRefFilteredFactory * curveRefFilteredFactory, - QObject * parent = nullptr); + QLabel *curveImage, CurveReference & curve, ModelData & model, CompoundItemModelFactory * sharedItemModels, + CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent = nullptr); - explicit CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, const ModelData & model, + explicit CurveReferenceUIManager(QComboBox *curveValueCB, CurveReference & curve, ModelData & model, CompoundItemModelFactory * sharedItemModels, CurveRefFilteredFactory * curveRefFilteredFactory, QObject * parent = nullptr) : - CurveReferenceUIManager(nullptr, nullptr, nullptr, curveValueCB, curve, model, curveRefFilteredFactory, parent) {} + CurveReferenceUIManager(nullptr, nullptr, nullptr, curveValueCB, nullptr, curve, model, sharedItemModels, curveRefFilteredFactory, parent) {} virtual ~CurveReferenceUIManager(); + signals: + void resized(); + protected slots: void gvarCBChanged(int); void typeChanged(int); void valueSBChanged(); void valueCBChanged(); void update(); + void curveImageDoubleClicked(); + void onItemModelAboutToBeUpdated(); + void onItemModelUpdateComplete(); private: QComboBox *curveTypeCB; @@ -112,11 +120,14 @@ class CurveReferenceUIManager : public QObject { QSpinBox *curveValueSB; QComboBox *curveValueCB; CurveReference & curveRef; - const ModelData & model; - bool lock; - bool hasCapabilityGvars; + ModelData & model; + CompoundItemModelFactory *sharedItemModels; CurveRefFilteredFactory *filteredModelFactory; + bool lock; + CurveImageWidget *curveImageWidget = nullptr; + + bool hasCapabilityGvars; + + void connectItemModelEvents(const FilteredItemModel * itemModel); void populateValueCB(QComboBox * cb); }; - -#endif // CURVEREFERENCE_H diff --git a/companion/src/firmwares/edgetx/yaml_curvedata.cpp b/companion/src/firmwares/edgetx/yaml_curvedata.cpp index ec02f0b38..c75cfb874 100644 --- a/companion/src/firmwares/edgetx/yaml_curvedata.cpp +++ b/companion/src/firmwares/edgetx/yaml_curvedata.cpp @@ -48,11 +48,6 @@ void YamlReadCurvePoints(const YAML::Node& node, CurveData* curves) } } curve.points[curve.count - 1].x = +100; - } else { - // Standard curve: X points are generated - for (int k = 0; k < curve.count; k++) { - curve.points[k].x = -100 + (200 * k) / (curve.count - 1); - } } } } diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index 5f749fa33..cfa628c06 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -584,18 +584,18 @@ TableLayout::TableLayout(QWidget * parent, int rowCount, const QStringList & hea #endif } -void TableLayout::addWidget(int row, int column, QWidget * widget) +void TableLayout::addWidget(int row, int column, QWidget * widget, Qt::Alignment alignment) { #if defined(TABLE_LAYOUT) QHBoxLayout * layout = new QHBoxLayout(tableWidget); layout->addWidget(widget); addLayout(row, column, layout); #else - gridWidget->addWidget(widget, row + 1, column); + gridWidget->addWidget(widget, row + 1, column, alignment); #endif } -void TableLayout::addLayout(int row, int column, QLayout * layout) +void TableLayout::addLayout(int row, int column, QLayout * layout, Qt::Alignment alignment) { #if defined(TABLE_LAYOUT) layout->setContentsMargins(1, 3, 1, 3); @@ -603,7 +603,7 @@ void TableLayout::addLayout(int row, int column, QLayout * layout) containerWidget->setLayout(layout); tableWidget->setCellWidget(row, column, containerWidget); #else - gridWidget->addLayout(layout, row + 1, column); + gridWidget->addLayout(layout, row + 1, column, alignment); #endif } diff --git a/companion/src/helpers.h b/companion/src/helpers.h index 98783439b..e2f2f7ff0 100644 --- a/companion/src/helpers.h +++ b/companion/src/helpers.h @@ -186,8 +186,8 @@ public: TableLayout(QWidget * parent, int rowCount, const QStringList & headerLabels); // ~TableLayout() ; - void addWidget(int row, int column, QWidget * widget); - void addLayout(int row, int column, QLayout * layout); + void addWidget(int row, int column, QWidget * widget, Qt::Alignment alignment = Qt::Alignment()); + void addLayout(int row, int column, QLayout * layout, Qt::Alignment alignment = Qt::Alignment()); void resizeColumnsToContents(); void setColumnWidth(int col, int width); diff --git a/companion/src/modeledit/CMakeLists.txt b/companion/src/modeledit/CMakeLists.txt index 7404f821e..d36ee79af 100644 --- a/companion/src/modeledit/CMakeLists.txt +++ b/companion/src/modeledit/CMakeLists.txt @@ -8,7 +8,6 @@ set(modeledit_NAMES modeledit setup heli - curves telemetry expodialog mixerdialog @@ -27,6 +26,7 @@ set(modeledit_SRCS # edge.cpp ## commenting them here avoids a "duplicate target" warning eg. from ninja colorcustomscreens.cpp telemetry_customscreens.cpp + curves.cpp ) set(modeledit_HDRS @@ -40,6 +40,7 @@ set(modeledit_HDRS # node.h colorcustomscreens.h telemetry_customscreens.h + curves.h ) set(modeledit_UIS diff --git a/companion/src/modeledit/channels.cpp b/companion/src/modeledit/channels.cpp index 806ea144e..19f020e9d 100644 --- a/companion/src/modeledit/channels.cpp +++ b/companion/src/modeledit/channels.cpp @@ -21,6 +21,7 @@ #include "channels.h" #include "helpers.h" #include "filtereditemmodels.h" +#include "curveimagewidget.h" LimitsGroup::LimitsGroup(Firmware * firmware, TableLayout * tableLayout, int row, int col, int & value, const ModelData & model, int min, int max, int deflt, FilteredItemModel * gvarModel, ModelPanel * panel): @@ -107,7 +108,7 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting } headerLabels << tr("Subtrim") << tr("Min") << tr("Max") << tr("Direction"); if (IS_HORUS_OR_TARANIS(firmware->getBoard())) - headerLabels << tr("Curve"); + headerLabels << tr("Curve") << tr("Plot"); if (firmware->getCapability(PPMCenter)) headerLabels << tr("PPM Center"); if (firmware->getCapability(SYMLimits)) @@ -159,9 +160,20 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { curveCB[i] = new QComboBox(this); curveCB[i]->setProperty("index", i); + curveCB[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents); curveCB[i]->setModel(dialogFilteredItemModels->getItemModel(crvid)); connect(curveCB[i], SIGNAL(currentIndexChanged(int)), this, SLOT(curveEdited())); tableLayout->addWidget(i, col++, curveCB[i]); + + curveImage[i] = new CurveImageWidget(this); + int crvidx = model.limitData[i].curve.value - 1; + QColor pencolor = colors[crvidx >= 0 ? crvidx : Qt::black]; + curveImage[i]->set(&model, firmware, sharedItemModels, crvidx, pencolor, 3); + curveImage[i]->setGrid(Qt::gray, 2); + curveImage[i]->setProperty("index", i); + curveImage[i]->setFixedSize(QSize(100, 100)); + connect(curveImage[i], &CurveImageWidget::doubleClicked, this, &ChannelsPanel::on_curveImageDoubleClicked); + tableLayout->addWidget(i, col++, curveImage[i]); } // PPM center @@ -191,7 +203,9 @@ ChannelsPanel::ChannelsPanel(QWidget * parent, ModelData & model, GeneralSetting disableMouseScrolling(); tableLayout->resizeColumnsToContents(); - tableLayout->pushRowsUp(chnCapability+1); + tableLayout->pushRowsUp(chnCapability + 1); + + adjustSize(); } ChannelsPanel::~ChannelsPanel() @@ -260,8 +274,13 @@ void ChannelsPanel::curveEdited() QComboBox *cb = qobject_cast(sender()); int index = cb->property("index").toInt(); // ignore unnecessary updates that could be triggered by updates to the data model - if (model->limitData[index].curve != CurveReference(CurveReference::CURVE_REF_CUSTOM, cb->itemData(cb->currentIndex()).toInt())) { - model->limitData[index].curve = CurveReference(CurveReference::CURVE_REF_CUSTOM, cb->itemData(cb->currentIndex()).toInt()); + LimitData &ld = model->limitData[index]; + if (ld.curve != CurveReference(CurveReference::CURVE_REF_CUSTOM, cb->itemData(cb->currentIndex()).toInt())) { + ld.curve = CurveReference(CurveReference::CURVE_REF_CUSTOM, cb->itemData(cb->currentIndex()).toInt()); + curveImage[index]->setIndex(abs(ld.curve.value) - 1); + if (abs(ld.curve.value) > 0) + curveImage[index]->setPen(colors[abs(ld.curve.value) - 1], 3); + updateLine(index); emit modified(); } } @@ -298,6 +317,13 @@ void ChannelsPanel::updateLine(int i) invCB[i]->setCurrentIndex((chn.revert) ? 1 : 0); if (IS_HORUS_OR_TARANIS(firmware->getBoard())) { curveCB[i]->setCurrentIndex(curveCB[i]->findData(chn.curve.value)); + if (chn.curve.value == 0) { + curveImage[i]->setVisible(false); + } + else { + curveImage[i]->setVisible(true); + curveImage[i]->draw(); + } } if (firmware->getCapability(PPMCenter)) { centerSB[i]->setValue(chn.ppmCenter + 1500); @@ -468,3 +494,14 @@ void ChannelsPanel::updateItemModels() sharedItemModels->update(AbstractItemModel::IMUE_Channels); emit modified(); } + +void ChannelsPanel::on_curveImageDoubleClicked() +{ + bool ok = false; + int index = sender()->property("index").toInt(&ok); + + if (ok && curveImage[abs(index)]->edit()) { + updateLine(index); + emit modified(); + } +} diff --git a/companion/src/modeledit/channels.h b/companion/src/modeledit/channels.h index 5d7600718..572734df4 100644 --- a/companion/src/modeledit/channels.h +++ b/companion/src/modeledit/channels.h @@ -27,6 +27,7 @@ class CompoundItemModelFactory; class FilteredItemModelFactory; +class CurveImageWidget; constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel"; @@ -85,24 +86,28 @@ class ChannelsPanel : public ModelPanel void onCustomContextMenuRequested(QPoint pos); void onItemModelAboutToBeUpdated(); void onItemModelUpdateComplete(); + void on_curveImageDoubleClicked(); private: - bool hasClipboardData(QByteArray * data = nullptr) const; - bool insertAllowed() const; - bool moveDownAllowed() const; - bool moveUpAllowed() const; QLineEdit *name[CPN_MAX_CHNOUT]; LimitsGroup *chnOffset[CPN_MAX_CHNOUT]; LimitsGroup *chnMin[CPN_MAX_CHNOUT]; LimitsGroup *chnMax[CPN_MAX_CHNOUT]; QComboBox *invCB[CPN_MAX_CHNOUT]; QComboBox *curveCB[CPN_MAX_CHNOUT]; + CurveImageWidget *curveImage[CPN_MAX_CHNOUT]; QSpinBox *centerSB[CPN_MAX_CHNOUT]; QCheckBox *symlimitsChk[CPN_MAX_CHNOUT]; int selectedIndex; int chnCapability; CompoundItemModelFactory *sharedItemModels; + FilteredItemModelFactory *dialogFilteredItemModels; + + bool hasClipboardData(QByteArray * data = nullptr) const; + bool insertAllowed() const; + bool moveDownAllowed() const; + bool moveUpAllowed() const; + void updateItemModels(); void connectItemModelEvents(const FilteredItemModel * itemModel); - FilteredItemModelFactory *dialogFilteredItemModels; }; diff --git a/companion/src/modeledit/curves.cpp b/companion/src/modeledit/curves.cpp index 210130efa..2ffa36bd6 100644 --- a/companion/src/modeledit/curves.cpp +++ b/companion/src/modeledit/curves.cpp @@ -19,652 +19,140 @@ */ #include "curves.h" -#include "ui_curves.h" -#include "node.h" -#include "edge.h" +#include "curveimagewidget.h" +#include "compounditemmodels.h" +#include "datahelpers.h" #include "helpers.h" -#include "filtereditemmodels.h" -#define GFX_MARGIN 16 - -#define CURVE_COEFF_ENABLE 1 -#define CURVE_YMID_ENABLE 2 -#define CURVE_YMIN_ENABLE 4 - -float curveLinear(float x, float coeff, float yMin, float yMid, float yMax) -{ - float a = (yMax - yMin) / 200.0; - return yMin + a * (x + 100.0); -} - -float c9xexpou(float point, float coeff) -{ - float x = point * 1024.0 / 100.0; - float k = coeff * 256.0 / 100.0; - return ((k * x * x * x / (1024 * 1024) + x * (256 - k) + 128) / 256) / 1024.0 * 100; -} - -float curveExpo(float x, float coeff, float yMin, float yMid, float yMax) -{ - float a = (yMax - yMin) / 100.0; - - x += 100.0; - x /= 2.0; - - if (coeff >= 0) { - return round(c9xexpou(x, coeff)*a + yMin); - } - else { - coeff = -coeff; - x = 100 - x; - return round((100.0 - c9xexpou(x, coeff)) * a + yMin); - } -} - -float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax) -{ - bool invert; - if (x < 0) { - x = -x; - invert = 1; - } - else { - invert = 0; - } - - float y; - if (coeff >= 0) { - y = round(c9xexpou(x, coeff) * (yMax / 100.0)); - } - else { - coeff = -coeff; - x = 100.0 - x; - y = round((100.0 - c9xexpou(x, coeff)) * (yMax / 100.0)); - } - - if (invert) { - y = -y; - } - - return y; -} - -float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax) -{ - float a = (yMax - yMid) / 100.0; - - if (x < 0) - x = -x; - - float y; - if (coeff >= 0) { - y = round(c9xexpou(x, coeff) * a + yMid); - } - else { - coeff = -coeff; - x = 100 - x; - y = round((100.0 - c9xexpou(x, coeff)) * a + yMid); - } - - return y; -} - -CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, - CompoundItemModelFactory * sharedItemModels): +CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), - ui(new Ui::Curves), - currentCurve(0), sharedItemModels(sharedItemModels) { - ui->setupUi(this); - lock = true; - maxCurves = firmware->getCapability(NumCurves); - hasNames = firmware->getCapability(HasCvNames); - hasEnhanced = firmware->getCapability(EnhancedCurves); - maxPoints = firmware->getCapability(NumCurvePoints); - if (!hasNames) { - ui->curveName->hide(); - ui->curveNameLabel->hide(); - } + QStringList headerLabels; + headerLabels << "#"; - scene = new CustomScene(ui->curvePreview); - scene->setItemIndexMethod(QGraphicsScene::NoIndex); - connect(scene, SIGNAL(newPoint(int, int)), this, SLOT(onSceneNewPoint(int, int))); + headerLabels << tr("Plot") << tr("Details"); - ui->curvePreview->setScene(scene); + TableLayout *tableLayout = new TableLayout(this, maxCurves, headerLabels); - int limit; - if (maxCurves > 16) { - limit = maxCurves / 2; - } else { - limit = maxCurves; - } for (int i = 0; i < maxCurves; i++) { - visibleCurves[i] = false; + int col = 0; - // The edit curve button - QPushButton * edit = new QPushButton(this); - edit->setProperty("index", i); + QLabel *label = new QLabel(this); + label->setText(tr("CV%1").arg(i+1)); + label->setProperty("index", i); + label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + label->setContextMenuPolicy(Qt::CustomContextMenu); + label->setToolTip(tr("Popup menu available")); + label->setMouseTracking(true); + connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint))); + tableLayout->addWidget(i, col++, label); - QString style = QString( - "QPushButton {" -#ifdef __GNUC__ - // creates more compact and likely consistent buttons across OS flavors - "background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;" -#else - "background-color: %1; color: white;" -#endif - "}" - "QToolTip { background: white; color: black; }"); + image[i] = new CurveImageWidget(this); + image[i]->set(&model, firmware, sharedItemModels, i, colors[i], 3); + image[i]->setGrid(Qt::gray, 2); + image[i]->setProperty("index", i); + image[i]->setFixedSize(QSize(100, 100)); + connect(image[i], &CurveImageWidget::doubleClicked, this, &CurvesPanel::on_curveImageDoubleClicked); - edit->setStyleSheet(style.arg(colors[i].name())); - edit->setText(tr("Curve %1").arg(i + 1)); - edit->setContextMenuPolicy(Qt::CustomContextMenu); - edit->setToolTip(tr("Popup menu available")); - connect(edit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onCustomContextMenuRequested(const QPoint&))); - connect(edit, SIGNAL(clicked()), this, SLOT(editCurve())); - if (i < limit) { - ui->curvesLayout->addWidget(edit, i, 1, 1, 1); - } else { - ui->curvesLayout2->addWidget(edit, i - limit, 2, 1, 1); - } + tableLayout->addWidget(i, col++, image[i]); - // The curve plot checkbox - QCheckBox * plot = new QCheckBox(this); - plot->setProperty("index", i); - connect(plot, SIGNAL(toggled(bool)), this, SLOT(plotCurve(bool))); - if (i < limit) { - ui->curvesLayout->addWidget(plot, i, 2, 1, 1); - } else { - ui->curvesLayout2->addWidget(plot, i-limit, 1, 1, 1); - } - } - QSpacerItem * item = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding); + grp[i] = new QWidget(); // layouts are not hideable so use widget as parent for grid - ui->curvesLayout->addItem(item,limit + 1, 1, 1, 1, 0); - if (limit != maxCurves) { - QSpacerItem * item2 = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding); - ui->curvesLayout2->addItem(item2,limit + 1, 1, 1, 1, 0); + grid[i] = new QGridLayout(grp[i]); + int row = 0; + + grid[i]->addWidget(new QLabel(tr("Name:")), row, 0, Qt::AlignLeft); + name[i] = new QLabel(); + grid[i]->addWidget(name[i], row++, 1, Qt::AlignLeft); + + grid[i]->addWidget(new QLabel(tr("Type:")), row, 0, Qt::AlignLeft); + type[i] = new QLabel(); + grid[i]->addWidget(type[i], row++, 1, Qt::AlignLeft); + + grid[i]->addWidget(new QLabel(tr("Points:")), row, 0, Qt::AlignLeft); + numpoints[i] = new QLabel(); + grid[i]->addWidget(numpoints[i], row, 1); + points[i] = new QLabel(); + grid[i]->addWidget(points[i], row++, 2, Qt::AlignLeft); + + grid[i]->addWidget(new QLabel(tr("Smooth:")), row, 0, Qt::AlignLeft); + smooth[i] = new QLabel(); + grid[i]->addWidget(smooth[i], row++, 1, Qt::AlignLeft); + + tableLayout->addWidget(i, col++, grp[i], Qt::AlignLeft); } - for (int i = 0; i < CPN_MAX_POINTS; i++) { - spnx[i] = new QSpinBox(this); - spnx[i]->setProperty("index", i); - spnx[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); - spnx[i]->setAccelerated(true); - connect(spnx[i], SIGNAL(valueChanged(int)), this, SLOT(onPointEdited())); - ui->pointsLayout->addWidget(spnx[i], i, 0, 1, 1); + tableLayout->resizeColumnsToContents(); + tableLayout->pushColumnsLeft(headerLabels.count()); + tableLayout->pushRowsUp(maxCurves + 1); - spny[i] = new QSpinBox(this); - spny[i]->setProperty("index", i); - spny[i]->setMinimum(-100); - spny[i]->setMaximum(+100); - spny[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); - spny[i]->setAccelerated(true); - connect(spny[i], SIGNAL(valueChanged(int)), this, SLOT(onPointEdited())); - ui->pointsLayout->addWidget(spny[i], i, 1, 1, 1); - - bool insert; - if (hasEnhanced) { - insert = (i >= 1); - } - else { - insert = (i == 2 || i == 4 || i ==8 || i == 16); - } - if (insert) { - ui->curvePoints->addItem(tr("%1 points").arg(i + 1), i + 1); - } + for (int i = 0; i < CPN_MAX_CURVES; i++) { + update(i); } - addTemplate(tr("Linear"), CURVE_YMIN_ENABLE, curveLinear); - addTemplate(tr("Single Expo"), CURVE_COEFF_ENABLE | CURVE_YMIN_ENABLE, curveExpo); - addTemplate(tr("Symmetrical f(x)=-f(-x)"), CURVE_COEFF_ENABLE, curveSymmetricalY); - addTemplate(tr("Symmetrical f(x)=f(-x)"), CURVE_COEFF_ENABLE | CURVE_YMID_ENABLE, curveSymmetricalX); - - ui->pointSize->setValue(10); - ui->pointSize->setMinimum(3); - ui->pointSize->setMaximum(20); - connect(ui->pointSize, SIGNAL(valueChanged(int)), this, SLOT(onPointSizeEdited())); - - disableMouseScrolling(); - lock = false; } CurvesPanel::~CurvesPanel() { - delete ui; -} - -void CurvesPanel::editCurve() -{ - QPushButton *button = (QPushButton *)sender(); - int index = button->property("index").toInt(); - setCurrentCurve(index); - update(); -} - -void CurvesPanel::plotCurve(bool checked) -{ - QCheckBox *chk = (QCheckBox *)sender(); - int index = chk->property("index").toInt(); - visibleCurves[index] = checked; - updateCurve(); + // compiler warning if delete[] + for (int i = 0; i < CPN_MAX_CURVES; i++) { + delete image[i]; + delete name[i]; + delete type[i]; + delete numpoints[i]; + delete points[i]; + delete smooth[i]; + delete grid[i]; + delete grp[i]; + } } void CurvesPanel::update() { - lock = true; - - if (hasNames) { - ui->curveName->setText(model->curves[currentCurve].name); - } - - updateCurveType(); - updateCurve(); - updateCurvePoints(); - - lock = false; -} - -void CurvesPanel::setCurrentCurve(int index) -{ - currentCurve = index; -} - -void CurvesPanel::updateCurveType() -{ - lock = true; - - int index = 0; - - if (hasEnhanced) { - index = model->curves[currentCurve].count - 2; - } - else { - ui->curveSmooth->hide(); - if (model->curves[currentCurve].count == 5) - index = 1; - else if (model->curves[currentCurve].count == 9) - index = 2; - else if (model->curves[currentCurve].count == 17) - index = 3; - } - - ui->curvePoints->setCurrentIndex(index); - ui->curveCustom->setCurrentIndex(model->curves[currentCurve].type); - ui->curveSmooth->setCurrentIndex(model->curves[currentCurve].smooth); - - lock = false; -} - -void CurvesPanel::updateCurve() -{ - lock = true; - - Node * nodel = 0; - Node * nodex = 0; - QColor color; - - scene->clear(); - - qreal width = scene->sceneRect().width(); - qreal height = scene->sceneRect().height(); - - qreal centerX = scene->sceneRect().left() + width / 2; - qreal centerY = scene->sceneRect().top() + height / 2; - - QGraphicsSimpleTextItem *ti = scene->addSimpleText(tr("Editing curve %1").arg(currentCurve + 1)); - ti->setPos(3, 3); - - scene->addLine(centerX, GFX_MARGIN, centerX, height + GFX_MARGIN); - scene->addLine(GFX_MARGIN, centerY, width + GFX_MARGIN, centerY); - - QPen pen; - pen.setWidth(1); - pen.setStyle(Qt::SolidLine); - - for (int k = 0; k < maxCurves; k++) { - pen.setColor(colors[k]); - if (currentCurve != k && visibleCurves[k]) { - int numpoints = model->curves[k].count; - for (int i = 0; i < numpoints - 1; i++) { - if (model->curves[k].type == CurveData::CURVE_TYPE_CUSTOM) - scene->addLine(centerX + (qreal)model->curves[k].points[i].x * width / 200, - centerY - (qreal)model->curves[k].points[i].y * height / 200, - centerX + (qreal)model->curves[k].points[i + 1].x * width / 200, - centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); - else - scene->addLine(GFX_MARGIN + i * width / (numpoints - 1), - centerY - (qreal)model->curves[k].points[i].y * height / 200, - GFX_MARGIN + (i + 1) * width / (numpoints - 1), - centerY - (qreal)model->curves[k].points[i + 1].y * height / 200, pen); - } - } - } - - int numpoints = model->curves[currentCurve].count; - for (int i = 0; i < numpoints; i++) { - nodel = nodex; - nodex = new Node(); - nodex->setProperty("index", i); - nodex->setColor(colors[currentCurve]); - nodex->setBallSize(ui->pointSize->value()); - nodex->setBallHeight(0); - if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) { - if (i > 0 && i < numpoints - 1) { - nodex->setFixedX(false); - nodex->setMinX(model->curves[currentCurve].points[i - 1].x); - nodex->setMaxX(model->curves[currentCurve].points[i + 1].x); - } - else { - nodex->setFixedX(true); - } - nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, - centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); - } - else { - nodex->setFixedX(true); - nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), - centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200); - } - connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int))); - connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus())); - connect(nodex, SIGNAL(unfocus()), this, SLOT(onNodeUnfocus())); - connect(nodex, SIGNAL(deleteMe()), this, SLOT(onNodeDelete())); - scene->addItem(nodex); - if (i > 0) - scene->addItem(new Edge(nodel, nodex)); - } - - lock = false; -} - -void CurvesPanel::updateCurvePoints() -{ - lock = true; - - int count = model->curves[currentCurve].count; - for (int i = 0; i < count; i++) { - spny[i]->show(); - spny[i]->setValue(model->curves[currentCurve].points[i].y); - if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) { - spnx[i]->show(); - if (i == 0 || i == model->curves[currentCurve].count - 1) { - spnx[i]->setDisabled(true); - spnx[i]->setMaximum(+100); - spnx[i]->setMinimum(-100); - } - else { - spnx[i]->setDisabled(false); - spnx[i]->setMaximum(model->curves[currentCurve].points[i + 1].x); - spnx[i]->setMinimum(model->curves[currentCurve].points[i - 1].x); - } - spnx[i]->setValue(model->curves[currentCurve].points[i].x); - } - else { - spnx[i]->hide(); - } - } - for (int i = count; i < CPN_MAX_POINTS; i++) { - spny[i]->hide(); - spnx[i]->hide(); - } - - lock = false; -} - -void CurvesPanel::onPointEdited() -{ - if (!lock) { - int index = sender()->property("index").toInt(); - model->curves[currentCurve].points[index].x = spnx[index]->value(); - model->curves[currentCurve].points[index].y = spny[index]->value(); - updateCurve(); - updateCurvePoints(); - emit modified(); - } -} - -void CurvesPanel::onNodeMoved(int x, int y) -{ - if (!lock) { - lock = true; - int index = sender()->property("index").toInt(); - model->curves[currentCurve].points[index].x = x; - model->curves[currentCurve].points[index].y = y; - spnx[index]->setValue(x); - spny[index]->setValue(y); - if (index > 0) - spnx[index - 1]->setMaximum(x); - if (index < model->curves[currentCurve].count - 1) - spnx[index + 1]->setMinimum(x); - emit modified(); - lock = false; - } -} - -void CurvesPanel::onNodeFocus() -{ - int index = sender()->property("index").toInt(); - spny[index]->setFocus(); -} - -void CurvesPanel::onNodeUnfocus() -{ - int index = sender()->property("index").toInt(); - spny[index]->clearFocus(); - updateCurve(); -} - -bool CurvesPanel::allowCurveType(int points, CurveData::CurveType type) -{ - int totalpoints = 0; for (int i = 0; i < maxCurves; i++) { - int cvPoints = (i == currentCurve ? points : model->curves[i].count); - CurveData::CurveType cvType = (i == currentCurve ? type : model->curves[i].type); - totalpoints += cvPoints + (cvType == CurveData::CURVE_TYPE_CUSTOM ? cvPoints - 2 : 0); + update(i); } +} - if (totalpoints > maxPoints) { - QMessageBox::warning(this, "companion", tr("Not enough free points in EEPROM to store the curve.")); - return false; +void CurvesPanel::update(int index) +{ + if (model->curves[index].isEmpty()) { + image[index]->setVisible(false); + grp[index]->setVisible(false); } else { - return true; + image[index]->setVisible(true); + grp[index]->setVisible(true); + + image[index]->draw(); + name[index]->setText(QString(model->curves[index].name)); + type[index]->setText(model->curves[index].typeToString()); + numpoints[index]->setText(QString::number(model->curves[index].count)); + points[index]->setText(model->curves[index].pointsToString()); + smooth[index]->setText(DataHelpers::boolToString(model->curves[index].smooth, DataHelpers::BOOL_FMT_YESNO)); } } -void CurvesPanel::on_curvePoints_currentIndexChanged(int index) +void CurvesPanel::editCurve(int index) { - if (!lock) { - int numpoints = ((QComboBox *)sender())->itemData(index).toInt(); - - if (allowCurveType(numpoints, model->curves[currentCurve].type)) { - model->curves[currentCurve].count = numpoints; - - // TODO something better + reuse! - for (int i = 0; i < CPN_MAX_POINTS; i++) { - model->curves[currentCurve].points[i].x = (i >= numpoints - 1 ? +100 : -100 + (200 * i) / (numpoints - 1)); - model->curves[currentCurve].points[i].y = 0; - } - - update(); - emit modified(); - } - else { - updateCurveType(); - } - } -} - -void CurvesPanel::on_curveCustom_currentIndexChanged(int index) -{ - if (!lock) { - CurveData::CurveType type = (CurveData::CurveType)index; - int numpoints = ui->curvePoints->itemData(ui->curvePoints->currentIndex()).toInt(); - - if (allowCurveType(model->curves[currentCurve].count, type)) { - model->curves[currentCurve].type = type; - - // TODO something better + reuse! - for (int i = 0; i < CPN_MAX_POINTS; i++) { - model->curves[currentCurve].points[i].x = (i >= numpoints - 1 ? +100 : -100 + (200 * i) / (numpoints - 1)); - model->curves[currentCurve].points[i].y = 0; - } - - update(); - emit modified(); - } - else { - updateCurveType(); - } - } -} - -void CurvesPanel::on_curveSmooth_currentIndexChanged(int index) -{ - model->curves[currentCurve].smooth = index; - update(); -} - -void CurvesPanel::on_curveName_editingFinished() -{ - if (ui->curveName->text() != model->curves[currentCurve].name) { - memset(model->curves[currentCurve].name, 0, sizeof(model->curves[currentCurve].name)); - strcpy(model->curves[currentCurve].name, ui->curveName->text().toLatin1()); - updateItemModels(); + if (image[index]->edit()) { + update(index); emit modified(); } } -void CurvesPanel::resizeEvent(QResizeEvent *event) +void CurvesPanel::on_curveImageDoubleClicked() { - QRect qr = ui->curvePreview->contentsRect(); - ui->curvePreview->scene()->setSceneRect(GFX_MARGIN, GFX_MARGIN, qr.width() - GFX_MARGIN * 2, qr.height() - GFX_MARGIN * 2); - updateCurve(); - ModelPanel::resizeEvent(event); -} + bool ok = false; + int index = sender()->property("index").toInt(&ok); -void CurvesPanel::on_curveType_currentIndexChanged(int index) -{ - unsigned int flags = templates[index].flags; - ui->curveCoeffLabel->setVisible(flags & CURVE_COEFF_ENABLE); - ui->curveCoeff->setVisible(flags & CURVE_COEFF_ENABLE); - ui->yMax->setValue(100); - ui->yMidLabel->setVisible(flags & CURVE_YMID_ENABLE); - ui->yMid->setVisible(flags & CURVE_YMID_ENABLE); - ui->yMid->setValue(0); - ui->yMinLabel->setVisible(flags & CURVE_YMIN_ENABLE); - ui->yMin->setVisible(flags & CURVE_YMIN_ENABLE); - ui->yMin->setValue(-100); -} - -void CurvesPanel::addTemplate(QString name, unsigned int flags, curveFunction function) -{ - CurveCreatorTemplate tmpl; - tmpl.name = name; - tmpl.flags = flags; - tmpl.function = function; - templates.append(tmpl); - ui->curveType->addItem(name); -} - -void CurvesPanel::on_curveApply_clicked() -{ - int index = ui->curveType->currentIndex(); - int numpoints = model->curves[currentCurve].count; - - for (int i = 0; i < numpoints; i++) { - float x; - if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) - x = model->curves[currentCurve].points[i].x; - else - x = -100.0 + (200.0 / (numpoints - 1)) * i; - - bool apply = false; - switch (ui->curveSide->currentIndex()) { - case 0: - apply = true; - break; - case 1: - if (x >= 0) - apply = true; - break; - case 2: - if (x < 0) - apply = true; - break; - } - - if (apply) { - model->curves[currentCurve].points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), - ui->yMax->value()); - } - } - - updateCurve(); - updateCurvePoints(); - emit modified(); -} - -void CurvesPanel::onPointSizeEdited() -{ - if (!lock) { - update(); - } -} - -void CurvesPanel::onNodeDelete() -{ - int index = sender()->property("index").toInt(); - int numpoints = model->curves[currentCurve].count; - if ((model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) && (index > 0) && (index < numpoints-1)) { - spny[index]->clearFocus(); - for (int i=index+1; icurves[currentCurve].points[i-1] = model->curves[currentCurve].points[i]; - } - numpoints--; - model->curves[currentCurve].points[numpoints].x = 0; - model->curves[currentCurve].points[numpoints].y = 0; - model->curves[currentCurve].count = numpoints; - update(); - emit modified(); - } -} - -void CurvesPanel::onSceneNewPoint(int x, int y) -{ - if ((model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) && (model->curves[currentCurve].count < CPN_MAX_POINTS)) { - int newidx = 0; - int numpoints = model->curves[currentCurve].count; - if (x < model->curves[currentCurve].points[0].x) { - newidx = 0; - } - else if (x > model->curves[currentCurve].points[numpoints - 1].x) { - newidx = numpoints; - } - else { - for (int i = 0; i < numpoints; i++) { - if (x < model->curves[currentCurve].points[i].x) { - newidx = i; - break; - } - } - } - numpoints++; - model->curves[currentCurve].count = numpoints; - for (int idx = (numpoints - 1); idx > newidx; idx--) { - model->curves[currentCurve].points[idx] = model->curves[currentCurve].points[idx - 1]; - } - model->curves[currentCurve].points[newidx].x = x; - model->curves[currentCurve].points[newidx].y = y; - update(); - emit modified(); - } + if (ok) + editCurve(index); } void CurvesPanel::onCustomContextMenuRequested(QPoint pos) @@ -674,10 +162,13 @@ void CurvesPanel::onCustomContextMenuRequested(QPoint pos) QPoint globalPos = button->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("new.png"), tr("Add"), this, SLOT(cmEdit()))->setEnabled(!curveExists()); + contextMenu.addAction(CompanionIcon("edit.png"), tr("Edit"), this, SLOT(cmEdit()))->setEnabled(curveExists()); + contextMenu.addSeparator(); + contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"), this, SLOT(cmCopy()))->setEnabled(curveExists()); + contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"), this, SLOT(cmCut()))->setEnabled(curveExists()); contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"), this, SLOT(cmPaste()))->setEnabled(hasClipboardData()); - contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmClear())); + contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmClear()))->setEnabled(curveExists()); contextMenu.addSeparator(); contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"), this, SLOT(cmInsert()))->setEnabled(insertAllowed()); contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"), this, SLOT(cmDelete())); @@ -701,6 +192,11 @@ bool CurvesPanel::hasClipboardData(QByteArray * data) const return false; } +bool CurvesPanel::curveExists() const +{ + return !model->curves[selectedIndex].isEmpty(); +} + bool CurvesPanel::insertAllowed() const { return ((selectedIndex < maxCurves - 1) && (model->curves[maxCurves - 1].isEmpty())); @@ -708,37 +204,38 @@ bool CurvesPanel::insertAllowed() const bool CurvesPanel::moveDownAllowed() const { - return selectedIndex < maxCurves - 1; + return selectedIndex < maxCurves - 1 && curveExists(); } bool CurvesPanel::moveUpAllowed() const { - return selectedIndex > 0; + return selectedIndex > 0 && curveExists(); } void CurvesPanel::cmClear(bool prompt) { if (prompt) { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } model->curves[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); - update(); + update(selectedIndex); updateItemModels(); emit modified(); } void CurvesPanel::cmClearAll() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Curves. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all curves. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; for (int i = 0; i < maxCurves; i++) { model->curves[i].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_CLEAR, i); } + update(); updateItemModels(); emit modified(); @@ -755,7 +252,7 @@ void CurvesPanel::cmCopy() void CurvesPanel::cmCut() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; cmCopy(); cmClear(false); @@ -763,23 +260,32 @@ void CurvesPanel::cmCut() void CurvesPanel::cmDelete() { - if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Curve. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) + if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; memmove(&model->curves[selectedIndex], &model->curves[selectedIndex + 1], (CPN_MAX_CURVES - (selectedIndex + 1)) * sizeof(CurveData)); model->curves[maxCurves - 1].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); - update(); + for (int i = selectedIndex; i < CPN_MAX_CURVES; i++) { + update(i); + } updateItemModels(); emit modified(); } +void CurvesPanel::cmEdit() +{ + editCurve(selectedIndex); +} + void CurvesPanel::cmInsert() { memmove(&model->curves[selectedIndex + 1], &model->curves[selectedIndex], (CPN_MAX_CURVES - (selectedIndex + 1)) * sizeof(CurveData)); model->curves[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); - update(); + for (int i = selectedIndex; i < CPN_MAX_CURVES; i++) { + update(i); + } updateItemModels(); emit modified(); } @@ -800,7 +306,7 @@ void CurvesPanel::cmPaste() if (hasClipboardData(&data)) { CurveData *cd = &model->curves[selectedIndex]; memcpy(cd, data.constData(), sizeof(CurveData)); - update(); + update(selectedIndex); updateItemModels(); emit modified(); } @@ -815,7 +321,8 @@ void CurvesPanel::swapData(int idx1, int idx2) memcpy(cd2, cd1, sizeof(CurveData)); memcpy(cd1, &cdtmp, sizeof(CurveData)); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); - update(); + update(idx1); + update(idx2); updateItemModels(); emit modified(); } @@ -825,23 +332,3 @@ void CurvesPanel::updateItemModels() { sharedItemModels->update(AbstractItemModel::IMUE_Curves); } - -CustomScene::CustomScene(QGraphicsView * view) : - QGraphicsScene(view) -{ -} - -void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) -{ - if (event->button() == Qt::LeftButton && event->modifiers() == Qt::ControlModifier) { - QRectF rect = sceneRect(); - QPointF pos = event->scenePos(); - QPointF p; - p.setX(-100 + ((pos.x() - rect.left()) * 200) / rect.width()); - p.setY(100 + (rect.top() - pos.y()) * 200 / rect.height()); - QGraphicsScene::mouseReleaseEvent(event); - emit newPoint((int)p.x(), (int)p.y()); - } - else - QGraphicsScene::mouseReleaseEvent(event); -} diff --git a/companion/src/modeledit/curves.h b/companion/src/modeledit/curves.h index c66f79cb7..943734bf5 100644 --- a/companion/src/modeledit/curves.h +++ b/companion/src/modeledit/curves.h @@ -18,111 +18,69 @@ * GNU General Public License for more details. */ -#ifndef _CURVES_H_ -#define _CURVES_H_ +#pragma once +#include "constants.h" #include "modeledit.h" -#include "eeprominterface.h" -#include -#include class CompoundItemModelFactory; -constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve"; +constexpr char MIMETYPE_CURVE[] = { "application/x-companion-curve" }; -namespace Ui { - class Curves; -} - -typedef float (*curveFunction) (float x, float coeff, float yMin, float yMid, float yMax); - -struct CurveCreatorTemplate { - QString name; - unsigned int flags; - curveFunction function; -}; - -class CustomScene : public QGraphicsScene -{ - Q_OBJECT - - public: - CustomScene(QGraphicsView * view); - - signals: - void newPoint(int, int); - - protected: - virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * event) override; -}; +class CurveImageWidget; class CurvesPanel : public ModelPanel { Q_OBJECT public: - CurvesPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); + CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, + CompoundItemModelFactory * sharedItemModels); virtual ~CurvesPanel(); - virtual void update(); - private slots: - void editCurve(); - void plotCurve(bool checked); - void on_curveName_editingFinished(); - void on_curvePoints_currentIndexChanged(int index); - void on_curveCustom_currentIndexChanged(int index); - void on_curveSmooth_currentIndexChanged(int index); - void onPointEdited(); - void onNodeMoved(int x, int y); - void onNodeFocus(); - void onNodeUnfocus(); - void on_curveType_currentIndexChanged(int index); - void on_curveApply_clicked(); - void onSceneNewPoint(int x, int y); - void onPointSizeEdited(); - void onNodeDelete(); + void update(); + void update(int index); + void on_curveImageDoubleClicked(); + void onCustomContextMenuRequested(QPoint pos); void cmClear(bool prompt = true); void cmClearAll(); void cmCopy(); void cmCut(); void cmDelete(); + void cmEdit(); void cmInsert(); - void cmPaste(); void cmMoveDown(); void cmMoveUp(); - - protected: - virtual void resizeEvent(QResizeEvent *event); - void addTemplate(QString name, unsigned int flags, curveFunction function); + void cmPaste(); private: - Ui::Curves *ui; - int currentCurve; - bool visibleCurves[CPN_MAX_CURVES]; - QSpinBox * spnx[CPN_MAX_POINTS]; - QSpinBox * spny[CPN_MAX_POINTS]; - QVector templates; - void setCurrentCurve(int index); - void updateCurve(); - void updateCurveType(); - void updateCurvePoints(); - bool allowCurveType(int points, CurveData::CurveType type); - void setPointY(int i, int x, int y); - CustomScene * scene; + void editCurve(int index); + int maxCurves; - int hasNames; - int hasEnhanced; - int maxPoints; - int selectedIndex; + + void swapData(int idx1, int idx2); + void updateItemModels(); + + // context menu bool hasClipboardData(QByteArray * data = nullptr) const; + bool curveExists() const; bool insertAllowed() const; bool moveDownAllowed() const; bool moveUpAllowed() const; - void swapData(int idx1, int idx2); - CompoundItemModelFactory * sharedItemModels; - void updateItemModels(); -}; + int selectedIndex; -#endif // _CURVES_H_ + // keep constructor pointer to enable triggering update of item models when curves change + CompoundItemModelFactory *sharedItemModels; + + // ui widgets + QWidget *grp[CPN_MAX_CURVES]; + QGridLayout *grid[CPN_MAX_CURVES]; + CurveImageWidget *image[CPN_MAX_CURVES]; + QLabel *name[CPN_MAX_CURVES]; + QLabel *type[CPN_MAX_CURVES]; + QLabel *numpoints[CPN_MAX_CURVES]; + QLabel *points[CPN_MAX_CURVES]; + QLabel *smooth[CPN_MAX_CURVES]; +}; diff --git a/companion/src/modeledit/expodialog.cpp b/companion/src/modeledit/expodialog.cpp index 842a5b6e1..ff78e6f3a 100644 --- a/companion/src/modeledit/expodialog.cpp +++ b/companion/src/modeledit/expodialog.cpp @@ -53,13 +53,21 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef"); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, ed->weight, model, 100, -100, 100, 1.0, dialogFilteredItemModels->getItemModel(id)); + gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, ed->offset, model, 0, -100, 100, 1.0, dialogFilteredItemModels->getItemModel(id)); curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, firmware->getCapability(HasInputDiff) ? 0 : FilteredItemModel::PositiveFilter); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ed->curve, model, - curveRefFilteredItemModels, this); + + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ui->curveImage, ed->curve, model, + sharedItemModels, curveRefFilteredItemModels, this); + + + connect(curveGroup, &CurveReferenceUIManager::resized, this, [=] () { + this->adjustSize(); + this->adjustSize(); // second call seems to be required when hidden fields otherwise not all padding removed + }); id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch), RawSwitch::MixesContext), "RawSwitch"); @@ -143,14 +151,16 @@ ExpoDialog::ExpoDialog(QWidget *parent, ModelData & model, ExpoData *expoData, G connect(ui->trimCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged())); connect(ui->switchesCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged())); connect(ui->sideCB, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged())); + for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) { connect(cb_fp[i], SIGNAL(toggled(bool)), this, SLOT(valuesChanged())); } + if (firmware->getCapability(VirtualInputs)) { connect(ui->inputName, SIGNAL(editingFinished()), this, SLOT(valuesChanged())); } - QTimer::singleShot(0, this, SLOT(shrink())); + adjustSize(); } ExpoDialog::~ExpoDialog() @@ -224,11 +234,6 @@ void ExpoDialog::valuesChanged() } } -void ExpoDialog::shrink() -{ - resize(0, 0); -} - void ExpoDialog::label_phases_customContextMenuRequested(const QPoint & pos) { QLabel *label = (QLabel *)sender(); diff --git a/companion/src/modeledit/expodialog.h b/companion/src/modeledit/expodialog.h index 4a24f0150..fcc17cbda 100644 --- a/companion/src/modeledit/expodialog.h +++ b/companion/src/modeledit/expodialog.h @@ -18,8 +18,7 @@ * GNU General Public License for more details. */ -#ifndef _EXPODIALOG_H_ -#define _EXPODIALOG_H_ +#pragma once #include #include "eeprominterface.h" @@ -47,7 +46,6 @@ class ExpoDialog : public QDialog { private slots: void valuesChanged(); - void shrink(); void label_phases_customContextMenuRequested(const QPoint & pos); void fmClearAll(); void fmSetAll(); @@ -70,5 +68,3 @@ class ExpoDialog : public QDialog { CurveRefFilteredFactory *curveRefFilteredItemModels; int carryTrimFilterFlags = 0; }; - -#endif // _EXPODIALOG_H_ diff --git a/companion/src/modeledit/expodialog.ui b/companion/src/modeledit/expodialog.ui index 99e45fb1a..04237a9d9 100644 --- a/companion/src/modeledit/expodialog.ui +++ b/companion/src/modeledit/expodialog.ui @@ -6,12 +6,12 @@ 0 0 - 394 - 488 + 442 + 411 - + 0 0 @@ -30,6 +30,237 @@ :/icon.png:/icon.png + + + + + NEG + + + + + POS + + + + + ALL + + + + + + + + + + GV + + + + + + + + + + The source for the mixer + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + + 0 + 25 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + + + 6 + + + + + + + Curve + + + + + + + Switch + + + + + + + Flight modes + + + + + + + Stick Side + + + + + + + Weight + + + + + + + + + GV + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Switch used to enable the line. +If blank then the input is considered to be "ON" all the time. + + + + + + + Offset + + + + + + + Source + + + + + + + Input name + + + + + + + Source for the mixer. + + + + + + + 6 + + + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + + + + + unit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -187,25 +418,15 @@ - - - - 6 - - - - - + + - Offset + Scale - - - - + @@ -233,102 +454,27 @@ - - - - - - Stick Side - - - - - - - Source for the mixer. - - - - - - - - GV - - - - - - - - - - The source for the mixer - - - - - - - Qt::Horizontal - - + + - 40 - 0 + 100 + 100 - + + + 100 + 100 + + + + image + + - - - - - NEG - - - - - POS - - - - - ALL - - - - - - - - Qt::Vertical - - - - 20 - 30 - - - - - - - - Switch - - - - - - - 6 - - - @@ -336,76 +482,6 @@ - - - - Flight modes - - - - - - - Input name - - - - - - - - - GV - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Switch used to enable the line. -If blank then the input is considered to be "ON" all the time. - - - - - - - Weight - - - - - - - Source - - - @@ -413,78 +489,15 @@ If blank then the input is considered to be "ON" all the time. - - - - Curve - - - - - - - Scale - - - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - - - - - unit - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 0 - 25 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - true - - - + + + CurveImageWidget + QLabel +
curveimagewidget.h
+
+
inputName lineName diff --git a/companion/src/modeledit/mixerdialog.cpp b/companion/src/modeledit/mixerdialog.cpp index 6cc39ffd1..7da9758fd 100644 --- a/companion/src/modeledit/mixerdialog.cpp +++ b/companion/src/modeledit/mixerdialog.cpp @@ -55,17 +55,25 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, ui->sourceCB->setCurrentIndex(ui->sourceCB->findData(md->srcRaw.toValue())); int limit = firmware->getCapability(OffsetWeight); + id = dialogFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_GVarRef)), "GVarRef"); gvWeightGroup = new GVarGroup(ui->weightGV, ui->weightSB, ui->weightCB, md->weight, model, 100, -limit, limit, 1.0, dialogFilteredItemModels->getItemModel(id)); + gvOffsetGroup = new GVarGroup(ui->offsetGV, ui->offsetSB, ui->offsetCB, md->sOffset, model, 0, -limit, limit, 1.0, dialogFilteredItemModels->getItemModel(id)); curveRefFilteredItemModels = new CurveRefFilteredFactory(sharedItemModels, firmware->getCapability(HasMixerExpo) ? 0 : FilteredItemModel::PositiveFilter); - curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, md->curve, model, - curveRefFilteredItemModels, this); + + curveGroup = new CurveReferenceUIManager(ui->curveTypeCB, ui->curveGVarCB, ui->curveValueSB, ui->curveValueCB, ui->curveImage, md->curve, + model, sharedItemModels, curveRefFilteredItemModels, this); + + connect(curveGroup, &CurveReferenceUIManager::resized, this, [=] () { + this->adjustSize(); + this->adjustSize(); // second call seems to be required when hidden fields otherwise not all padding removed + }); ui->MixDR_CB->setChecked(md->noExpo == 0); @@ -141,9 +149,9 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, ui->delayUpSB->setSingleStep(1.0 / scale); ui->delayUpSB->setDecimals((scale == 1 ? 0 : 1)); ui->delayUpSB->setValue((float)md->delayUp / scale); - QTimer::singleShot(0, this, SLOT(shrink())); valuesChanged(); + connect(ui->mixerName,SIGNAL(editingFinished()),this,SLOT(valuesChanged())); connect(ui->sourceCB,SIGNAL(currentIndexChanged(int)),this,SLOT(valuesChanged())); connect(ui->trimCB,SIGNAL(currentIndexChanged(int)),this,SLOT(valuesChanged())); @@ -155,9 +163,12 @@ MixerDialog::MixerDialog(QWidget *parent, ModelData & model, MixData * mixdata, connect(ui->delayUpSB,SIGNAL(editingFinished()),this,SLOT(valuesChanged())); connect(ui->slowDownSB,SIGNAL(editingFinished()),this,SLOT(valuesChanged())); connect(ui->slowUpSB,SIGNAL(editingFinished()),this,SLOT(valuesChanged())); + for (int i=0; i #include "eeprominterface.h" @@ -46,7 +45,6 @@ class MixerDialog : public QDialog { private slots: void valuesChanged(); - void shrink(); void label_phases_customContextMenuRequested(const QPoint & pos); void fmClearAll(); void fmSetAll(); @@ -66,5 +64,3 @@ class MixerDialog : public QDialog { FilteredItemModelFactory *dialogFilteredItemModels; CurveRefFilteredFactory *curveRefFilteredItemModels; }; - -#endif // _MIXERDIALOG_H_ diff --git a/companion/src/modeledit/mixerdialog.ui b/companion/src/modeledit/mixerdialog.ui index 45afda9e8..dfffc02e9 100644 --- a/companion/src/modeledit/mixerdialog.ui +++ b/companion/src/modeledit/mixerdialog.ui @@ -6,12 +6,12 @@ 0 0 - 412 - 541 + 527 + 571 - + 0 0 @@ -24,158 +24,9 @@ :/icon.png:/icon.png - - - - - - Delay - - - - - - - Slow - - - - - - - Up - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Down - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - 7.500000000000000 - - - 0.500000000000000 - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - 7.500000000000000 - - - 0.500000000000000 - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - + + 6 + @@ -587,7 +438,7 @@ If blank then the mix is considered to be "ON" all the time. - + @@ -605,12 +456,196 @@ If blank then the mix is considered to be "ON" all the time. + + + + + 100 + 100 + + + + + 100 + 100 + + + + imgage + + + + + + + + + Delay + + + + + + + Slow + + + + + + + Up + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Down + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 7.500000000000000 + + + 0.500000000000000 + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 7.500000000000000 + + + 0.500000000000000 + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; text-decoration: underline;">Delay ans Slow</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600; text-decoration: underline;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">These values control the speed and delay of the output of the mix. </p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Delay is not zero the actuation of the mix will be delayed by the specified amount of seconds.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If Slow is not zero then the speed of the mix will be set by the value specified -&gt; the value states the number of seconds it takes to transit from -100 to 100.</p></body></html> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + CurveImageWidget + QLabel +
curveimagewidget.h
+
+
mixerName sourceCB diff --git a/companion/src/modelprinter.cpp b/companion/src/modelprinter.cpp index bb16d26e2..1c4c5f7ba 100644 --- a/companion/src/modelprinter.cpp +++ b/companion/src/modelprinter.cpp @@ -25,6 +25,7 @@ #include "helpers_html.h" #include "appdata.h" #include "adjustmentreference.h" +#include "curveimage.h" #include #include @@ -646,55 +647,8 @@ QString ModelPrinter::printCurveName(int idx) QString ModelPrinter::printCurve(int idx) { - QString result; const CurveData & curve = model.curves[idx]; - result += (curve.type == CurveData::CURVE_TYPE_CUSTOM) ? tr("Custom") : tr("Standard"); - result += ", ["; - if (curve.type == CurveData::CURVE_TYPE_CUSTOM) { - for (int j=0; j= 0) { + return round(curveexpou(x, coeff)*a + yMin); + } + else { + coeff = -coeff; + x = 100 - x; + return round((100.0 - curveexpou(x, coeff)) * a + yMin); + } +} + +float curveSymmetricalY(float x, float coeff, float yMin, float yMid, float yMax) +{ + bool invert; + if (x < 0) { + x = -x; + invert = 1; + } + else { + invert = 0; + } + + float y; + if (coeff >= 0) { + y = round(curveexpou(x, coeff) * (yMax / 100.0)); + } + else { + coeff = -coeff; + x = 100.0 - x; + y = round((100.0 - curveexpou(x, coeff)) * (yMax / 100.0)); + } + + if (invert) { + y = -y; + } + + return y; +} + +float curveSymmetricalX(float x, float coeff, float yMin, float yMid, float yMax) +{ + float a = (yMax - yMid) / 100.0; + + if (x < 0) + x = -x; + + float y; + if (coeff >= 0) { + y = round(curveexpou(x, coeff) * a + yMid); + } + else { + coeff = -coeff; + x = 100 - x; + y = round((100.0 - curveexpou(x, coeff)) * a + yMid); + } + + return y; +} + +#define GFX_MARGIN 16 + +#define CURVE_COEFF_ENABLE 1 +#define CURVE_YMID_ENABLE 2 +#define CURVE_YMIN_ENABLE 4 + +/* + CurveDialog +*/ + +CurveDialog::CurveDialog(QWidget * parent, ModelData & model, const int curveIdx, Firmware * firmware, + CompoundItemModelFactory * sharedItemModels) : + QDialog(parent), + ui(new Ui::CurveDialog), + model(model), + curve(model.curves[curveIdx]), + curveIdx(curveIdx), + sharedItemModels(sharedItemModels) +{ + ui->setupUi(this); + + if (curve.isEmpty()) + curve.init(); + + lock = true; + + maxCurves = firmware->getCapability(NumCurves); + maxPoints = firmware->getCapability(NumCurvePoints); + + ui->curveName->setField(curve.name, CURVEDATA_NAME_LEN); + connect(ui->curveName, &AutoLineEdit::currentDataChanged, this, &CurveDialog::on_curveNameChanged); + on_curveNameChanged(); + + dialogFilterFactory = new FilteredItemModelFactory(); + int id = dialogFilterFactory->registerItemModel(new FilteredItemModel(CurveData::typeItemModel()), + "Curve Type"); + ui->curveType->setModel(dialogFilterFactory->getItemModel(id)); + ui->curveType->setField((int &)curve.type); + connect(ui->curveType, &AutoComboBox::currentDataChanged, this, [&] () { on_curveTypeChanged(ui->curveType->currentIndex()) ;}); + + id = dialogFilterFactory->registerItemModel(new FilteredItemModel(CurveData::pointsItemModel()), + "Curve Points"); + + ui->curvePoints->setModel(dialogFilterFactory->getItemModel(id)); + ui->curvePoints->setField(curve.count); + connect(ui->curvePoints, &AutoComboBox::currentDataChanged, this, [&] () { on_curvePointsChanged(ui->curvePoints->currentIndex()) ;}); + + ui->curveSmooth->setField(curve.smooth); + connect(ui->curveSmooth, &AutoCheckBox::currentDataChanged, this, [&] () { update() ;}); + + scene = new CustomScene(ui->curvePreview); + scene->setItemIndexMethod(QGraphicsScene::NoIndex); + connect(scene, &CustomScene::newPoint, this, &CurveDialog::onSceneNewPoint); + + ui->curvePreview->setScene(scene); + + for (int i = 0; i < CPN_MAX_POINTS; i++) { + spnx[i] = new QSpinBox(this); + spnx[i]->setProperty("index", i); + spnx[i]->setMinimum(-100); + spnx[i]->setMaximum(+100); + spnx[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + spnx[i]->setAccelerated(true); + connect(spnx[i], QOverload::of(&QSpinBox::valueChanged), this, &CurveDialog::onPointEdited); + ui->pointsLayout->addWidget(spnx[i], i, 0, 1, 1); + + spny[i] = new QSpinBox(this); + spny[i]->setProperty("index", i); + spny[i]->setMinimum(-100); + spny[i]->setMaximum(+100); + spny[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + spny[i]->setAccelerated(true); + connect(spny[i], QOverload::of(&QSpinBox::valueChanged), this, &CurveDialog::onPointEdited); + ui->pointsLayout->addWidget(spny[i], i, 1, 1, 1); + } + + addTemplate(tr("Linear"), CURVE_YMIN_ENABLE, curveLinear); + addTemplate(tr("Single Expo"), CURVE_COEFF_ENABLE | CURVE_YMIN_ENABLE, curveExpo); + addTemplate(tr("Symmetrical f(x)=-f(-x)"), CURVE_COEFF_ENABLE, curveSymmetricalY); + addTemplate(tr("Symmetrical f(x)=f(-x)"), CURVE_COEFF_ENABLE | CURVE_YMID_ENABLE, curveSymmetricalX); + on_curveTemplateChanged(0); + + ui->pointSize->setValue(10); + ui->pointSize->setMinimum(3); + ui->pointSize->setMaximum(20); + connect(ui->pointSize, QOverload::of(&QSpinBox::valueChanged), this, [&] () { if (!lock) update(); }); + + connect(ui->curveTemplate, QOverload::of(&QComboBox::currentIndexChanged), this, &CurveDialog::on_curveTemplateChanged); + connect(ui->curveApply, &QPushButton::clicked, this, &CurveDialog::on_curveApplyClicked); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + connect(this, &QDialog::accepted, this, &CurveDialog::onDialogAccepted); + + update(); + shrink(); + + lock = false; +} + +CurveDialog::~CurveDialog() +{ + delete ui; + delete dialogFilterFactory; +} + +void CurveDialog::onDialogAccepted() +{ + model.curves[curveIdx] = curve; + if (sharedItemModels) + sharedItemModels->update(AbstractItemModel::IMUE_Curves); +} + +void CurveDialog::update() +{ + lock = true; + + ui->curveName->update(); + updateCurveType(); + updateCurve(); + updateCurvePoints(); + shrink(); + + lock = false; +} + +void CurveDialog::updateCurveType() +{ + lock = true; + + ui->curveType->update(); + ui->curvePoints->update(); + ui->curveSmooth->update(); + + lock = false; +} + +void CurveDialog::updateCurve() +{ + lock = true; + + Node * nodel = 0; + Node * nodex = 0; + QColor color; + + scene->clear(); + + qreal width = scene->sceneRect().width(); + qreal height = scene->sceneRect().height(); + + qreal leftX = scene->sceneRect().left() + width / 4 * 1; + qreal centerX = scene->sceneRect().left() + width / 2; + qreal rightX = scene->sceneRect().left() + width / 4 * 3; + qreal topY = scene->sceneRect().top() + height / 4 * 1; + qreal centerY = scene->sceneRect().top() + height / 2; + qreal bottomY = scene->sceneRect().top() + height / 4 * 3; + + scene->addLine(leftX, GFX_MARGIN, leftX, height + GFX_MARGIN); + scene->addLine(centerX, GFX_MARGIN, centerX, height + GFX_MARGIN); + scene->addLine(rightX, GFX_MARGIN, rightX, height + GFX_MARGIN); + scene->addLine(GFX_MARGIN, topY, width + GFX_MARGIN, topY); + scene->addLine(GFX_MARGIN, centerY, width + GFX_MARGIN, centerY); + scene->addLine(GFX_MARGIN, bottomY, width + GFX_MARGIN, bottomY); + + QPen pen; + pen.setWidth(1); + pen.setStyle(Qt::SolidLine); + + pen.setColor(colors[0]); // needed???? + + int numpoints = curve.count; + + for (int i = 0; i < numpoints - 1; i++) { + if (curve.type == CurveData::CURVE_TYPE_CUSTOM) + scene->addLine(centerX + (qreal)curve.points[i].x * width / 200, + centerY - (qreal)curve.points[i].y * height / 200, + centerX + (qreal)curve.points[i + 1].x * width / 200, + centerY - (qreal)curve.points[i + 1].y * height / 200, pen); + else + scene->addLine(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)curve.points[i].y * height / 200, + GFX_MARGIN + (i + 1) * width / (numpoints - 1), + centerY - (qreal)curve.points[i + 1].y * height / 200, pen); + } + + for (int i = 0; i < numpoints; i++) { + nodel = nodex; + nodex = new Node(); + nodex->setProperty("index", i); + nodex->setColor(colors[0]); + nodex->setBallSize(ui->pointSize->value()); + nodex->setBallHeight(0); + if (curve.type == CurveData::CURVE_TYPE_CUSTOM) { + if (i > 0 && i < numpoints - 1) { + nodex->setFixedX(false); + nodex->setMinX(curve.points[i - 1].x); + nodex->setMaxX(curve.points[i + 1].x); + } + else { + nodex->setFixedX(true); + } + nodex->setPos(centerX + (qreal)curve.points[i].x * width / 200, + centerY - (qreal)curve.points[i].y * height / 200); + } + else { + nodex->setFixedX(true); + nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), + centerY - (qreal)curve.points[i].y * height / 200); + } + connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int))); + connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus())); + connect(nodex, SIGNAL(unfocus()), this, SLOT(onNodeUnfocus())); + connect(nodex, SIGNAL(deleteMe()), this, SLOT(onNodeDelete())); + scene->addItem(nodex); + if (i > 0) + scene->addItem(new Edge(nodel, nodex)); + } + + lock = false; +} + +void CurveDialog::updateCurvePoints() +{ + lock = true; + + int count = curve.count; + for (int i = 0; i < count; i++) { + spny[i]->show(); + spny[i]->setValue(curve.points[i].y); + if (curve.type == CurveData::CURVE_TYPE_CUSTOM) { + spnx[i]->show(); + if (i == 0 || i == count - 1) { + spnx[i]->setDisabled(true); + spnx[i]->setMaximum(+100); + spnx[i]->setMinimum(-100); + } + else { + spnx[i]->setDisabled(false); + spnx[i]->setMaximum(curve.points[i + 1].x); + spnx[i]->setMinimum(curve.points[i - 1].x); + } + spnx[i]->setValue(curve.points[i].x); + } + else { + spnx[i]->hide(); + } + } + + for (int i = count; i < CPN_MAX_POINTS; i++) { + spny[i]->hide(); + spnx[i]->hide(); + } + + lock = false; +} + +void CurveDialog::onPointEdited(int i) +{ + if (!lock) { + int index = sender()->property("index").toInt(); + curve.points[index].x = spnx[index]->value(); + curve.points[index].y = spny[index]->value(); + updateCurve(); + updateCurvePoints(); + } +} + +void CurveDialog::onNodeMoved(int x, int y) +{ + if (!lock) { + lock = true; + int index = sender()->property("index").toInt(); + curve.points[index].x = x; + curve.points[index].y = y; + spnx[index]->setValue(x); + spny[index]->setValue(y); + if (index > 0) + spnx[index - 1]->setMaximum(x); + if (index < curve.count - 1) + spnx[index + 1]->setMinimum(x); + lock = false; + } +} + +void CurveDialog::onNodeFocus() +{ + int index = sender()->property("index").toInt(); + spny[index]->setFocus(); +} + +void CurveDialog::onNodeUnfocus() +{ + int index = sender()->property("index").toInt(); + spny[index]->clearFocus(); + updateCurve(); +} + +bool CurveDialog::allowCurveType(int points, CurveData::CurveType type) +{ + int totalpoints = 0; + for (int i = 0; i < maxCurves; i++) { + int cvPoints = (i == curveIdx ? points : model.curves[i].count); + CurveData::CurveType cvType = (i == curveIdx ? type : model.curves[i].type); + totalpoints += cvPoints + (cvType == CurveData::CURVE_TYPE_CUSTOM ? cvPoints - 2 : 0); + } + + if (totalpoints > maxPoints) { + QMessageBox::warning(this, CPN_STR_APP_NAME, tr("Not enough free points in global points array to store the curve.")); + return false; + } + else { + return true; + } +} + +void CurveDialog::on_curveNameChanged() +{ + setWindowTitle(tr("Editing curve %1: %2").arg(curveIdx + 1).arg(QString(curve.name))); +} + +void CurveDialog::on_curvePointsChanged(int index) +{ + if (!lock) { + int numpoints = ((QComboBox *)sender())->itemData(index).toInt(); + + if (allowCurveType(numpoints, curve.type)) { + curve.count = numpoints; + curve.init(); + update(); + } + else { + updateCurveType(); + } + } +} + +void CurveDialog::on_curveTypeChanged(int index) +{ + if (!lock) { + CurveData::CurveType type = (CurveData::CurveType)index; + int numpoints = ui->curvePoints->itemData(ui->curvePoints->currentIndex()).toInt(); + + if (allowCurveType(numpoints, type)) { + //curve.clear(numpoints); + curve.type = type; + //curve.init(); + update(); + } + else { + updateCurveType(); + } + } +} + +void CurveDialog::resizeEvent(QResizeEvent *event) +{ + QRect qr = ui->curvePreview->contentsRect(); + ui->curvePreview->scene()->setSceneRect(GFX_MARGIN, GFX_MARGIN, qr.width() - GFX_MARGIN * 2, qr.height() - GFX_MARGIN * 2); + updateCurve(); +} + +void CurveDialog::on_curveTemplateChanged(int index) +{ + unsigned int flags = templates[index].flags; + ui->curveCoeffLabel->setVisible(flags & CURVE_COEFF_ENABLE); + ui->curveCoeff->setVisible(flags & CURVE_COEFF_ENABLE); + ui->yMax->setValue(100); + ui->yMidLabel->setVisible(flags & CURVE_YMID_ENABLE); + ui->yMid->setVisible(flags & CURVE_YMID_ENABLE); + ui->yMid->setValue(0); + ui->yMinLabel->setVisible(flags & CURVE_YMIN_ENABLE); + ui->yMin->setVisible(flags & CURVE_YMIN_ENABLE); + ui->yMin->setValue(-100); +} + +void CurveDialog::addTemplate(QString name, unsigned int flags, curveFunction function) +{ + CurveCreatorTemplate tmpl; + tmpl.name = name; + tmpl.flags = flags; + tmpl.function = function; + templates.append(tmpl); + ui->curveTemplate->addItem(name); +} + +void CurveDialog::on_curveApplyClicked() +{ + int index = ui->curveTemplate->currentIndex(); + int numpoints = curve.count; + + for (int i = 0; i < numpoints; i++) { + float x; + if (curve.type == CurveData::CURVE_TYPE_CUSTOM) + x = curve.points[i].x; + else + x = -100.0 + (200.0 / (numpoints - 1)) * i; + + bool apply = false; + switch (ui->curveSide->currentIndex()) { + case 0: + apply = true; + break; + case 1: + if (x >= 0) + apply = true; + break; + case 2: + if (x < 0) + apply = true; + break; + } + + if (apply) { + curve.points[i].y = templates[index].function(x, ui->curveCoeff->value(), ui->yMin->value(), ui->yMid->value(), + ui->yMax->value()); + } + } + + updateCurve(); + updateCurvePoints(); +} + +void CurveDialog::onNodeDelete() +{ + int index = sender()->property("index").toInt(); + int numpoints = curve.count; + if ((curve.type == CurveData::CURVE_TYPE_CUSTOM) && (index > 0) && (index < numpoints - 1)) { + spny[index]->clearFocus(); + for (int i = index + 1; i < numpoints; i++) { + curve.points[i - 1] = curve.points[i]; + } + numpoints--; + curve.points[numpoints].x = 0; + curve.points[numpoints].y = 0; + curve.count = numpoints; + update(); + } +} + +void CurveDialog::onSceneNewPoint(int x, int y) +{ + if ((curve.type == CurveData::CURVE_TYPE_CUSTOM) && (curve.count < CPN_MAX_POINTS)) { + int newidx = 0; + int numpoints = curve.count; + if (x < curve.points[0].x) { + newidx = 0; + } + else if (x > curve.points[numpoints - 1].x) { + newidx = numpoints; + } + else { + for (int i = 0; i < numpoints; i++) { + if (x < curve.points[i].x) { + newidx = i; + break; + } + } + } + numpoints++; + curve.count = numpoints; + for (int idx = (numpoints - 1); idx > newidx; idx--) { + curve.points[idx] = curve.points[idx - 1]; + } + curve.points[newidx].x = x; + curve.points[newidx].y = y; + update(); + } +} + +void CurveDialog::shrink() +{ + resize(0, 0); +} + +CustomScene::CustomScene(QGraphicsView * view) : + QGraphicsScene(view) +{ +} + +void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) +{ + if (event->button() == Qt::LeftButton && event->modifiers() == Qt::ControlModifier) { + QRectF rect = sceneRect(); + QPointF pos = event->scenePos(); + QPointF p; + p.setX(-100 + ((pos.x() - rect.left()) * 200) / rect.width()); + p.setY(100 + (rect.top() - pos.y()) * 200 / rect.height()); + QGraphicsScene::mouseReleaseEvent(event); + emit newPoint((int)p.x(), (int)p.y()); + } + else + QGraphicsScene::mouseReleaseEvent(event); +} diff --git a/companion/src/shared/curvedialog.h b/companion/src/shared/curvedialog.h new file mode 100644 index 000000000..e48808070 --- /dev/null +++ b/companion/src/shared/curvedialog.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#pragma once + +#include "constants.h" +#include "modeldata.h" + +#include +#include +#include + +class Firmware; +class FilteredItemModelFactory; +class CompoundItemModelFactory; + +namespace Ui { + class CurveDialog; +} + +typedef float (*curveFunction) (float x, float coeff, float yMin, float yMid, float yMax); + +struct CurveCreatorTemplate { + QString name; + unsigned int flags; + curveFunction function; +}; + +class CustomScene : public QGraphicsScene +{ + Q_OBJECT + + public: + CustomScene(QGraphicsView * view); + + signals: + void newPoint(int, int); + + protected: + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * event) override; +}; + +class CurveDialog : public QDialog +{ + Q_OBJECT + + public: + CurveDialog(QWidget *parent, ModelData & model, const int curveIdx, Firmware * firmware, CompoundItemModelFactory * sharedItemModels); + virtual ~CurveDialog(); + + private slots: + void shrink(); + void on_curveNameChanged(); + void on_curvePointsChanged(int index); + void on_curveTypeChanged(int index); + void onPointEdited(int i); + void onNodeMoved(int x, int y); + void onNodeFocus(); + void onNodeUnfocus(); + void on_curveTemplateChanged(int index); + void on_curveApplyClicked(); + void onSceneNewPoint(int x, int y); + void onNodeDelete(); + void onDialogAccepted(); + + protected: + virtual void resizeEvent(QResizeEvent *event); + void addTemplate(QString name, unsigned int flags, curveFunction function); + + private: + Ui::CurveDialog *ui; + ModelData &model; + CurveData curve; // copy initialised in constructor and copied back to model if dialog is accepted + const int curveIdx; + CompoundItemModelFactory *sharedItemModels; + + QSpinBox *spnx[CPN_MAX_POINTS]; + QSpinBox *spny[CPN_MAX_POINTS]; + QVector templates; + CustomScene *scene; + int maxCurves; + int maxPoints; + bool lock; + FilteredItemModelFactory * dialogFilterFactory; + + void update(); + void updateCurve(); + void updateCurveType(); + void updateCurvePoints(); + bool allowCurveType(int points, CurveData::CurveType type); + void setPointY(int i, int x, int y); +}; diff --git a/companion/src/modeledit/curves.ui b/companion/src/shared/curvedialog.ui similarity index 60% rename from companion/src/modeledit/curves.ui rename to companion/src/shared/curvedialog.ui index f03252942..f847108f9 100644 --- a/companion/src/modeledit/curves.ui +++ b/companion/src/shared/curvedialog.ui @@ -1,153 +1,119 @@ - Curves - + CurveDialog + 0 0 - 672 - 539 + 658 + 523 + + + 0 + 0 + + - - - - - - - - 0 - + + true + + + true + + + + QLayout::SetDefaultConstraint + + + - - - - 0 - 0 - - - - - 400 - 400 - - - - Qt::AlignCenter - - - - - - - - - Curve name + + + + + + 0 + 0 + + + + + 400 + 400 + - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignCenter - - - - 0 - - - - - - - - - Fixed X - - - - - Custom X - - + + + + + + Name + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - - - - - Lines - - - - - Smooth - - + + + + 3 + + + + + + + Type + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + Smooth + - - - - 3 - - - - - - - Curve type - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + - - - - - + + - - - 0 - - - 0 - - - 0 - - - 2 - - + - + Qt::Vertical @@ -161,7 +127,7 @@ - + @@ -175,7 +141,17 @@ Curve Creator - + + + + + Template + + + + + + @@ -183,51 +159,8 @@ - - - - Curve type - - - - - - - Side - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - -100 - - - 100 - - - - - + + 0 @@ -249,7 +182,76 @@ - + + + + + 0 + 0 + + + + -100 + + + 100 + + + + + + + Y at X=0 + + + + + + + + 0 + 0 + + + + -100 + + + 100 + + + + + + + Y at X=100 + + + + + + + + 0 + 0 + + + + -100 + + + 100 + + + + + + + Side + + + + @@ -268,43 +270,17 @@ - - - - Y at X=100 - - - - - - - - 0 - 0 - - - - -100 - - - 100 - - - - + Apply - - - - - - - Y at X=0 + + + + Qt::Horizontal @@ -315,57 +291,74 @@ - - - - - 0 - 0 - - - - -100 - - - 100 - - - - + - - + + - Qt::Horizontal + Qt::Vertical - + + + 20 + 40 + + + - + Qt::Horizontal - 10 + 1 20 + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + AutoLineEdit + QLineEdit +
autolineedit.h
+
+ + AutoComboBox + QComboBox +
autocombobox.h
+
+ + AutoCheckBox + QCheckBox +
autocheckbox.h
+
+
curvePreview curvePoints - curveCustom - curveSmooth - curveName - curveType + curveTemplate curveCoeff yMin yMid diff --git a/companion/src/shared/curveimage.cpp b/companion/src/shared/curveimage.cpp new file mode 100644 index 000000000..6fb69835d --- /dev/null +++ b/companion/src/shared/curveimage.cpp @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#include "curveimage.h" +#include "curvedata.h" + +constexpr int IMAGE_SIZE = { 200 }; + +CurveImage::CurveImage(QColor gridColor, qreal gridWidth): + size(IMAGE_SIZE), + image(size + 1, size + 1, QImage::Format_RGB32), + painter(&image) +{ + painter.setBrush(QBrush(Qt::white)); + painter.setPen(Qt::black); + painter.drawRect(0, 0, size, size); + + painter.setPen(QPen(gridColor, gridWidth)); + const int step = size / 4; + painter.drawLine(0, step * 1, size, step * 1); + painter.drawLine(0, step * 2, size, step * 2); + painter.drawLine(0, step * 3, size, step * 3); + painter.drawLine(step * 1, 0, step * 1, size); + painter.drawLine(step * 2, 0, step * 2, size); + painter.drawLine(step * 3, 0, step * 3, size); +} + +void CurveImage::drawCurve(const CurveData & curve, QColor color, qreal width) +{ + painter.setPen(QPen(color, width, Qt::SolidLine)); + + for (int j = 1; j < curve.count; j++) { + if (curve.type == CurveData::CURVE_TYPE_CUSTOM) + painter.drawLine(size / 2 + (size * curve.points[j - 1].x) / IMAGE_SIZE, + size / 2 - (size * curve.points[j - 1].y) / IMAGE_SIZE, + size / 2 + (size * curve.points[j].x) / IMAGE_SIZE, + size / 2 - (size * curve.points[j].y) / IMAGE_SIZE); + else + painter.drawLine(size * (j-1) / (curve.count - 1), + size / 2 - (size * curve.points[j - 1].y) / IMAGE_SIZE, + size * (j) / (curve.count - 1), + size / 2 - (size * curve.points[j].y) / IMAGE_SIZE); + } +} diff --git a/companion/src/shared/curveimage.h b/companion/src/shared/curveimage.h new file mode 100644 index 000000000..2e8b79723 --- /dev/null +++ b/companion/src/shared/curveimage.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +#include + +class CurveData; + +class CurveImage +{ + public: + CurveImage(QColor gridColor = Qt::darkGray, qreal gridWidth = 1); + + void drawCurve(const CurveData & curve, QColor color = Qt::black, qreal width = 2); + const QImage & get() const { return image; } + + protected: + int size; + QImage image; + QPainter painter; +}; diff --git a/companion/src/shared/curveimagewidget.cpp b/companion/src/shared/curveimagewidget.cpp new file mode 100644 index 000000000..4d2d81ac8 --- /dev/null +++ b/companion/src/shared/curveimagewidget.cpp @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#include "curveimagewidget.h" +#include "curveimage.h" +#include "curvedialog.h" +#include "compounditemmodels.h" +#include "modeldata.h" +#include "eeprominterface.h" + +CurveImageWidget::CurveImageWidget(QWidget * parent) : + QLabel(parent) +{ + setToolTip(tr("Double click to edit")); +} + +void CurveImageWidget::set(ModelData * model, Firmware * firmware, CompoundItemModelFactory * sharedItemModels, int index, QColor penColor, qreal penWidth) +{ + this->model = model; + this->firmware = firmware; + this->sharedItemModels = sharedItemModels; + this->penWidth = penWidth; + this->penColor = penColor; + setIndex(index); + setGrid(); +} + +void CurveImageWidget::setIndex(int index) +{ + this->index = (index >= 0 && index < CPN_MAX_CURVES) ? index : -1; +} + +void CurveImageWidget::setGrid(QColor color, qreal width) +{ + gridColor = color; + gridWidth = width; +} + +void CurveImageWidget::setPen(QColor color, qreal width) +{ + penColor = color; + penWidth = width; +} + +int CurveImageWidget::edit() +{ + int ret = 0; + + if (index >= 0 && index < CPN_MAX_CURVES) { + CurveDialog *dlg = new CurveDialog(this, *model, index, firmware, sharedItemModels); + ret = dlg->exec(); + delete dlg; + } + + return ret; +} + +void CurveImageWidget::draw() +{ + if (index >= 0 && index < CPN_MAX_CURVES) { + CurveImage *curveImage = new CurveImage(gridColor, gridWidth); + curveImage->drawCurve(model->curves[index], penColor, penWidth); + + QImage image = curveImage->get(); + setPixmap(QPixmap::fromImage(image.scaled(height(), width()))); + + delete curveImage; + } + else { + CurveImage *curveImage = new CurveImage(gridColor, gridWidth); + QImage image = curveImage->get(); + setPixmap(QPixmap::fromImage(image.scaled(height(), width()))); + } +} + +void CurveImageWidget::mouseDoubleClickEvent(QMouseEvent * event) +{ + QLabel::mouseDoubleClickEvent(event); + emit doubleClicked(); +} diff --git a/companion/src/shared/curveimagewidget.h b/companion/src/shared/curveimagewidget.h new file mode 100644 index 000000000..aad8b6ee5 --- /dev/null +++ b/companion/src/shared/curveimagewidget.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#pragma once + +#include + +class ModelData; +class Firmware; +class CompoundItemModelFactory; + +class CurveImageWidget : public QLabel +{ + Q_OBJECT + + public: + CurveImageWidget(QWidget * parent); + virtual ~CurveImageWidget() {} + + void set(ModelData * model, Firmware * firmware, CompoundItemModelFactory * sharedItemModels, int index, QColor penColor = Qt::black, qreal penWidth = 2); + void setIndex(int index); + void setGrid(QColor color = Qt::darkGray, qreal width = 1); + void setPen(QColor color = Qt::black, qreal width = 2); + int edit(); + void draw(); + + signals: + void doubleClicked(); + + protected: + virtual void mouseDoubleClickEvent(QMouseEvent * event); + + private: + ModelData *model = nullptr; + Firmware *firmware = nullptr; + CompoundItemModelFactory *sharedItemModels; + int index; + qreal penWidth; + QColor penColor; + qreal gridWidth; + QColor gridColor; +}; diff --git a/companion/src/simulation/CMakeLists.txt b/companion/src/simulation/CMakeLists.txt index 091867696..348016614 100644 --- a/companion/src/simulation/CMakeLists.txt +++ b/companion/src/simulation/CMakeLists.txt @@ -119,4 +119,4 @@ qt5_wrap_ui(simulation_SRCS ${simulation_UIS}) qt5_wrap_cpp(simulation_SRCS ${simulation_HDRS}) add_library(simulation ${simulation_SRCS} ${simulation_HDRS}) -target_link_libraries(simulation PRIVATE ${CPN_COMMON_LIB} Qt5::Core Qt5::Widgets Qt5::Svg) +target_link_libraries(simulation PRIVATE ${CPN_COMMON_LIB} shared Qt5::Core Qt5::Widgets Qt5::Svg)