Allow header row editing

This commit is contained in:
Gavin MacGregor
2025-03-18 14:48:03 +00:00
parent 1d462f4355
commit 0493f0e270
8 changed files with 89 additions and 15 deletions

View File

@@ -68,6 +68,7 @@ TeletextDocument::TeletextDocument()
m_undoStack = new QUndoStack(this); m_undoStack = new QUndoStack(this);
m_cursorRow = 1; m_cursorRow = 1;
m_cursorColumn = 0; m_cursorColumn = 0;
m_rowZeroAllowed = false;
m_selectionCornerRow = m_selectionCornerColumn = -1; m_selectionCornerRow = m_selectionCornerColumn = -1;
m_selectionSubPage = nullptr; m_selectionSubPage = nullptr;
@@ -252,7 +253,7 @@ void TeletextDocument::cursorUp(bool shiftKey)
if (shiftKey && !selectionActive()) if (shiftKey && !selectionActive())
setSelectionCorner(m_cursorRow, m_cursorColumn); setSelectionCorner(m_cursorRow, m_cursorColumn);
if (--m_cursorRow == 0) if (--m_cursorRow == 0 - (int)m_rowZeroAllowed)
m_cursorRow = 24; m_cursorRow = 24;
if (shiftKey) if (shiftKey)
@@ -269,7 +270,7 @@ void TeletextDocument::cursorDown(bool shiftKey)
setSelectionCorner(m_cursorRow, m_cursorColumn); setSelectionCorner(m_cursorRow, m_cursorColumn);
if (++m_cursorRow == 25) if (++m_cursorRow == 25)
m_cursorRow = 1; m_cursorRow = (int)!m_rowZeroAllowed;
if (shiftKey) if (shiftKey)
emit selectionMoved(); emit selectionMoved();
@@ -333,6 +334,13 @@ void TeletextDocument::moveCursor(int cursorRow, int cursorColumn, bool selectio
emit cursorMoved(); emit cursorMoved();
} }
void TeletextDocument::setRowZeroAllowed(bool allowed)
{
m_rowZeroAllowed = allowed;
if (m_cursorRow == 0 && !allowed)
cursorDown();
}
void TeletextDocument::setSelectionCorner(int row, int column) void TeletextDocument::setSelectionCorner(int row, int column)
{ {
if (m_selectionCornerRow != row || m_selectionCornerColumn != column) { if (m_selectionCornerRow != row || m_selectionCornerColumn != column) {

View File

@@ -89,6 +89,8 @@ public:
void cursorLeft(bool shiftKey=false); void cursorLeft(bool shiftKey=false);
void cursorRight(bool shiftKey=false); void cursorRight(bool shiftKey=false);
void moveCursor(int cursorRow, int cursorColumn, bool selectionInProgress=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 selectionTopRow() const { return m_selectionCornerRow == -1 ? m_cursorRow : qMin(m_selectionCornerRow, m_cursorRow); }
int selectionBottomRow() const { return qMax(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); } int selectionLeftColumn() const { return m_selectionCornerColumn == -1 ? m_cursorColumn : qMin(m_selectionCornerColumn, m_cursorColumn); }
@@ -123,6 +125,7 @@ private:
std::vector<LevelOnePage *> m_recycleSubPages; std::vector<LevelOnePage *> m_recycleSubPages;
QUndoStack *m_undoStack; QUndoStack *m_undoStack;
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn; int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
bool m_rowZeroAllowed;
LevelOnePage *m_selectionSubPage; LevelOnePage *m_selectionSubPage;
ClutModel *m_clutModel; ClutModel *m_clutModel;
}; };

View File

@@ -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::C13NOS, m_inLine[9] & 0x04);
document->subPage(0)->setControlBit(PageBase::C14NOS, m_inLine[9] & 0x02); 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; continue;
} }
} }

View File

