From 0a24aff1fa58a910356a3f86acc41fd37f2b502c Mon Sep 17 00:00:00 2001 From: "G.K.MacGregor" Date: Sat, 19 Sep 2020 19:34:41 +0100 Subject: [PATCH] Add X/27/4 and X/27/5 compositional link editing --- README.md | 1 + document.cpp | 30 ++++- levelonepage.cpp | 80 +++++++++++- levelonepage.h | 17 +++ pageenhancementsdockwidget.cpp | 217 ++++++++++++++++++++++++++++++--- pageenhancementsdockwidget.h | 8 ++ 6 files changed, 330 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 68c460d..3cff8f7 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Features - Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5 - Rendering of Local Objects and side panels. - Interactive X/26 Local Enhancement Data triplet editor. +- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages. - Palette editor. - Configurable zoom. - View teletext pages in 4:3, 16:9 pillar-box and 16:9 stretch aspect ratios. diff --git a/document.cpp b/document.cpp index dcd6846..b4562b5 100644 --- a/document.cpp +++ b/document.cpp @@ -207,10 +207,26 @@ void TeletextDocument::setPageNumber(QString newPageNumberString) // The LineEdit should check if a valid hex number was entered, but just in case... bool newPageNumberOk; int newPageNumberRead = newPageNumberString.toInt(&newPageNumberOk, 16); - if (newPageNumberOk && newPageNumberRead >= 0x100 && newPageNumberRead <= 0x8fe) { - m_pageNumber = newPageNumberRead; - for (auto &subPage : m_subPages) - subPage->setPageNumber(newPageNumberRead); + if ((!newPageNumberOk) || newPageNumberRead < 0x100 || newPageNumberRead > 0x8fe) + return; + + // If the magazine number was changed, we'll need to update the relative magazine numbers in X/27 + int oldMagazine = (m_pageNumber & 0xf00); + int newMagazine = (newPageNumberRead & 0xf00); + // Fix magazine 0 to 8 + if (oldMagazine == 0x800) + oldMagazine = 0x000; + if (newMagazine == 0x800) + newMagazine = 0x000; + int magazineFlip = oldMagazine ^ newMagazine; + + m_pageNumber = newPageNumberRead; + + for (auto &subPage : m_subPages) { + subPage->setPageNumber(newPageNumberRead); + if (magazineFlip) + for (int i=0; i<8; i++) + subPage->setComposeLinkPageNumber(i, subPage->composeLinkPageNumber(i) ^ magazineFlip); } } @@ -224,8 +240,10 @@ void TeletextDocument::setFastTextLink(int linkNumber, QString newPageNumberStri // The LineEdit should check if a valid hex number was entered, but just in case... bool newPageNumberOk; int newPageNumberRead = newPageNumberString.toInt(&newPageNumberOk, 16); - if (newPageNumberOk && newPageNumberRead >= 0x100 && newPageNumberRead <= 0x8ff) - m_fastTextLink[linkNumber] = newPageNumberRead; + if ((!newPageNumberOk) || newPageNumberRead < 0x100 || newPageNumberRead > 0x8ff) + return; + + m_fastTextLink[linkNumber] = newPageNumberRead; } void TeletextDocument::cursorUp() diff --git a/levelonepage.cpp b/levelonepage.cpp index e3ab3f8..ba7c798 100644 --- a/levelonepage.cpp +++ b/levelonepage.cpp @@ -55,6 +55,11 @@ void LevelOnePage::clearPage() for (int r=0; r<25; r++) for (int c=0; c<40; c++) m_level1Page[r][c] = 0x20; + for (int i=0; i<8; i++) { + setControlBit(i, false); + m_composeLink[i] = { (i<4) ? i : 0, false, i>=4, 0x0ff, 0x0000 }; + } + /* m_subPageNumber = 0x0000; */ m_cycleValue = 8; m_cycleType = CTseconds; @@ -119,7 +124,23 @@ QByteArray LevelOnePage::packet(int packetNumber, int designationCode) return result; } - // TODO packet 27 + // TODO packet 27/0 + + if (packetNumber == 27 && (designationCode == 4 || designationCode == 5)) { + for (int i=0; i<(designationCode == 4 ? 6 : 2); i++) { + int pageLinkNumber = i+(designationCode == 4 ? 0 : 6); + + result[i*6+1] = (m_composeLink[pageLinkNumber].level3p5 << 3) | (m_composeLink[pageLinkNumber].level2p5 << 2) | m_composeLink[pageLinkNumber].function; + result[i*6+2] = ((m_composeLink[pageLinkNumber].pageNumber & 0x100) >> 3) | 0x10 | (m_composeLink[pageLinkNumber].pageNumber & 0x00f); + result[i*6+3] = ((m_composeLink[pageLinkNumber].pageNumber & 0x0f0) >> 2) | ((m_composeLink[pageLinkNumber].pageNumber & 0x600) >> 9); + + result[i*6+4] = ((m_composeLink[pageLinkNumber].subPageCodes & 0x000f) << 2); + result[i*6+5] = ((m_composeLink[pageLinkNumber].subPageCodes & 0x03f0) >> 4); + result[i*6+6] = ((m_composeLink[pageLinkNumber].subPageCodes & 0xfc00) >> 10); + } + + return result; + } if (packetNumber == 28 && (designationCode == 0 || designationCode == 4)) { int CLUToffset = (designationCode == 0) ? 16 : 0; @@ -184,7 +205,26 @@ bool LevelOnePage::setPacket(int packetNumber, int designationCode, QByteArray p return true; } - // TODO packet 27 + // TODO packet 27/0 + + if (packetNumber == 27 && (designationCode == 4 || designationCode == 5)) { + for (int i=0; i<(designationCode == 4 ? 6 : 2); i++) { + int pageLinkNumber = i+(designationCode == 4 ? 0 : 6); + int pageFunction = packetContents.at(i*6+1) & 0x03; + if (i >= 4) + m_composeLink[pageLinkNumber].function = pageFunction; + else if (i != pageFunction) + qDebug("X/27/4 link number %d fixed at function %d. Attempted to set to %d.", pageLinkNumber, pageLinkNumber, pageFunction); + + m_composeLink[pageLinkNumber].level2p5 = packetContents.at(i*6+1) & 0x04; + m_composeLink[pageLinkNumber].level3p5 = packetContents.at(i*6+1) & 0x08; + + m_composeLink[pageLinkNumber].pageNumber = ((packetContents.at(i*6+3) & 0x03) << 9) | ((packetContents.at(i*6+2) & 0x20) << 3) | ((packetContents.at(i*6+3) & 0x3c) << 2) | (packetContents.at(i*6+2) & 0x0f); + + m_composeLink[pageLinkNumber].subPageCodes = (packetContents.at(i*6+4) >> 2) | (packetContents.at(i*6+5) << 4) | (packetContents.at(i*6+6) << 10); + } + return true; + } if (packetNumber == 28 && (designationCode == 0 || designationCode == 4)) { int CLUToffset = (designationCode == 0) ? 16 : 0; @@ -226,7 +266,16 @@ bool LevelOnePage::packetNeeded(int packetNumber, int designationCode) const if (packetNumber == 26) return ((localEnhance.size()+12) / 13) > designationCode; - // TODO packet 27 + // TODO packet 27/0 + + if (packetNumber == 27 && (designationCode == 4 || designationCode == 5)) { + for (int i=0; i<(designationCode == 4 ? 6 : 2); i++) { + int pageLinkNumber = i+(designationCode == 4 ? 0 : 6); + if ((m_composeLink[pageLinkNumber].pageNumber & 0x0ff) != 0x0ff) + return true; + } + return false; + } if (packetNumber == 28) { if (designationCode == 0) { @@ -425,6 +474,31 @@ void LevelOnePage::setRightSidePanelDisplayed(bool newRightSidePanelDisplayed) { void LevelOnePage::setSidePanelColumns(int newSidePanelColumns) { m_sidePanelColumns = newSidePanelColumns; } void LevelOnePage::setSidePanelStatusL25(bool newSidePanelStatusL25) { m_sidePanelStatusL25 = newSidePanelStatusL25; } +void LevelOnePage::setComposeLinkFunction(int linkNumber, int newFunction) +{ + m_composeLink[linkNumber].function = newFunction; +} + +void LevelOnePage::setComposeLinkLevel2p5(int linkNumber, bool newRequired) +{ + m_composeLink[linkNumber].level2p5 = newRequired; +} + +void LevelOnePage::setComposeLinkLevel3p5(int linkNumber, bool newRequired) +{ + m_composeLink[linkNumber].level3p5 = newRequired; +} + +void LevelOnePage::setComposeLinkPageNumber(int linkNumber, int newPageNumber) +{ + m_composeLink[linkNumber].pageNumber = newPageNumber; +} + +void LevelOnePage::setComposeLinkSubPageCodes(int linkNumber, int newSubPageCodes) +{ + m_composeLink[linkNumber].subPageCodes = newSubPageCodes; +} + QString LevelOnePage::colourHash(int whichCLUT) { QString resultHash; diff --git a/levelonepage.h b/levelonepage.h index 0b4526d..4dbb4e0 100644 --- a/levelonepage.h +++ b/levelonepage.h @@ -86,6 +86,17 @@ public: void setSidePanelColumns(int); bool sidePanelStatusL25() const { return m_sidePanelStatusL25; } void setSidePanelStatusL25(bool); + int composeLinkFunction(int linkNumber) const { return m_composeLink[linkNumber].function; } + void setComposeLinkFunction(int, int); + bool composeLinkLevel2p5(int linkNumber) const { return m_composeLink[linkNumber].level2p5; } + void setComposeLinkLevel2p5(int, bool); + bool composeLinkLevel3p5(int linkNumber) const { return m_composeLink[linkNumber].level3p5; } + void setComposeLinkLevel3p5(int, bool); + int composeLinkPageNumber(int linkNumber) const { return m_composeLink[linkNumber].pageNumber; } + void setComposeLinkPageNumber(int, int); + int composeLinkSubPageCodes(int linkNumber) const { return m_composeLink[linkNumber].subPageCodes; } + void setComposeLinkSubPageCodes(int, int); + QString colourHash(int); QList localEnhance; @@ -101,6 +112,12 @@ private: int m_defaultScreenColour, m_defaultRowColour, m_colourTableRemap, m_sidePanelColumns; bool m_blackBackgroundSubst, m_leftSidePanelDisplayed, m_rightSidePanelDisplayed, m_sidePanelStatusL25; int m_CLUT[32]; + struct composeLink { + int function; + bool level2p5, level3p5; + int pageNumber, subPageCodes; + } m_composeLink[8]; + X26Triplet m_paddingX26Triplet; const int defaultCLUT[32] = { diff --git a/pageenhancementsdockwidget.cpp b/pageenhancementsdockwidget.cpp index 36b9b0d..99f6dec 100644 --- a/pageenhancementsdockwidget.cpp +++ b/pageenhancementsdockwidget.cpp @@ -20,22 +20,32 @@ #include #include #include +#include #include +#include +#include +#include #include +#include #include "pageenhancementsdockwidget.h" PageEnhancementsDockWidget::PageEnhancementsDockWidget(TeletextWidget *parent): QDockWidget(parent) { - QGridLayout *pageEnhancementsLayout = new QGridLayout; + QVBoxLayout *pageEnhancementsLayout = new QVBoxLayout; QWidget *pageEnhancementsWidget = new QWidget; m_parentMainWidget = parent; this->setObjectName("PageEnhancementsDockWidget"); this->setWindowTitle("Page enhancements"); - pageEnhancementsLayout->addWidget(new QLabel(tr("Default screen colour")), 0, 0, 1, 1); - pageEnhancementsLayout->addWidget(new QLabel(tr("Default row colour")), 1, 0, 1, 1); + + QGroupBox *x28GroupBox = new QGroupBox(tr("X/28 enhancements")); + QGridLayout *x28Layout = new QGridLayout; + + // Default screen and default row colours + x28Layout->addWidget(new QLabel(tr("Default screen colour")), 0, 0, 1, 1); + x28Layout->addWidget(new QLabel(tr("Default row colour")), 1, 0, 1, 1); m_defaultScreenColourCombo = new QComboBox; m_defaultRowColourCombo = new QComboBox; for (int r=0; r<=3; r++) @@ -43,11 +53,13 @@ PageEnhancementsDockWidget::PageEnhancementsDockWidget(TeletextWidget *parent): m_defaultScreenColourCombo->addItem(tr("CLUT %1:%2").arg(r).arg(c)); m_defaultRowColourCombo->addItem(tr("CLUT %1:%2").arg(r).arg(c)); } - pageEnhancementsLayout->addWidget(m_defaultScreenColourCombo, 0, 1, 1, 1, Qt::AlignTop); + x28Layout->addWidget(m_defaultScreenColourCombo, 0, 1, 1, 1, Qt::AlignTop); connect(m_defaultScreenColourCombo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setDefaultScreenColour(index); }); - pageEnhancementsLayout->addWidget(m_defaultRowColourCombo, 1, 1, 1, 1, Qt::AlignTop); + x28Layout->addWidget(m_defaultRowColourCombo, 1, 1, 1, 1, Qt::AlignTop); connect(m_defaultRowColourCombo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setDefaultRowColour(index); }); - pageEnhancementsLayout->addWidget(new QLabel(tr("Colour remapping")), 2, 0, 1, 1); + + // CLUT remapping + x28Layout->addWidget(new QLabel(tr("CLUT remapping")), 2, 0, 1, 1); m_colourTableCombo = new QComboBox; m_colourTableCombo->addItem("Fore 0 Back 0"); m_colourTableCombo->addItem("Fore 0 Back 1"); @@ -57,29 +69,92 @@ PageEnhancementsDockWidget::PageEnhancementsDockWidget(TeletextWidget *parent): m_colourTableCombo->addItem("Fore 2 Back 1"); m_colourTableCombo->addItem("Fore 2 Back 2"); m_colourTableCombo->addItem("Fore 2 Back 3"); - pageEnhancementsLayout->addWidget(m_colourTableCombo, 2, 1, 1, 1, Qt::AlignTop); + x28Layout->addWidget(m_colourTableCombo, 2, 1, 1, 1, Qt::AlignTop); connect(m_colourTableCombo, QOverload::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setColourTableRemap(index); }); + + // Black background colour substitution m_blackBackgroundSubstAct = new QCheckBox("Black background colour substitution"); - pageEnhancementsLayout->addWidget(m_blackBackgroundSubstAct, 3, 0, 1, 2, Qt::AlignTop); + x28Layout->addWidget(m_blackBackgroundSubstAct, 3, 0, 1, 2, Qt::AlignTop); connect(m_blackBackgroundSubstAct, &QCheckBox::stateChanged, m_parentMainWidget, &TeletextWidget::setBlackBackgroundSubst); - pageEnhancementsLayout->addWidget(new QLabel(tr("Left side panel columns")), 4, 0, 1, 1); + // Side panel columns + x28Layout->addWidget(new QLabel(tr("Left side panel columns")), 4, 0, 1, 1); m_leftSidePanelSpinBox = new QSpinBox(this); m_leftSidePanelSpinBox->setMaximum(16); - pageEnhancementsLayout->addWidget(m_leftSidePanelSpinBox, 4, 1, 1, 1, Qt::AlignTop); + x28Layout->addWidget(m_leftSidePanelSpinBox, 4, 1, 1, 1, Qt::AlignTop); connect(m_leftSidePanelSpinBox, QOverload::of(&QSpinBox::valueChanged), [=](int index){ setLeftSidePanelWidth(index); }); - pageEnhancementsLayout->addWidget(new QLabel(tr("Right side panel columns")), 5, 0, 1, 1); + x28Layout->addWidget(new QLabel(tr("Right side panel columns")), 5, 0, 1, 1); m_rightSidePanelSpinBox = new QSpinBox(this); m_rightSidePanelSpinBox->setMaximum(16); - pageEnhancementsLayout->addWidget(m_rightSidePanelSpinBox, 5, 1, 1, 1, Qt::AlignTop); + x28Layout->addWidget(m_rightSidePanelSpinBox, 5, 1, 1, 1, Qt::AlignTop); connect(m_rightSidePanelSpinBox, QOverload::of(&QSpinBox::valueChanged), [=](int index){ setRightSidePanelWidth(index); }); + // Side panels status m_sidePanelStatusAct = new QCheckBox("Side panels at level 3.5 only"); - pageEnhancementsLayout->addWidget(m_sidePanelStatusAct, 6, 0, 1, 2, Qt::AlignTop); + x28Layout->addWidget(m_sidePanelStatusAct, 6, 0, 1, 2, Qt::AlignTop); connect(m_sidePanelStatusAct, &QCheckBox::stateChanged, m_parentMainWidget, &TeletextWidget::setSidePanelAtL35Only); - pageEnhancementsLayout->setRowStretch(6, 1); + x28GroupBox->setLayout(x28Layout); + pageEnhancementsLayout->addWidget(x28GroupBox); + + QGroupBox *x27GroupBox = new QGroupBox(tr("X/27 links - Level 2.5 and 3.5")); + QGridLayout *x27Layout = new QGridLayout; + + // Link functions + x27Layout->addWidget(new QLabel(tr("GPOP")), 1, 0, 1, 1); + x27Layout->addWidget(new QLabel(tr("POP")), 2, 0, 1, 1); + x27Layout->addWidget(new QLabel(tr("GDRCS")), 3, 0, 1, 1); + x27Layout->addWidget(new QLabel(tr("DRCS")), 4, 0, 1, 1); + + // Labels across the top + x27Layout->addWidget(new QLabel(tr("L2.5")), 0, 1, 1, 1); + x27Layout->addWidget(new QLabel(tr("L3.5")), 0, 2, 1, 1); + x27Layout->addWidget(new QLabel(tr("Page no")), 0, 3, 1, 1); + x27Layout->addWidget(new QLabel(tr("Sub pages required")), 0, 4, 1, 1); + + QLabel *level3p5OnlyLabel = new QLabel(tr("Level 3.5 only")); + level3p5OnlyLabel->setAlignment(Qt::AlignCenter); + x27Layout->addWidget(level3p5OnlyLabel, 5, 0, 1, 5); + + for (int i=0; i<8; i++) { + if (i < 4) { + // Required at which Levels + m_composeLinkLevelCheckbox[i][0] = new QCheckBox(this); + x27Layout->addWidget(m_composeLinkLevelCheckbox[i][0], i+1, 1, 1, 1); + connect(m_composeLinkLevelCheckbox[i][0], &QCheckBox::stateChanged, [=](bool active) { m_parentMainWidget->document()->currentSubPage()->setComposeLinkLevel2p5(i, active); }); + m_composeLinkLevelCheckbox[i][1] = new QCheckBox(this); + x27Layout->addWidget(m_composeLinkLevelCheckbox[i][1], i+1, 2, 1, 1); + connect(m_composeLinkLevelCheckbox[i][1], &QCheckBox::stateChanged, [=](bool active) { m_parentMainWidget->document()->currentSubPage()->setComposeLinkLevel3p5(i, active); }); + } else { + // Selectable link functions for Level 3.5 + m_composeLinkFunctionComboBox[i-4] = new QComboBox; + m_composeLinkFunctionComboBox[i-4]->addItem("GPOP"); + m_composeLinkFunctionComboBox[i-4]->addItem("POP"); + m_composeLinkFunctionComboBox[i-4]->addItem("GDRCS"); + m_composeLinkFunctionComboBox[i-4]->addItem("DRCS"); + x27Layout->addWidget(m_composeLinkFunctionComboBox[i-4], i+2, 0, 1, 1, Qt::AlignTop); + connect(m_composeLinkFunctionComboBox[i-4], QOverload::of(&QComboBox::currentIndexChanged), [=](int index) { m_parentMainWidget->document()->currentSubPage()->setComposeLinkFunction(i, index); } ); + } + // Page link + m_composeLinkPageNumberLineEdit[i] = new QLineEdit("100"); + m_composeLinkPageNumberLineEdit[i]->setMaxLength(3); + m_composeLinkPageNumberLineEdit[i]->setInputMask("DHH"); + // TODO restrict first digit of page number to 1-8 + x27Layout->addWidget(m_composeLinkPageNumberLineEdit[i], i+(i<4 ? 1 : 2), 3, 1, 1); + connect(m_composeLinkPageNumberLineEdit[i], &QLineEdit::textEdited, [=](QString value) { setComposeLinkPageNumber(i, value); } ); + + // Sub page numbers required + m_composeLinkSubPageNumbersLineEdit[i] = new QLineEdit(this); + x27Layout->addWidget(m_composeLinkSubPageNumbersLineEdit[i], i+(i<4 ? 1 : 2), 4, 1, 1); + connect(m_composeLinkSubPageNumbersLineEdit[i], &QLineEdit::textEdited, [=](QString value) { setComposeLinkSubPageNumbers(i, value); } ); + } + + x27GroupBox->setLayout(x27Layout); + pageEnhancementsLayout->addWidget(x27GroupBox); + + pageEnhancementsLayout->addStretch(1); + pageEnhancementsWidget->setLayout(pageEnhancementsLayout); this->setWidget(pageEnhancementsWidget); } @@ -106,6 +181,60 @@ void PageEnhancementsDockWidget::setRightSidePanelWidth(int newRightPanelSize) m_parentMainWidget->setSidePanelWidths(m_leftSidePanelSpinBox->value(), newRightPanelSize); } +void PageEnhancementsDockWidget::setComposeLinkPageNumber(int linkNumber, const QString &newPageNumberString) +{ + // The LineEdit should check if a valid hex number was entered, but just in case... + bool newPageNumberOk; + int newPageNumberRead = newPageNumberString.toInt(&newPageNumberOk, 16); + if ((!newPageNumberOk) || newPageNumberRead < 0x100 || newPageNumberRead > 0x8ff) + return; + + // Stored as page link with relative magazine number, convert from absolute page number that was entered + newPageNumberRead ^= (m_parentMainWidget->document()->pageNumber() & 0x700); + m_parentMainWidget->document()->currentSubPage()->setComposeLinkPageNumber(linkNumber, newPageNumberRead); +} + +void PageEnhancementsDockWidget::setComposeLinkSubPageNumbers(int linkNumber, const QString &newSubPagesString) +{ + int newSubPageCodes = 0x0000; + + if (!newSubPagesString.isEmpty()) { + // Turn user-entered comma separated subpages and subpage-ranges into bits + // First we do the "comma separated" + const QStringList items = newSubPagesString.split(QLatin1Char(','), QString::SkipEmptyParts); + // Now see if there's valid single numbers or ranges between the commas + for (const QString &item : items) { + if (item.isEmpty()) + return; + if (item.contains(QLatin1Char('-'))) { + // Dash found, should be a range of subpages: check for two things either side of dash + const QStringList rangeItems = item.split('-'); + if (rangeItems.count() != 2) + return; + // Now check if the two things are valid numbers 0-15, with first number less than second + bool ok1, ok2; + const int number1 = rangeItems[0].toInt(&ok1); + const int number2 = rangeItems[1].toInt(&ok2); + if (!ok1 || !ok2 || number1 < 0 || number2 < 0 || number1 > 15 || number2 > 15 || number2 < number1) + return; + // Yes, it is a valid range. Apply it to the result + for (int i=number1; i<=number2; i++) + newSubPageCodes |= (1 << i); + } else { + // A single thing (maybe extracted between commas): check if the thing is a number 0-15 + bool ok; + const int number = item.toInt(&ok); + if (!ok || number < 0 || number > 15) + return; + // Yes, it is a valid number. Apply it to the result + newSubPageCodes |= (1 << number); + } + } + } + + m_parentMainWidget->document()->currentSubPage()->setComposeLinkSubPageCodes(linkNumber, newSubPageCodes); +} + void PageEnhancementsDockWidget::updateWidgets() { int leftSidePanelColumnsResult = 0; @@ -139,4 +268,64 @@ void PageEnhancementsDockWidget::updateWidgets() m_sidePanelStatusAct->blockSignals(true); m_sidePanelStatusAct->setChecked(!m_parentMainWidget->document()->currentSubPage()->sidePanelStatusL25()); m_sidePanelStatusAct->blockSignals(false); + + for (int i=0; i<8; i++) { + if (i >= 4) { + m_composeLinkFunctionComboBox[i-4]->blockSignals(true); + m_composeLinkFunctionComboBox[i-4]->setCurrentIndex(m_parentMainWidget->document()->currentSubPage()->composeLinkFunction(i)); + m_composeLinkFunctionComboBox[i-4]->blockSignals(false); + } else { + m_composeLinkLevelCheckbox[i][0]->blockSignals(true); + m_composeLinkLevelCheckbox[i][0]->setChecked(m_parentMainWidget->document()->currentSubPage()->composeLinkLevel2p5(i)); + m_composeLinkLevelCheckbox[i][0]->blockSignals(false); + m_composeLinkLevelCheckbox[i][1]->blockSignals(true); + m_composeLinkLevelCheckbox[i][1]->setChecked(m_parentMainWidget->document()->currentSubPage()->composeLinkLevel3p5(i)); + m_composeLinkLevelCheckbox[i][1]->blockSignals(false); + } + // Stored as page link with relative magazine number, convert to absolute page number for display + int absoluteLinkPageNumber = m_parentMainWidget->document()->currentSubPage()->composeLinkPageNumber(i) ^ (m_parentMainWidget->document()->pageNumber() & 0x700); + // Fix magazine 0 to 8 + if ((absoluteLinkPageNumber & 0x700) == 0x000) + absoluteLinkPageNumber |= 0x800; + m_composeLinkPageNumberLineEdit[i]->blockSignals(true); + m_composeLinkPageNumberLineEdit[i]->setText(QString::number(absoluteLinkPageNumber, 16).toUpper()); + m_composeLinkPageNumberLineEdit[i]->blockSignals(false); + + // Turn subpage bits into user-friendly comma separated numbers and number-ranges + QString rangeString; + + if (m_parentMainWidget->document()->currentSubPage()->composeLinkSubPageCodes(i) != 0x0000) { + // First build a list of consecutive ranges seen + // The "b-index" is based on https://codereview.stackexchange.com/a/90074 + QMap> ranges; + int index = 0; + for (int b=0; b<16; b++) + if ((m_parentMainWidget->document()->currentSubPage()->composeLinkSubPageCodes(i) >> b) & 0x01) { + if (!ranges.contains(b-index)) + ranges.insert(b-index, qMakePair(b, b)); + else + ranges[b-index].second = b; + index++; + } + // Now go through the list and add single numbers or ranges as appropriate + QPair range; + foreach (range, ranges) { + // For second and subsequent entries only, append a comma first + if (!rangeString.isEmpty()) + rangeString.append(','); + // Append the single number or the first number of the range + rangeString.append(QString("%1").arg(range.first)); + // If that was part of a range and not a single orphaned number + if (range.first != range.second) { + // Ranges of 3 or more use a dash. A range of 2 can make do with a comma + rangeString.append((range.first+1 == range.second) ? ',' : '-'); + // Append the second number of the range + rangeString.append(QString("%1").arg(range.second)); + } + } + } + m_composeLinkSubPageNumbersLineEdit[i]->blockSignals(true); + m_composeLinkSubPageNumbersLineEdit[i]->setText(rangeString); + m_composeLinkSubPageNumbersLineEdit[i]->blockSignals(false); + } } diff --git a/pageenhancementsdockwidget.h b/pageenhancementsdockwidget.h index 7ee9781..e8ab92b 100644 --- a/pageenhancementsdockwidget.h +++ b/pageenhancementsdockwidget.h @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include "mainwidget.h" @@ -38,11 +40,17 @@ public: private: void setLeftSidePanelWidth(int); void setRightSidePanelWidth(int); + void setComposeLinkPageNumber(int, const QString &); + void setComposeLinkSubPageNumbers(int, const QString &); TeletextWidget *m_parentMainWidget; QComboBox *m_defaultScreenColourCombo, *m_defaultRowColourCombo, *m_colourTableCombo; QCheckBox *m_blackBackgroundSubstAct, *m_sidePanelStatusAct; QSpinBox *m_leftSidePanelSpinBox, *m_rightSidePanelSpinBox; + + QCheckBox *m_composeLinkLevelCheckbox[4][2]; // For links 0-3 + QComboBox *m_composeLinkFunctionComboBox[4]; // For links 4-7; remember to subtract 4! + QLineEdit *m_composeLinkPageNumberLineEdit[8], *m_composeLinkSubPageNumbersLineEdit[8]; }; #endif