#include #include "logcategories.h" #include "memories.h" #include "ui_memories.h" memories::memories(bool isAdmin, bool slowLoad, QWidget *parent) : QWidget(parent), slowLoad(slowLoad), ui(new Ui::memories) { ui->setupUi(this); ui->table->setColumnCount(totalColumns); ui->table->editing(false); statusBar = new QStatusBar(this); ui->verticalLayout->addWidget(statusBar); ui->csvImport->setEnabled(false); progress = new QProgressBar(this); statusBar->addWidget(progress,1); this->setObjectName("memories"); queue = cachingQueue::getInstance(); rigCaps = queue->getRigCaps(); if (rigCaps == nullptr) { qInfo() << "We have no rigCaps, so cannot continue"; return; } if (rigCaps->manufacturer == manufKenwood || rigCaps->manufacturer == manufYaesu) { if (rigCaps->commands.contains(funcMemoryWrite)) writeCommand = funcMemoryWrite; if (rigCaps->commands.contains(funcMemorySelect)) selectCommand = funcMemorySelect; } if (!isAdmin) { ui->disableEditing->setEnabled(false); } progress->setRange(rigCaps->memStart,rigCaps->memories); if (!rigCaps->commands.contains(funcScanning)) { ui->scanButton->setVisible(false); } else { ui->scanButton->setCheckable(true); } QStringList headers; /* columnRecall=0, columnNum,columnSplit,columnSkip,columnScan,columnFrequency,columnClarOffset, columnRXClar, columnTXClar columcolumnMode,columnFilter,columnData,columnDuplex, columnToneMode,columnTuningStep, columnCustomTuningStep, columnAttenuator, columnPreamplifier, columnAntenna, columnIPPlus, columnDSQL,columnTone,columnTSQL,columnDTCS,columnDTCSPolarity,columnDVSquelch,columnOffset,columnUR,columnR1,columnR2, columnP25Sql,columnP25Nac,columnDPmrSql,columnDPmrComid,columnDPmrCc,columnDPmrSCRM,columnDPmrKey,columnNxdnSql, columnNnxdnRan,columnNxdnEnc,columnNxdnKey,columnDcrSql,columnDcrUc,columnDcrEnc,columnDcrKey,columnFrequencyB, columnModeB,columnFilterB,columnDataB,columnDuplexB,columnToneModeB,columnDSQLB,columnToneB,columnTSQLB,columnDTCSB, columnDTCSPolarityB,columnDVSquelchB,columnOffsetB,columnURB,columnR1B,columnR2B,totalColumns, */ headers << "" << "Num" << "Name" << "Split" << "Skip" << "Scan" << "VFO" << "Freq" << "Clarifier" << "Clar RX" << "Clar TX" << "Mode" << "Filter" << "Data" <<"Duplex" << "Tn Mode" << "Step" << "Prog Step" << "Atten" << "Preamp" << "Ant" << "IP Plus" << "DSQL" << "Tone" << "TSQL" << "DTCS" << "DTCS Pol" << "DV Sql" << "Offset" << "UR" << "R1" << "R2" << "P25 SQL" << "P25 NAC" << "dPMR SQL" << "dPMR COM ID" << "dPMR CC" << "dPMR SCRM" << "dPMR Key" << "NXDN SQL" << "NXDN RAN" << "NXDN Enc" << "NXDN Key" << "DCR SQL" << "DCR UC" << "DCR Enc" << "DCR Key" << "VFO B" << "Freq B" << "Mode B" << "Filter B" << "Data B" << "Duplex B" << "Tn Mode B" << "DSQL B" << "Tone B" << "TSQL B" << "DTCS B" << "DTCSP B" << "DV Sql B" << "Offset B" << "UR B" << "R1 B" << "R2 B"; clar << "OFF" << "ON"; skip << "OFF" << "SKIP" << "PSKIP"; dataModes << "OFF" << "DATA1"; if (rigCaps->commands.contains(funcDATA2Mod)) dataModes.append("DATA2"); if (rigCaps->commands.contains(funcDATA3Mod)) dataModes.append("DATA3"); duplexModes << "OFF" << "DUP-" << "DUP+" << "RPS"; if (rigCaps->manufacturer == manufKenwood || rigCaps->manufacturer == manufYaesu) { scan << "VFO" << "MEM" << "TUNE" << "QMB" << "NONE" << "PMS"; if (rigCaps->commands.contains(funcMemoryWrite)) writeCommand = funcMemoryWrite; if (rigCaps->commands.contains(funcMemorySelect)) selectCommand = funcMemorySelect; if (rigCaps->manufacturer == manufYaesu) toneModes << "OFF" << "ENC/DEC" << "ENC"; else toneModes << "OFF" << "TONE" << "CTCSS"; filters << "FILTER A" << "FILTER B"; split << "SIMP" << "PLUS" << "MINUS"; } else { scan << "OFF" << "*1" << "*2" << "*3"; if (rigCaps->hasTransmit) toneModes << "OFF" << "TONE" << "TSQL"; else toneModes << "OFF" << "TSQL"; filters << "FIL1" << "FIL2" << "FIL3"; split << "OFF" << "ON"; } if (rigCaps->commands.contains(funcRepeaterDTCS)) { toneModes.append("DTCS"); // 03 toneModes.append(""); toneModes.append(""); toneModes.append("DTCS(T)"); toneModes.append("TONE(T)/DTCS(R)"); toneModes.append("DTCS(T)/TSQL(R)"); toneModes.append("TONE(T)/TSQL(R)"); } for (const auto&t : rigCaps->ctcss) { tones.append(t.name); } for (const auto&t : rigCaps->dtcs) { dtcs.append(t.name); } dsql << "OFF" << "DSQL" << "CSQL"; p25Sql << "OFF" << "NAC"; dPmrSql << "OFF" << "COM ID" << "CC"; dPmrSCRM << "OFF" << "ON"; nxdnSql << "OFF" << "ON"; nxdnEnc << "OFF" << "ON"; dcrSql << "OFF" << "UC"; dcrEnc << "OFF" << "ON"; if (rigCaps->hasTransmit) dtcsp << "BOTH N" << "N/R" << "R/N" << "BOTH R"; else dtcsp << "NORMAL" << "REVERSE"; ipplus << "OFF" << "ON"; // We can populate the other comboboxes from RigCaps (ensuring they are valid) tuningSteps << "None"; for (const auto &step: rigCaps->steps) { if (step.num) { tuningSteps.append(step.name); } } for (const auto &atten: rigCaps->attenuators) { attenuators.append(atten.name); } if (attenuators.isEmpty()) attenuators.append("None"); for (const auto &preamp: rigCaps->preamps) { preamps.append(preamp.name); } if (preamps.isEmpty()) preamps.append("None"); for (const auto &ant: rigCaps->antennas) { antennas.append(ant.name); } if (antennas.isEmpty()) antennas.append("None"); ui->table->setHorizontalHeaderLabels(headers); ui->group->hide(); //ui->modeButton->hide(); ui->modeButton->setCheckable(true); //ui->loadingMemories->setVisible(false); //ui->loadingMemories->setStyleSheet("QLabel {color: #ff0000}"); ui->group->blockSignals(true); ui->group->addItem("Memory Group",-1); // Set currentIndex to an invalid value to force an update. ui->group->setCurrentIndex(-1); for (int i=rigCaps->memStart;i<=rigCaps->memGroups;i++) { if (i == rigCaps->memStart) { // Disable title if any groups to stop it being selected. auto* model = qobject_cast(ui->group->model()); model->item(0)->setEnabled(false); } ui->group->addItem(QString("Group %0").arg(i,2,10,QChar('0')),i); } if (rigCaps->satMemories && rigCaps->commands.contains(funcSatelliteMode)){ ui->group->addItem("Satellite",MEMORY_SATGROUP); } ui->group->blockSignals(false); for (int i=0;i<100;i++) { dvsql.append(QString::number(i).rightJustified(2,'0')); } if (rigCaps->commands.contains(funcVFOEqualAB)) { vfos << "VFOA" << "VFOB"; } else if (rigCaps->commands.contains(funcVFOEqualMS)) { vfos << "MAIN" << "SUB"; } for (auto &mode: rigCaps->modes){ modes.append(mode.name); } if (modes.isEmpty()) modes.append("None"); connect(ui->table,SIGNAL(rowAdded(int)),this,SLOT(rowAdded(int))); connect(ui->table,SIGNAL(rowValDeleted(quint32)),this,SLOT(rowDeleted(quint32))); connect(ui->table,SIGNAL(menuAction(QAction*, quint32)),this,SLOT(menuAction(QAction*, quint32))); connect(&timeoutTimer, SIGNAL(timeout()), this, SLOT(timeout())); ui->table->sortByColumn(columnRecall,Qt::AscendingOrder); ui->table->addAction(useCurrent); ui->table->addAction(addCurrent); } void memories::menuAction(QAction* action, quint32 row) { // First we need to populate a memory struct: memoryType mem; vfoCommandType ta = queue->getVfoCommand(vfoA,0,false); vfoCommandType tb; if (rigCaps->numReceiver > 1) tb = queue->getVfoCommand(vfoA,1,false); else tb = queue->getVfoCommand(vfoB,0,false); mem.split = queue->getCache(funcSplitStatus).value.value(); mem.frequency = queue->getCache(ta.freqFunc,ta.receiver).value.value(); mem.mode = queue->getCache(ta.modeFunc,ta.receiver).value.value().reg; mem.datamode = queue->getCache(ta.modeFunc,ta.receiver).value.value().data; mem.filter = queue->getCache(ta.modeFunc,ta.receiver).value.value().filter; mem.tonemode = queue->getCache(funcRepeaterTone,ta.receiver).value.value(); mem.tone = queue->getCache(funcToneFreq,ta.receiver).value.value().name; mem.tsql = queue->getCache(funcTSQLFreq,ta.receiver).value.value().name; mem.dtcs = queue->getCache(funcDTCSCode,ta.receiver).value.value(); mem.dvsql = queue->getCache(funcCSQLCode,ta.receiver).value.value(); mem.frequencyB = queue->getCache(tb.freqFunc,tb.receiver).value.value(); mem.modeB = queue->getCache(tb.modeFunc,tb.receiver).value.value().reg; mem.datamodeB = queue->getCache(tb.modeFunc,tb.receiver).value.value().data; mem.filterB = queue->getCache(tb.modeFunc,tb.receiver).value.value().filter; mem.tonemodeB = queue->getCache(funcRepeaterTone,tb.receiver).value.value(); mem.toneB = queue->getCache(funcToneFreq,tb.receiver).value.value().name; mem.tsqlB = queue->getCache(funcTSQLFreq,tb.receiver).value.value().name; mem.dtcsB = queue->getCache(funcDTCSCode,tb.receiver).value.value(); mem.dvsqlB = queue->getCache(funcCSQLCode,tb.receiver).value.value(); memset(mem.UR, 0x0, sizeof(mem.UR)); memset(mem.URB, 0x0, sizeof(mem.URB)); memset(mem.R1, 0x0, sizeof(mem.R1)); memset(mem.R1B, 0x0, sizeof(mem.R1B)); memset(mem.R2, 0x0, sizeof(mem.R2)); memset(mem.R2B, 0x0, sizeof(mem.R2B)); if (action == useCurrent) { qInfo() << "Use Current" << row; memcpy(mem.name,"",10); updateRow(row,mem); } else if (action == addCurrent) { qInfo() << "Add Current" << row; memcpy(mem.name,"",6); row=ui->table->rowCount(); ui->table->insertRow(ui->table->rowCount()); rowAdded(row,mem); } else { qInfo() << "Unknown action" << row; } } void memories::enableCell(int row, int col, bool en) { if (!ui->table->isColumnHidden(col) && ui->table->item(row,col) != NULL) { if (en) ui->table->item(row,col)->setFlags(ui->table->item(row,col)->flags() | Qt::ItemIsEnabled); else ui->table->item(row,col)->setFlags(ui->table->item(row,col)->flags() & ~Qt::ItemIsEnabled); } } void memories::populate() { if (ui->group->count() > 1) { qInfo() << "Memory group items:" << ui->group->count(); ui->group->setCurrentIndex(1); } else { ui->group->setCurrentIndex(0); } } memories::~memories() { qInfo() << "Deleting memories table"; ui->table->clear(); delete ui; } int memories::updateRow(int row, memoryType mem, bool store) { ui->table->blockSignals(true); int validData=0; // We have 1 more row than will ever be received. validData += updateEntry(split,row,columnSplit,mem.split); validData += updateEntry(skip,row,columnSkip,mem.skip); validData += updateEntry(scan,row,columnScan,mem.scan); ui->table->model()->setData(ui->table->model()->index(row,columnFrequency),QString::number(double(mem.frequency.Hz/1000000.0),'f',5)); validData++; if (!rigCaps->commands.contains(funcSplitMemory)) { if (mem.frequencyB.Hz > 0) ui->table->model()->setData(ui->table->model()->index(row,columnFrequencyB),QString::number(double(mem.frequencyB.Hz/1000000.0),'f',5)); else ui->table->model()->setData(ui->table->model()->index(row,columnFrequencyB),QString::number(double(mem.frequency.Hz/1000000.0),'f',5)); } validData++; if (mem.clarifier > -10000 && mem.clarifier < 10000) { ui->table->model()->setData(ui->table->model()->index(row,columnClarOffset),QString("%1%2").arg(mem.clarifier < 0 ? '-' : '+').arg(abs(mem.clarifier),4,10,QChar('0'))); validData++; } validData += updateEntry(clar,row,columnRXClar,mem.clarRX); validData += updateEntry(clar,row,columnTXClar,mem.clarTX); // Make sure these are populated before mode so they can be enabled/disabled. validData += updateEntry(toneModes,row,columnToneMode,mem.tonemode); validData += updateEntry(toneModes,row,columnToneModeB,mem.tonemodeB); validData += updateEntry(dsql,row,columnDSQL,mem.dsql); validData += updateEntry(dsql,row,columnDSQLB,mem.dsqlB); validData += updateEntry(tones,row,columnTSQL,mem.tsql); validData += updateEntry(tones,row,columnTSQLB,mem.tsqlB); validData += updateEntry(dvsql,row,columnDVSquelch,QString::number(mem.dvsql).rightJustified(2,'0')); validData += updateEntry(dvsql,row,columnDVSquelchB,QString::number(mem.dvsqlB).rightJustified(2,'0')); validData += updateEntry(dtcs,row,columnDTCS,mem.dtcs); validData += updateEntry(dtcs,row,columnDTCSB,mem.dtcsB); validData += updateEntry(dtcsp,row,columnDTCSPolarity,mem.dtcsp); validData += updateEntry(dtcsp,row,columnDTCSPolarityB,mem.dtcspB); validData += updateEntry(dataModes,row,columnData,mem.datamode); validData += updateEntry(dataModes,row,columnDataB,mem.datamodeB); if (rigCaps->manufacturer == manufKenwood) { validData += updateEntry(filters,row,columnFilter,mem.filter); validData += updateEntry(filters,row,columnFilterB,mem.filterB); } else { validData += updateEntry(filters,row,columnFilter,mem.filter-1); validData += updateEntry(filters,row,columnFilterB,mem.filterB-1); } validData += updateEntry(duplexModes,row,columnDuplex,mem.duplex); validData += updateEntry(duplexModes,row,columnDuplexB,mem.duplexB); validData += updateEntry(tones,row,columnTone,mem.tone); validData += updateEntry(tones,row,columnToneB,mem.toneB); validData += updateEntry(tuningSteps,row,columnTuningStep,mem.tuningStep); ui->table->model()->setData(ui->table->model()->index(row,columnCustomTuningStep),QString::number(mem.progTs)); validData++; if (ui->table->item(row,columnTuningStep) != NULL && ui->table->item(row,columnTuningStep)->text() != "Prog") { ui->table->item(row,columnCustomTuningStep)->setFlags(ui->table->item(row,columnCustomTuningStep)->flags() & ~Qt::ItemIsEnabled); } validData += updateEntry(attenuators,row,columnAttenuator,mem.atten); validData += updateEntry(preamps,row,columnPreamplifier,mem.preamp); validData += updateEntry(antennas,row,columnAntenna,mem.antenna); validData += updateEntry(ipplus,row,columnIPPlus,mem.ipplus); ui->table->model()->setData(ui->table->model()->index(row,columnOffset),QString::number(double(mem.duplexOffset.Hz/10000.0),'f',3)); validData++; ui->table->model()->setData(ui->table->model()->index(row,columnOffsetB),QString::number(double(mem.duplexOffsetB.Hz/10000.0),'f',3)); validData++; if (checkASCII(mem.UR)) { ui->table->model()->setData(ui->table->model()->index(row,columnUR),QString(mem.UR)); validData++; } else qInfo() << "Invalid data in ur"; if (checkASCII(mem.URB)) { ui->table->model()->setData(ui->table->model()->index(row,columnURB),QString(mem.URB)); validData++; } else qInfo() << "Invalid data in urb"; if (checkASCII(mem.R1)) { ui->table->model()->setData(ui->table->model()->index(row,columnR1),QString(mem.R1)); validData++; } else qInfo() << "Invalid data in r1"; if (checkASCII(mem.R1B)) { ui->table->model()->setData(ui->table->model()->index(row,columnR1B),QString(mem.R1B)); validData++; } else qInfo() << "Invalid data in r1b"; if (checkASCII(mem.R2)) { ui->table->model()->setData(ui->table->model()->index(row,columnR2),QString(mem.R2)); validData++; } else qInfo() << "Invalid data in r2"; if (checkASCII(mem.R2B)) { ui->table->model()->setData(ui->table->model()->index(row,columnR2B),QString(mem.R2B)); validData++; } else qInfo() << "Invalid data in r2b"; if (!rigCaps->commands.contains(funcMemoryTag) && !rigCaps->commands.contains(funcMemoryTagB)) { if (checkASCII(mem.name)) { ui->table->model()->setData(ui->table->model()->index(row,columnName),QString(mem.name)); validData++; } else { qInfo() << "Invalid data in name"; } } if (!rigCaps->commands.contains(funcSplitMemory)) { if (mem.frequencyB.Hz > 0) ui->table->model()->setData(ui->table->model()->index(row,columnFrequencyB),QString::number(double(mem.frequencyB.Hz/1000000.0),'f',5)); else ui->table->model()->setData(ui->table->model()->index(row,columnFrequencyB),QString::number(double(mem.frequency.Hz/1000000.0),'f',5)); } // These are optional so don't update validdata updateEntry(p25Sql,row,columnP25Sql,mem.p25Sql); ui->table->model()->setData(ui->table->model()->index(row,columnP25Nac),QString::number(mem.p25Nac,16).rightJustified(3,'0')); updateEntry(dPmrSql,row,columnDPmrSql,mem.dPmrSql); ui->table->model()->setData(ui->table->model()->index(row,columnDPmrComid),QString::number(mem.dPmrComid).rightJustified(3,'0')); ui->table->model()->setData(ui->table->model()->index(row,columnDPmrCc),QString::number(mem.dPmrCc).rightJustified(2,'0')); updateEntry(dPmrSCRM,row,columnDPmrSCRM,mem.dPmrSCRM); ui->table->model()->setData(ui->table->model()->index(row,columnDPmrKey),QString::number(mem.dPmrKey).rightJustified(5,'0')); updateEntry(nxdnSql,row,columnNxdnSql,mem.nxdnSql); ui->table->model()->setData(ui->table->model()->index(row,columnNxdnRan),QString::number(mem.nxdnRan).rightJustified(2,'0')); updateEntry(nxdnEnc,row,columnNxdnEnc,mem.nxdnEnc); ui->table->model()->setData(ui->table->model()->index(row,columnNxdnKey),QString::number(mem.nxdnKey).rightJustified(5,'0')); updateEntry(dcrSql,row,columnDcrSql,mem.dcrSql); ui->table->model()->setData(ui->table->model()->index(row,columnDcrUc),QString::number(mem.dcrUc).rightJustified(3,'0')); updateEntry(dcrEnc,row,columnDcrEnc,mem.dcrEnc); ui->table->model()->setData(ui->table->model()->index(row,columnDcrKey),QString::number(mem.dcrKey).rightJustified(5,'0')); // Do this after all other fields to ensure they have been populated. for (uint i=0;imodes.size();i++) { if (mem.mode == rigCaps->modes[i].reg) { validData += updateEntry(modes,row,columnMode,i); // This mode is the one we are interested in! configColumns(row, rigCaps->modes[i],mem.split); } if (mem.modeB == rigCaps->modes[i].reg) { validData += updateEntry(modes,row,columnModeB,i); configColumnsB(row, rigCaps->modes[i]); } } ui->table->blockSignals(false); if (store) { on_table_cellChanged(row,columnNum); // Force an attempt to store this row. } return validData; } void memories::rowAdded(int row, memoryType mem) { // Find the next available memory number: quint32 num=rigCaps->memStart; /* This feels unnecessarily complicated, but here we are: * Set the memory number to 1 * create a vector of all current memory numbers (ignoring any that are NULL) * If empty use 1 as the memory number * if not, sort it into numerical order * check if there is a gap using adjacent_find iterator (use that if there is) * If not, go back to previous, check we haven't exceeded the total allowed and if not add 1 to previous * If no empty memories available, log a warning and quit */ std::vector rows; ui->table->blockSignals(true); for (int i=0;i < ui->table->rowCount();i++) { if (ui->table->item(i,columnNum) != NULL) { rows.push_back(ui->table->item(i,columnNum)->text().toUInt()); } } if (rows.size()!=0) { std::sort(rows.begin(),rows.end()); auto i = std::adjacent_find( rows.begin(), rows.end(), [](int l, int r){return l+1group->currentData().toInt() != 200 && rows.back() < groupMemories-1) || (ui->group->currentData().toInt() == MEMORY_SATGROUP && rows.back() < rigCaps->satMemories-1)) { num = rows.back() + 1; } else if (rows.front() == rigCaps->memStart) { qWarning() << "Sorry no free memory slots found, please delete one first"; ui->table->removeRow(row); } } else { num = 1 + *i; } } QPushButton* recall = new QPushButton("Recall"); ui->table->setCellWidget(row,columnRecall,recall); connect(recall, &QPushButton::clicked, this, [=]() { recallMem(num); }); updateEntry(row,columnNum,QString::number(num).rightJustified(3,'0')); updateRow(row,mem); } void memories::recallMem(quint32 num) { qInfo() << "Recalling" << num; if (rigCaps->commands.contains(funcMemoryGroup)) { queue->add(priorityImmediate,queueItem(funcMemoryGroup,QVariant::fromValue(quint16(ui->group->currentData().toUInt())))); } else { for (auto &band: rigCaps->bands) { if (band.region == "" || band.region == region) { if (ui->group->isVisible() && band.memGroup == ui->group->currentData().toInt()) { bandStackType bs; bs.band = band.bsr; bs.reg = 1; queue->add(priorityImmediate,queueItem(funcBandStackReg, QVariant::fromValue(bs),false,uchar(0))); break; } } } } queue->add(priorityImmediate,queueItem(selectCommand,QVariant::fromValue(quint32((ui->group->currentData().toUInt() << 16) | num)))); // We also should request the current frequency/mode etc so that the UI is updated. vfoCommandType t = queue->getVfoCommand(vfoA,0,false); queue->add(priorityImmediate,t.freqFunc,false,t.receiver); queue->add(priorityImmediate,t.modeFunc,false,t.receiver); if (ui->modeButton->isVisible() && !ui->modeButton->isChecked()) { on_modeButton_clicked(true); } } void memories::rowDeleted(quint32 mem) { if (mem >= rigCaps->memStart && mem <= rigCaps->memories) { qInfo() << "Mem Deleted" << mem; memoryType currentMemory; if (ui->group->currentData().toInt() == MEMORY_SATGROUP) { currentMemory.sat=true; } currentMemory.group=ui->group->currentData().toInt(); currentMemory.channel = mem; currentMemory.del = true; queue->add(priorityImmediate,queueItem((currentMemory.sat?funcSatelliteMemory:writeCommand),QVariant::fromValue(currentMemory))); } } /* A cell has been updated, verify the data and send to the radio */ void memories::on_table_cellChanged(int row, int col) { // If the import is updating a hidden column, ignore it. if (ui->table->isColumnHidden(col)) return; memoryType currentMemory; currentMemory.group = ui->group->currentData().toInt(); currentMemory.channel = (ui->table->item(row,columnNum) == NULL) ? 0 : ui->table->item(row,columnNum)->text().toInt(); if (currentMemory.group == MEMORY_SATGROUP) { currentMemory.sat=true; } // The table shouldn't be updated below, simply queried for data. // We need to do this here to ensure we have the correct mode. for (auto &m: rigCaps->modes){ if (!ui->table->isColumnHidden(columnModeB) && ui->table->item(row,columnModeB) != NULL && ui->table->item(row,columnModeB)->text()==m.name) { currentMemory.modeB=m.reg; } if (!ui->table->isColumnHidden(columnMode) && ui->table->item(row,columnMode) != NULL && ui->table->item(row,columnMode)->text()==m.name) { currentMemory.mode=m.reg; } } if (!ui->table->isColumnHidden(columnSplit) && ui->table->item(row,columnSplit) != NULL) { currentMemory.split = split.indexOf(ui->table->item(row,columnSplit)->text().toUpper()); } if (!ui->table->isColumnHidden(columnSkip) && ui->table->item(row,columnSkip) != NULL) { currentMemory.skip = skip.indexOf(ui->table->item(row,columnSkip)->text().toUpper()); } if (!ui->table->isColumnHidden(columnScan) && ui->table->item(row,columnScan) != NULL) { currentMemory.scan = scan.indexOf(ui->table->item(row,columnScan)->text().toUpper()); } if (!ui->table->isColumnHidden(columnVFO) && ui->table->item(row,columnVFO) != NULL) { currentMemory.vfo = vfos.indexOf(ui->table->item(row,columnVFO)->text().toUpper()); } if (!ui->table->isColumnHidden(columnVFOB) && ui->table->item(row,columnVFOB) != NULL) { currentMemory.vfoB = vfos.indexOf(ui->table->item(row,columnVFOB)->text().toUpper()); } currentMemory.frequency.Hz = (ui->table->item(row,columnFrequency) == NULL) ? 0 : quint64(ui->table->item(row,columnFrequency)->text().toDouble()*1000000.0); currentMemory.frequencyB.Hz = (ui->table->item(row,columnFrequencyB) == NULL) ? 0 : quint64(ui->table->item(row,columnFrequencyB)->text().toDouble()*1000000.0); if (!ui->table->isColumnHidden(columnClarOffset) && ui->table->item(row,columnClarOffset) != NULL) { currentMemory.clarifier = (ui->table->item(row,columnClarOffset) == NULL) ? 0 : ui->table->item(row,columnClarOffset)->text().toShort(); qInfo() << "Clarifier offset:" << ui->table->item(row,columnClarOffset)->text() << "now:" << currentMemory.clarifier; } if (!ui->table->isColumnHidden(columnRXClar) && ui->table->item(row,columnRXClar) != NULL) { currentMemory.clarRX = clar.indexOf(ui->table->item(row,columnRXClar)->text().toUpper()); } if (!ui->table->isColumnHidden(columnTXClar) && ui->table->item(row,columnTXClar) != NULL) { currentMemory.clarTX = clar.indexOf(ui->table->item(row,columnTXClar)->text().toUpper()); } if (currentMemory.clarifier != 0) { //ClarRX or ClarTX MUST be enabled if (!currentMemory.clarRX && !currentMemory.clarTX) { currentMemory.clarRX=true; currentMemory.clarTX=true; } } if (!ui->table->isColumnHidden(columnData) && ui->table->item(row,columnData) != NULL) { currentMemory.datamode = dataModes.indexOf(ui->table->item(row,columnData)->text().toUpper()); } if (!ui->table->isColumnHidden(columnDataB) && ui->table->item(row,columnDataB) != NULL) { currentMemory.datamodeB = dataModes.indexOf(ui->table->item(row,columnDataB)->text().toUpper()); } if (!ui->table->isColumnHidden(columnFilter) && ui->table->item(row,columnFilter) != NULL) { if (rigCaps->manufacturer==manufKenwood) currentMemory.filter = filters.indexOf(ui->table->item(row,columnFilter)->text().toUpper()); else currentMemory.filter = filters.indexOf(ui->table->item(row,columnFilter)->text().toUpper())+1; } if (!ui->table->isColumnHidden(columnFilterB) && ui->table->item(row,columnFilterB) != NULL) { if (rigCaps->manufacturer==manufKenwood) currentMemory.filterB = filters.indexOf(ui->table->item(row,columnFilterB)->text().toUpper()); else currentMemory.filterB = filters.indexOf(ui->table->item(row,columnFilterB)->text().toUpper())+1; } if (!ui->table->isColumnHidden(columnDuplex) && ui->table->item(row,columnDuplex) != NULL) { currentMemory.duplex = duplexModes.indexOf(ui->table->item(row,columnDuplex)->text().toUpper()); } if (!ui->table->isColumnHidden(columnDuplexB) && ui->table->item(row,columnDuplexB) != NULL) { currentMemory.duplexB = duplexModes.indexOf(ui->table->item(row,columnDuplexB)->text().toUpper()); } if (!ui->table->isColumnHidden(columnToneMode) && ui->table->item(row,columnToneMode) != NULL) { currentMemory.tonemode = toneModes.indexOf(ui->table->item(row,columnToneMode)->text().toUpper()); } if (!ui->table->isColumnHidden(columnToneModeB) && ui->table->item(row,columnToneModeB) != NULL) { currentMemory.tonemodeB = toneModes.indexOf(ui->table->item(row,columnToneModeB)->text().toUpper()); } if (!ui->table->isColumnHidden(columnTuningStep) && ui->table->item(row,columnTuningStep) != NULL) { currentMemory.tuningStep = tuningSteps.indexOf(ui->table->item(row,columnTuningStep)->text()); } if (!ui->table->isColumnHidden(columnCustomTuningStep) && ui->table->item(row,columnCustomTuningStep) != NULL) { currentMemory.progTs =(ui->table->item(row,columnCustomTuningStep) == NULL) ? 0 : int(ui->table->item(row,columnCustomTuningStep)->text().toUInt()); } if (!ui->table->isColumnHidden(columnAttenuator) && ui->table->item(row,columnAttenuator) != NULL) { currentMemory.atten = (ui->table->item(row,columnAttenuator) == NULL) ? 0 : int(ui->table->item(row,columnAttenuator)->text().toUInt()); } if (!ui->table->isColumnHidden(columnPreamplifier) && ui->table->item(row,columnPreamplifier) != NULL) { currentMemory.preamp = preamps.indexOf(ui->table->item(row,columnPreamplifier)->text().toUpper()); } if (!ui->table->isColumnHidden(columnAntenna) && ui->table->item(row,columnAntenna) != NULL) { currentMemory.antenna = antennas.indexOf(ui->table->item(row,columnAntenna)->text().toUpper()); } if (!ui->table->isColumnHidden(columnIPPlus) && ui->table->item(row,columnIPPlus) != NULL) { currentMemory.ipplus = ipplus.indexOf(ui->table->item(row,columnIPPlus)->text().toUpper()); } if (!ui->table->isColumnHidden(columnDSQL) && ui->table->item(row,columnDSQL) != NULL) { currentMemory.dsql = dsql.indexOf(ui->table->item(row,columnDSQL)->text().toUpper()); } if (!ui->table->isColumnHidden(columnDSQLB) && ui->table->item(row,columnDSQLB) != NULL) { currentMemory.dsqlB = dsql.indexOf(ui->table->item(row,columnDSQLB)->text().toUpper()); } currentMemory.tone = (ui->table->item(row,columnTone) == NULL) ? "67.0" : ui->table->item(row,columnTone)->text(); currentMemory.toneB = (ui->table->item(row,columnToneB) == NULL) ? "67.0" : ui->table->item(row,columnToneB)->text(); currentMemory.tsql = (ui->table->item(row,columnTSQL) == NULL) ? "67.0" : ui->table->item(row,columnTSQL)->text(); currentMemory.tsqlB = (ui->table->item(row,columnTSQLB) == NULL) ? "67.0" : ui->table->item(row,columnTSQLB)->text(); currentMemory.dtcs = (ui->table->item(row,columnDTCS) == NULL) ? 23 : int(ui->table->item(row,columnDTCS)->text().toUInt()); currentMemory.dtcsB = (ui->table->item(row,columnDTCSB) == NULL) ? 23 : int(ui->table->item(row,columnDTCSB)->text().toUInt()); if (!ui->table->isColumnHidden(columnDTCSPolarity) && ui->table->item(row,columnDTCSPolarity) != NULL) { currentMemory.dtcsp = dtcsp.indexOf(ui->table->item(row,columnDTCSPolarity)->text().toUpper()); } if (!ui->table->isColumnHidden(columnDTCSPolarityB) && ui->table->item(row,columnDTCSPolarityB) != NULL) { currentMemory.dtcspB = dtcsp.indexOf(ui->table->item(row,columnDTCSPolarityB)->text().toUpper()); } currentMemory.dvsql = (ui->table->item(row,columnDVSquelch) == NULL) ? 0 : int(ui->table->item(row,columnDVSquelch)->text().toUInt()); currentMemory.dvsqlB = (ui->table->item(row,columnDVSquelchB) == NULL) ? 0 : int(ui->table->item(row,columnDVSquelchB)->text().toUInt()); currentMemory.duplexOffset.MHzDouble = (ui->table->item(row,columnOffset) == NULL) ? 0.0 : ui->table->item(row,columnOffset)->text().toDouble(); currentMemory.duplexOffset.Hz=currentMemory.duplexOffset.MHzDouble*1000000.0; currentMemory.duplexOffset.VFO=selVFO_t::activeVFO; currentMemory.duplexOffsetB.MHzDouble = (ui->table->item(row,columnOffsetB) == NULL) ? 0.0 : ui->table->item(row,columnOffsetB)->text().toDouble(); currentMemory.duplexOffsetB.Hz=currentMemory.duplexOffsetB.MHzDouble*1000000.0; currentMemory.duplexOffsetB.VFO=selVFO_t::activeVFO; memcpy(currentMemory.UR,((ui->table->item(row,columnUR) == NULL) ? "" : ui->table->item(row,columnUR)->text()).toStdString().c_str(),8); memcpy(currentMemory.URB,((ui->table->item(row,columnURB) == NULL) ? "" : ui->table->item(row,columnURB)->text()).toStdString().c_str(),8); memcpy(currentMemory.R1,((ui->table->item(row,columnR1) == NULL) ? "" : ui->table->item(row,columnR1)->text()).toStdString().c_str(),8); memcpy(currentMemory.R1B,((ui->table->item(row,columnR1B) == NULL) ? "" : ui->table->item(row,columnR1B)->text()).toStdString().c_str(),8); memcpy(currentMemory.R2,((ui->table->item(row,columnR2) == NULL) ? "" : ui->table->item(row,columnR2)->text()).toStdString().c_str(),8); memcpy(currentMemory.R2B,((ui->table->item(row,columnR2B) == NULL) ? "" : ui->table->item(row,columnR2B)->text()).toStdString().c_str(),8); memcpy(currentMemory.name,((ui->table->item(row,columnName) == NULL) ? "" : ui->table->item(row,columnName)->text()).toStdString().c_str(),16); if (ui->table->item(row,columnP25Sql) != NULL) { currentMemory.p25Sql = p25Sql.indexOf(ui->table->item(row,columnP25Sql)->text().toUpper()); } currentMemory.p25Nac = (ui->table->item(row,columnP25Nac) == NULL) ? 0 : int(ui->table->item(row,columnP25Nac)->text().toUInt(nullptr,16)); if (ui->table->item(row,columnDPmrSql) != NULL) { currentMemory.dPmrSql = dPmrSql.indexOf(ui->table->item(row,columnDPmrSql)->text().toUpper()); } currentMemory.dPmrComid = (ui->table->item(row,columnDPmrComid) == NULL) ? 0 : int(ui->table->item(row,columnDPmrComid)->text().toUInt()); currentMemory.dPmrCc = (ui->table->item(row,columnDPmrCc) == NULL) ? 0 : int(ui->table->item(row,columnDPmrCc)->text().toUInt()); if (ui->table->item(row,columnDPmrSCRM) != NULL) { currentMemory.dPmrSCRM = dPmrSCRM.indexOf(ui->table->item(row,columnDPmrSCRM)->text().toUpper()); } currentMemory.dPmrKey = (ui->table->item(row,columnDPmrKey) == NULL) ? 0 : int(ui->table->item(row,columnDPmrKey)->text().toUInt()); if (ui->table->item(row,columnNxdnSql) != NULL) { currentMemory.nxdnSql = nxdnSql.indexOf(ui->table->item(row,columnNxdnSql)->text().toUpper()); } currentMemory.nxdnRan = (ui->table->item(row,columnNxdnRan) == NULL) ? 0 : int(ui->table->item(row,columnNxdnRan)->text().toUInt()); if (ui->table->item(row,columnNxdnEnc) != NULL) { currentMemory.nxdnEnc = nxdnEnc.indexOf(ui->table->item(row,columnNxdnEnc)->text().toUpper()); } currentMemory.nxdnKey = (ui->table->item(row,columnNxdnKey) == NULL) ? 0 : int(ui->table->item(row,columnNxdnKey)->text().toUInt()); if (ui->table->item(row,columnDcrSql) != NULL) { currentMemory.dcrSql = dcrSql.indexOf(ui->table->item(row,columnDcrSql)->text().toUpper()); } currentMemory.dcrUc = (ui->table->item(row,columnDcrUc) == NULL) ? 0 : int(ui->table->item(row,columnDcrUc)->text().toUInt()); if (ui->table->item(row,columnDcrEnc) != NULL) { currentMemory.dcrEnc = dcrEnc.indexOf(ui->table->item(row,columnDcrEnc)->text().toUpper()); } currentMemory.dcrKey = (ui->table->item(row,columnDcrKey) == NULL) ? 0 : int(ui->table->item(row,columnDcrKey)->text().toUInt()); // This may update any dependant columns so signals must be blocked. bool block=false; if (!ui->table->signalsBlocked()) { block=true; ui->table->blockSignals(true); } switch (col) { case columnVFO: case columnVFOB: if (!ui->table->isColumnHidden(columnVFOB) && ui->table->item(row,columnVFOB) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnVFOB),ui->table->item(row,columnVFO)->text()); else if (!ui->table->isColumnHidden(columnVFO) && ui->table->item(row,columnVFO) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnVFO),ui->table->item(row,columnVFOB)->text()); break; case columnFrequency: case columnFrequencyB: if (!ui->table->isColumnHidden(columnFrequencyB) && ui->table->item(row,columnFrequencyB) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnFrequencyB),ui->table->item(row,columnFrequency)->text()); else if (!ui->table->isColumnHidden(columnFrequency) && ui->table->item(row,columnFrequency) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnFrequency),ui->table->item(row,columnFrequencyB)->text()); break; case columnMode: case columnModeB: { if (!ui->table->isColumnHidden(columnModeB) && ui->table->item(row,columnModeB) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnModeB),ui->table->item(row,columnMode)->text()); else if (!ui->table->isColumnHidden(columnMode) && ui->table->item(row,columnMode) == NULL) ui->table->model()->setData(ui->table->model()->index(row,columnMode),ui->table->item(row,columnModeB)->text()); for (auto &m: rigCaps->modes){ if (!ui->table->isColumnHidden(columnMode) && ui->table->item(row,columnMode) != NULL && ui->table->item(row,columnMode)->text()==m.name) { // This mode is the one we are interested in! configColumns(row,m,currentMemory.split); break; } if (!ui->table->isColumnHidden(columnModeB) && ui->table->item(row,columnModeB) != NULL && ui->table->item(row,columnModeB)->text()==m.name) { // This mode is the one we are interested in! configColumnsB(row,m); break; } } break; } case columnDuplex: ui->table->model()->setData(ui->table->model()->index(row,columnDuplexB),ui->table->item(row,columnDuplex)->text()); break; case columnDuplexB: ui->table->model()->setData(ui->table->model()->index(row,columnDuplex),ui->table->item(row,columnDuplexB)->text()); break; case columnOffset: ui->table->model()->setData(ui->table->model()->index(row,columnOffsetB),ui->table->item(row,columnOffset)->text()); break; case columnOffsetB: ui->table->model()->setData(ui->table->model()->index(row,columnOffset),ui->table->item(row,columnOffsetB)->text()); break; case columnTuningStep: if (ui->table->item(row,columnTuningStep) != NULL && ui->table->item(row,columnTuningStep)->text() == "Prog") enableCell(row,columnCustomTuningStep,true); else enableCell(row,columnCustomTuningStep,false); break; default: break; } if (block) ui->table->blockSignals(false); // Only write the memory if ALL required values are non-null bool write=true; for (int f=1; ftable->columnCount();f++) { if (!ui->table->isColumnHidden(f) && ui->table->item(row,f) == NULL) { write=false; qInfo() << "Column << f << is nulL, cannot write."; //ui->table->model()->setData(ui->table->model()->index(row,f),QString("")); //ui->table->item(row,f)->setBackground(Qt::red); } else { //ui->table->item(row,f)->setBackground(Qt::NoBrush); } } // Quick sanity check as we MUST have at least one valid frequency if (currentMemory.frequency.Hz == 0 || (rigCaps->manufacturer == manufIcom && currentMemory.split && currentMemory.frequencyB.Hz == 0)) { write=false; qInfo() << "Frequency is zero"; } if (write) { //Sent command to write memory followed by slightly lower priority command to read. queue->add(priorityImmediate,queueItem((currentMemory.sat?funcSatelliteMemory:writeCommand),QVariant::fromValue(currentMemory))); if (rigCaps->commands.contains(funcMemoryTag) || rigCaps->commands.contains(funcMemoryTagB)) { memoryTagType tag; tag.num=currentMemory.channel; tag.name = QString(currentMemory.name); tag.en=1; queue->add(priorityHighest,queueItem(rigCaps->commands.contains(funcMemoryTagB)?funcMemoryTagB:funcMemoryTag,QVariant::fromValue(tag))); queue->add(priorityHigh,queueItem(rigCaps->commands.contains(funcMemoryTagB)?funcMemoryTagB:funcMemoryTag,QVariant::fromValue(currentMemory.channel))); } if (rigCaps->commands.contains(funcSplitMemory)) { // Yaesu memory split command. memorySplitType s; s.num=currentMemory.channel; s.en = (currentMemory.frequency.Hz != currentMemory.frequencyB.Hz && currentMemory.frequencyB.Hz != 0)?true:false; s.freq = currentMemory.frequencyB.Hz; qInfo() << "Adding split command for:" <add(priorityHighest,queueItem(funcSplitMemory,QVariant::fromValue(s))); queue->add(priorityHigh,queueItem(funcSplitMemory,QVariant::fromValue(currentMemory.channel))); } queue->add(priorityHigh,queueItem(funcMemoryContents,QVariant::fromValue((currentMemory.group<<16) | (currentMemory.channel & 0xffff)))); qDebug() << "Sending memory, group:" << currentMemory.group << "channel" << currentMemory.channel; // Set number to not be editable once written. Not sure why but this crashes? //ui->table->item(row,columnNum)->setFlags(ui->table->item(row,columnNum)->flags() & (~Qt::ItemIsEditable)); } } /* * This function is called whenever the group changes (and on startup) * It configures the table with the required columns and requests the * first memory from the radio. */ void memories::on_group_currentIndexChanged(int index) { Q_UNUSED(index) startup=true; timeoutTimer.start(MEMORY_TIMEOUT); visibleColumns=1; // Special case for group 100 on the IC705! if (ui->group->currentData().toInt() == MEMORY_SATGROUP) groupMemories=rigCaps->satMemories; else if (ui->group->currentData().toInt() == MEMORY_SHORTGROUP) groupMemories=3; else groupMemories=rigCaps->memories; progress->setRange(rigCaps->memStart,groupMemories); //ui->loadingMemories->setVisible(true); ui->table->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->group->setEnabled(false); ui->table->blockSignals(true); ui->table->setRowCount(0); ui->table->blockSignals(false); // Hide all columns except recall for (int i=1;itable->columnCount();i++) { ui->table->hideColumn(i); } QVector parser; if (ui->group->currentData().toInt() == MEMORY_SATGROUP) { on_modeButton_clicked(true); queue->add(priorityImmediate,queueItem(funcSatelliteMode,QVariant::fromValue(true),false,0)); parser = rigCaps->satParser; } else { // If the rig has memory groups, select it now. queue->add(priorityImmediate,queueItem(funcSatelliteMode,QVariant::fromValue(false),false,0)); // Make sure we are in not in satellite mode. queue->add(priorityImmediate,queueItem(funcMemoryGroup,QVariant::fromValue(ui->group->currentData().toInt()))); parser = rigCaps->memParser; } for (auto &parse: parser) { switch (parse.spec) { case 'a': ui->group->show(); disableCommands = true; break; case 'b': if (numEditor != nullptr) delete numEditor; numEditor = new tableEditor("999",ui->table); ui->table->setItemDelegateForColumn(columnNum, numEditor); ui->table->showColumn(columnNum); visibleColumns++; break; #if defined __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif case 'C': if (skipList != nullptr) delete skipList; skipList = new tableCombobox(createModel(skipModel, skip),false,ui->table); ui->table->setItemDelegateForColumn(columnSkip, skipList); ui->table->showColumn(columnSkip); visibleColumns++; // Fallthrough to add Scanlist column as well. scan.clear(); scan << "OFF" << "*1" << "*2" << "*3" << "*4" << "*5" << "*6" << "*7" << "*8" << "*9"; case 'c': if (scanList != nullptr) delete scanList; scanList = new tableCombobox(createModel(scanModel, scan),false,ui->table); ui->table->setItemDelegateForColumn(columnScan, scanList); ui->table->showColumn(columnScan); visibleColumns++; break; #if defined __GNUC__ #pragma GCC diagnostic pop #endif case 'd': if (splitList != nullptr) delete splitList; splitList = new tableCombobox(createModel(splitModel, split),false,ui->table); ui->table->setItemDelegateForColumn(columnSplit, splitList); if (scanList != nullptr) delete scanList; scanList = new tableCombobox(createModel(scanModel, scan),false,ui->table); ui->table->setItemDelegateForColumn(columnScan, scanList); ui->table->showColumn(columnSplit); ui->table->showColumn(columnScan); visibleColumns++; visibleColumns++; break; case 'D': // Duplex only added for IC-R8600 (most rigs use j for duplex+tone) if (duplexList != nullptr) delete duplexList; duplexList = new tableCombobox(createModel(duplexModel, duplexModes),false,ui->table); ui->table->setItemDelegateForColumn(columnDuplex, duplexList); ui->table->showColumn(columnDuplex); visibleColumns++; break; case 'e': if (vfoList != nullptr) delete vfoList; vfoList = new tableCombobox(createModel(vfoModel, vfos),false,ui->table); ui->table->setItemDelegateForColumn(columnVFO, vfoList); ui->table->showColumn(columnVFO); visibleColumns++; break; case 'E': if (vfoListB != nullptr) delete vfoListB; vfoListB = new tableCombobox(createModel(vfoModelB, vfos),false,ui->table); ui->table->setItemDelegateForColumn(columnVFOB, vfoListB); ui->table->showColumn(columnVFOB); visibleColumns++; break; case 'f': if (freqEditor != nullptr) delete freqEditor; freqEditor = new tableEditor("00000.00000",ui->table); ui->table->setItemDelegateForColumn(columnFrequency, freqEditor); ui->table->showColumn(columnFrequency); visibleColumns++; break; case 'F': if (freqEditorB != nullptr) delete freqEditorB; freqEditorB = new tableEditor("00000.00000",ui->table); ui->table->setItemDelegateForColumn(columnFrequencyB, freqEditorB); ui->table->showColumn(columnFrequencyB); visibleColumns++; break; case 'g': if (modesList != nullptr) delete modesList; modesList = new tableCombobox(createModel(modesModel, modes),false,ui->table); ui->table->setItemDelegateForColumn(columnMode, modesList); ui->table->showColumn(columnMode); visibleColumns++; break; case 'G': if (modesListB != nullptr) delete modesListB; modesListB = new tableCombobox(createModel(modesModelB, modes),false,ui->table); ui->table->setItemDelegateForColumn(columnModeB, modesListB); ui->table->showColumn(columnModeB); visibleColumns++; break; case 'h': if (filterList != nullptr) delete filterList; filterList = new tableCombobox(createModel(filterModel, filters),false,ui->table); ui->table->setItemDelegateForColumn(columnFilter, filterList); ui->table->showColumn(columnFilter); visibleColumns++; break; case 'H': if (filterListB != nullptr) delete filterListB; filterListB = new tableCombobox(createModel(filterModelB, filters),false,ui->table); ui->table->setItemDelegateForColumn(columnFilterB, filterListB); ui->table->showColumn(columnFilterB); visibleColumns++; break; case 'i': if (dataList != nullptr) delete dataList; dataList = new tableCombobox(createModel(dataModel, dataModes),false,ui->table); ui->table->setItemDelegateForColumn(columnData, dataList); ui->table->showColumn(columnData); visibleColumns++; break; case 'I': if (dataListB != nullptr) delete dataListB; dataListB = new tableCombobox(createModel(dataModelB, dataModes),false,ui->table); ui->table->setItemDelegateForColumn(columnDataB, dataListB); ui->table->showColumn(columnDataB); visibleColumns++; break; case 'j': if (duplexList != nullptr) delete duplexList; duplexList = new tableCombobox(createModel(duplexModel, duplexModes),false,ui->table); ui->table->setItemDelegateForColumn(columnDuplex, duplexList); if (toneModesList != nullptr) delete toneModesList; toneModesList = new tableCombobox(createModel(toneModesModel, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneMode, toneModesList); ui->table->showColumn(columnDuplex); ui->table->showColumn(columnToneMode); visibleColumns++; visibleColumns++; break; case 'J': if (duplexListB != nullptr) delete duplexListB; duplexListB = new tableCombobox(createModel(duplexModelB, duplexModes),false,ui->table); ui->table->setItemDelegateForColumn(columnDuplexB, duplexListB); if (toneModesListB != nullptr) delete toneModesListB; toneModesListB = new tableCombobox(createModel(toneModesModelB, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneModeB, toneModesListB); ui->table->showColumn(columnDuplexB); ui->table->showColumn(columnToneModeB); visibleColumns++; visibleColumns++; break; case 'k': if (dataList != nullptr) delete dataList; dataList = new tableCombobox(createModel(dataModel, dataModes),false,ui->table); ui->table->setItemDelegateForColumn(columnData, dataList); if (toneModesList != nullptr) delete toneModesList; toneModesList = new tableCombobox(createModel(toneModesModel, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneMode, toneModesList); ui->table->showColumn(columnData); ui->table->showColumn(columnToneMode); visibleColumns++; visibleColumns++; break; case 'K': if (dataListB != nullptr) delete dataListB; dataListB = new tableCombobox(createModel(dataModelB, dataModes),false,ui->table); ui->table->setItemDelegateForColumn(columnDataB, dataListB); if (toneModesListB != nullptr) delete toneModesListB; toneModesListB = new tableCombobox(createModel(toneModesModelB, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneModeB, toneModesListB); ui->table->showColumn(columnDataB); ui->table->showColumn(columnToneModeB); visibleColumns++; visibleColumns++; break; case 'l': if (toneModesList != nullptr) delete toneModesList; toneModesList = new tableCombobox(createModel(toneModesModel, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneMode, toneModesList); ui->table->showColumn(columnToneMode); visibleColumns++; break; case 'L': if (toneModesListB != nullptr) delete toneModesListB; toneModesListB = new tableCombobox(createModel(toneModesModelB, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneModeB, toneModesListB); ui->table->showColumn(columnToneModeB); visibleColumns++; break; case 'm': if (dsqlList != nullptr) delete dsqlList; dsqlList = new tableCombobox(createModel(dsqlModel, dsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDSQL, dsqlList); ui->table->showColumn(columnDSQL); visibleColumns++; break; case 'M': if (dsqlListB != nullptr) delete dsqlListB; dsqlListB = new tableCombobox(createModel(dsqlModelB, dsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDSQLB, dsqlListB); ui->table->showColumn(columnDSQLB); visibleColumns++; break; case 'n': if (tonesList != nullptr) delete tonesList; tonesList = new tableCombobox(createModel(tonesModel, tones),false,ui->table); ui->table->setItemDelegateForColumn(columnTone, tonesList); ui->table->showColumn(columnTone); visibleColumns++; break; case 'N': if (tonesListB != nullptr) delete tonesListB; tonesListB = new tableCombobox(createModel(tonesModelB, tones),false,ui->table); ui->table->setItemDelegateForColumn(columnToneB, tonesListB); ui->table->showColumn(columnToneB); visibleColumns++; break; case 'o': if (tsqlList != nullptr) delete tsqlList; tsqlList = new tableCombobox(createModel(tsqlModel, tones),false,ui->table); ui->table->setItemDelegateForColumn(columnTSQL, tsqlList); ui->table->showColumn(columnTSQL); visibleColumns++; break; case 'O': if (tsqlListB != nullptr) delete tsqlListB; tsqlListB = new tableCombobox(createModel(tsqlModelB, tones),false,ui->table); ui->table->setItemDelegateForColumn(columnTSQLB, tsqlListB); ui->table->showColumn(columnTSQLB); visibleColumns++; break; case 'p': if (dtcspList != nullptr) delete dtcspList; dtcspList = new tableCombobox(createModel(dtcspModel, dtcsp),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCSPolarity, dtcspList); ui->table->showColumn(columnDTCSPolarity); visibleColumns++; break; case 'P': if (dtcspListB != nullptr) delete dtcspListB; dtcspListB = new tableCombobox(createModel(dtcspModelB, dtcsp),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCSPolarityB, dtcspListB); ui->table->showColumn(columnDTCSPolarityB); visibleColumns++; break; case 'q': if (dtcsList != nullptr) delete dtcsList; dtcsList = new tableCombobox(createModel(dtcsModel, dtcs),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCS, dtcsList); ui->table->showColumn(columnDTCS); visibleColumns++; break; case 'Q': if (dtcsListB != nullptr) delete dtcsListB; dtcsListB = new tableCombobox(createModel(dtcsModel, dtcs),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCSB, dtcsList); ui->table->showColumn(columnDTCSB); visibleColumns++; break; case 'r': if (dvsqlList != nullptr) delete dvsqlList; dvsqlList = new tableCombobox(createModel(dvsqlModel, dvsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDVSquelch, dvsqlList); ui->table->showColumn(columnDVSquelch); visibleColumns++; break; case 'R': if (dvsqlListB != nullptr) delete dvsqlListB; dvsqlListB = new tableCombobox(createModel(dvsqlModelB, dvsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDVSquelchB, dvsqlListB); ui->table->showColumn(columnDVSquelchB); visibleColumns++; break; case 's': if (offsetEditor != nullptr) delete offsetEditor; offsetEditor = new tableEditor("00.000000",ui->table); ui->table->setItemDelegateForColumn(columnOffset, offsetEditor); ui->table->showColumn(columnOffset); visibleColumns++; break; case 'S': if (offsetEditorB != nullptr) delete offsetEditorB; offsetEditorB = new tableEditor("00.000000",ui->table); ui->table->setItemDelegateForColumn(columnOffsetB, offsetEditorB); ui->table->showColumn(columnOffsetB); visibleColumns++; break; case 't': if (urEditor != nullptr) delete urEditor; urEditor = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnUR, urEditor); ui->table->showColumn(columnUR); visibleColumns++; break; case 'T': if (urEditorB != nullptr) delete urEditorB; urEditorB = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnURB, urEditorB); ui->table->showColumn(columnURB); visibleColumns++; break; case 'u': if (r1Editor != nullptr) delete r1Editor; r1Editor = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnR1, r1Editor); ui->table->showColumn(columnR1); visibleColumns++; break; case 'U': if (r1EditorB != nullptr) delete r1EditorB; r1EditorB = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnR1B, r1EditorB); ui->table->showColumn(columnR1B); visibleColumns++; break; case 'v': if (r2Editor != nullptr) delete r2Editor; r2Editor = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnR2, r2Editor); ui->table->showColumn(columnR2); visibleColumns++; break; case 'V': if (r2EditorB != nullptr) delete r2EditorB; r2EditorB = new tableEditor(">xxxxxxxx;_",ui->table); ui->table->setItemDelegateForColumn(columnR2B, r2EditorB); ui->table->showColumn(columnR2B); visibleColumns++; break; case 'W': if (clarEditor != nullptr) { delete clarEditor; } clarEditor = new tableEditor("x00000",ui->table); ui->table->setItemDelegateForColumn(columnClarOffset, clarEditor); ui->table->showColumn(columnClarOffset); visibleColumns++; break; case 'X': if (clarRXList == nullptr) { clarRXList = new tableCombobox(createModel(clarRXModel, clar),false,ui->table); } ui->table->setItemDelegateForColumn(columnRXClar, clarRXList); ui->table->showColumn(columnRXClar); visibleColumns++; break; case 'Y': if (clarTXList == nullptr) { clarTXList = new tableCombobox(createModel(clarTXModel, clar),false,ui->table); } ui->table->setItemDelegateForColumn(columnTXClar, clarTXList); ui->table->showColumn(columnTXClar); visibleColumns++; break; case 'w': if (tuningStepsList != nullptr) delete tuningStepsList; tuningStepsList = new tableCombobox(createModel(tuningStepsModel, tuningSteps),false,ui->table); ui->table->setItemDelegateForColumn(columnTuningStep, tuningStepsList); ui->table->showColumn(columnTuningStep); visibleColumns++; if (tuningStepEditor != nullptr) delete tuningStepEditor; tuningStepEditor = new tableEditor("0000",ui->table); ui->table->setItemDelegateForColumn(columnCustomTuningStep, tuningStepEditor); ui->table->showColumn(columnCustomTuningStep); visibleColumns++; break; case 'x': if (attenuatorsList != nullptr) delete attenuatorsList; attenuatorsList = new tableCombobox(createModel(attenuatorsModel, attenuators),false,ui->table); ui->table->setItemDelegateForColumn(columnAttenuator, attenuatorsList); ui->table->showColumn(columnAttenuator); visibleColumns++; if (preampsList != nullptr) delete preampsList; preampsList = new tableCombobox(createModel(preampsModel, preamps),false,ui->table); ui->table->setItemDelegateForColumn(columnPreamplifier, preampsList); ui->table->showColumn(columnPreamplifier); visibleColumns++; break; case 'y': if (antennasList != nullptr) delete antennasList; antennasList = new tableCombobox(createModel(antennasModel, antennas),false,ui->table); ui->table->setItemDelegateForColumn(columnAntenna, antennasList); ui->table->showColumn(columnAntenna); visibleColumns++; break; case '+': if (ipplusList != nullptr) delete ipplusList; ipplusList = new tableCombobox(createModel(ipplusModel, ipplus),false,ui->table); ui->table->setItemDelegateForColumn(columnIPPlus, ipplusList); ui->table->showColumn(columnIPPlus); visibleColumns++; break; case 'z': if (nameEditor != nullptr) delete nameEditor; nameEditor = new tableEditor(QString("%0;_").arg("",parse.len,'x'),ui->table); ui->table->setItemDelegateForColumn(columnName, nameEditor); ui->table->showColumn(columnName); visibleColumns++; break; case 'Z': extendedView = true; if (toneModesList != nullptr) delete toneModesList; toneModesList = new tableCombobox(createModel(toneModesModel, toneModes),false,ui->table); ui->table->setItemDelegateForColumn(columnToneMode, toneModesList); ui->table->showColumn(columnToneMode); //visibleColumns++; if (tsqlList != nullptr) delete tsqlList; tsqlList = new tableCombobox(createModel(tsqlModel, tones),false,ui->table); ui->table->setItemDelegateForColumn(columnTSQL, tsqlList); ui->table->showColumn(columnTSQL); //visibleColumns++; if (dtcsList != nullptr) delete dtcsList; dtcsList = new tableCombobox(createModel(dtcsModel, dtcs),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCS, dtcsList); ui->table->showColumn(columnDTCS); //visibleColumns++; if (dtcspList != nullptr) delete dtcspList; dtcspList = new tableCombobox(createModel(dtcspModel, dtcsp),false,ui->table); ui->table->setItemDelegateForColumn(columnDTCSPolarity, dtcspList); ui->table->showColumn(columnDTCSPolarity); //visibleColumns++; if (dsqlList != nullptr) delete dsqlList; dsqlList = new tableCombobox(createModel(dsqlModel, dsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDSQL, dsqlList); ui->table->showColumn(columnDSQL); //visibleColumns++; if (dvsqlList != nullptr) delete dvsqlList; dvsqlList = new tableCombobox(createModel(dvsqlModel, dvsql),false,ui->table); ui->table->setItemDelegateForColumn(columnDVSquelch, dvsqlList); ui->table->showColumn(columnDVSquelch); //visibleColumns++; /* P25 */ if (p25SqlList != nullptr) delete p25SqlList; p25SqlList = new tableCombobox(createModel(p25SqlModel, p25Sql),false,ui->table); ui->table->setItemDelegateForColumn(columnP25Sql, p25SqlList); ui->table->showColumn(columnP25Sql); //visibleColumns++; if (p25NacEditor != nullptr) delete p25NacEditor; p25NacEditor = new tableEditor("HHH",ui->table); ui->table->setItemDelegateForColumn(columnP25Nac, p25NacEditor); ui->table->showColumn(columnP25Nac); //visibleColumns++; /* dPMR */ if (dPmrSqlList != nullptr) delete dPmrSqlList; dPmrSqlList = new tableCombobox(createModel(dPmrSqlModel, dPmrSql),false,ui->table); ui->table->setItemDelegateForColumn(columnDPmrSql, dPmrSqlList); ui->table->showColumn(columnDPmrSql); //visibleColumns++; if (dPmrComIdEditor != nullptr) delete dPmrComIdEditor; dPmrComIdEditor = new tableEditor("999",ui->table); ui->table->setItemDelegateForColumn(columnDPmrComid, dPmrComIdEditor); ui->table->showColumn(columnDPmrComid); //visibleColumns++; if (dPmrCcEditor != nullptr) delete dPmrCcEditor; dPmrCcEditor = new tableEditor("99",ui->table); ui->table->setItemDelegateForColumn(columnDPmrCc, dPmrCcEditor); ui->table->showColumn(columnDPmrCc); //visibleColumns++; if (dPmrSCRMList != nullptr) delete dPmrSCRMList; dPmrSCRMList = new tableCombobox(createModel(dPmrSCRMModel, dPmrSCRM),false,ui->table); ui->table->setItemDelegateForColumn(columnDPmrSCRM, dPmrSCRMList); ui->table->showColumn(columnDPmrSCRM); //visibleColumns++; if (dPmrKeyEditor != nullptr) delete dPmrKeyEditor; dPmrKeyEditor = new tableEditor("99999",ui->table); ui->table->setItemDelegateForColumn(columnDPmrKey, dPmrKeyEditor); ui->table->showColumn(columnDPmrKey); //visibleColumns++; /* NXDN */ if (nxdnSqlList != nullptr) delete nxdnSqlList; nxdnSqlList = new tableCombobox(createModel(nxdnSqlModel, nxdnSql),false,ui->table); ui->table->setItemDelegateForColumn(columnNxdnSql, nxdnSqlList); ui->table->showColumn(columnNxdnSql); //visibleColumns++; if (nxdnRanEditor != nullptr) delete nxdnRanEditor; nxdnRanEditor = new tableEditor("99",ui->table); ui->table->setItemDelegateForColumn(columnNxdnRan, nxdnRanEditor); ui->table->showColumn(columnNxdnRan); //visibleColumns++; if (nxdnEncList != nullptr) delete nxdnEncList; nxdnEncList = new tableCombobox(createModel(nxdnEncModel, nxdnEnc),false,ui->table); ui->table->setItemDelegateForColumn(columnNxdnEnc, nxdnEncList); ui->table->showColumn(columnNxdnEnc); //visibleColumns++; if (nxdnKeyEditor != nullptr) delete nxdnKeyEditor; nxdnKeyEditor = new tableEditor("99999",ui->table); ui->table->setItemDelegateForColumn(columnNxdnKey, nxdnKeyEditor); ui->table->showColumn(columnNxdnKey); //visibleColumns++; /* DCR */ if (dcrSqlList != nullptr) delete dcrSqlList; dcrSqlList = new tableCombobox(createModel(dcrSqlModel, dcrSql),false,ui->table); ui->table->setItemDelegateForColumn(columnDcrSql, dcrSqlList); ui->table->showColumn(columnDcrSql); //visibleColumns++; if (dcrUcEditor != nullptr) delete dcrUcEditor; dcrUcEditor = new tableEditor("999",ui->table); ui->table->setItemDelegateForColumn(columnDcrUc, dcrUcEditor); ui->table->showColumn(columnDcrUc); //visibleColumns++; if (dcrEncList != nullptr) delete dcrEncList; dcrEncList = new tableCombobox(createModel(dcrEncModel, dcrEnc),false,ui->table); ui->table->setItemDelegateForColumn(columnDcrEnc, dcrEncList); ui->table->showColumn(columnDcrEnc); //visibleColumns++; if (dcrKeyEditor != nullptr) delete dcrKeyEditor; dcrKeyEditor = new tableEditor("99999",ui->table); ui->table->setItemDelegateForColumn(columnDcrKey, dcrKeyEditor); ui->table->showColumn(columnDcrKey); //visibleColumns++; break; default: break; } } // Yaesu description is separate! if ((rigCaps->commands.contains(funcMemoryTag) || rigCaps->commands.contains(funcMemoryTagB)) && ui->table->isColumnHidden(columnName)) { if (nameEditor != nullptr) delete nameEditor; nameEditor = new tableEditor(QString("%0;_").arg("",12,'x'),ui->table); ui->table->setItemDelegateForColumn(columnName, nameEditor); ui->table->showColumn(columnName); visibleColumns++; } if (rigCaps->commands.contains(funcSplitMemory) && ui->table->isColumnHidden(columnFrequencyB)) { if (freqEditorB != nullptr) delete freqEditorB; freqEditorB = new tableEditor("00000.00000",ui->table); ui->table->setItemDelegateForColumn(columnFrequencyB, freqEditorB); ui->table->showColumn(columnFrequencyB); visibleColumns++; } if (visibleColumns > 15 || extendedView) { ui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); } else { ui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); } if (ui->group->currentData().toInt() == MEMORY_SATGROUP) { lastMemoryRequested = rigCaps->memStart; if (slowLoad) { QTimer::singleShot(MEMORY_SLOWLOAD, this, [this]{ queue->add(priorityImmediate,queueItem(funcSatelliteMemory,QVariant::fromValue(lastMemoryRequested & 0xffff))); }); } else { queue->add(priorityImmediate,queueItem(funcSatelliteMemory,QVariant::fromValue(lastMemoryRequested & 0xffff))); } } else { lastMemoryRequested = quint32((ui->group->currentData().toInt())<<16) | (rigCaps->memStart & 0xffff); if (slowLoad) { QTimer::singleShot(MEMORY_SLOWLOAD, this, [this]{ queue->add(priorityImmediate,queueItem(funcMemoryContents,QVariant::fromValue(lastMemoryRequested))); }); } else { // Is the current group attached to a particular band? for (auto &band: rigCaps->bands) { if (band.region == "" || band.region == region) { if (ui->group->isVisible() && band.memGroup == ui->group->currentData().toInt()) { bandStackType bs; bs.band = band.bsr; bs.reg = 1; queue->add(priorityImmediate,queueItem(funcBandStackReg, QVariant::fromValue(bs),false,uchar(0))); break; } } } queue->add(priorityImmediate,queueItem(funcMemoryContents,QVariant::fromValue(lastMemoryRequested))); } } if (rigCaps->commands.contains(funcMemoryTag) || rigCaps->commands.contains(funcMemoryTagB)) { // Yaesu specific as tag (name) is stored seperately. if (slowLoad) { QTimer::singleShot(MEMORY_SLOWLOAD, this, [this]{ queue->add(priorityImmediate,queueItem(rigCaps->commands.contains(funcMemoryTagB)?funcMemoryTagB:funcMemoryTag,QVariant::fromValue(lastMemoryRequested))); }); } else { queue->add(priorityImmediate,queueItem(rigCaps->commands.contains(funcMemoryTagB)?funcMemoryTagB:funcMemoryTag,QVariant::fromValue(lastMemoryRequested))); } } if (rigCaps->commands.contains(funcSplitMemory)) { // Yaesu specific as tag (name) is stored seperately. if (slowLoad) { QTimer::singleShot(MEMORY_SLOWLOAD, this, [this]{ queue->add(priorityImmediate,queueItem(funcSplitMemory,QVariant::fromValue(lastMemoryRequested))); }); } else { queue->add(priorityImmediate,queueItem(funcSplitMemory,QVariant::fromValue(lastMemoryRequested))); } } } void memories::on_modeButton_clicked(bool on) { emit memoryMode(on); static bool checked=false; if (!on && checked) { qInfo() << "Selecting VFO Mode"; // The IC9700 may be in satellite mode, so disable that first. if (rigCaps->commands.contains(funcSatelliteMode)) queue->add(priorityImmediate,queueItem(funcSatelliteMode,QVariant::fromValue(false),false,0)); // Make sure we are in not in satellite mode. ui->modeButton->setText("Select Memory Mode"); if (disableCommands) { for( const commandList &c: activeCommands) { queue->add(c.prio,c.func,true,c.receiver); } activeCommands.clear(); } for (uchar i = 0;i < rigCaps->numReceiver;i++) { queue->add(priorityImmediate,funcVFOModeSelect,false,i); } checked=false; } else if (on && !checked) { qInfo() << "Selecting Memory Mode"; ui->modeButton->setText("Select VFO Mode"); for (uchar i = 0;i < rigCaps->numReceiver;i++) { queue->add(priorityImmediate,funcMemoryMode,false,i); } if (disableCommands) { activeCommands.clear(); for (int i=0;inumReceiver;i++) { for (const funcs &func:disabledCommands) { queuePriority prio = queue->del(func,i); if (prio != priorityNone) { activeCommands.append(commandList(prio,func,i)); } } } } checked=true; } ui->modeButton->setChecked(checked); } void memories::receiveMemorySplit(memorySplitType s) { // Now process the incoming memory name qDebug(logRig()) << "Memories: received memory split id:" << s.num << " en:" << s.en << " freq:" << s.freq; for (int n = 0; ntable->rowCount();n++) { if (ui->table->item(n,columnNum) != NULL && ui->table->item(n,columnNum)->text().toInt() == s.num) { ui->table->blockSignals(true); if (s.freq > 0) ui->table->model()->setData(ui->table->model()->index(n,columnFrequencyB),QString::number(double(s.freq/1000000.0),'f',5)); ui->table->blockSignals(false); break; } } } void memories::receiveMemoryName(memoryTagType tag) { // We should already have the memory contents, so just fill in the name. qDebug(logRig()) << "Memories: received memory tag id:" << tag.num << " en:" << tag.en << " name:" << tag.name; // Now process the incoming memory name for (int n = 0; ntable->rowCount();n++) { if (ui->table->item(n,columnNum) != NULL && ui->table->item(n,columnNum)->text().toInt() == tag.num) { if (checkASCII(tag.name)) { ui->table->blockSignals(true); ui->table->model()->setData(ui->table->model()->index(n,columnName),tag.name); ui->table->blockSignals(false); } break; } } } void memories::receiveMemory(memoryType mem) { qDebug() << QString("Loading Memory %0/%1 (this may take a while!)").arg(lastMemoryRequested&0xffff,3,10,QLatin1Char('0')).arg(rigCaps->memories,3,10,QLatin1Char('0')); progress->setValue(lastMemoryRequested & 0xffff); // First, do we need to request the next memory? if ((lastMemoryRequested & 0xffff) < groupMemories && startup) { lastMemoryRequested++; if (mem.sat) { queue->add(priorityImmediate,queueItem(funcSatelliteMemory,QVariant::fromValue(lastMemoryRequested & 0xffff))); } else { queue->add(priorityImmediate,queueItem(funcMemoryContents,QVariant::fromValue(lastMemoryRequested))); if (rigCaps->commands.contains(funcMemoryTag)) { queue->add(priorityImmediate,queueItem(funcMemoryTag,QVariant::fromValue(lastMemoryRequested))); } if (rigCaps->commands.contains(funcMemoryTagB)) { queue->add(priorityImmediate,queueItem(funcMemoryTagB,QVariant::fromValue(lastMemoryRequested))); } if (rigCaps->commands.contains(funcSplitMemory)) { queue->add(priorityImmediate,queueItem(funcSplitMemory,QVariant::fromValue(lastMemoryRequested))); } } timeoutTimer.start(MEMORY_TIMEOUT); } else if (mem.channel == groupMemories) { timeoutTimer.stop(); ui->group->setEnabled(true); //ui->loadingMemories->setVisible(false); if (!ui->disableEditing->isChecked()) { ui->table->setEditTriggers(QAbstractItemView::DoubleClicked); } startup=false; } timeoutCount=0; // We have received a memory, so set the timeout to zero. int validData=1; // We have 1 more row than will ever be received. // Now process the incoming memory int row=-1; for (int n = 0; ntable->rowCount();n++) { if (ui->table->item(n,columnNum) != NULL && ui->table->item(n,columnNum)->text().toInt() == mem.channel && (rigCaps->memGroups < 2 || mem.sat || mem.group == ui->group->currentData().toInt())) { row = n; break; } } if (mem.scan < 4) { ui->table->blockSignals(true); if (row == -1) { ui->table->insertRow(ui->table->rowCount()); row=ui->table->rowCount()-1; QPushButton* recall = new QPushButton("Recall"); ui->table->setCellWidget(row,columnRecall,recall); connect(recall, &QPushButton::clicked, this, [=]() { recallMem(mem.channel); }); } ui->table->model()->setData(ui->table->model()->index(row,columnNum),QString::number(mem.channel & 0xffff).rightJustified(3,'0')); ui->table->item(row,columnNum)->setFlags(ui->table->item(row,columnNum)->flags() & (~Qt::ItemIsEditable)); ui->table->item(row,columnNum)->setBackground(Qt::transparent); validData++; validData = validData + updateRow(row,mem,false); if (retries > 2) { retries=0; return; } if (validData < visibleColumns) { qInfo(logRig()) << "Memory" << mem.channel << "Received valid data for" << validData << "columns, " << "expected" << visibleColumns << "requesting again"; if (mem.sat) { queue->add(priorityImmediate,queueItem(funcSatelliteMemory,QVariant::fromValue(mem.channel & 0xffff))); } else { queue->add(priorityImmediate,queueItem(funcMemoryContents,QVariant::fromValue((mem.group<<16) | (mem.channel & 0xffff)))); } retries++; } } else if (row != -1) { // Check if we already have this memory as it might have failed to write? //ui->loadingMemories->setStyleSheet("QLabel {color: #ff0000}"); ui->table->item(row,columnNum)->setBackground(Qt::red); } } int memories::updateEntry(QStringList& combo, int row, columns column, quint8 data) { int ret=1; if (!ui->table->isColumnHidden(column) && combo.size() > data) { ui->table->model()->setData(ui->table->model()->index(row,column),combo[data]); } else if (!ui->table->isColumnHidden(column)) { qInfo() << "Column" << ui->table->horizontalHeaderItem(column)->text() << "Invalid data:" << data << "setting to default"; ui->table->model()->setData(ui->table->model()->index(row,column),combo[0]); ret=1; } else { ret=0; } return ret; } int memories::updateEntry(QStringList& combo, int row, columns column, QString data) { int ret=1; if (!ui->table->isColumnHidden(column) && combo.contains(data)) { ui->table->model()->setData(ui->table->model()->index(row,column),data); } else if (!ui->table->isColumnHidden(column)) { qInfo() << "Column" << ui->table->horizontalHeaderItem(column)->text() << "Invalid data:" << data; ret=0; } else { ret=0; } return ret; } int memories::updateEntry(int row, columns column, QString data) { int ret=0; if (!ui->table->isColumnHidden(column)) { ui->table->model()->setData(ui->table->model()->index(row,column),data); ret=1; } return ret; } bool memories::checkASCII(QString str) { static QRegularExpression exp = QRegularExpression(QStringLiteral("[^\\x{0020}-\\x{007E}]")); bool containsNonASCII = str.contains(exp); return !containsNonASCII; } void memories::timeout() { if (rigCaps->manufacturer == manufYaesu) { timeoutTimer.stop(); ui->group->setEnabled(true); if (!ui->disableEditing->isChecked()) { ui->table->setEditTriggers(QAbstractItemView::DoubleClicked); } // We have reached the last memory and Yaesu usefully doesn't respond to non-existant ones! startup=false; } else { if (timeoutCount < 3 ) { qInfo(logRig()) << "Timeout receiving memory:" << (lastMemoryRequested & 0xffff) << "in group" << (lastMemoryRequested >> 16 & 0xffff); if (ui->group->currentData().toInt() == MEMORY_SATGROUP) { queue->add(priorityImmediate,queueItem(funcSatelliteMemory,QVariant::fromValue(lastMemoryRequested & 0xffff))); } else { queue->add(priorityImmediate,queueItem(funcMemoryContents,QVariant::fromValue(lastMemoryRequested))); } timeoutTimer.start(MEMORY_TIMEOUT); timeoutCount++; } else { timeoutCount=0; //ui->loadingMemories->setVisible(false); timeoutTimer.stop(); ui->group->setEnabled(true); if (!ui->disableEditing->isChecked()) { ui->table->setEditTriggers(QAbstractItemView::DoubleClicked); } QMessageBox::information(this,"Timeout", "Timeout receiving memories, check rig connection", QMessageBox::Ok); } } } QStandardItemModel* memories::createModel(QStandardItemModel* model, QStringList strings) { model = new QStandardItemModel(); for (int i=0; i < strings.size();i++) { QStandardItem *itemName = new QStandardItem(strings[i]); QStandardItem *itemId = new QStandardItem(i); QList row; row << itemName << itemId; model->appendRow(row); } return model; } void memories::on_csvImport_clicked() { QString file = QFileDialog::getOpenFileName(this,"Select import filename","","CSV Files (*.csv)"); if (!file.isEmpty()) { ui->table->blockSignals(true); ui->table->sortByColumn(0,Qt::AscendingOrder); // Force natural order QFile data(file); if (!data.open(QIODevice::ReadOnly)) { qInfo() << "Couldn't open file for .csv import" << data.errorString(); return; } int lastcol=0; for (int i=0;itable->columnCount();i++) { if (!ui->table->isColumnHidden(i)) lastcol=i; } QTextStream input(&data); QStringList row; int rows=0; progress->setRange(rigCaps->memStart,rigCaps->memories); while (readCSVRow(input, &row)) { progress->setValue(rows); if (!rows++) continue; // Skip the first row int rownum = -1; for(int i=0;itable->rowCount();i++) { if (ui->table->item(i, 1)->text().toInt() == row[0].toInt()) { rownum=i; break; } } if (rownum == -1) { // We need to add a new row rownum = ui->table->rowCount(); ui->table->insertRow(rownum); QPushButton* recall = new QPushButton("Recall"); ui->table->setCellWidget(rownum,columnRecall,recall); connect(recall, &QPushButton::clicked, this, [=]() { recallMem(row[0].toInt()); }); } // rownum is now the row we need to work on. int colnum=1; for (int i=0; icheckAllFields->isChecked()) { while (colnum < ui->table->columnCount() && ui->table->isColumnHidden(colnum)) { colnum++; } } if (colnum < ui->table->columnCount() && row.size() > i) { QString data = row[i]; for (int n= data.size(); n<=0; --n) { if (!data.at(n).isSpace()) { data.truncate(n+1); break; } } switch (colnum) { // special cases: case columnFrequency: case columnFrequencyB: data= QString::number(data.toDouble(),'f',6); break; case columnDTCS: case columnDTCSB: data=QString::number(data.toInt()).rightJustified(3,'0'); break; case columnDVSquelch: case columnDVSquelchB: data=QString::number(data.toInt()).rightJustified(2,'0'); break; case columnTone: case columnToneB: case columnTSQL: case columnTSQLB: if (data.endsWith("Hz")) data=data.mid(0,data.length()-2); break; case columnFilter: case columnFilterB: if (!data.startsWith("FIL")) data = "FIL"+data; break; case columnAntenna: if (!data.startsWith("ANT")) data = "ANT "+data; default: break; } ui->table->model()->setData(ui->table->model()->index(rownum,colnum),data); if (colnum == lastcol) { on_table_cellChanged(rownum,colnum); // store this row. } colnum++; } } } ui->table->blockSignals(false); progress->setValue(rigCaps->memories); } } void memories::on_csvExport_clicked() { QString file = QFileDialog::getSaveFileName(this,"Select export filename","","CSV Files (*.csv)"); if (!file.isEmpty()) { ui->table->sortByColumn(0,Qt::AscendingOrder); // Force natural order QFile data(file); if (data.open(QFile::WriteOnly | QIODevice::Truncate)) { QTextStream output(&data); for(int i=1;itable->columnCount();i++) { if (!ui->table->isColumnHidden(i) || ui->checkAllFields->isChecked()) { output << "\"" << ui->table->horizontalHeaderItem(i)->text() << "\""; if (i < ui->table->columnCount()-1) output << ","; else output << "\n"; } else if (i == ui->table->columnCount()-1) { output << "\n"; } } for(int i=0;itable->rowCount();i++) { for(int j=1;jtable->columnCount();j++) { if (!ui->table->isColumnHidden(j) || ui->checkAllFields->isChecked()) { output << "\"" << ((ui->table->item(i,j) == NULL) ? "" : ui->table->item(i,j)->text()) << "\""; if (j < ui->table->columnCount()-1) output << ","; else output << "\n"; } else if (j == ui->table->columnCount()-1) { output << "\n"; } } } } data.close(); } } // Public Domain CSV parser from user iamantony // https://www.appsloveworld.com/cplus/100/37/parsing-through-a-csv-file-in-qt bool memories::readCSVRow(QTextStream &in, QStringList *row) { static const int delta[][5] = { // , " \n ? eof { 1, 2, -1, 0, -1 }, // 0: parsing (store char) { 1, 2, -1, 0, -1 }, // 1: parsing (store column) { 3, 4, 3, 3, -2 }, // 2: quote entered (no-op) { 3, 4, 3, 3, -2 }, // 3: parsing inside quotes (store char) { 1, 3, -1, 0, -1 }, // 4: quote exited (no-op) // -1: end of row, store column, success // -2: eof inside quotes }; row->clear(); if (in.atEnd()) return false; int state = 0, t; char ch; QString cell; while (state >= 0) { if (in.atEnd()) t = 4; else { in >> ch; if (ch == ',') t = 0; else if (ch == '\"') t = 1; else if (ch == '\n') t = 2; else t = 3; } state = delta[state][t]; switch (state) { case 0: case 3: cell += ch; break; case -1: case 1: row->append(cell); cell = ""; break; } } if (state == -2) { qInfo() << "End-of-file found while inside quotes."; return false; } return true; } void memories::on_disableEditing_toggled(bool dis) { if (dis) { ui->table->setEditTriggers(QAbstractItemView::NoEditTriggers); } else { ui->table->setEditTriggers(QAbstractItemView::DoubleClicked); } ui->csvImport->setEnabled(!dis); ui->table->editing(!dis); } void memories::on_scanButton_toggled(bool scan) { // Change the priority of funcSelectedFreq so that it updates quicker during scans! static queuePriority prio = priorityNone; if (scan) { ui->scanButton->setText("Stop Scan"); prio=queue->changePriority(priorityHighest,funcSelectedFreq,0); } else { ui->scanButton->setText("Start Scan"); if (prio != priorityNone) queue->changePriority(prio,funcSelectedFreq,0); } queue->add(priorityImmediate,queueItem(funcScanning,QVariant::fromValue(uchar(scan)))); } void memories::configColumns(int row, modeInfo mode, quint8 split) { enableCell(row,columnTone,false); enableCell(row,columnToneMode,false); enableCell(row,columnTSQL,false); enableCell(row,columnDTCS,false); enableCell(row,columnDTCSPolarity,false); enableCell(row,columnDVSquelch,false); enableCell(row,columnDSQL,false); enableCell(row,columnR1,false); enableCell(row,columnR2,false); enableCell(row,columnUR,false); enableCell(row,columnP25Nac,false); enableCell(row,columnP25Sql,false); enableCell(row,columnDPmrSql,false); enableCell(row,columnDPmrComid,false); enableCell(row,columnDPmrCc,false); enableCell(row,columnDPmrSCRM,false); enableCell(row,columnDPmrKey,false); enableCell(row,columnNxdnSql,false); enableCell(row,columnNxdnRan,false); enableCell(row,columnNxdnEnc,false); enableCell(row,columnNxdnKey,false); enableCell(row,columnDcrSql,false); enableCell(row,columnDcrUc,false); enableCell(row,columnDcrEnc,false); enableCell(row,columnDcrKey,false); switch (mode.mk) { case modeFM: enableCell(row,columnTone,true); enableCell(row,columnToneMode,true); enableCell(row,columnTSQL,true); enableCell(row,columnDTCS,true); enableCell(row,columnDTCSPolarity,true); break; case modeP25: enableCell(row,columnP25Nac,true); enableCell(row,columnP25Sql,true); break; case modedPMR: enableCell(row,columnDPmrSql,true); enableCell(row,columnDPmrComid,true); enableCell(row,columnDPmrCc,true); enableCell(row,columnDPmrSCRM,true); enableCell(row,columnDPmrKey,true); break; case modeNXDN_N: case modeNXDN_VN: enableCell(row,columnNxdnSql,true); enableCell(row,columnNxdnRan,true); enableCell(row,columnNxdnEnc,true); enableCell(row,columnNxdnKey,true); break; case modeDCR: enableCell(row,columnDcrSql,true); enableCell(row,columnDcrUc,true); enableCell(row,columnDcrEnc,true); enableCell(row,columnDcrKey,true); break; case modeDV: enableCell(row,columnDVSquelch,true); enableCell(row,columnDSQL,true); enableCell(row,columnR1,true); enableCell(row,columnR2,true); enableCell(row,columnUR,true); break; default: break; } if (rigCaps->manufacturer==manufYaesu) { enableCell(row,columnFrequencyB,bool(split>0)); } } void memories::configColumnsB(int row, modeInfo mode) { enableCell(row,columnToneB,false); enableCell(row,columnToneModeB,false); enableCell(row,columnTSQLB,false); enableCell(row,columnDTCSB,false); enableCell(row,columnDTCSPolarityB,false); enableCell(row,columnDVSquelchB,false); enableCell(row,columnDSQLB,false); enableCell(row,columnR1B,false); enableCell(row,columnR2B,false); enableCell(row,columnURB,false); switch (mode.mk) { case modeFM: enableCell(row,columnToneB,true); enableCell(row,columnToneModeB,true); enableCell(row,columnTSQLB,true); enableCell(row,columnDTCSB,true); enableCell(row,columnDTCSPolarityB,true); break; case modeDV: enableCell(row,columnDVSquelchB,true); enableCell(row,columnDSQLB,true); enableCell(row,columnR1B,true); enableCell(row,columnR2B,true); enableCell(row,columnURB,true); break; default: break; } }