@@ -543,8 +543,10 @@ QPair<int, int> TeletextWidget::mouseToRowAndColumn(const QPoint &mousePosition)
{ {
int row = mousePosition.y() / 10; int row = mousePosition.y() / 10;
int column = mousePosition.x() / 12 - m_pageDecode.leftSidePanelColumns(); int column = mousePosition.x() / 12 - m_pageDecode.leftSidePanelColumns();
if (row < 1) const int topRow = (int)!m_teletextDocument->rowZeroAllowed();
row = 1;
if (row < topRow)
row = topRow;
if (row > 24) if (row > 24)
row = 24; row = 24;
if (column < 0) if (column < 0)
@@ -647,6 +649,9 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi
m_mainGridItemGroup = new QGraphicsItemGroup; m_mainGridItemGroup = new QGraphicsItemGroup;
m_mainGridItemGroup->setVisible(false); m_mainGridItemGroup->setVisible(false);
addItem(m_mainGridItemGroup); addItem(m_mainGridItemGroup);
m_rowZeroGridItemGroup = new QGraphicsItemGroup;
m_rowZeroGridItemGroup->setVisible(false);
addItem(m_rowZeroGridItemGroup);
// Additional vertical pieces of grid for side panels // Additional vertical pieces of grid for side panels
for (int i=0; i<32; i++) { for (int i=0; i<32; i++) {
m_sidePanelGridNeeded[i] = false; m_sidePanelGridNeeded[i] = false;
@@ -654,14 +659,17 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi
m_sidePanelGridItemGroup[i]->setVisible(false); m_sidePanelGridItemGroup[i]->setVisible(false);
addItem(m_sidePanelGridItemGroup[i]); 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++) { for (int c=0; c<40; c++) {
QGraphicsRectItem *gridPiece = new QGraphicsRectItem(c*12, r*10, 12, 10); QGraphicsRectItem *gridPiece = new QGraphicsRectItem(c*12, r*10, 12, 10);
gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, r<24 ? 192 : 128)), 0)); gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, (r != 0 && r != 24) ? 192 : 128)), 0));
m_mainGridItemGroup->addToGroup(gridPiece); 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++) { for (int c=0; c<32; c++) {
QGraphicsRectItem *gridPiece = new QGraphicsRectItem(0, r*10, 12, 10); QGraphicsRectItem *gridPiece = new QGraphicsRectItem(0, r*10, 12, 10);
gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, 64)), 0)); 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 // Position grid to cover central 40 columns
m_mainGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders); m_mainGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders);
m_rowZeroGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders);
updateCursor(); updateCursor();
updateSelection(); updateSelection();
@@ -772,12 +781,23 @@ void LevelOneScene::setRenderMode(TeletextPageRender::RenderMode renderMode)
void LevelOneScene::toggleGrid(bool gridOn) void LevelOneScene::toggleGrid(bool gridOn)
{ {
m_grid = gridOn; m_grid = gridOn;
m_mainGridItemGroup->setVisible(gridOn); m_mainGridItemGroup->setVisible(gridOn);
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->rowZeroAllowed())
m_rowZeroGridItemGroup->setVisible(gridOn);
for (int i=0; i<32; i++) for (int i=0; i<32; i++)
if (m_sidePanelGridNeeded[i]) if (m_sidePanelGridNeeded[i])
m_sidePanelGridItemGroup[i]->setVisible(gridOn); m_sidePanelGridItemGroup[i]->setVisible(gridOn);
} }
void LevelOneScene::toggleRowZeroAllowed(bool allowed)
{
static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->setRowZeroAllowed(allowed);
if (m_grid)
m_rowZeroGridItemGroup->setVisible(allowed);
}
void LevelOneScene::hideGUIElements(bool hidden) void LevelOneScene::hideGUIElements(bool hidden)
{ {
if (hidden) { if (hidden) {

View File

@@ -125,6 +125,7 @@ public slots:
void updateSelection(); void updateSelection();
void setRenderMode(TeletextPageRender::RenderMode renderMode); void setRenderMode(TeletextPageRender::RenderMode renderMode);
void toggleGrid(bool gridOn); void toggleGrid(bool gridOn);
void toggleRowZeroAllowed(bool allowed);
void hideGUIElements(bool hidden); void hideGUIElements(bool hidden);
void setFullScreenColour(const QColor &newColor); void setFullScreenColour(const QColor &newColor);
void setFullRowColour(int row, const QColor &newColor); void setFullRowColour(int row, const QColor &newColor);
@@ -143,7 +144,7 @@ private:
QGraphicsRectItem *m_fullRowLeftRectItem[25], *m_fullRowRightRectItem[25]; QGraphicsRectItem *m_fullRowLeftRectItem[25], *m_fullRowRightRectItem[25];
QGraphicsProxyWidget *m_levelOneProxyWidget; QGraphicsProxyWidget *m_levelOneProxyWidget;
QGraphicsRectItem *m_cursorRectItem, *m_selectionRectItem; 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]; bool m_grid, m_sidePanelGridNeeded[32];
}; };

View File

@@ -576,6 +576,13 @@ void MainWindow::createActions()
m_deleteSubPageAction->setStatusTip(tr("Delete this subpage")); m_deleteSubPageAction->setStatusTip(tr("Delete this subpage"));
connect(m_deleteSubPageAction, &QAction::triggered, this, &MainWindow::deleteSubPage); 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")); QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
QAction *revealAct = viewMenu->addAction(tr("&Reveal")); QAction *revealAct = viewMenu->addAction(tr("&Reveal"));
@@ -1090,6 +1097,12 @@ void MainWindow::loadFile(const QString &fileName)
m_reExportWarning = loadingFormat->reExportWarning(); m_reExportWarning = loadingFormat->reExportWarning();
for (int i=0; i<m_textWidget->document()->numberOfSubPages(); i++)
if (m_textWidget->document()->subPage(i)->packetExists(0)) {
m_rowZeroAct->setChecked(true);
break;
}
setCurrentFile(fileName); setCurrentFile(fileName);
statusBar()->showMessage(tr("File loaded"), 2000); statusBar()->showMessage(tr("File loaded"), 2000);
} }

