Files
edgetx-gx12/companion/src/modeledit/logicalswitches.cpp
T
Neil HorneandGitHub 5a500cc055 Companion enhanced telemetry sensors context menu and other refinements across context menus (#7716)
Companion enhanced telemetry sensors context menu with reference updates
2020-09-04 18:36:58 +02:00

650 lines
21 KiB
C++

/*
* 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 "logicalswitches.h"
#include "rawitemfilteredmodel.h"
#include "helpers.h"
#include <TimerEdit>
LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware),
selectedIndex(0)
{
Stopwatch s1("LogicalSwitchesPanel");
rawSwitchItemModel = new RawSwitchFilterItemModel(&generalSettings, &model, RawSwitch::LogicalSwitchesContext, this);
const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup);
rawSourceItemModel = new RawSourceFilterItemModel(&generalSettings, &model, srcGroups, this);
lsCapability = firmware->getCapability(LogicalSwitches);
lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt);
QStringList headerLabels;
headerLabels << "#" << tr("Function") << tr("V1") << tr("V2") << tr("AND Switch");
if (lsCapabilityExt) {
headerLabels << tr("Duration") << tr("Delay");
}
TableLayout * tableLayout = new TableLayout(this, lsCapability, headerLabels);
s1.report("header");
const int channelsMax = model.getChannelsMax(true);
lock = true;
for (int i=0; i<lsCapability; i++) {
// The label
QLabel * label = new QLabel(this);
label->setProperty("index", i);
label->setText(RawSwitch(SWITCH_TYPE_VIRTUAL, i+1).toString());
label->setContextMenuPolicy(Qt::CustomContextMenu);
label->setToolTip(tr("Popup menu available"));
label->setMouseTracking(true);
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint)));
tableLayout->addWidget(i, 0, label);
// The function
cbFunction[i] = new QComboBox(this);
cbFunction[i]->setProperty("index", i);
populateFunctionCB(cbFunction[i]);
connect(cbFunction[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onFunctionChanged()));
tableLayout->addWidget(i, 1, cbFunction[i]);
// V1
QHBoxLayout *v1Layout = new QHBoxLayout();
cbSource1[i] = new QComboBox(this);
cbSource1[i]->setProperty("index",i);
connect(cbSource1[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onV1Changed(int)));
v1Layout->addWidget(cbSource1[i]);
cbSource1[i]->setVisible(false);
dsbValue[i] = new QDoubleSpinBox(this);
dsbValue[i]->setMaximum(channelsMax);
dsbValue[i]->setMinimum(-channelsMax);
dsbValue[i]->setAccelerated(true);
dsbValue[i]->setDecimals(0);
dsbValue[i]->setProperty("index", i);
connect(dsbValue[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
v1Layout->addWidget(dsbValue[i]);
dsbValue[i]->setVisible(false);
tableLayout->addLayout(i, 2, v1Layout);
// V2
QHBoxLayout *v2Layout = new QHBoxLayout();
cbSource2[i] = new QComboBox(this);
cbSource2[i]->setProperty("index", i);
connect(cbSource2[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onV2Changed(int)));
v2Layout->addWidget(cbSource2[i]);
cbSource2[i]->setVisible(false);
dsbOffset[i] = new QDoubleSpinBox(this);
dsbOffset[i]->setProperty("index",i);
dsbOffset[i]->setMaximum(channelsMax);
dsbOffset[i]->setMinimum(-channelsMax);
dsbOffset[i]->setAccelerated(true);
dsbOffset[i]->setDecimals(0);
connect(dsbOffset[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
dsbOffset[i]->setVisible(false);
v2Layout->addWidget(dsbOffset[i]);
dsbOffset2[i] = new QDoubleSpinBox(this);
dsbOffset2[i]->setProperty("index",i);
dsbOffset2[i]->setMaximum(channelsMax);
dsbOffset2[i]->setMinimum(-channelsMax);
dsbOffset2[i]->setAccelerated(true);
dsbOffset2[i]->setDecimals(0);
dsbOffset2[i]->setSpecialValueText(" " + tr("(instant)"));
connect(dsbOffset2[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
dsbOffset2[i]->setVisible(false);
v2Layout->addWidget(dsbOffset2[i]);
teOffset[i] = new TimerEdit(this);
teOffset[i]->setProperty("index",i);
connect(teOffset[i],SIGNAL(editingFinished()),this,SLOT(onOffsetChanged()));
v2Layout->addWidget(teOffset[i]);
teOffset[i]->setVisible(false);
tableLayout->addLayout(i, 3, v2Layout);
// AND
cbAndSwitch[i] = new QComboBox(this);
cbAndSwitch[i]->setProperty("index", i);
populateAndSwitchCB(cbAndSwitch[i]);
connect(cbAndSwitch[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onAndSwitchChanged(int)));
tableLayout->addWidget(i, 4, cbAndSwitch[i]);
if (lsCapabilityExt) {
// Duration
dsbDuration[i] = new QDoubleSpinBox(this);
dsbDuration[i]->setProperty("index", i);
dsbDuration[i]->setSingleStep(0.1);
dsbDuration[i]->setMaximum(25);
dsbDuration[i]->setMinimum(0);
dsbDuration[i]->setAccelerated(true);
dsbDuration[i]->setDecimals(1);
connect(dsbDuration[i], SIGNAL(valueChanged(double)), this, SLOT(onDurationChanged(double)));
tableLayout->addWidget(i, 5, dsbDuration[i]);
// Delay
dsbDelay[i] = new QDoubleSpinBox(this);
dsbDelay[i]->setProperty("index", i);
dsbDelay[i]->setSingleStep(0.1);
dsbDelay[i]->setMaximum(25);
dsbDelay[i]->setMinimum(0);
dsbDelay[i]->setAccelerated(true);
dsbDelay[i]->setDecimals(1);
connect(dsbDelay[i], SIGNAL(valueChanged(double)), this, SLOT(onDelayChanged(double)));
tableLayout->addWidget(i, 6, dsbDelay[i]);
}
}
s1.report("added elements");
disableMouseScrolling();
lock = false;
update();
tableLayout->resizeColumnsToContents();
tableLayout->pushRowsUp(lsCapability+1);
s1.report("end");
}
LogicalSwitchesPanel::~LogicalSwitchesPanel()
{
}
void LogicalSwitchesPanel::updateDataModels()
{
const bool oldLock = lock;
lock = true;
rawSwitchItemModel->update();
rawSourceItemModel->update();
lock = oldLock;
}
void LogicalSwitchesPanel::onFunctionChanged()
{
int i = sender()->property("index").toInt();
unsigned newFunc = cbFunction[i]->currentData().toUInt();
if (model->logicalSw[i].func == newFunc)
return;
const unsigned oldFunc = model->logicalSw[i].func;
CSFunctionFamily oldFuncFamily = model->logicalSw[i].getFunctionFamily();
model->logicalSw[i].func = newFunc;
CSFunctionFamily newFuncFamily = model->logicalSw[i].getFunctionFamily();
if (oldFuncFamily != newFuncFamily) {
model->logicalSw[i].clear();
model->logicalSw[i].func = newFunc;
if (newFuncFamily == LS_FAMILY_TIMER) {
model->logicalSw[i].val1 = -119;
model->logicalSw[i].val2 = -119;
}
else if (newFuncFamily == LS_FAMILY_EDGE) {
model->logicalSw[i].val2 = -129;
}
}
if (bool(oldFunc) != bool(newFunc))
update();
else
updateLine(i);
emit modified();
}
void LogicalSwitchesPanel::onV1Changed(int value)
{
if (!lock) {
int i = sender()->property("index").toInt();
model->logicalSw[i].val1 = cbSource1[i]->itemData(value).toInt();
if (model->logicalSw[i].getFunctionFamily() == LS_FAMILY_VOFS) {
if (!offsetChangedAt(i))
updateLine(i);
}
else {
emit modified();
}
}
}
void LogicalSwitchesPanel::onV2Changed(int value)
{
if (!lock) {
int i = sender()->property("index").toInt();
model->logicalSw[i].val2 = cbSource2[i]->itemData(value).toInt();
emit modified();
}
}
void LogicalSwitchesPanel::onAndSwitchChanged(int value)
{
if (!lock) {
int index = sender()->property("index").toInt();
model->logicalSw[index].andsw = cbAndSwitch[index]->itemData(value).toInt();
emit modified();
}
}
void LogicalSwitchesPanel::onDurationChanged(double duration)
{
if (!lock) {
int index = sender()->property("index").toInt();
model->logicalSw[index].duration = (uint8_t)round(duration*10);
emit modified();
}
}
void LogicalSwitchesPanel::onDelayChanged(double delay)
{
if (!lock) {
int index = sender()->property("index").toInt();
model->logicalSw[index].delay = (uint8_t)round(delay*10);
emit modified();
}
}
void LogicalSwitchesPanel::onOffsetChanged()
{
offsetChangedAt(sender()->property("index").toInt());
}
bool LogicalSwitchesPanel::offsetChangedAt(int index)
{
if (lock)
return false;
lock = true;
bool mod = false;
int value;
switch (model->logicalSw[index].getFunctionFamily())
{
case LS_FAMILY_VOFS:
{
RawSource source = RawSource(model->logicalSw[index].val1);
RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[index].getRangeFlags());
double currVal = source.isTimeBased() ? teOffset[index]->timeInSeconds() : dsbOffset[index]->value();
value = round((currVal - range.offset) / range.step);
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
break;
}
case LS_FAMILY_TIMER:
value = TimToVal(dsbValue[index]->value());
mod = (mod || value != model->logicalSw[index].val1);
model->logicalSw[index].val1 = value;
value = TimToVal(dsbOffset[index]->value());
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
break;
case LS_FAMILY_EDGE:
if (sender() == dsbOffset[index]) {
value = TimToVal(dsbOffset[index]->value());
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
}
else {
value = TimToVal(dsbOffset2[index]->value()) - model->logicalSw[index].val2;
mod = (mod || value != model->logicalSw[index].val3);
model->logicalSw[index].val3 = value;
}
break;
default:
break;
}
lock = false;
if (mod) {
updateLine(index);
emit modified();
}
return mod;
}
void LogicalSwitchesPanel::updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum)
{
sb->setVisible(true);
sb->setDecimals(1);
sb->setMinimum(minimum);
sb->setMaximum(175);
float value = ValToTim(timer);
if (value>=60)
sb->setSingleStep(1);
else if (value>=2)
sb->setSingleStep(0.5);
else
sb->setSingleStep(0.1);
sb->setValue(value);
}
#define SOURCE1_VISIBLE 0x1
#define SOURCE2_VISIBLE 0x2
#define VALUE1_VISIBLE 0x4
#define VALUE2_VISIBLE 0x8
#define VALUE3_VISIBLE 0x10
#define VALUE_TO_VISIBLE 0x20
#define DELAY_ENABLED 0x40
#define DURATION_ENABLED 0x80
#define LINE_ENABLED 0x100
void LogicalSwitchesPanel::updateLine(int i)
{
lock = true;
unsigned int mask;
cbFunction[i]->setCurrentIndex(cbFunction[i]->findData(model->logicalSw[i].func));
cbAndSwitch[i]->setCurrentIndex(cbAndSwitch[i]->findData(RawSwitch(model->logicalSw[i].andsw).toValue()));
if (!model->logicalSw[i].func) {
mask = 0;
}
else {
mask = LINE_ENABLED | DELAY_ENABLED | DURATION_ENABLED;
switch (model->logicalSw[i].getFunctionFamily())
{
case LS_FAMILY_VOFS:
{
mask |= SOURCE1_VISIBLE;
RawSource source = RawSource(model->logicalSw[i].val1);
RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[i].getRangeFlags());
double value = range.step * model->logicalSw[i].val2 + range.offset; /* TODO+source.getRawOffset(model)*/
cbSource1[i]->setModel(rawSourceItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(source.toValue()));
if (source.isTimeBased()) {
mask |= VALUE_TO_VISIBLE;
teOffset[i]->setTimeRange(range.min, range.max);
teOffset[i]->setSingleStep(range.step);
teOffset[i]->setPageStep(range.step * 60);
teOffset[i]->setShowSeconds(range.step != 60);
teOffset[i]->setTime((int)value);
}
else {
mask |= VALUE2_VISIBLE;
if (range.unit.isEmpty())
dsbOffset[i]->setSuffix("");
else
dsbOffset[i]->setSuffix(" " + range.unit);
dsbOffset[i]->setDecimals(range.decimals);
dsbOffset[i]->setMinimum(range.min);
dsbOffset[i]->setMaximum(range.max);
dsbOffset[i]->setSingleStep(range.step);
dsbOffset[i]->setValue(value);
}
break;
}
case LS_FAMILY_STICKY: // no break
case LS_FAMILY_VBOOL:
mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE;
cbSource1[i]->setModel(rawSwitchItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
cbSource2[i]->setModel(rawSwitchItemModel);
cbSource2[i]->setCurrentIndex(cbSource2[i]->findData(model->logicalSw[i].val2));
break;
case LS_FAMILY_EDGE:
mask |= SOURCE1_VISIBLE | VALUE2_VISIBLE | VALUE3_VISIBLE;
mask &= ~DELAY_ENABLED;
cbSource1[i]->setModel(rawSwitchItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
updateTimerParam(dsbOffset[i], model->logicalSw[i].val2, 0.0);
updateTimerParam(dsbOffset2[i], model->logicalSw[i].val2+model->logicalSw[i].val3, ValToTim(TimToVal(dsbOffset[i]->value())-1));
dsbOffset2[i]->setSuffix((model->logicalSw[i].val3) ? "" : tr(" (infinite)"));
break;
case LS_FAMILY_VCOMP:
mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE;
cbSource1[i]->setModel(rawSourceItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
cbSource2[i]->setModel(rawSourceItemModel);
cbSource2[i]->setCurrentIndex(cbSource2[i]->findData(model->logicalSw[i].val2));
break;
case LS_FAMILY_TIMER:
mask |= VALUE1_VISIBLE | VALUE2_VISIBLE;
updateTimerParam(dsbValue[i], model->logicalSw[i].val1, 0.1);
updateTimerParam(dsbOffset[i], model->logicalSw[i].val2, 0.1);
break;
}
}
cbSource1[i]->setVisible(mask & SOURCE1_VISIBLE);
cbSource2[i]->setVisible(mask & SOURCE2_VISIBLE);
dsbValue[i]->setVisible(mask & VALUE1_VISIBLE);
dsbOffset[i]->setVisible(mask & VALUE2_VISIBLE);
dsbOffset2[i]->setVisible(mask & VALUE3_VISIBLE);
teOffset[i]->setVisible(mask & VALUE_TO_VISIBLE);
cbAndSwitch[i]->setVisible(mask & LINE_ENABLED);
if (lsCapabilityExt) {
dsbDuration[i]->setVisible(mask & DURATION_ENABLED);
dsbDelay[i]->setVisible(mask & DELAY_ENABLED);
if (mask & DURATION_ENABLED)
dsbDuration[i]->setValue(model->logicalSw[i].duration/10.0);
if (mask & DELAY_ENABLED)
dsbDelay[i]->setValue(model->logicalSw[i].delay/10.0);
}
lock = false;
}
void LogicalSwitchesPanel::populateFunctionCB(QComboBox *b)
{
int order[] = {
LS_FN_OFF,
LS_FN_VEQUAL, // added at the end to avoid everything renumbered
LS_FN_VALMOSTEQUAL, // added at the end to avoid everything renumbered
LS_FN_VPOS,
LS_FN_VNEG,
// LS_FN_RANGE,
LS_FN_APOS,
LS_FN_ANEG,
LS_FN_AND,
LS_FN_OR,
LS_FN_XOR,
LS_FN_EDGE,
LS_FN_EQUAL,
LS_FN_NEQUAL,
LS_FN_GREATER,
LS_FN_LESS,
LS_FN_EGREATER,
LS_FN_ELESS,
LS_FN_DPOS,
LS_FN_DAPOS,
LS_FN_TIMER,
LS_FN_STICKY
};
b->clear();
for (int i=0; i<LS_FN_MAX; i++) {
int func = order[i];
if (func == LS_FN_NEQUAL || func == LS_FN_EGREATER || func == LS_FN_ELESS)
continue;
b->addItem(LogicalSwitchData(func).funcToString(), func);
}
b->setMaxVisibleItems(10);
}
void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b)
{
b->setModel(rawSwitchItemModel);
b->setVisible(true);
}
void LogicalSwitchesPanel::update()
{
updateDataModels();
for (int i=0; i<lsCapability; i++) {
updateLine(i);
}
}
void LogicalSwitchesPanel::cmPaste()
{
QByteArray data;
if (hasClipboardData(&data)) {
memcpy(&model->logicalSw[selectedIndex], data.constData(), sizeof(LogicalSwitchData));
updateDataModels();
updateLine(selectedIndex);
emit modified();
}
}
void LogicalSwitchesPanel::cmDelete()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
memmove(&model->logicalSw[selectedIndex], &model->logicalSw[selectedIndex + 1], (CPN_MAX_LOGICAL_SWITCHES - (selectedIndex + 1)) * sizeof(LogicalSwitchData));
model->logicalSw[lsCapability - 1].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1);
update();
emit modified();
}
void LogicalSwitchesPanel::cmCopy()
{
QByteArray data;
data.append((char*)&model->logicalSw[selectedIndex], sizeof(LogicalSwitchData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_LOGICAL_SWITCH, data);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void LogicalSwitchesPanel::cmCut()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
cmCopy();
cmClear(false);
}
// TODO make something generic here!
void LogicalSwitchesPanel::onCustomContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(cmInsert()))->setEnabled(insertAllowed());
contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(cmDelete()));
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(cmMoveUp()))->setEnabled(moveUpAllowed());
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(cmMoveDown()))->setEnabled(moveDownAllowed());
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll()));
contextMenu.exec(globalPos);
}
bool LogicalSwitchesPanel::hasClipboardData(QByteArray * data) const
{
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_LOGICAL_SWITCH)) {
if (data)
data->append(mimeData->data(MIMETYPE_LOGICAL_SWITCH));
return true;
}
return false;
}
bool LogicalSwitchesPanel::insertAllowed() const
{
return ((selectedIndex < lsCapability - 1) && (model->logicalSw[lsCapability - 1].isEmpty()));
}
bool LogicalSwitchesPanel::moveDownAllowed() const
{
return selectedIndex < lsCapability - 1;
}
bool LogicalSwitchesPanel::moveUpAllowed() const
{
return selectedIndex > 0;
}
void LogicalSwitchesPanel::cmMoveUp()
{
swapData(selectedIndex, selectedIndex - 1);
}
void LogicalSwitchesPanel::cmMoveDown()
{
swapData(selectedIndex, selectedIndex + 1);
}
void LogicalSwitchesPanel::cmClear(bool prompt)
{
if (prompt) {
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
}
model->logicalSw[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_CLEAR, selectedIndex);
update();
emit modified();
}
void LogicalSwitchesPanel::cmClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Logical Switches. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i=0; i<lsCapability; i++) {
model->logicalSw[i].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_CLEAR, i);
}
update();
emit modified();
}
void LogicalSwitchesPanel::cmInsert()
{
memmove(&model->logicalSw[selectedIndex + 1], &model->logicalSw[selectedIndex], (CPN_MAX_LOGICAL_SWITCHES - (selectedIndex + 1)) * sizeof(LogicalSwitchData));
model->logicalSw[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1);
update();
emit modified();
}
void LogicalSwitchesPanel::swapData(int idx1, int idx2)
{
if ((idx1 != idx2) && (!model->logicalSw[idx1].isEmpty() || !model->logicalSw[idx2].isEmpty())) {
LogicalSwitchData lstmp = model->logicalSw[idx2];
LogicalSwitchData *lsw1 = &model->logicalSw[idx1];
LogicalSwitchData *lsw2 = &model->logicalSw[idx2];
memcpy(lsw2, lsw1, sizeof(LogicalSwitchData));
memcpy(lsw1, &lstmp, sizeof(LogicalSwitchData));
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SWAP, idx1, idx2);
update();
emit modified();
}
}