#include "dbcsignaleditor.h" #include "ui_dbcsignaleditor.h" #include #include #include #include #include #include #include #include "helpwindow.h" DBCSignalEditor::DBCSignalEditor(QWidget *parent) : QDialog(parent), ui(new Ui::DBCSignalEditor) { ui->setupUi(this); readSettings(); dbcHandler = DBCHandler::getReference(); dbcMessage = nullptr; currentSignal = nullptr; inhibitMsgProc = false; QStringList headers2; headers2 << "Value" << "Text"; ui->valuesTable->setColumnCount(2); ui->valuesTable->setColumnWidth(0, 200); ui->valuesTable->setColumnWidth(1, 440); ui->valuesTable->setHorizontalHeaderLabels(headers2); ui->valuesTable->horizontalHeader()->setStretchLastSection(true); ui->comboType->addItem("UNSIGNED INTEGER"); ui->comboType->addItem("SIGNED INTEGER"); ui->comboType->addItem("SINGLE PRECISION"); ui->comboType->addItem("DOUBLE PRECISION"); ui->comboType->addItem("STRING"); ui->bitfield->setMode(GridMode::SIGNAL_VIEW); connect(ui->bitfield, SIGNAL(gridClicked(int)), this, SLOT(bitfieldLeftClicked(int))); connect(ui->bitfield, SIGNAL(gridRightClicked(int)), this, SLOT(bitfieldRightClicked(int))); connect(ui->valuesTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomMenuValues(QPoint))); ui->valuesTable->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->valuesTable, SIGNAL(cellChanged(int,int)), this, SLOT(onValuesCellChanged(int,int))); //now with 100% more lambda expressions just to make it interesting (and shorter, and easier...) connect(ui->cbIntelFormat, &QCheckBox::toggled, [=]() { if (currentSignal == nullptr) return; if (currentSignal->intelByteOrder != ui->cbIntelFormat->isChecked()) { dbcFile->setDirtyFlag(); pushToUndoBuffer(); currentSignal->intelByteOrder = ui->cbIntelFormat->isChecked(); //fillSignalForm(currentSignal); refreshBitGrid(); } }); connect(ui->comboReceiver, &QComboBox::currentTextChanged, [=]() { if (currentSignal == nullptr) return; if (inhibitMsgProc) return; DBC_NODE *node = dbcFile->findNodeByName(ui->comboReceiver->currentText()); if (currentSignal->receiver != node) { dbcFile->setDirtyFlag(); pushToUndoBuffer(); currentSignal->receiver = node; } }); connect(ui->comboType, &QComboBox::currentTextChanged, [=]() { if (currentSignal == nullptr) return; switch (ui->comboType->currentIndex()) { case 0: if (currentSignal->valType != UNSIGNED_INT) { pushToUndoBuffer(); currentSignal->valType = UNSIGNED_INT; dbcFile->setDirtyFlag(); fillSignalForm(currentSignal); } break; case 1: if (currentSignal->valType != SIGNED_INT) { pushToUndoBuffer(); currentSignal->valType = SIGNED_INT; dbcFile->setDirtyFlag(); fillSignalForm(currentSignal); } break; case 2: if (currentSignal->valType != SP_FLOAT) { pushToUndoBuffer(); currentSignal->valType = SP_FLOAT; dbcFile->setDirtyFlag(); if (dbcMessage) //if we have a good msg reference we can use it to get the # of bytes expected. { int maxBit = ((dbcMessage->len * 8) - 32 + 7); if (maxBit < 0) maxBit = 0; if (currentSignal->startBit > maxBit) currentSignal->startBit = maxBit; } else if (currentSignal->startBit > 39) currentSignal->startBit = 39; currentSignal->signalSize = 32; fillSignalForm(currentSignal); } break; case 3: if (currentSignal->valType != DP_FLOAT) { pushToUndoBuffer(); currentSignal->valType = DP_FLOAT; dbcFile->setDirtyFlag(); if (dbcMessage) { int maxBit = ((dbcMessage->len * 8) - 64 + 7); if (currentSignal->startBit > maxBit) currentSignal->startBit = maxBit; } else currentSignal->startBit = 7; //has to be! currentSignal->signalSize = 64; fillSignalForm(currentSignal); } break; case 4: if (currentSignal->valType != STRING) { pushToUndoBuffer(); currentSignal->valType = STRING; dbcFile->setDirtyFlag(); fillSignalForm(currentSignal); } break; } }); connect(ui->txtBias, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; double temp; bool result; temp = ui->txtBias->text().toDouble(&result); if (result) { if (currentSignal->bias != temp) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->bias = temp; } } }); connect(ui->txtMaxVal, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; double temp; bool result; temp = ui->txtMaxVal->text().toDouble(&result); if (result) { if (currentSignal->max != temp) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->max = temp; } } }); connect(ui->txtMinVal, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; double temp; bool result; temp = ui->txtMinVal->text().toDouble(&result); if (result) { if (currentSignal->min != temp) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->min = temp; } } }); connect(ui->txtScale, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; double temp; bool result; temp = ui->txtScale->text().toDouble(&result); if (result) { if (currentSignal->factor != temp) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->factor = temp; } } }); connect(ui->txtComment, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; if (currentSignal->comment != ui->txtComment->text().simplified().replace(' ','_')) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->comment = ui->txtComment->text().simplified().replace(' ', '_'); emit updatedTreeInfo(currentSignal); } }); connect(ui->txtUnitName, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; if (currentSignal->unitName != ui->txtUnitName->text().simplified().replace(' ','_')) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->unitName = ui->txtUnitName->text().simplified().replace(' ', '_'); } }); connect(ui->txtBitLength, &QLineEdit::textChanged, [=]() { if (currentSignal == nullptr) return; int temp; temp = Utility::ParseStringToNum(ui->txtBitLength->text()); if (temp < 1) return; if (dbcMessage) { if (temp > (int)(dbcMessage->len * 8)) return; } else if (temp > 64) return; if (currentSignal->valType == SP_FLOAT) temp = 32; if (currentSignal->valType == DP_FLOAT) temp = 64; if (currentSignal->signalSize != temp) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->signalSize = temp; //fillSignalForm(currentSignal); refreshBitGrid(); } }); connect(ui->txtName, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; QString tempNameStr = ui->txtName->text().simplified().replace(' ', '_'); if (tempNameStr.length() == 0) return; //can't do that! if (currentSignal->name != tempNameStr) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->name = tempNameStr; refreshBitGrid(); //need to update the tree too. emit updatedTreeInfo(currentSignal); } }); connect(ui->txtMultiplexValues, &QLineEdit::editingFinished, [=]() { if (currentSignal == nullptr) return; int temp; if (currentSignal->multiplexDbcString(DBC_SIGNAL::MuxStringFormat_UI) != ui->txtMultiplexValues->text()) { //TODO: could look up the multiplexor and ensure that the value is within a range that the multiplexor could return QString errorString; if (!currentSignal->parseDbcMultiplexUiString(ui->txtMultiplexValues->text(), errorString)) { QMessageBox::critical(this, tr("Error"), tr("The multiplex values field contains errors:\n%1").arg(errorString)); } else { pushToUndoBuffer(); dbcFile->setDirtyFlag(); } } }); connect(ui->rbExtended, &QRadioButton::toggled, [=](bool state) { if (!currentSignal) return; if (!state) return; //we only need to handle the case where it is true //only do anything if this is different from the current state. It should be because we're in a toggle event but let's be sure if (!currentSignal->isMultiplexed || !currentSignal->isMultiplexor) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->isMultiplexed = true; currentSignal->isMultiplexor = true; //an extended multi signal cannot be the root multiplexor for a message so make sure to remove it if it was. if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr; ui->txtMultiplexValues->setEnabled(currentSignal->isMultiplexed); ui->cbMultiplexParent->setEnabled(currentSignal->isMultiplexed); fillSignalForm(currentSignal); } }); connect(ui->rbMultiplexed, &QRadioButton::toggled, [=](bool state) { if (!currentSignal) return; if (!state) return; //we only need to handle the case where it is true //only do anything if this is different from the current state. It should be because we're in a toggle event but let's be sure if (!currentSignal->isMultiplexed || currentSignal->isMultiplexor) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->isMultiplexed = true; currentSignal->isMultiplexor = false; //if the set multiplexor for the message was this signal then clear it if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr; ui->txtMultiplexValues->setEnabled(currentSignal->isMultiplexed); ui->cbMultiplexParent->setEnabled(currentSignal->isMultiplexed); fillSignalForm(currentSignal); } }); connect(ui->rbMultiplexor, &QRadioButton::toggled, [=](bool state) { if (!currentSignal) return; if (!state) return; //we only need to handle the case where it is true if (currentSignal->isMultiplexed || !currentSignal->isMultiplexor) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->isMultiplexed = false; currentSignal->isMultiplexor = true; //we just set that this is the multiplexor so update the message to show that as well. dbcMessage->multiplexorSignal = currentSignal; ui->txtMultiplexValues->setEnabled(currentSignal->isMultiplexed); ui->cbMultiplexParent->setEnabled(currentSignal->isMultiplexed); fillSignalForm(currentSignal); } }); connect(ui->rbNotMulti, &QRadioButton::toggled, [=](bool state) { if (!currentSignal) return; if (!state) return; //we only need to handle the case where it is true if (currentSignal->isMultiplexed || currentSignal->isMultiplexor) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); currentSignal->isMultiplexed = false; currentSignal->isMultiplexor = false; if (dbcMessage->multiplexorSignal == currentSignal) dbcMessage->multiplexorSignal = nullptr; ui->txtMultiplexValues->setEnabled(currentSignal->isMultiplexed); ui->cbMultiplexParent->setEnabled(currentSignal->isMultiplexed); fillSignalForm(currentSignal); } }); connect(ui->cbMultiplexParent, &QComboBox::textActivated, [=]() { if (currentSignal == nullptr) return; if (inhibitMsgProc) return; //qDebug() << "Curr text: :" << ui->cbMultiplexParent->currentText(); //try to look up the signal that we're set to now, remove this signal from existing children list //add it to this one, update this signal's parent multiplexor DBC_SIGNAL *newSig = dbcMessage->sigHandler->findSignalByName(ui->cbMultiplexParent->currentText()); DBC_SIGNAL *oldParent = currentSignal->multiplexParent; if (newSig && oldParent && (newSig != oldParent)) { pushToUndoBuffer(); dbcFile->setDirtyFlag(); oldParent->multiplexedChildren.removeOne(currentSignal); currentSignal->multiplexParent = newSig; newSig->multiplexedChildren.append(currentSignal); refreshBitGrid(); emit updatedTreeInfo(currentSignal); } }); installEventFilter(this); } DBCSignalEditor::~DBCSignalEditor() { removeEventFilter(this); delete ui; } void DBCSignalEditor::closeEvent(QCloseEvent *event) { Q_UNUSED(event); writeSettings(); } bool DBCSignalEditor::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyRelease) { QKeyEvent *keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_F1: HelpWindow::getRef()->showHelp("signaleditor.md"); break; case Qt::Key_Z: if (keyEvent->modifiers() == Qt::ControlModifier) { popFromUndoBuffer(); } break; } return true; } else { // standard event processing return QObject::eventFilter(obj, event); } return false; } void DBCSignalEditor::setFileIdx(int idx) { if (idx < 0 || idx > dbcHandler->getFileCount() - 1) return; dbcFile = dbcHandler->getFileByIdx(idx); for (int x = 0; x < dbcFile->dbc_nodes.count(); x++) { ui->comboReceiver->addItem(dbcFile->dbc_nodes[x].name); } } void DBCSignalEditor::readSettings() { QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { resize(settings.value("DBCSignalEditor/WindowSize", QSize(1000, 600)).toSize()); move(Utility::constrainedWindowPos(settings.value("DBCSignalEditor/WindowPos", QPoint(100, 100)).toPoint())); } } void DBCSignalEditor::writeSettings() { QSettings settings; if (settings.value("Main/SaveRestorePositions", false).toBool()) { settings.setValue("DBCSignalEditor/WindowSize", size()); settings.setValue("DBCSignalEditor/WindowPos", pos()); } } void DBCSignalEditor::setMessageRef(DBC_MESSAGE *msg) { dbcMessage = msg; } void DBCSignalEditor::setSignalRef(DBC_SIGNAL *sig) { currentSignal = sig; } void DBCSignalEditor::showEvent(QShowEvent* event) { QDialog::showEvent(event); fillSignalForm(currentSignal); fillValueTable(currentSignal); } void DBCSignalEditor::refreshView() { fillSignalForm(currentSignal); fillValueTable(currentSignal); } void DBCSignalEditor::onValuesCellChanged(int row,int col) { if (inhibitCellChanged) return; if (row == ui->valuesTable->rowCount() - 1) { DBC_VAL_ENUM_ENTRY newVal; newVal.value = 0; newVal.descript = "No Description"; currentSignal->valList.append(newVal); qDebug() << "Created new entry in value list"; //QTableWidgetItem *widgetVal = new QTableWidgetItem(QString::number(newVal.value)); //ui->valuesTable->setItem(row, 0, widgetVal); //QTableWidgetItem *widgetDesc = new QTableWidgetItem(newVal.descript); //ui->valuesTable->setItem(row, 1, widgetDesc); //add the blank at the end again ui->valuesTable->insertRow(ui->valuesTable->rowCount()); } if (col == 0) { currentSignal->valList[row].value = Utility::ParseStringToNum(ui->valuesTable->item(row, col)->text()); } else if (col == 1) { currentSignal->valList[row].descript = ui->valuesTable->item(row, col)->text().simplified().replace(' ', '_'); } } void DBCSignalEditor::onCustomMenuValues(QPoint point) { QMenu *menu = new QMenu(this); menu->setAttribute(Qt::WA_DeleteOnClose); menu->addAction(tr("Delete currently selected value"), this, SLOT(deleteCurrentValue())); menu->popup(ui->valuesTable->mapToGlobal(point)); } void DBCSignalEditor::deleteCurrentValue() { int currIdx = ui->valuesTable->currentRow(); if (currIdx > -1) { ui->valuesTable->removeRow(currIdx); currentSignal->valList.removeAt(currIdx); } } /* fillSignalForm also handles group "enabled" state */ /* WARNING: fillSignalForm can be called recursively since it is in the listener of cbIntelFormat */ void DBCSignalEditor::fillSignalForm(DBC_SIGNAL *sig) { //sanity checks if (!dbcMessage) return; if (!dbcMessage->sigHandler) return; inhibitMsgProc = true; if (sig == nullptr) { //ui->groupBox->setEnabled(false); ui->txtName->setText(""); ui->txtBias->setText(""); ui->txtBitLength->setText(""); ui->txtComment->setText(""); ui->txtMaxVal->setText(""); ui->txtMinVal->setText(""); ui->txtScale->setText(""); ui->txtUnitName->setText(""); ui->txtMultiplexValues->setText(""); ui->rbMultiplexed->setChecked(false); ui->rbMultiplexor->setChecked(false); ui->rbNotMulti->setChecked(true); ui->comboReceiver->setCurrentIndex(0); ui->comboType->setCurrentIndex(0); inhibitMsgProc = false; return; } /* we have a signal */ //ui->groupBox->setEnabled(true); ui->txtName->setText(sig->name); ui->txtBias->setText(QString::number(sig->bias)); ui->txtBitLength->setText(QString::number(sig->signalSize)); ui->txtMultiplexValues->setText(sig->multiplexDbcString(DBC_SIGNAL::MuxStringFormat_UI)); ui->txtComment->setText(sig->comment); ui->txtMaxVal->setText(QString::number(sig->max)); ui->txtMinVal->setText(QString::number(sig->min)); ui->txtScale->setText(QString::number(sig->factor)); ui->txtUnitName->setText(sig->unitName); if (sig->isMultiplexed && sig->isMultiplexor) { ui->rbMultiplexed->setChecked(false); ui->rbMultiplexor->setChecked(false); ui->rbNotMulti->setChecked(false); ui->rbExtended->setChecked(true); } else { ui->rbMultiplexed->setChecked(sig->isMultiplexed); ui->rbMultiplexor->setChecked(sig->isMultiplexor); ui->rbNotMulti->setChecked( !(sig->isMultiplexor | sig->isMultiplexed) ); } qDebug() << sig->isMultiplexor << "*" << sig->isMultiplexed; ui->cbMultiplexParent->clear(); int numSigs = dbcMessage->sigHandler->getCount(); for (int i = 0; i < numSigs; i++) { DBC_SIGNAL *sig_iter = dbcMessage->sigHandler->findSignalByIdx(i); if (sig_iter && sig_iter->isMultiplexor && (sig_iter != sig)) { //only add this entry if there are no other entries with that name yet if (ui->cbMultiplexParent->findText(sig_iter->name) == -1) ui->cbMultiplexParent->addItem(sig_iter->name); if (sig->multiplexParent == sig_iter) ui->cbMultiplexParent->setCurrentIndex(ui->cbMultiplexParent->count() - 1); } } ui->txtMultiplexValues->setEnabled(sig->isMultiplexed); ui->cbMultiplexParent->setEnabled(sig->isMultiplexed); ui->cbIntelFormat->setChecked(sig->intelByteOrder); switch (sig->valType) { case UNSIGNED_INT: ui->comboType->setCurrentIndex(0); break; case SIGNED_INT: ui->comboType->setCurrentIndex(1); break; case SP_FLOAT: ui->comboType->setCurrentIndex(2); break; case DP_FLOAT: ui->comboType->setCurrentIndex(3); break; case STRING: ui->comboType->setCurrentIndex(4); break; } for (int i = 0; i < ui->comboReceiver->count(); i++) { if (ui->comboReceiver->itemText(i) == sig->receiver->name) { ui->comboReceiver->setCurrentIndex(i); break; } } refreshBitGrid(); inhibitMsgProc = false; } void DBCSignalEditor::refreshBitGrid() { unsigned char bitpattern[64]; memset(bitpattern, 0, 64); //clear it out ui->bitfield->setReference(bitpattern, false); ui->bitfield->updateData(bitpattern, true); ui->bitfield->clearSignalNames(); for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++) { DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x); //only set a signal name for signals which match multiplexparent with our currentsignal if (!sig->multiplexParent || ((sig->multiplexParent == currentSignal->multiplexParent) && (sig->multiplexesIdenticalToSignal(currentSignal)) )) { ui->bitfield->setSignalNames(x, sig->name); //qDebug() << sig->name << sig->multiplexParent; } } generateUsedBits(); memset(bitpattern, 0, 64); //clear it out first. int startBit, endBit; startBit = currentSignal->startBit; bitpattern[startBit / 8] |= 1 << (startBit % 8); //make the start bit a different color to set it apart ui->bitfield->setReference(bitpattern, false); if (currentSignal->intelByteOrder) { endBit = startBit + currentSignal->signalSize - 1; if (startBit < 0) startBit = 0; if (endBit > 511) endBit = 511; for (int y = startBit; y <= endBit; y++) { int byt = y / 8; bitpattern[byt] |= 1 << (y % 8); } } else //big endian / motorola format { //much more irritating than the intel version... int size = currentSignal->signalSize; while (size > 0) { int byt = startBit / 8; bitpattern[byt] |= 1 << (startBit % 8); size--; if ((startBit % 8) == 0) startBit += 15; else startBit--; if (startBit > 511) startBit = 511; } } ui->bitfield->updateData(bitpattern, true); } /* fillValueTable also handles "enabled" state */ void DBCSignalEditor::fillValueTable(DBC_SIGNAL *sig) { int rowIdx; inhibitCellChanged = true; ui->valuesTable->clearContents(); ui->valuesTable->setRowCount(0); if (sig == nullptr) { ui->valuesTable->setEnabled(false); inhibitCellChanged = false; return; } ui->valuesTable->setEnabled(true); for (int i = 0; i < sig->valList.count(); i++) { QTableWidgetItem *val = new QTableWidgetItem(Utility::formatNumber((uint64_t)sig->valList[i].value)); QTableWidgetItem *desc = new QTableWidgetItem(sig->valList[i].descript); rowIdx = ui->valuesTable->rowCount(); ui->valuesTable->insertRow(rowIdx); ui->valuesTable->setItem(rowIdx, 0, val); ui->valuesTable->setItem(rowIdx, 1, desc); } //Add a blank row at the end to allow for adding records easily ui->valuesTable->insertRow(ui->valuesTable->rowCount()); inhibitCellChanged = false; } //Left clicking the grid sets the "starting bit" for the current signal void DBCSignalEditor::bitfieldLeftClicked(int bit) { if (currentSignal == nullptr) return; pushToUndoBuffer(); currentSignal->startBit = bit; if (currentSignal->valType == SP_FLOAT) { if (dbcMessage) { int maxBit = ((dbcMessage->len * 8) - 32 + 7); if (maxBit < 0) maxBit = 0; if (currentSignal->startBit > maxBit) currentSignal->startBit = maxBit; } else if (currentSignal->startBit > 31) currentSignal->startBit = 39; } if (currentSignal->valType == DP_FLOAT) { if (dbcMessage) { int maxBit = ((dbcMessage->len * 8) - 64 + 7); if (maxBit < 0) maxBit = 0; if (currentSignal->startBit > maxBit) currentSignal->startBit = maxBit; } else currentSignal->startBit = 7; } fillSignalForm(currentSignal); } //Right clicking the grid starts editing on whichever signal currently "owns" that bit. //If there is no other signal then nothing happens (right now). //Would be possible to create a new signal in that case void DBCSignalEditor::bitfieldRightClicked(int bit) { //will return -1 if there is no signal there. Otherwise, returns signal number //which is quite luckily also the index into the signal handler table int sigNum = ui->bitfield->getUsedSignalNum(bit); if (sigNum < 0) return; pushToUndoBuffer(); // undo to resume editing the previous signal currentSignal = dbcMessage->sigHandler->findSignalByIdx(sigNum); if (currentSignal) { fillSignalForm(currentSignal); fillValueTable(currentSignal); } } void DBCSignalEditor::generateUsedBits() { uint8_t usedBits[64]; int startBit, endBit; memset(usedBits, 0, 64); if (!dbcMessage || !dbcMessage->sigHandler) return; for (int x = 0; x < dbcMessage->sigHandler->getCount(); x++) { DBC_SIGNAL *sig = dbcMessage->sigHandler->findSignalByIdx(x); //only pay attention to this signal if it's multiplexParent matches currentSignal or is null if (sig->multiplexParent) { if (sig->multiplexParent != currentSignal->multiplexParent) continue; //go thee away! if (!sig->multiplexesIdenticalToSignal(currentSignal)) continue; //buzz off } startBit = sig->startBit; if (sig->intelByteOrder) { endBit = startBit + sig->signalSize - 1; if (startBit < 0) startBit = 0; int maxBit = (dbcMessage->len * 8) - 1; if (endBit > maxBit) endBit = maxBit; for (int y = startBit; y <= endBit; y++) { int byt = y / 8; usedBits[byt] |= 1 << (y % 8); ui->bitfield->setUsedSignalNum(y, x); } } else //big endian / motorola format { //much more irritating than the intel version... int size = sig->signalSize; while (size > 0) { int byt = startBit / 8; usedBits[byt] |= 1 << (startBit % 8); ui->bitfield->setUsedSignalNum(startBit, x); size--; if ((startBit % 8) == 0) startBit += 15; else startBit--; int maxBit = (dbcMessage->len * 8) - 1; if (startBit > maxBit) startBit = maxBit; } } } ui->bitfield->setUsed(usedBits, false); ui->bitfield->setBytesToDraw(dbcMessage->len); } //Copy the current signal in its entirety to the undo buffer. Just for safe keeping //Called before an edit is done to save the state so we can revert if necessary void DBCSignalEditor::pushToUndoBuffer() { if (!currentSignal) return; //store a copy of the pointer so that if we need to pop we can pop to the proper place currentSignal->self = currentSignal; undoBuffer.append(*currentSignal); //save the whole thing qDebug() << "Pushing to undo buffer"; } //Pop the last copy of a signal from the stack and begin editing it void DBCSignalEditor::popFromUndoBuffer() { if (undoBuffer.empty()) { dbcFile->clearDirtyFlag(); //TODO: Don't do this. Implement per-item dirty flags. qDebug() << "Undo buffer empty"; return; //can't pop if there are no stored entries! } qDebug() << "Popping undo buffer"; DBC_SIGNAL sig = undoBuffer.back(); undoBuffer.pop_back(); currentSignal = sig.self; //restore the pointer *currentSignal = sig; //write the contents into the memory pointed to fillSignalForm(currentSignal); fillValueTable(currentSignal); }