View File

@@ -131,6 +131,7 @@ private:
QAction *m_borderActs[3]; QAction *m_borderActs[3];
QAction *m_aspectRatioActs[4]; QAction *m_aspectRatioActs[4];
QAction *m_smoothTransformAction; QAction *m_smoothTransformAction;
QAction *m_rowZeroAct;
QLabel *m_subPageLabel, *m_cursorPositionLabel; QLabel *m_subPageLabel, *m_cursorPositionLabel;
QToolButton *m_previousSubPageButton, *m_nextSubPageButton; QToolButton *m_previousSubPageButton, *m_nextSubPageButton;

View File

@@ -202,6 +202,10 @@ void SaveTTIFormat::writeSubPageStart(const PageBase &subPage, int subPageNumber
void SaveTTIFormat::writeSubPageBody(const PageBase &subPage) void SaveTTIFormat::writeSubPageBody(const PageBase &subPage)
{ {
// Header row
if (subPage.packetExists(0))
writePacket(format7BitPacket(subPage.packet(0)), 0);
// FLOF links // FLOF links
bool writeFLCommand = false; bool writeFLCommand = false;
if (m_document->pageFunction() == TeletextDocument::PFLevelOnePage && subPage.packetExists(27,0)) { 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) void SaveT42Format::writeSubPageStart(const PageBase &subPage, int subPageNumber)
{ {
QByteArray packet;
// Convert integer to Binary Coded Decimal // Convert integer to Binary Coded Decimal
subPageNumber = QString::number(subPageNumber).toInt(nullptr, 16); 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; m_magazineNumber = (m_document->pageNumber() & 0xf00) >> 8;
if (m_magazineNumber == 8) if (m_magazineNumber == 8)
m_magazineNumber = 0; m_magazineNumber = 0;
// Write X/0 separately as it features both Hamming 8/4 and 7-bit odd parity within // Retrieve and apply odd parity to header row if there's text there,
packet[0] = m_magazineNumber & 0x07; // otherwise create an initial packet of (odd parity valid) spaces
packet[1] = 0; // Packet number 0 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[2] = m_document->pageNumber() & 0x00f;
packet[3] = (m_document->pageNumber() & 0x0f0) >> 4; packet[3] = (m_document->pageNumber() & 0x0f0) >> 4;
packet[4] = subPageNumber & 0xf; packet[4] = subPageNumber & 0xf;