diff --git a/mainwindow.cpp b/mainwindow.cpp index 093fd54..92a8522 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -909,6 +909,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect (&splashTimer, &QTimer::timeout, this, &MainWindow::splash_done); splashTimer.setSingleShot (true); splashTimer.start (20 * 1000); + /* if(m_config.my_callsign()=="K1JT" or m_config.my_callsign()=="K9AN" or m_config.my_callsign()=="G4WJS" || m_config.my_callsign () == "W9XYZ" or @@ -2169,6 +2170,11 @@ void MainWindow::closeEvent(QCloseEvent * e) QMainWindow::closeEvent (e); } +void MainWindow::on_labDialFreq_clicked() //dialFrequency +{ + ui->bandComboBox->setFocus(); +} + void MainWindow::on_stopButton_clicked() //stopButton { monitor (false); @@ -2939,10 +2945,45 @@ void MainWindow::readFromStdout() //readFromStdout m_logBook,m_config.color_CQ(),m_config.color_MyCall(), m_config.color_DXCC(), m_config.color_NewCall(), m_config.ppfx(),(ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked())); - ui->textEditRXAll->append(decodedtext.messageWords().first().trimmed()); + + // TODO: parse decode... + //ui->textEditRXAll->append(decodedtext.messageWords().first().trimmed()); + //ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount()); + //ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount()-1, 0, new QTableWidgetItem(QString("%1").arg(decodedtext.frequencyOffset()))); + //ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount()-1, 1, new QTableWidgetItem(decodedtext.messageWords().first().trimmed())); + if(decodedtext.messageWords().length() > 0){ + int offset = decodedtext.frequencyOffset(); + + if(!m_bandActivity.contains(offset)){ + QList offsets = {offset - 1, offset - 2, offset - 3, offset + 1, offset + 2, offset + 3}; + foreach(int prevOffset, offsets){ + if(!m_bandActivity.contains(prevOffset)){ continue; } + m_bandActivity[offset] = m_bandActivity[prevOffset]; + m_bandActivity.remove(prevOffset); + break; + } + } + + ActivityDetail d; + d.freq = offset; + d.text = decodedtext.messageWords().first().trimmed(); + d.timestamp = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); + d.snr = decodedtext.snr(); + m_bandActivity[offset].append(d); + + while(m_bandActivity[offset].count() > 10){ + m_bandActivity[offset].removeFirst(); + } + } + QString cqCall = decodedtext.CQersCall(); if(!cqCall.isEmpty()){ - ui->listWidget->addItem(cqCall); + CallDetail d; + d.call = cqCall; + d.snr = decodedtext.snr(); + d.freq = decodedtext.frequencyOffset(); + d.timestamp = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); + m_callActivity[cqCall] = d; } } } @@ -2985,6 +3026,7 @@ void MainWindow::readFromStdout() //readFromStdout } m_QSOText = decodedtext.string ().trimmed (); + // TODO: parse decode... //ui->textEditRXAll->insertHtml(decodedtext.messageWords().first().trimmed() + "\n"); } @@ -3055,6 +3097,8 @@ void MainWindow::readFromStdout() //readFromStdout } } } + + // See MainWindow::postDecode for displaying the latest decodes } // @@ -3700,6 +3744,11 @@ void MainWindow::guiUpdate() QString utc = t.date().toString("yyyy MMM dd") + "\n " + t.time().toString() + " "; ui->labUTC->setText(utc); + + auto delta = t.msecsTo(m_nextBeacon)/1000; + auto beacon = ui->beaconButton->isChecked() ? delta > 0 ? QString("%1 s").arg(delta) : "queued!" : "disabled"; + ui->labBeacon->setText(QString("Next Beacon: %1").arg(beacon)); + if(!m_monitoring and !m_diskData) { ui->signal_meter_widget->setValue(0,0); } @@ -6066,6 +6115,13 @@ void MainWindow::on_pbT2R_clicked() } } +void MainWindow::on_beaconButton_clicked() +{ + if(ui->beaconButton->isChecked()){ + m_nextBeacon = QDateTime::currentDateTimeUtc().addSecs(300); + } +} + void MainWindow::on_readFreq_clicked() { @@ -6812,6 +6868,50 @@ void MainWindow::postDecode (bool is_new, QString const& message) , QChar {'?'} == decode.mid (has_seconds ? 24 + 21 : 22 + 21, 1) , m_diskData); } + + + + + // TODO: keep track of selection + int now = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch(); + for(int i = ui->tableWidgetRXAll->rowCount(); i >= 0; i--){ + ui->tableWidgetRXAll->removeRow(i); + } + QList keys = m_bandActivity.keys(); + qSort(keys.begin(), keys.end()); + foreach (int offset, keys) { + auto items = m_bandActivity[offset]; + if(items.length() > 0){ + QStringList text; + int snr = 0; + foreach(auto item, items){ + if(now - item.timestamp > 90000){ + continue; + } + text.append(item.text); + snr = item.snr; + } + auto joined = text.join(" … "); + if(joined.isEmpty()){ + continue; + } + + ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount()); + ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, new QTableWidgetItem(QString("%1").arg(offset))); + ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, new QTableWidgetItem(QString("%1").arg(snr))); + ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, new QTableWidgetItem(joined)); + } + } + + // TODO: keep track of selection + ui->listWidget->clear(); + ui->listWidget->addItem("allcall"); + QList calls = m_callActivity.keys(); + qSort(calls.begin(), calls.end()); + foreach(QString call, calls){ + ui->listWidget->addItem(call); + } + } void MainWindow::postWSPRDecode (bool is_new, QStringList parts) diff --git a/mainwindow.h b/mainwindow.h index fad49d0..c379fbf 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include "astro.h" #include "MessageBox.hpp" #include "NetworkAccessManager.hpp" +#include "qorderedmap.h" #define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync #define NUM_JT65_SYMBOLS 126 //63 data + 63 sync @@ -135,6 +137,7 @@ private slots: void on_monitorButton_clicked (bool); void on_actionAbout_triggered(); void on_autoButton_clicked (bool); + void on_labDialFreq_clicked(); void on_stopTxButton_clicked(); void on_stopButton_clicked(); void on_actionRelease_Notes_triggered (); @@ -226,6 +229,7 @@ private slots: void on_tuneButton_clicked (bool); void on_pbR2T_clicked(); void on_pbT2R_clicked(); + void on_beaconButton_clicked(); void acceptQSO (QDateTime const&, QString const& call, QString const& grid , Frequency dial_freq, QString const& mode , QString const& rpt_sent, QString const& rpt_received @@ -292,7 +296,7 @@ private slots: void on_actionMSK144_triggered(); void on_actionQRA64_triggered(); void on_actionFreqCal_triggered(); - void splash_done (); + void splash_done (); void on_measure_check_box_stateChanged (int); void on_sbNlist_valueChanged(int n); void on_sbNslots_valueChanged(int n); @@ -591,6 +595,24 @@ private: qint32 ncall; }; + struct CallDetail + { + QString call; + int freq; + int timestamp; + int snr; + }; + + struct ActivityDetail + { + int freq; + QString text; + int timestamp; + int snr; + }; + + QMap> m_bandActivity; // freq -> [(text, last timestamp), ...] + QMap m_callActivity; // call -> (last freq, last timestamp) QMap m_foxQSO; QMap m_loggedByFox; @@ -599,6 +621,7 @@ private: QQueue m_foxRR73Queue; QQueue m_foxRateQueue; + QDateTime m_nextBeacon; QDateTime m_dateTimeQSOOn; QDateTime m_dateTimeLastTX; diff --git a/mainwindow.ui b/mainwindow.ui index 13d341c..f0a4dd3 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -17,7 +17,7 @@ - + 4 @@ -50,6 +50,645 @@ true + + + + 0 + 0 + + + + + 0 + 120 + + + + + 16777215 + 120 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + color:white; background-color:rgb(0,0,0); + + + QFrame::NoFrame + + + QFrame::Sunken + + + + + + + 75 + 0 + + + + true + + + <html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html> + + + background-color:white; +color:black; + + + true + + + QComboBox::NoInsert + + + QComboBox::AdjustToMinimumContentsLength + + + + + + + QPushButton { + font-family: MS Shell Dlg 2; + font-size: 20pt; + color : white; + background-color : black; +} +QPushButton[oob="true"] { + color : #EEEEEE; + font-style: italic; +} + + + + 14.074 000 + + + true + + + + + + + + 0 + 0 + + + + Current frequency offset + + + QLabel { + font-family: MS Shell Dlg 2; + font-size: 12pt; + font-style: italic; + color : white; + background-color : black; +} + + + + 1500 Hz + + + Qt::AlignCenter + + + 0 + + + + + + + + + + background-color : black; +o + + + QFrame::StyledPanel + + + QFrame::Plain + + + 2 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QLabel { +font-family: MS Shell Dlg 2; +font-size: 14pt; +color : white; +} + + + QFrame::StyledPanel + + + QFrame::Sunken + + + 2 + + + 0 + + + <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> + + + Qt::AlignCenter + + + 5 + + + + + + + QLabel { +font-family: MS Shell Dlg 2; +font-size: 12pt; +color : white; +} + + + Next Beacon: disabled + + + Qt::RichText + + + Qt::AlignCenter + + + 5 + + + + + + + + + + + 0 + 0 + + + + color:white; background-color:rgb(0,0,0); + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + false + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html> + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: white; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} + + + CAT + + + + + + + true + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: lightgray; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} + + + LOG + + + + + + + true + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: lightgray; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} +QPushButton:checked { +background-color: #6699ff; +} + + + BCN + + + true + + + + + + + false + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: lightgray; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} +QPushButton:checked { +background-color: #00ff00; +} + + + TX + + + true + + + + + + + true + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: lightgray; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px;; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} +QPushButton:checked { +background-color: #00ff00; +} + + + RX + + + true + + + + + + + true + + + + 0 + 0 + + + + + 40 + 30 + + + + + 40 + 30 + + + + QPushButton { +font-family: helvetica; +font-size: 9pt; +font-weight: bold; +background-color: lightgray; +color: black; +border-style: solid; +border-width:0px; +border-color: gray; +max-height:30px; +min-width:40px; +min-height:30px; +max-width:40px; +} +QPushButton[state="error"] { +background-color: red; +} +QPushButton[state="warning"] { +background-color: orange; +} +QPushButton[state="ok"] { +background-color: #00ff00; +} +QPushButton:checked { +background-color: yellow; +} + + + TUNE + + + true + + + + + + + + + + + @@ -69,29 +708,80 @@ 4 + + + + 6 + 0 + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + + Qt::DotLine + + + true + + + true + + + true + + + false + + + + Offset + + + + + SNR + + + + + Text + + + + + + 10 + 0 + + Qt::Vertical 4 - - - background-color: #dfe6e9; - - - true - - - All band activity will appear here. - - - - 0 - 0 + + 4 + 2 @@ -114,9 +804,9 @@ - - 0 - 0 + + 4 + 1 @@ -133,16 +823,10 @@ - 0 + 1 0 - - - 50 - 0 - - 16777215 @@ -159,11 +843,154 @@ - - - + + + + 0 + 0 + + + + + 0 + 30 + + + + + 16777215 + 30 + + + + 0 + + 0 + + + 0 + + + 0 + + + + + + 0 + 30 + + + + QTC + + + + + + + + 0 + 30 + + + + Halt + + + + + + + + 0 + 30 + + + + CQ + + + false + + + + + + + + 0 + 30 + + + + Send + + + + + + + + 0 + 30 + + + + QTH + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 0 + 30 + + + + SNR + + + + + + + + + 0 + 2 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 4 + 0 @@ -177,58 +1004,30 @@ 0 - - - 0 - - - - - <html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html> - - - true - - - QComboBox::NoInsert - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 - - 0 - - - 0 - - - 0 - - - 0 - - + 0 @@ -241,7 +1040,7 @@ QFrame::Plain - + 0 @@ -252,24 +1051,18 @@ 0 - 0 + 4 0 - - - - 0 - 0 - - - - false - - - color:white; background-color:rgb(0,0,0); + + + + 0 + 0 + QFrame::NoFrame @@ -277,152 +1070,50 @@ QFrame::Plain - - - - - - 0 - 0 - - - - USB dial frequency - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 20pt; - color : white; - background-color : black; -} -QLabel[oob="true"] { - color : #EEEEEE; - font-style: italic; -} - - - - 14.078 000 - - - Qt::AlignCenter - - - 0 - - - - - - - - 0 - 0 - - - - Current frequency offset - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 12pt; - font-style: italic; - color : white; - background-color : black; -} - - - - 1500 Hz - - - Qt::AlignCenter - - - 0 - - - - - - - Qt::Vertical - - - - 20 - 20 - - - - - - - - - 0 - 0 - - - - QLabel { - font-family: MS Shell Dlg 2; - font-size: 12pt; - background-color : black; - color : white; -} - - - QFrame::StyledPanel - - - QFrame::Sunken - - - 2 - - - 0 - - - <html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html> - - - Qt::AlignCenter - - - 5 - - - - - - - Qt::Vertical - - - - 20 - 20 - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + - + - + 0 0 + + + 75 + 0 + + + + + 16777215 + 16777215 + + + + <html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html> + - color:white; background-color:rgb(0,0,0); + background-color:black; QFrame::NoFrame @@ -430,358 +1121,79 @@ QLabel[oob="true"] { QFrame::Plain - - - - - false - - - <html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html> - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: white; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} - - - CAT - - - - - - - true - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: lightgray; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} -QPushButton:checked { -background-color: #00ff00; -} - - - RX - - - true - - - - - - - false - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: lightgray; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} -QPushButton:checked { -background-color: #00ff00; -} - - - TX - - - true - - - - - - - true - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: lightgray; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} -QPushButton:checked { -background-color: yellow; -} - - - TUNE - - - true - - - - - - - true - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: lightgray; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} -QPushButton:checked { -background-color: #6699ff; -} - - - BCN - - - true - - - - - - - true - - - QPushButton { -font-family: helvetica; -font-size: 9pt; -font-weight: bold; -background-color: lightgray; -color: black; -border-style: solid; -border-width:0px; -border-color: gray; -max-height:20px; -min-width:20px; -min-height:20px; -max-width:40px; -} -QPushButton[state="error"] { -background-color: red; -} -QPushButton[state="warning"] { -background-color: orange; -} -QPushButton[state="ok"] { -background-color: #00ff00; -} - - - HALT - - - - - monitorButton - readFreq - beaconButton - autoButton - stopTxButton - tuneButton + + + + + + + 0 + 0 + + + + + 0 + 50 + + + + QFrame::Plain + + + + + 0 + 0 + 0 + + + + + + + - - - - 0 - 0 - - - - - 75 - 0 - - - - - 16777215 - 16777215 - - + - <html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html> + Adjust Tx audio level - background-color:black; + selection-background-color: rgb(70, 70, 70); - - QFrame::NoFrame + + 450 - - QFrame::Plain + + 0 + + + Qt::Vertical + + + true + + + true + + + QSlider::TicksBothSides + + + 50 - - - - - - - - - 0 - 0 - - - - - 0 - 50 - - - - QFrame::Plain - - - - - 0 - 0 - 0 - - - + + - - - - - - Adjust Tx audio level - - - selection-background-color: rgb(70, 70, 70); - - - 450 - - - 0 - - - Qt::Vertical - - - true - - - true - - - QSlider::TicksBothSides - - - 50 - - - - - diff --git a/qorderedmap.h b/qorderedmap.h new file mode 100644 index 0000000..5ded9d7 --- /dev/null +++ b/qorderedmap.h @@ -0,0 +1,611 @@ +#ifndef ORDEREDMAP_H +#define ORDEREDMAP_H + +#include +#include +#include +#include +#include + +template inline bool oMHashEqualToKey(const Key &key1, const Key &key2) +{ + // Key type must provide '==' operator + return key1 == key2; +} + +template inline bool oMHashEqualToKey(Ptr *key1, Ptr *key2) +{ + Q_ASSERT(sizeof(quintptr) == sizeof(Ptr *)); + return quintptr(key1) == quintptr(key2); +} + +template inline bool oMHashEqualToKey(const Ptr *key1, const Ptr *key2) +{ + Q_ASSERT(sizeof(quintptr) == sizeof(const Ptr *)); + return quintptr(key1) == quintptr(key2); +} + +template +class OrderedMap +{ + class OMHash; + + typedef typename QLinkedList::iterator QllIterator; + typedef typename QLinkedList::const_iterator QllConstIterator; + typedef QPair OMHashValue; + + typedef typename OMHash::iterator OMHashIterator; + typedef typename OMHash::const_iterator OMHashConstIterator; + +public: + + class iterator; + class const_iterator; + + typedef typename OrderedMap::iterator Iterator; + typedef typename OrderedMap::const_iterator ConstIterator; + + explicit OrderedMap(); + + OrderedMap(const OrderedMap& other); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + OrderedMap(OrderedMap&& other); +#endif + + void clear(); + + bool contains(const Key &key) const; + + int count() const; + + bool empty() const; + + iterator insert(const Key &key, const Value &value); + + bool isEmpty() const; + + QList keys() const; + + int remove(const Key &key); + + int size() const; + + Value take(const Key &key); + + Value value(const Key &key) const; + + Value value(const Key &key, const Value &defaultValue) const; + + QList values() const; + + OrderedMap & operator=(const OrderedMap& other); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + OrderedMap & operator=(OrderedMap&& other); +#endif + + bool operator==(const OrderedMap &other) const; + + bool operator!=(const OrderedMap &other) const; + + Value& operator[](const Key &key); + + const Value operator[](const Key &key) const; + + iterator begin(); + + const_iterator begin() const; + + iterator end(); + + const_iterator end() const; + + iterator erase(iterator pos); + + iterator find(const Key& key); + + const_iterator find(const Key& key) const; + + class const_iterator; + + class iterator + { + QllIterator qllIter; + OMHash *data; + friend class const_iterator; + friend class OrderedMap; + + public: + iterator() : data(NULL) {} + + iterator(const QllIterator &qllIter, OMHash *data) : + qllIter(qllIter), data(data) {} + + const Key & key() const + { + return *qllIter; + } + + Value & value() const + { + OMHashIterator hit = data->find(*qllIter); + OMHashValue &pair = hit.value(); + return pair.first; + } + + Value & operator*() const + { + return value(); + } + + iterator operator+(int i) const + { + QllIterator q = qllIter; + q += i; + + return iterator(q, data); + } + + iterator operator-(int i) const + { + return operator +(- i); + } + + iterator& operator+=(int i) + { + qllIter += i; + return *this; + } + + iterator& operator-=(int i) + { + qllIter -= i; + return *this; + } + + iterator& operator++() + { + ++qllIter; + return *this; + } + + iterator operator++(int) + { + iterator it = *this; + qllIter++; + return it; + } + + iterator operator--() + { + --qllIter; + return *this; + } + + iterator operator--(int) + { + iterator it = *this; + qllIter--; + return it; + } + + bool operator ==(const iterator &other) const + { + return (qllIter == other.qllIter); + } + + bool operator !=(const iterator &other) const + { + return (qllIter != other.qllIter); + } + }; + + class const_iterator + { + + QllConstIterator qllConstIter; + const OMHash *data; + + public: + const_iterator() : data(NULL) {} + + const_iterator(const iterator &i) : + qllConstIter(i.qllIter), data(i.data) {} + + const_iterator(const QllConstIterator &qllConstIter, const OMHash* data) : + qllConstIter(qllConstIter), data(data) {} + + const Key & key() const + { + return *qllConstIter; + } + + const Value & value() const + { + OMHashConstIterator hit = data->find(*qllConstIter); + const OMHashValue &pair = hit.value(); + return pair.first; + } + + const Value & operator*() const + { + return value(); + } + + const_iterator operator+(int i) const + { + QllConstIterator q = qllConstIter; + q += i; + + return const_iterator(q, data); + } + + const_iterator operator-(int i) const + { + return operator +(- i); + } + + const_iterator& operator+=(int i) + { + qllConstIter += i; + return *this; + } + + const_iterator& operator-=(int i) + { + qllConstIter -= i; + return *this; + } + + const_iterator& operator++() + { + ++qllConstIter; + return *this; + } + + const_iterator operator++(int) + { + const_iterator it = *this; + qllConstIter++; + return it; + } + + const_iterator operator--() + { + --qllConstIter; + return *this; + } + + const_iterator operator--(int) + { + const_iterator it = *this; + qllConstIter--; + return it; + } + + bool operator ==(const const_iterator &other) const + { + return (qllConstIter == other.qllConstIter); + } + + bool operator !=(const const_iterator &other) const + { + return (qllConstIter != other.qllConstIter); + } + }; + +private: + + class OMHash : public QHash + { + public: + bool operator == (const OMHash &other) const + { + if (size() != other.size()) { + return false; + } + + if (QHash::operator ==(other)) { + return true; + } + + typename QHash::const_iterator it1 = this->constBegin(); + typename QHash::const_iterator it2 = other.constBegin(); + + while(it1 != this->end()) { + OMHashValue v1 = it1.value(); + OMHashValue v2 = it2.value(); + + if ((v1.first != v2.first) || !oMHashEqualToKey(it1.key(), it2.key())) { + return false; + } + ++it1; + ++it2; + } + return true; + } + }; + +private: + void copy(const OrderedMap &other); + + OMHash data; + QLinkedList insertOrder; +}; + +template +OrderedMap::OrderedMap() {} + +template +OrderedMap::OrderedMap(const OrderedMap& other) +{ + copy(other); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) +template +OrderedMap::OrderedMap(OrderedMap&& other) +{ + data = std::move(other.data); + insertOrder = std::move(other.insertOrder); +} +#endif + +template +void OrderedMap::clear() +{ + data.clear(); + insertOrder.clear(); +} + +template +bool OrderedMap::contains(const Key &key) const +{ + return data.contains(key); +} + +template +int OrderedMap::count() const +{ + return data.count(); +} + +template +bool OrderedMap::empty() const +{ + return data.empty(); +} + +template +typename OrderedMap::iterator OrderedMap::insert(const Key &key, const Value &value) +{ + OMHashIterator it = data.find(key); + + if (it == data.end()) { + // New key + QllIterator ioIter = insertOrder.insert(insertOrder.end(), key); + OMHashValue pair(value, ioIter); + data.insert(key, pair); + return iterator(ioIter, &data); + } + + OMHashValue pair = it.value(); + // remove old reference + insertOrder.erase(pair.second); + // Add new reference + QllIterator ioIter = insertOrder.insert(insertOrder.end(), key); + pair.first = value; + pair.second = ioIter; + return iterator(ioIter, &data); +} + +template +bool OrderedMap::isEmpty() const +{ + return data.isEmpty(); +} + +template +QList OrderedMap::keys() const +{ + return QList::fromStdList(insertOrder.toStdList()); +} + +template +int OrderedMap::remove(const Key &key) +{ + OMHashIterator it = data.find(key); + if (it == data.end()) { + return 0; + } + OMHashValue pair = it.value(); + insertOrder.erase(pair.second); + data.erase(it); + return 1; +} + +template +int OrderedMap::size() const +{ + return data.size(); +} + +template +void OrderedMap::copy(const OrderedMap &other) +{ + /* Since I'm storing iterators of QLinkedList, I simply cannot make + * a trivial copy of the linked list. This is a limitation due to implicit + * sharing used in Qt containers, due to which iterator active on one + * QLL can change the data of another QLL even after creating a copy. + * + * Because of this, the old iterators have to be invalidated and new ones + * have to be generated. + */ + insertOrder.clear(); + // Copy hash + data = other.data; + + QllConstIterator cit = other.insertOrder.begin(); + for (; cit != other.insertOrder.end(); ++cit) { + Key key = *cit; + QllIterator ioIter = insertOrder.insert(insertOrder.end(), key); + OMHashIterator it = data.find(key); + (*it).second = ioIter; + } +} + +template +Value OrderedMap::take(const Key &key) +{ + OMHashIterator it = data.find(key); + if (it == data.end()) { + return Value(); + } + OMHashValue pair = it.value(); + insertOrder.erase(pair.second); + data.erase(it); + return pair.first; +} + +template +Value OrderedMap::value(const Key &key) const +{ + return data.value(key).first; +} + +template +Value OrderedMap::value(const Key &key, const Value &defaultValue) const +{ + OMHashConstIterator it = data.constFind(key); + if (it == data.end()) { + return defaultValue; + } + OMHashValue pair = it.value(); + return pair.first; +} + +template +QList OrderedMap::values() const +{ + QList values; + foreach (const Key &key, insertOrder.toStdList()) { + OMHashValue v = data.value(key); + values.append(v.first); + } + return values; +} + +template +OrderedMap & OrderedMap::operator=(const OrderedMap& other) +{ + if (this != &other) { + copy(other); + } + return *this; +} + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) +template +OrderedMap & OrderedMap::operator=(OrderedMap&& other) +{ + if (this != &other) { + data = other.data; + insertOrder = other.insertOrder; + } + return *this; +} +#endif + +template +bool OrderedMap::operator==(const OrderedMap &other) const +{ + // 2 Ordered maps are equal if they have the same contents in the same order + return ((data == other.data) && (insertOrder == other.insertOrder)); +} + +template +bool OrderedMap::operator!=(const OrderedMap &other) const +{ + return ((data != other.data) || (insertOrder != other.insertOrder)); +} + +template +Value& OrderedMap::operator[](const Key &key) +{ + OMHashIterator it = data.find(key); + if (it == data.end()) { + insert(key, Value()); + it = data.find(key); + } + OMHashValue &pair = it.value(); + return pair.first; +} + +template +const Value OrderedMap::operator[](const Key &key) const +{ + return value(key); +} + +template +typename OrderedMap::iterator OrderedMap::begin() +{ + return iterator(insertOrder.begin(), &data); +} + +template +typename OrderedMap::const_iterator OrderedMap::begin() const +{ + return const_iterator(insertOrder.begin(), &data); +} + + +template +typename OrderedMap::iterator OrderedMap::end() +{ + return iterator(insertOrder.end(), &data); +} + +template +typename OrderedMap::const_iterator OrderedMap::end() const +{ + return const_iterator(insertOrder.end(), &data); +} + +template +typename OrderedMap::iterator OrderedMap::erase(iterator pos) +{ + OMHashIterator hit = data.find(*(pos.qllIter)); + if (hit == data.end()) { + return pos; + } + data.erase(hit); + QllIterator ioIter = insertOrder.erase(pos.qllIter); + + return iterator(ioIter, &data); +} + +template +typename OrderedMap::iterator OrderedMap::find(const Key& key) +{ + OMHashIterator hit = data.find(key); + if (hit == data.end()) { + return end(); + } + + return iterator(hit.value().second, &data); +} + +template +typename OrderedMap::const_iterator OrderedMap::find(const Key& key) const +{ + OMHashConstIterator hit = data.find(key); + if (hit == data.end()) { + return end(); + } + + return const_iterator(hit.value().second, &data); +} + +#endif // ORDEREDMAP_H diff --git a/wsjtx.pro b/wsjtx.pro index 1a327c9..277f876 100644 --- a/wsjtx.pro +++ b/wsjtx.pro @@ -83,7 +83,8 @@ HEADERS += qt_helpers.hpp \ logbook/logbook.h logbook/countrydat.h logbook/countriesworked.h logbook/adif.h \ messageaveraging.h echoplot.h echograph.h fastgraph.h fastplot.h Modes.hpp WSPRBandHopping.hpp \ WsprTxScheduler.h SampleDownloader.hpp MultiSettings.hpp PhaseEqualizationDialog.hpp \ - IARURegions.hpp MessageBox.hpp EqualizationToolsDialog.hpp + IARURegions.hpp MessageBox.hpp EqualizationToolsDialog.hpp \ + qorderedmap.h INCLUDEPATH += qmake_only