diff --git a/src/qteletextdecoder/document.cpp b/src/qteletextdecoder/document.cpp index d044ee4..bf26bb7 100644 --- a/src/qteletextdecoder/document.cpp +++ b/src/qteletextdecoder/document.cpp @@ -68,6 +68,7 @@ TeletextDocument::TeletextDocument() m_undoStack = new QUndoStack(this); m_cursorRow = 1; m_cursorColumn = 0; + m_rowZeroAllowed = false; m_selectionCornerRow = m_selectionCornerColumn = -1; m_selectionSubPage = nullptr; @@ -252,7 +253,7 @@ void TeletextDocument::cursorUp(bool shiftKey) if (shiftKey && !selectionActive()) setSelectionCorner(m_cursorRow, m_cursorColumn); - if (--m_cursorRow == 0) + if (--m_cursorRow == 0 - (int)m_rowZeroAllowed) m_cursorRow = 24; if (shiftKey) @@ -269,7 +270,7 @@ void TeletextDocument::cursorDown(bool shiftKey) setSelectionCorner(m_cursorRow, m_cursorColumn); if (++m_cursorRow == 25) - m_cursorRow = 1; + m_cursorRow = (int)!m_rowZeroAllowed; if (shiftKey) emit selectionMoved(); @@ -333,6 +334,13 @@ void TeletextDocument::moveCursor(int cursorRow, int cursorColumn, bool selectio emit cursorMoved(); } +void TeletextDocument::setRowZeroAllowed(bool allowed) +{ + m_rowZeroAllowed = allowed; + if (m_cursorRow == 0 && !allowed) + cursorDown(); +} + void TeletextDocument::setSelectionCorner(int row, int column) { if (m_selectionCornerRow != row || m_selectionCornerColumn != column) { diff --git a/src/qteletextdecoder/document.h b/src/qteletextdecoder/document.h index 4a9a6b0..13d58a9 100644 --- a/src/qteletextdecoder/document.h +++ b/src/qteletextdecoder/document.h @@ -89,6 +89,8 @@ public: void cursorLeft(bool shiftKey=false); void cursorRight(bool shiftKey=false); void moveCursor(int cursorRow, int cursorColumn, bool selectionInProgress=false); + bool rowZeroAllowed() const { return m_rowZeroAllowed; }; + void setRowZeroAllowed(bool allowed); int selectionTopRow() const { return m_selectionCornerRow == -1 ? m_cursorRow : qMin(m_selectionCornerRow, m_cursorRow); } int selectionBottomRow() const { return qMax(m_selectionCornerRow, m_cursorRow); } int selectionLeftColumn() const { return m_selectionCornerColumn == -1 ? m_cursorColumn : qMin(m_selectionCornerColumn, m_cursorColumn); } @@ -123,6 +125,7 @@ private: std::vector m_recycleSubPages; QUndoStack *m_undoStack; int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn; + bool m_rowZeroAllowed; LevelOnePage *m_selectionSubPage; ClutModel *m_clutModel; }; diff --git a/src/qteletextmaker/loadformats.cpp b/src/qteletextmaker/loadformats.cpp index c7ffda6..80820be 100644 --- a/src/qteletextmaker/loadformats.cpp +++ b/src/qteletextmaker/loadformats.cpp @@ -274,6 +274,22 @@ bool LoadT42Format::load(QFile *inFile, TeletextDocument *document) document->subPage(0)->setControlBit(PageBase::C13NOS, m_inLine[9] & 0x04); document->subPage(0)->setControlBit(PageBase::C14NOS, m_inLine[9] & 0x02); + // See if there's text in the header row + bool headerText = false; + + for (int i=10; i<42; i++) + if (m_inLine[i] != 0x20) { + // TODO - obey odd parity? + m_inLine[i] &= 0x7f; + headerText = true; + } + if (headerText) { + // Clear the page address and control bits to spaces before putting the row in + for (int i=0; i<10; i++) + m_inLine[i] = 0x20; + + document->subPage(0)->setPacket(0, QByteArray((const char *)&m_inLine[2], 40)); + } continue; } } diff --git a/src/qteletextmaker/mainwidget.cpp b/src/qteletextmaker/mainwidget.cpp index 21d7a07..7cf3e55 100644 --- a/src/qteletextmaker/mainwidget.cpp +++ b/src/qteletextmaker/mainwidget.cpp @@ -543,8 +543,10 @@ QPair TeletextWidget::mouseToRowAndColumn(const QPoint &mousePosition) { int row = mousePosition.y() / 10; int column = mousePosition.x() / 12 - m_pageDecode.leftSidePanelColumns(); - if (row < 1) - row = 1; + const int topRow = (int)!m_teletextDocument->rowZeroAllowed(); + + if (row < topRow) + row = topRow; if (row > 24) row = 24; if (column < 0) @@ -647,6 +649,9 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi m_mainGridItemGroup = new QGraphicsItemGroup; m_mainGridItemGroup->setVisible(false); addItem(m_mainGridItemGroup); + m_rowZeroGridItemGroup = new QGraphicsItemGroup; + m_rowZeroGridItemGroup->setVisible(false); + addItem(m_rowZeroGridItemGroup); // Additional vertical pieces of grid for side panels for (int i=0; i<32; i++) { m_sidePanelGridNeeded[i] = false; @@ -654,14 +659,17 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi m_sidePanelGridItemGroup[i]->setVisible(false); addItem(m_sidePanelGridItemGroup[i]); } - for (int r=1; r<25; r++) { + for (int r=0; r<25; r++) { for (int c=0; c<40; c++) { QGraphicsRectItem *gridPiece = new QGraphicsRectItem(c*12, r*10, 12, 10); - gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, r<24 ? 192 : 128)), 0)); - m_mainGridItemGroup->addToGroup(gridPiece); + gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, (r != 0 && r != 24) ? 192 : 128)), 0)); + if (r == 0) + m_rowZeroGridItemGroup->addToGroup(gridPiece); + else + m_mainGridItemGroup->addToGroup(gridPiece); } - if (r < 24) + if (r != 0 && r != 24) for (int c=0; c<32; c++) { QGraphicsRectItem *gridPiece = new QGraphicsRectItem(0, r*10, 12, 10); gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, 64)), 0)); @@ -686,6 +694,7 @@ void LevelOneScene::setBorderDimensions(int sceneWidth, int sceneHeight, int wid // Position grid to cover central 40 columns m_mainGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders); + m_rowZeroGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders); updateCursor(); updateSelection(); @@ -772,12 +781,23 @@ void LevelOneScene::setRenderMode(TeletextPageRender::RenderMode renderMode) void LevelOneScene::toggleGrid(bool gridOn) { m_grid = gridOn; + m_mainGridItemGroup->setVisible(gridOn); + if (static_cast(m_levelOneProxyWidget->widget())->document()->rowZeroAllowed()) + m_rowZeroGridItemGroup->setVisible(gridOn); for (int i=0; i<32; i++) if (m_sidePanelGridNeeded[i]) m_sidePanelGridItemGroup[i]->setVisible(gridOn); } +void LevelOneScene::toggleRowZeroAllowed(bool allowed) +{ + static_cast(m_levelOneProxyWidget->widget())->document()->setRowZeroAllowed(allowed); + + if (m_grid) + m_rowZeroGridItemGroup->setVisible(allowed); +} + void LevelOneScene::hideGUIElements(bool hidden) { if (hidden) { diff --git a/src/qteletextmaker/mainwidget.h b/src/qteletextmaker/mainwidget.h index 71866a9..172172e 100644 --- a/src/qteletextmaker/mainwidget.h +++ b/src/qteletextmaker/mainwidget.h @@ -125,6 +125,7 @@ public slots: void updateSelection(); void setRenderMode(TeletextPageRender::RenderMode renderMode); void toggleGrid(bool gridOn); + void toggleRowZeroAllowed(bool allowed); void hideGUIElements(bool hidden); void setFullScreenColour(const QColor &newColor); void setFullRowColour(int row, const QColor &newColor); @@ -143,7 +144,7 @@ private: QGraphicsRectItem *m_fullRowLeftRectItem[25], *m_fullRowRightRectItem[25]; QGraphicsProxyWidget *m_levelOneProxyWidget; QGraphicsRectItem *m_cursorRectItem, *m_selectionRectItem; - QGraphicsItemGroup *m_mainGridItemGroup, *m_sidePanelGridItemGroup[32]; + QGraphicsItemGroup *m_mainGridItemGroup, *m_rowZeroGridItemGroup, *m_sidePanelGridItemGroup[32]; bool m_grid, m_sidePanelGridNeeded[32]; }; diff --git a/src/qteletextmaker/mainwindow.cpp b/src/qteletextmaker/mainwindow.cpp index cebcf67..c630e0a 100644 --- a/src/qteletextmaker/mainwindow.cpp +++ b/src/qteletextmaker/mainwindow.cpp @@ -576,6 +576,13 @@ void MainWindow::createActions() m_deleteSubPageAction->setStatusTip(tr("Delete this subpage")); connect(m_deleteSubPageAction, &QAction::triggered, this, &MainWindow::deleteSubPage); + editMenu->addSeparator(); + + m_rowZeroAct = editMenu->addAction(tr("Edit header row")); + m_rowZeroAct->setCheckable(true); + m_rowZeroAct->setStatusTip(tr("Allow editing of header row")); + connect(m_rowZeroAct, &QAction::toggled, m_textScene, &LevelOneScene::toggleRowZeroAllowed); + QMenu *viewMenu = menuBar()->addMenu(tr("&View")); QAction *revealAct = viewMenu->addAction(tr("&Reveal")); @@ -1090,6 +1097,12 @@ void MainWindow::loadFile(const QString &fileName) m_reExportWarning = loadingFormat->reExportWarning(); + for (int i=0; idocument()->numberOfSubPages(); i++) + if (m_textWidget->document()->subPage(i)->packetExists(0)) { + m_rowZeroAct->setChecked(true); + break; + } + setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); } diff --git a/src/qteletextmaker/mainwindow.h b/src/qteletextmaker/mainwindow.h index 7325647..03b227f 100644 --- a/src/qteletextmaker/mainwindow.h +++ b/src/qteletextmaker/mainwindow.h @@ -131,6 +131,7 @@ private: QAction *m_borderActs[3]; QAction *m_aspectRatioActs[4]; QAction *m_smoothTransformAction; + QAction *m_rowZeroAct; QLabel *m_subPageLabel, *m_cursorPositionLabel; QToolButton *m_previousSubPageButton, *m_nextSubPageButton; diff --git a/src/qteletextmaker/saveformats.cpp b/src/qteletextmaker/saveformats.cpp index 6d088cd..5bd3afe 100644 --- a/src/qteletextmaker/saveformats.cpp +++ b/src/qteletextmaker/saveformats.cpp @@ -202,6 +202,10 @@ void SaveTTIFormat::writeSubPageStart(const PageBase &subPage, int subPageNumber void SaveTTIFormat::writeSubPageBody(const PageBase &subPage) { + // Header row + if (subPage.packetExists(0)) + writePacket(format7BitPacket(subPage.packet(0)), 0); + // FLOF links bool writeFLCommand = false; if (m_document->pageFunction() == TeletextDocument::PFLevelOnePage && subPage.packetExists(27,0)) { @@ -358,19 +362,27 @@ int SaveT42Format::writePacket(QByteArray packet, int packetNumber, int designat void SaveT42Format::writeSubPageStart(const PageBase &subPage, int subPageNumber) { + QByteArray packet; + // Convert integer to Binary Coded Decimal subPageNumber = QString::number(subPageNumber).toInt(nullptr, 16); - // Displayable row header we export as spaces, hence the (odd parity valid) 0x20 init value - QByteArray packet(42, 0x20); - m_magazineNumber = (m_document->pageNumber() & 0xf00) >> 8; if (m_magazineNumber == 8) m_magazineNumber = 0; - // Write X/0 separately as it features both Hamming 8/4 and 7-bit odd parity within - packet[0] = m_magazineNumber & 0x07; - packet[1] = 0; // Packet number 0 + // Retrieve and apply odd parity to header row if there's text there, + // otherwise create an initial packet of (odd parity valid) spaces + if (subPage.packetExists(0)) + packet = format7BitPacket(subPage.packet(0)); + else + packet.fill(0x20, 40); + + // Byte 1 of MRAG - packet number 0 + packet.prepend((char)0); + // Byte 0 of MRAG - magazine number, and packet number 0 + packet.prepend(m_magazineNumber & 0x07); + packet[2] = m_document->pageNumber() & 0x00f; packet[3] = (m_document->pageNumber() & 0x0f0) >> 4; packet[4] = subPageNumber & 0xf;