Compare commits
17 Commits
0.5.4-alph
...
0.6-alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3125762133 | ||
|
|
c0670c8281 | ||
|
|
7493c8f527 | ||
|
|
9bd9f180c2 | ||
|
|
c64be6a4c9 | ||
|
|
eb752835fd | ||
|
|
72a2ef9660 | ||
|
|
213eace512 | ||
|
|
06e0b401ca | ||
|
|
bc8780608c | ||
|
|
536c231941 | ||
|
|
4faed597c0 | ||
|
|
75816e7750 | ||
|
|
8b655afb2d | ||
|
|
abf649d2ab | ||
|
|
a8f2152c92 | ||
|
|
9d05126e8f |
@@ -14,13 +14,15 @@ Features
|
|||||||
- Configurable zoom.
|
- Configurable zoom.
|
||||||
- View teletext pages in 4:3, 16:9 pillar-box and 16:9 stretch aspect ratios.
|
- View teletext pages in 4:3, 16:9 pillar-box and 16:9 stretch aspect ratios.
|
||||||
|
|
||||||
Although designed on and developed for Linux, the Qt 5 libraries are cross platform so a Windows executable can be built. A Windows executable can be found within the "Releases" link, compiled on a Linux host using [MXE](https://github.com/mxe/mxe) based on [these instructions](https://blog.8bitbuddhism.com/2018/08/22/cross-compiling-windows-applications-with-mxe/). After MXE is installed `make qtbase` should be enough to build QTeletextMaker.
|
Although designed on and developed for Linux, the Qt 5 libraries are cross platform so a Windows executable can be built. A Windows executable can be found within the "Releases" link, compiled on a Linux host using [MXE](https://github.com/mxe/mxe) based on [these instructions](https://blog.8bitbuddhism.com/2018/08/22/cross-compiling-windows-applications-with-mxe/). After MXE is installed `make qtbase` should build and install the required dependencies to build QTeletextMaker.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
### Linux
|
### Linux
|
||||||
Install the QtCore, QtGui and QtWidgets libraries and build headers, along with the qmake tool. Then type `qmake5 && make -j3` in a terminal, you can replace -j3 with the number of processor cores used for the compile process.
|
Install the QtCore, QtGui and QtWidgets libraries and build headers, along with the qmake tool. Depending on how qmake is installed, type `qmake && make -j3` or `qmake5 && make -j3` in a terminal to build, you can replace -j3 with the number of processor cores used for the compile process.
|
||||||
|
|
||||||
The above will place the qteletextmaker executable in the same directory as the source, type `./qteletextmaker` in the terminal to launch. Optionally, type `make install` afterwards to place the executable into /usr/local/bin.
|
The above should place the qteletextmaker executable in the same directory as the source, type `./qteletextmaker` in the terminal to launch. Some Qt installs may place the executable into a "release" directory.
|
||||||
|
|
||||||
|
Optionally, type `make install` afterwards to place the executable into /usr/local/bin.
|
||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
The following X/26 enhancement triplets are not rendered by the editor, although the list is fully aware of them.
|
The following X/26 enhancement triplets are not rendered by the editor, although the list is fully aware of them.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -68,7 +68,13 @@ void TeletextPageDecode::setLevel(int level)
|
|||||||
{
|
{
|
||||||
if (level == m_level)
|
if (level == m_level)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_level = level;
|
m_level = level;
|
||||||
|
|
||||||
|
for (int r=0; r<25; r++)
|
||||||
|
for (int c=0; c<72; c++)
|
||||||
|
m_refresh[r][c] = true;
|
||||||
|
|
||||||
decodePage();
|
decodePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
decode.h
2
decode.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
21
document.cpp
21
document.cpp
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -94,6 +94,25 @@ bool TeletextDocument::isEmpty() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeletextDocument::clear()
|
||||||
|
{
|
||||||
|
LevelOnePage *blankSubPage = new LevelOnePage;
|
||||||
|
|
||||||
|
m_subPages.insert(m_subPages.begin(), blankSubPage);
|
||||||
|
|
||||||
|
emit aboutToChangeSubPage();
|
||||||
|
m_currentSubPageIndex = 0;
|
||||||
|
m_clutModel->setSubPage(m_subPages[0]);
|
||||||
|
emit subPageSelected();
|
||||||
|
cancelSelection();
|
||||||
|
m_undoStack->clear();
|
||||||
|
|
||||||
|
for (int i=m_subPages.size()-1; i>0; i--) {
|
||||||
|
delete(m_subPages[i]);
|
||||||
|
m_subPages.erase(m_subPages.begin()+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void TeletextDocument::setPageFunction(PageFunctionEnum newPageFunction)
|
void TeletextDocument::setPageFunction(PageFunctionEnum newPageFunction)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -56,6 +56,7 @@ public:
|
|||||||
~TeletextDocument();
|
~TeletextDocument();
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
PageFunctionEnum pageFunction() const { return m_pageFunction; }
|
PageFunctionEnum pageFunction() const { return m_pageFunction; }
|
||||||
// void setPageFunction(PageFunctionEnum);
|
// void setPageFunction(PageFunctionEnum);
|
||||||
|
|||||||
12
keymap.h
12
keymap.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -528,15 +528,15 @@ static const QMap<QChar, char> keymapping[24] = {
|
|||||||
|
|
||||||
// Native scan codes to toggle mosaic bits - different platforms have different scan codes!
|
// Native scan codes to toggle mosaic bits - different platforms have different scan codes!
|
||||||
// Order is top left, top right, middle left, middle right, bottom left, bottom right,
|
// Order is top left, top right, middle left, middle right, bottom left, bottom right,
|
||||||
// invert, set all, clear all
|
// invert, set all, clear all, dither
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
static constexpr quint32 mosaicNativeScanCodes[9] = {
|
static constexpr quint32 mosaicNativeScanCodes[10] = {
|
||||||
0x18, 0x19, 0x26, 0x27, 0x34, 0x35, 0x1b, 0x29, 0x36
|
0x18, 0x19, 0x26, 0x27, 0x34, 0x35, 0x1b, 0x29, 0x36, 0x28
|
||||||
};
|
};
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
static constexpr quint32 mosaicNativeScanCodes[9] = {
|
static constexpr quint32 mosaicNativeScanCodes[10] = {
|
||||||
0x10, 0x11, 0x1e, 0x1f, 0x2c, 0x2d, 0x13, 0x21, 0x2e
|
0x10, 0x11, 0x1e, 0x1f, 0x2c, 0x2d, 0x13, 0x21, 0x2e, 0x20
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
#define QTTM_NONATIVESCANCODES
|
#define QTTM_NONATIVESCANCODES
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -106,6 +106,8 @@ ToggleMosaicBitCommand::ToggleMosaicBitCommand(TeletextDocument *teletextDocumen
|
|||||||
m_oldCharacter = teletextDocument->currentSubPage()->character(m_row, m_column);
|
m_oldCharacter = teletextDocument->currentSubPage()->character(m_row, m_column);
|
||||||
if (bitToToggle == 0x20 || bitToToggle == 0x7f)
|
if (bitToToggle == 0x20 || bitToToggle == 0x7f)
|
||||||
m_newCharacter = bitToToggle;
|
m_newCharacter = bitToToggle;
|
||||||
|
else if (bitToToggle == 0x66)
|
||||||
|
m_newCharacter = (m_row & 1) ? 0x66 : 0x39;
|
||||||
else
|
else
|
||||||
m_newCharacter = m_oldCharacter ^ bitToToggle;
|
m_newCharacter = m_oldCharacter ^ bitToToggle;
|
||||||
|
|
||||||
@@ -413,8 +415,18 @@ PasteCommand::PasteCommand(TeletextDocument *teletextDocument, int pageCharSet,
|
|||||||
if (m_selectionActive) {
|
if (m_selectionActive) {
|
||||||
m_selectionCornerRow = m_teletextDocument->selectionCornerRow();
|
m_selectionCornerRow = m_teletextDocument->selectionCornerRow();
|
||||||
m_selectionCornerColumn = m_teletextDocument->selectionCornerColumn();
|
m_selectionCornerColumn = m_teletextDocument->selectionCornerColumn();
|
||||||
|
m_pasteTopRow = m_teletextDocument->selectionTopRow();
|
||||||
|
m_pasteBottomRow = m_teletextDocument->selectionBottomRow();
|
||||||
|
m_pasteLeftColumn = m_teletextDocument->selectionLeftColumn();
|
||||||
|
m_pasteRightColumn = m_teletextDocument->selectionRightColumn();
|
||||||
|
} else {
|
||||||
|
m_pasteTopRow = m_row;
|
||||||
|
m_pasteLeftColumn = m_column;
|
||||||
|
// m_pasteBottomRow and m_pasteRightColumn will be filled in later
|
||||||
|
// when the size of the clipboard data is known
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zero size here represents invalid or empty clipboard data
|
||||||
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
||||||
|
|
||||||
// Try to get something from the clipboard
|
// Try to get something from the clipboard
|
||||||
@@ -422,6 +434,7 @@ PasteCommand::PasteCommand(TeletextDocument *teletextDocument, int pageCharSet,
|
|||||||
nativeData = mimeData->data("application/x-teletext");
|
nativeData = mimeData->data("application/x-teletext");
|
||||||
if (nativeData.size() > 2) {
|
if (nativeData.size() > 2) {
|
||||||
// Native clipboard data: we put it there ourselves
|
// Native clipboard data: we put it there ourselves
|
||||||
|
m_plainText = false;
|
||||||
m_clipboardDataHeight = nativeData.at(0);
|
m_clipboardDataHeight = nativeData.at(0);
|
||||||
m_clipboardDataWidth = nativeData.at(1);
|
m_clipboardDataWidth = nativeData.at(1);
|
||||||
|
|
||||||
@@ -429,25 +442,119 @@ PasteCommand::PasteCommand(TeletextDocument *teletextDocument, int pageCharSet,
|
|||||||
if (m_clipboardDataHeight > 0 && m_clipboardDataWidth > 0 && m_clipboardDataHeight <= 25 && m_clipboardDataWidth <= 40 && nativeData.size() == m_clipboardDataHeight * m_clipboardDataWidth + 2)
|
if (m_clipboardDataHeight > 0 && m_clipboardDataWidth > 0 && m_clipboardDataHeight <= 25 && m_clipboardDataWidth <= 40 && nativeData.size() == m_clipboardDataHeight * m_clipboardDataWidth + 2)
|
||||||
for (int r=0; r<m_clipboardDataHeight; r++)
|
for (int r=0; r<m_clipboardDataHeight; r++)
|
||||||
m_pastingCharacters.append(nativeData.mid(2 + r * m_clipboardDataWidth, m_clipboardDataWidth));
|
m_pastingCharacters.append(nativeData.mid(2 + r * m_clipboardDataWidth, m_clipboardDataWidth));
|
||||||
else
|
else {
|
||||||
// Invalidate
|
// Invalidate
|
||||||
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_selectionActive) {
|
||||||
|
m_pasteBottomRow = m_row + m_clipboardDataHeight - 1;
|
||||||
|
m_pasteRightColumn = m_column + m_clipboardDataWidth - 1;
|
||||||
|
}
|
||||||
} else if (mimeData->hasText()) {
|
} else if (mimeData->hasText()) {
|
||||||
// Plain text
|
// Plain text
|
||||||
|
m_plainText = true;
|
||||||
|
|
||||||
|
const int rightColumn = m_selectionActive ? m_pasteRightColumn : 39;
|
||||||
|
|
||||||
|
// Parse line-feeds in the clipboard data
|
||||||
QStringList plainTextData = mimeData->text().split(QRegExp("\n|\r\n|\r"));
|
QStringList plainTextData = mimeData->text().split(QRegExp("\n|\r\n|\r"));
|
||||||
|
|
||||||
|
// "if" statement will be false if clipboard data is a single line of text
|
||||||
|
// that will fit from the cursor position
|
||||||
|
if (plainTextData.size() != 1 || m_pasteLeftColumn + plainTextData.at(0).size() - 1 > rightColumn) {
|
||||||
|
bool wrappingNeeded = false;
|
||||||
|
|
||||||
|
if (!m_selectionActive) {
|
||||||
|
// If selection is NOT active, use the full width of the page to paste.
|
||||||
|
// The second and subsequent lines will start at column 1, unless the
|
||||||
|
// cursor is explicitly on column 0.
|
||||||
|
if (m_pasteLeftColumn != 0)
|
||||||
|
m_pasteLeftColumn = 1;
|
||||||
|
|
||||||
|
// Check if first word in the first line will fit from the cursor position
|
||||||
|
bool firstWordFits = true;
|
||||||
|
const int firstSpace = plainTextData.at(0).indexOf(' ');
|
||||||
|
|
||||||
|
if (firstSpace == -1 && m_column + plainTextData.at(0).size() > 40)
|
||||||
|
firstWordFits = false; // Only one word in first line, and it won't fit
|
||||||
|
else if (m_column + firstSpace > 40)
|
||||||
|
firstWordFits = false; // First word in first line won't fit
|
||||||
|
|
||||||
|
// If the first word WILL fit at the cursor position, pad the first line
|
||||||
|
// to match the cursor position using null characters.
|
||||||
|
// In the QString null characters represent character cells in the
|
||||||
|
// pasting rectangle that won't overwrite what's on the page.
|
||||||
|
// If the first word WON'T fit, start pasting at the beginning of the next row.
|
||||||
|
if (firstWordFits)
|
||||||
|
plainTextData[0] = QString(m_column-m_pasteLeftColumn, QChar::Null) + plainTextData.at(0);
|
||||||
|
else if (m_pasteTopRow < 24)
|
||||||
|
m_pasteTopRow++;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pasteWidth = rightColumn - m_pasteLeftColumn + 1;
|
||||||
|
|
||||||
|
// Find out if we need to word-wrap
|
||||||
|
for (int i=0; i<plainTextData.size(); i++)
|
||||||
|
if (plainTextData.at(i).size() > pasteWidth) {
|
||||||
|
wrappingNeeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrappingNeeded) {
|
||||||
|
QStringList wrappedText;
|
||||||
|
|
||||||
|
for (int i=0; i<plainTextData.size(); i++) {
|
||||||
|
// Split this line into individual words
|
||||||
|
QStringList lineWords = plainTextData.at(i).split(' ');
|
||||||
|
|
||||||
|
// If there's any words which are too long to fit,
|
||||||
|
// break them across multiple lines
|
||||||
|
for (int j=0; j<lineWords.size(); j++)
|
||||||
|
if (lineWords.at(j).size() > pasteWidth) {
|
||||||
|
lineWords.insert(j+1, lineWords.at(j).mid(pasteWidth));
|
||||||
|
lineWords[j].truncate(pasteWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now reassemble the words into lines that will fit
|
||||||
|
QString currentLine = lineWords.at(0);
|
||||||
|
|
||||||
|
for (int j=1; j<lineWords.size(); j++)
|
||||||
|
if (currentLine.size() + 1 + lineWords.at(j).size() <= pasteWidth)
|
||||||
|
currentLine.append(' ' + lineWords.at(j));
|
||||||
|
else {
|
||||||
|
wrappedText.append(currentLine);
|
||||||
|
currentLine = lineWords.at(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedText.append(currentLine);
|
||||||
|
}
|
||||||
|
plainTextData.swap(wrappedText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_clipboardDataHeight = plainTextData.size();
|
m_clipboardDataHeight = plainTextData.size();
|
||||||
m_clipboardDataWidth = 0;
|
m_clipboardDataWidth = 0;
|
||||||
|
|
||||||
|
// Convert the unicode clipboard text into teletext bytes matching the current Level 1
|
||||||
|
// character set of this page
|
||||||
for (int r=0; r<m_clipboardDataHeight; r++) {
|
for (int r=0; r<m_clipboardDataHeight; r++) {
|
||||||
m_pastingCharacters.append(QByteArray());
|
m_pastingCharacters.append(QByteArray());
|
||||||
for (int c=0; c<plainTextData.at(r).size(); c++) {
|
for (int c=0; c<plainTextData.at(r).size(); c++) {
|
||||||
// Try to map the unicode character to the current Level 1 character set of this page
|
|
||||||
|
|
||||||
char convertedChar;
|
char convertedChar;
|
||||||
const QChar charToConvert = plainTextData.at(r).at(c);
|
const QChar charToConvert = plainTextData.at(r).at(c);
|
||||||
|
|
||||||
if (keymapping[pageCharSet].contains(charToConvert))
|
// Map a null character in the QString to 0xff (or -1)
|
||||||
|
// In the QByteArray 0xff bytes represent character cells in the pasting rectangle
|
||||||
|
// that won't overwrite what's on the page
|
||||||
|
if (charToConvert == QChar::Null)
|
||||||
|
convertedChar = -1;
|
||||||
|
else if (charToConvert >= 0x01 && charToConvert <= 0x1f)
|
||||||
|
convertedChar = ' ';
|
||||||
|
else if (keymapping[pageCharSet].contains(charToConvert))
|
||||||
// Remapped character or non-Latin character converted successfully
|
// Remapped character or non-Latin character converted successfully
|
||||||
convertedChar = keymapping[pageCharSet].value(charToConvert);
|
convertedChar = keymapping[pageCharSet].value(charToConvert);
|
||||||
else {
|
else {
|
||||||
@@ -463,26 +570,19 @@ PasteCommand::PasteCommand(TeletextDocument *teletextDocument, int pageCharSet,
|
|||||||
}
|
}
|
||||||
m_clipboardDataWidth = qMax(m_pastingCharacters.at(r).size(), m_clipboardDataWidth);
|
m_clipboardDataWidth = qMax(m_pastingCharacters.at(r).size(), m_clipboardDataWidth);
|
||||||
}
|
}
|
||||||
// Pad short lines with spaces to make a box
|
// Pad the end of short lines with spaces to make a box
|
||||||
for (int r=0; r<m_clipboardDataHeight; r++)
|
for (int r=0; r<m_clipboardDataHeight; r++)
|
||||||
m_pastingCharacters[r] = m_pastingCharacters.at(r).leftJustified(m_clipboardDataWidth);
|
m_pastingCharacters[r] = m_pastingCharacters.at(r).leftJustified(m_clipboardDataWidth);
|
||||||
|
|
||||||
|
if (!m_selectionActive) {
|
||||||
|
m_pasteBottomRow = m_pasteTopRow + m_clipboardDataHeight - 1;
|
||||||
|
m_pasteRightColumn = m_pasteLeftColumn + m_clipboardDataWidth - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_clipboardDataWidth == 0 || m_clipboardDataHeight == 0)
|
if (m_clipboardDataWidth == 0 || m_clipboardDataHeight == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_selectionActive) {
|
|
||||||
m_pasteTopRow = m_teletextDocument->selectionTopRow();
|
|
||||||
m_pasteBottomRow = m_teletextDocument->selectionBottomRow();
|
|
||||||
m_pasteLeftColumn = m_teletextDocument->selectionLeftColumn();
|
|
||||||
m_pasteRightColumn = m_teletextDocument->selectionRightColumn();
|
|
||||||
} else {
|
|
||||||
m_pasteTopRow = m_row;
|
|
||||||
m_pasteBottomRow = m_row + m_clipboardDataHeight - 1;
|
|
||||||
m_pasteLeftColumn = m_column;
|
|
||||||
m_pasteRightColumn = m_column + m_clipboardDataWidth - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store copy of the characters that we're about to overwrite
|
// Store copy of the characters that we're about to overwrite
|
||||||
for (int r=m_pasteTopRow; r<=m_pasteBottomRow; r++) {
|
for (int r=m_pasteTopRow; r<=m_pasteBottomRow; r++) {
|
||||||
QByteArray rowArray;
|
QByteArray rowArray;
|
||||||
@@ -517,11 +617,21 @@ void PasteCommand::redo()
|
|||||||
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
||||||
// Guard against size of pasted block going beyond last line or column
|
// Guard against size of pasted block going beyond last line or column
|
||||||
if (r < 25 && c < 40) {
|
if (r < 25 && c < 40) {
|
||||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_pastingCharacters[arrayR].at(arrayC++));
|
// Check for 0xff bytes using "-1"
|
||||||
|
// gcc complains about "comparision always true due to limited range"
|
||||||
|
if (m_pastingCharacters.at(arrayR).at(arrayC) != -1)
|
||||||
|
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_pastingCharacters.at(arrayR).at(arrayC));
|
||||||
|
|
||||||
|
arrayC++;
|
||||||
|
|
||||||
// If paste area is wider than clipboard data, repeat the pattern
|
// If paste area is wider than clipboard data, repeat the pattern
|
||||||
if (arrayC == m_clipboardDataWidth)
|
// if it wasn't plain text
|
||||||
arrayC = 0;
|
if (arrayC == m_clipboardDataWidth) {
|
||||||
|
if (!m_plainText)
|
||||||
|
arrayC = 0;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r < 25)
|
if (r < 25)
|
||||||
@@ -529,8 +639,13 @@ void PasteCommand::redo()
|
|||||||
|
|
||||||
arrayR++;
|
arrayR++;
|
||||||
// If paste area is taller than clipboard data, repeat the pattern
|
// If paste area is taller than clipboard data, repeat the pattern
|
||||||
if (arrayR == m_clipboardDataHeight)
|
// if it wasn't plain text
|
||||||
arrayR = 0;
|
if (arrayR == m_clipboardDataHeight) {
|
||||||
|
if (!m_plainText)
|
||||||
|
arrayR = 0;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_selectionActive) {
|
if (m_selectionActive) {
|
||||||
@@ -556,8 +671,11 @@ void PasteCommand::undo()
|
|||||||
arrayC = 0;
|
arrayC = 0;
|
||||||
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
||||||
// Guard against size of pasted block going beyond last line or column
|
// Guard against size of pasted block going beyond last line or column
|
||||||
if (r < 25 && c < 40)
|
if (r < 25 && c < 40) {
|
||||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_deletedCharacters[arrayR].at(arrayC++));
|
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_deletedCharacters[arrayR].at(arrayC));
|
||||||
|
|
||||||
|
arrayC++;
|
||||||
|
}
|
||||||
|
|
||||||
if (r < 25)
|
if (r < 25)
|
||||||
emit m_teletextDocument->contentsChange(r);
|
emit m_teletextDocument->contentsChange(r);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -179,7 +179,7 @@ private:
|
|||||||
int m_pasteTopRow, m_pasteBottomRow, m_pasteLeftColumn, m_pasteRightColumn;
|
int m_pasteTopRow, m_pasteBottomRow, m_pasteLeftColumn, m_pasteRightColumn;
|
||||||
int m_clipboardDataHeight, m_clipboardDataWidth;
|
int m_clipboardDataHeight, m_clipboardDataWidth;
|
||||||
int m_selectionCornerRow, m_selectionCornerColumn;
|
int m_selectionCornerRow, m_selectionCornerColumn;
|
||||||
bool m_selectionActive;
|
bool m_selectionActive, m_plainText;
|
||||||
};
|
};
|
||||||
#endif // !QT_NO_CLIPBOARD
|
#endif // !QT_NO_CLIPBOARD
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include "levelonepage.h"
|
#include "levelonepage.h"
|
||||||
|
|
||||||
|
#include "x26triplets.h"
|
||||||
|
|
||||||
LevelOnePage::LevelOnePage()
|
LevelOnePage::LevelOnePage()
|
||||||
{
|
{
|
||||||
m_enhancements.reserve(maxEnhancements());
|
m_enhancements.reserve(maxEnhancements());
|
||||||
@@ -403,17 +405,21 @@ bool LevelOnePage::isPaletteDefault(int fromColour, int toColour) const
|
|||||||
|
|
||||||
int LevelOnePage::levelRequired() const
|
int LevelOnePage::levelRequired() const
|
||||||
{
|
{
|
||||||
|
// X/28/4 present i.e. CLUTs 0 or 1 redefined - Level 3.5
|
||||||
if (!isPaletteDefault(0, 15))
|
if (!isPaletteDefault(0, 15))
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
// TODO Check for X/28/1 for DCLUT for mode 1-3 DRCS characters - return 3
|
// TODO Check for X/28/1 for DCLUT for mode 1-3 DRCS characters - return 3
|
||||||
|
|
||||||
|
// Assume Level 2.5 if any X/28 page enhancements are present, otherwise assume Level 1
|
||||||
int levelSeen = (!isPaletteDefault(16,31) || m_leftSidePanelDisplayed || m_rightSidePanelDisplayed || m_defaultScreenColour !=0 || m_defaultRowColour !=0 || m_blackBackgroundSubst || m_colourTableRemap !=0 || m_defaultCharSet != 0 || m_secondCharSet != 0xf) ? 2 : 0;
|
int levelSeen = (!isPaletteDefault(16,31) || m_leftSidePanelDisplayed || m_rightSidePanelDisplayed || m_defaultScreenColour !=0 || m_defaultRowColour !=0 || m_blackBackgroundSubst || m_colourTableRemap !=0 || m_defaultCharSet != 0 || m_secondCharSet != 0xf) ? 2 : 0;
|
||||||
|
|
||||||
|
// If there's no X/26 triplets, exit here as Level 1 or 2.5
|
||||||
if (m_enhancements.isEmpty())
|
if (m_enhancements.isEmpty())
|
||||||
return levelSeen;
|
return levelSeen;
|
||||||
|
|
||||||
for (int i=0; i<m_enhancements.size(); i++) {
|
for (int i=0; i<m_enhancements.size(); i++) {
|
||||||
|
// Font style - Level 3.5 only triplet
|
||||||
if (m_enhancements.at(i).modeExt() == 0x2e) // Font style
|
if (m_enhancements.at(i).modeExt() == 0x2e) // Font style
|
||||||
return 3;
|
return 3;
|
||||||
|
|
||||||
@@ -426,9 +432,10 @@ int LevelOnePage::levelRequired() const
|
|||||||
case 0x22: // G3 character @ Level 1.5
|
case 0x22: // G3 character @ Level 1.5
|
||||||
case 0x2f: // G2 character
|
case 0x2f: // G2 character
|
||||||
case 0x30 ... 0x3f: // G0 character with diacritical
|
case 0x30 ... 0x3f: // G0 character with diacritical
|
||||||
levelSeen = qMax(levelSeen, 1);
|
levelSeen = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (levelSeen < 2)
|
if (levelSeen < 2)
|
||||||
switch (m_enhancements.at(i).modeExt()) {
|
switch (m_enhancements.at(i).modeExt()) {
|
||||||
// Check for Level 2.5 triplets
|
// Check for Level 2.5 triplets
|
||||||
@@ -436,25 +443,26 @@ int LevelOnePage::levelRequired() const
|
|||||||
case 0x01: // Full row colour
|
case 0x01: // Full row colour
|
||||||
case 0x10 ... 0x13: // Origin Modifer and Object Invocation
|
case 0x10 ... 0x13: // Origin Modifer and Object Invocation
|
||||||
case 0x15 ... 0x17: // Object Definition
|
case 0x15 ... 0x17: // Object Definition
|
||||||
// Check if Object Definition is required only at Level 3.5
|
|
||||||
if ((m_enhancements.at(i).address() & 0x18) == 0x10)
|
|
||||||
return 3;
|
|
||||||
else
|
|
||||||
levelSeen = qMax(levelSeen, 2);
|
|
||||||
break;
|
|
||||||
case 0x18: // DRCS Mode
|
case 0x18: // DRCS Mode
|
||||||
// Check if DRCS is required only at Level 3.5
|
|
||||||
if ((m_enhancements.at(i).data() & 0x30) == 0x20)
|
|
||||||
return 3;
|
|
||||||
else
|
|
||||||
levelSeen = qMax(levelSeen, 2);
|
|
||||||
break;
|
|
||||||
case 0x20: // Foreground colour
|
case 0x20: // Foreground colour
|
||||||
case 0x21: // G1 character
|
case 0x21: // G1 character
|
||||||
case 0x23: // Background colour
|
case 0x23: // Background colour
|
||||||
case 0x27 ... 0x29: // Flash functions, G0 and G2 charset designation, G0 character @ Level 2.5
|
case 0x27 ... 0x29: // Flash functions, G0 and G2 charset designation, G0 character @ Level 2.5
|
||||||
case 0x2b ... 0x2d: // G3 character @ Level 2.5, display attributes, DRCS character
|
case 0x2b ... 0x2d: // G3 character @ Level 2.5, display attributes, DRCS character
|
||||||
levelSeen = qMax(levelSeen, 2);
|
levelSeen = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (levelSeen == 2)
|
||||||
|
switch (m_enhancements.at(i).modeExt()) {
|
||||||
|
// Check for triplets with "required at Level 3.5 only" parameters
|
||||||
|
case 0x15 ... 0x17: // Object Definition
|
||||||
|
if ((m_enhancements.at(i).address() & 0x18) == 0x10)
|
||||||
|
return 3;
|
||||||
|
break;
|
||||||
|
case 0x18: // DRCS Mode
|
||||||
|
if ((m_enhancements.at(i).data() & 0x30) == 0x20)
|
||||||
|
return 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
4
main.cpp
4
main.cpp
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
|
|||||||
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
||||||
QApplication::setOrganizationName("gkmac.co.uk");
|
QApplication::setOrganizationName("gkmac.co.uk");
|
||||||
QApplication::setOrganizationDomain("gkmac.co.uk");
|
QApplication::setOrganizationDomain("gkmac.co.uk");
|
||||||
QApplication::setApplicationVersion("0.5.4-alpha");
|
QApplication::setApplicationVersion("0.6-alpha");
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QApplication::applicationName());
|
parser.setApplicationDescription(QApplication::applicationName());
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -250,8 +250,8 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
if (event->key() < 0x01000000) {
|
if (event->key() < 0x01000000) {
|
||||||
// A character-typing key was pressed
|
// A character-typing key was pressed
|
||||||
// Try to keymap it, if not keymapped then plain ASCII code (may be) returned
|
// Try to keymap it, if not keymapped then plain ASCII code (may be) returned
|
||||||
char mappedKeyPress = keymapping[m_pageDecode.level1CharSet(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn())].value(event->text().at(0), *qPrintable(event->text()));
|
char mappedKeyPress = keymapping[m_pageDecode.level1CharSet(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn())].value(event->text().at(0), *qPrintable(event->text().at(0)));
|
||||||
if (mappedKeyPress < 0x20)
|
if (mappedKeyPress >= 0x00 && mappedKeyPress <= 0x1f)
|
||||||
return;
|
return;
|
||||||
// If outside ASCII map then the character can't be represented by current Level 1 character set
|
// If outside ASCII map then the character can't be represented by current Level 1 character set
|
||||||
// Map it to block character so it doesn't need to be inserted-between later on
|
// Map it to block character so it doesn't need to be inserted-between later on
|
||||||
@@ -259,7 +259,7 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
mappedKeyPress = 0x7f;
|
mappedKeyPress = 0x7f;
|
||||||
if (m_pageDecode.level1MosaicAttribute(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn()) && (mappedKeyPress < 0x40 || mappedKeyPress > 0x5f)) {
|
if (m_pageDecode.level1MosaicAttribute(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn()) && (mappedKeyPress < 0x40 || mappedKeyPress > 0x5f)) {
|
||||||
// We're on a mosaic and a blast-through character was NOT pressed
|
// We're on a mosaic and a blast-through character was NOT pressed
|
||||||
if (event->key() >= Qt::Key_1 && event->key() <= Qt::Key_9 && event->modifiers() & Qt::KeypadModifier) {
|
if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9 && event->modifiers() & Qt::KeypadModifier) {
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_7:
|
case Qt::Key_7:
|
||||||
toggleCharacterBit(0x01); // Top left
|
toggleCharacterBit(0x01); // Top left
|
||||||
@@ -288,6 +288,9 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
case Qt::Key_3:
|
case Qt::Key_3:
|
||||||
toggleCharacterBit(0x20); // Clear all
|
toggleCharacterBit(0x20); // Clear all
|
||||||
break;
|
break;
|
||||||
|
case Qt::Key_0:
|
||||||
|
toggleCharacterBit(0x66); // Dither
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -328,6 +331,9 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
case mosaicNativeScanCodes[8]:
|
case mosaicNativeScanCodes[8]:
|
||||||
toggleCharacterBit(0x20); // Clear all
|
toggleCharacterBit(0x20); // Clear all
|
||||||
break;
|
break;
|
||||||
|
case mosaicNativeScanCodes[9]:
|
||||||
|
toggleCharacterBit(0x66); // Dither
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else
|
} else
|
||||||
@@ -386,7 +392,7 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
|||||||
case Qt::Key_PageDown:
|
case Qt::Key_PageDown:
|
||||||
m_teletextDocument->selectSubPagePrevious();
|
m_teletextDocument->selectSubPagePrevious();
|
||||||
break;
|
break;
|
||||||
case Qt::Key_F5:
|
case Qt::Key_F6:
|
||||||
m_pageDecode.decodePage();
|
m_pageDecode.decodePage();
|
||||||
m_pageRender.renderPage(true);
|
m_pageRender.renderPage(true);
|
||||||
update();
|
update();
|
||||||
@@ -688,18 +694,13 @@ void LevelOneScene::toggleGrid(bool gridOn)
|
|||||||
void LevelOneScene::hideGUIElements(bool hidden)
|
void LevelOneScene::hideGUIElements(bool hidden)
|
||||||
{
|
{
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
m_mainGridItemGroup->setVisible(false);
|
|
||||||
m_cursorRectItem->setVisible(false);
|
m_cursorRectItem->setVisible(false);
|
||||||
m_selectionRectItem->setVisible(false);
|
m_selectionRectItem->setVisible(false);
|
||||||
for (int i=0; i<32; i++)
|
|
||||||
if (m_sidePanelGridNeeded[i])
|
|
||||||
m_sidePanelGridItemGroup[i]->setVisible(false);
|
|
||||||
} else {
|
} else {
|
||||||
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionActive())
|
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionActive())
|
||||||
m_selectionRectItem->setVisible(true);
|
m_selectionRectItem->setVisible(true);
|
||||||
|
|
||||||
m_cursorRectItem->setVisible(true);
|
m_cursorRectItem->setVisible(true);
|
||||||
toggleGrid(m_grid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
105
mainwindow.cpp
105
mainwindow.cpp
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -149,6 +149,34 @@ bool MainWindow::saveAs()
|
|||||||
return saveFile(fileName);
|
return saveFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::reload()
|
||||||
|
{
|
||||||
|
if (m_isUntitled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_textWidget->document()->undoStack()->isClean()) {
|
||||||
|
const QMessageBox::StandardButton ret = QMessageBox::warning(this, QApplication::applicationDisplayName(), tr("The document \"%1\" has been modified.\nDo you want to discard your changes?").arg(QFileInfo(m_curFile).fileName()), QMessageBox::Discard | QMessageBox::Cancel);
|
||||||
|
|
||||||
|
if (ret != QMessageBox::Discard)
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const QMessageBox::StandardButton ret = QMessageBox::warning(this, QApplication::applicationDisplayName(), tr("Do you want to reload the document \"%1\" from disk?").arg(QFileInfo(m_curFile).fileName()), QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (ret != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int subPageIndex = m_textWidget->document()->currentSubPageIndex();
|
||||||
|
|
||||||
|
m_textWidget->document()->clear();
|
||||||
|
loadFile(m_curFile);
|
||||||
|
|
||||||
|
if (subPageIndex >= m_textWidget->document()->numberOfSubPages())
|
||||||
|
subPageIndex = m_textWidget->document()->numberOfSubPages()-1;
|
||||||
|
|
||||||
|
m_textWidget->document()->selectSubPageIndex(subPageIndex, true);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::exportPNG()
|
void MainWindow::exportPNG()
|
||||||
{
|
{
|
||||||
QString exportFileName = QFileDialog::getSaveFileName(this, tr("Export PNG"), QString(), "PNG image (*.png)");
|
QString exportFileName = QFileDialog::getSaveFileName(this, tr("Export PNG"), QString(), "PNG image (*.png)");
|
||||||
@@ -158,9 +186,6 @@ void MainWindow::exportPNG()
|
|||||||
// Prepare widget image for extraction
|
// Prepare widget image for extraction
|
||||||
m_textWidget->pauseFlash(true);
|
m_textWidget->pauseFlash(true);
|
||||||
m_textScene->hideGUIElements(true);
|
m_textScene->hideGUIElements(true);
|
||||||
bool reshowControlCodes = m_textWidget->showControlCodes();
|
|
||||||
if (reshowControlCodes)
|
|
||||||
m_textWidget->setShowControlCodes(false);
|
|
||||||
// Disable exporting in Mix mode as it corrupts the background
|
// Disable exporting in Mix mode as it corrupts the background
|
||||||
bool reMix = m_textWidget->pageRender()->mix();
|
bool reMix = m_textWidget->pageRender()->mix();
|
||||||
if (reMix) {
|
if (reMix) {
|
||||||
@@ -178,8 +203,6 @@ void MainWindow::exportPNG()
|
|||||||
|
|
||||||
// Now we've extracted the image we can put the GUI things back
|
// Now we've extracted the image we can put the GUI things back
|
||||||
m_textScene->hideGUIElements(false);
|
m_textScene->hideGUIElements(false);
|
||||||
if (reshowControlCodes)
|
|
||||||
m_textWidget->setShowControlCodes(true);
|
|
||||||
if (reMix) {
|
if (reMix) {
|
||||||
m_textWidget->setMix(true);
|
m_textWidget->setMix(true);
|
||||||
m_textScene->setMix(true);
|
m_textScene->setMix(true);
|
||||||
@@ -218,7 +241,7 @@ void MainWindow::about()
|
|||||||
QMessageBox::about(this, tr("About"), QString("<b>%1</b><br>"
|
QMessageBox::about(this, tr("About"), QString("<b>%1</b><br>"
|
||||||
"An open source Level 2.5 teletext page editor.<br>"
|
"An open source Level 2.5 teletext page editor.<br>"
|
||||||
"<i>Version %2</i><br><br>"
|
"<i>Version %2</i><br><br>"
|
||||||
"Copyright (C) 2020-2022 Gavin MacGregor<br><br>"
|
"Copyright (C) 2020-2023 Gavin MacGregor<br><br>"
|
||||||
"Released under the GNU General Public License version 3<br>"
|
"Released under the GNU General Public License version 3<br>"
|
||||||
"<a href=\"https://github.com/gkthemac/qteletextmaker\">https://github.com/gkthemac/qteletextmaker</a>").arg(QApplication::applicationDisplayName()).arg(QApplication::applicationVersion()));
|
"<a href=\"https://github.com/gkthemac/qteletextmaker\">https://github.com/gkthemac/qteletextmaker</a>").arg(QApplication::applicationDisplayName()).arg(QApplication::applicationVersion()));
|
||||||
}
|
}
|
||||||
@@ -328,6 +351,11 @@ void MainWindow::createActions()
|
|||||||
saveAsAct->setShortcuts(QKeySequence::SaveAs);
|
saveAsAct->setShortcuts(QKeySequence::SaveAs);
|
||||||
saveAsAct->setStatusTip(tr("Save the document under a new name"));
|
saveAsAct->setStatusTip(tr("Save the document under a new name"));
|
||||||
|
|
||||||
|
const QIcon reloadIcon = QIcon::fromTheme("document-revert");
|
||||||
|
QAction *reloadAct = fileMenu->addAction(reloadIcon, tr("Reload"), this, &MainWindow::reload);
|
||||||
|
reloadAct->setShortcuts(QKeySequence::Refresh);
|
||||||
|
reloadAct->setStatusTip(tr("Reload the document from disk"));
|
||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
|
|
||||||
QMenu *recentMenu = fileMenu->addMenu(tr("Recent"));
|
QMenu *recentMenu = fileMenu->addMenu(tr("Recent"));
|
||||||
@@ -343,9 +371,16 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
||||||
|
|
||||||
|
m_exportAutoAct = fileMenu->addAction(tr("Export subpage..."));
|
||||||
|
m_exportAutoAct->setEnabled(false);
|
||||||
|
m_exportAutoAct->setShortcut(tr("Ctrl+E"));
|
||||||
|
m_exportAutoAct->setStatusTip("Export this subpage back to the imported file");
|
||||||
|
connect(fileMenu, &QMenu::aboutToShow, this, &MainWindow::updateExportAutoAction);
|
||||||
|
connect(m_exportAutoAct, &QAction::triggered, this, &MainWindow::exportAuto);
|
||||||
|
|
||||||
QAction *exportT42Act = fileMenu->addAction(tr("Export subpage as t42..."));
|
QAction *exportT42Act = fileMenu->addAction(tr("Export subpage as t42..."));
|
||||||
exportT42Act->setStatusTip("Export this subpage as a t42 file");
|
exportT42Act->setStatusTip("Export this subpage as a t42 file");
|
||||||
connect(exportT42Act, &QAction::triggered, this, &MainWindow::exportT42);
|
connect(exportT42Act, &QAction::triggered, this, [=]() { exportT42(false); });
|
||||||
|
|
||||||
QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export subpage to online editor"));
|
QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export subpage to online editor"));
|
||||||
|
|
||||||
@@ -906,10 +941,13 @@ void MainWindow::loadFile(const QString &fileName)
|
|||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
if (fileInfo.suffix() == "t42")
|
if (fileInfo.suffix() == "t42") {
|
||||||
importT42(&file, m_textWidget->document());
|
importT42(&file, m_textWidget->document());
|
||||||
else
|
m_exportAutoFileName = fileName;
|
||||||
|
} else {
|
||||||
loadTTI(&file, m_textWidget->document());
|
loadTTI(&file, m_textWidget->document());
|
||||||
|
m_exportAutoFileName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
levelSeen = m_textWidget->document()->levelRequired();
|
levelSeen = m_textWidget->document()->levelRequired();
|
||||||
m_levelRadioButton[levelSeen]->toggle();
|
m_levelRadioButton[levelSeen]->toggle();
|
||||||
@@ -993,6 +1031,18 @@ void MainWindow::updateRecentFileActions()
|
|||||||
m_recentFileActs[i]->setVisible(false);
|
m_recentFileActs[i]->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateExportAutoAction()
|
||||||
|
{
|
||||||
|
if (m_exportAutoFileName.isEmpty()) {
|
||||||
|
m_exportAutoAct->setText(tr("Export subpage..."));
|
||||||
|
m_exportAutoAct->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_exportAutoAct->setText(tr("Overwrite &%1").arg(MainWindow::strippedName(m_exportAutoFileName)));
|
||||||
|
m_exportAutoAct->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::openRecentFile()
|
void MainWindow::openRecentFile()
|
||||||
{
|
{
|
||||||
if (const QAction *action = qobject_cast<const QAction *>(sender()))
|
if (const QAction *action = qobject_cast<const QAction *>(sender()))
|
||||||
@@ -1023,16 +1073,30 @@ bool MainWindow::saveFile(const QString &fileName)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::exportT42()
|
void MainWindow::exportAuto()
|
||||||
|
{
|
||||||
|
// Menu should be disabled if m_exportAutoFileName is empty, but just in case...
|
||||||
|
if (m_exportAutoFileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
exportT42(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::exportT42(bool fromAuto)
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
QString exportFileName = m_curFile;
|
QString exportFileName;
|
||||||
|
|
||||||
changeSuffixFromTTI(exportFileName, "t42");
|
if (fromAuto)
|
||||||
|
exportFileName = m_exportAutoFileName;
|
||||||
|
else {
|
||||||
|
exportFileName = m_curFile;
|
||||||
|
changeSuffixFromTTI(exportFileName, "t42");
|
||||||
|
|
||||||
exportFileName = QFileDialog::getSaveFileName(this, tr("Export t42"), exportFileName, "t42 stream (*.t42)");
|
exportFileName = QFileDialog::getSaveFileName(this, tr("Export t42"), exportFileName, "t42 stream (*.t42)");
|
||||||
if (exportFileName.isEmpty())
|
if (exportFileName.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||||
QSaveFile file(exportFileName);
|
QSaveFile file(exportFileName);
|
||||||
@@ -1044,8 +1108,15 @@ void MainWindow::exportT42()
|
|||||||
errorMessage = tr("Cannot open file %1 for writing:\n%2.").arg(QDir::toNativeSeparators(exportFileName), file.errorString());
|
errorMessage = tr("Cannot open file %1 for writing:\n%2.").arg(QDir::toNativeSeparators(exportFileName), file.errorString());
|
||||||
QApplication::restoreOverrideCursor();
|
QApplication::restoreOverrideCursor();
|
||||||
|
|
||||||
if (!errorMessage.isEmpty())
|
if (!errorMessage.isEmpty()) {
|
||||||
QMessageBox::warning(this, QApplication::applicationDisplayName(), errorMessage);
|
QMessageBox::warning(this, QApplication::applicationDisplayName(), errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::prependToRecentFiles(exportFileName);
|
||||||
|
|
||||||
|
m_exportAutoFileName = exportFileName;
|
||||||
|
statusBar()->showMessage(tr("File exported"), 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::exportM29()
|
void MainWindow::exportM29()
|
||||||
|
|||||||
10
mainwindow.h
10
mainwindow.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -59,12 +59,15 @@ private slots:
|
|||||||
void open();
|
void open();
|
||||||
bool save();
|
bool save();
|
||||||
bool saveAs();
|
bool saveAs();
|
||||||
void exportT42();
|
void reload();
|
||||||
|
void exportAuto();
|
||||||
|
void exportT42(bool);
|
||||||
void exportZXNet();
|
void exportZXNet();
|
||||||
void exportEditTF();
|
void exportEditTF();
|
||||||
void exportPNG();
|
void exportPNG();
|
||||||
void exportM29();
|
void exportM29();
|
||||||
void updateRecentFileActions();
|
void updateRecentFileActions();
|
||||||
|
void updateExportAutoAction();
|
||||||
void openRecentFile();
|
void openRecentFile();
|
||||||
void about();
|
void about();
|
||||||
void updatePageWidgets();
|
void updatePageWidgets();
|
||||||
@@ -120,6 +123,7 @@ private:
|
|||||||
QAction *m_recentFileActs[m_MaxRecentFiles];
|
QAction *m_recentFileActs[m_MaxRecentFiles];
|
||||||
QAction *m_recentFileSeparator;
|
QAction *m_recentFileSeparator;
|
||||||
QAction *m_recentFileSubMenuAct;
|
QAction *m_recentFileSubMenuAct;
|
||||||
|
QAction *m_exportAutoAct;
|
||||||
QAction *m_deleteSubPageAction;
|
QAction *m_deleteSubPageAction;
|
||||||
QAction *m_borderActs[3];
|
QAction *m_borderActs[3];
|
||||||
QAction *m_aspectRatioActs[4];
|
QAction *m_aspectRatioActs[4];
|
||||||
@@ -130,7 +134,7 @@ private:
|
|||||||
QPushButton *m_insertModePushButton;
|
QPushButton *m_insertModePushButton;
|
||||||
QRadioButton *m_levelRadioButton[4];
|
QRadioButton *m_levelRadioButton[4];
|
||||||
|
|
||||||
QString m_curFile;
|
QString m_curFile, m_exportAutoFileName;
|
||||||
bool m_isUntitled;
|
bool m_isUntitled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -354,7 +354,9 @@ void TeletextPageRender::renderPage(bool force)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force || m_decoder->cellFlashMode(r, c) == 0)
|
if (force && m_decoder->cellFlashMode(r, c) != 0)
|
||||||
|
m_decoder->setRefresh(r, c, true);
|
||||||
|
else
|
||||||
m_decoder->setRefresh(r, c, false);
|
m_decoder->setRefresh(r, c, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
render.h
2
render.h
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -623,6 +623,8 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
|||||||
|
|
||||||
x26Layout->addLayout(insertDeleteLayout);
|
x26Layout->addLayout(insertDeleteLayout);
|
||||||
|
|
||||||
|
disableTripletWidgets();
|
||||||
|
|
||||||
x26Widget->setLayout(x26Layout);
|
x26Widget->setLayout(x26Layout);
|
||||||
this->setWidget(x26Widget);
|
this->setWidget(x26Widget);
|
||||||
|
|
||||||
@@ -674,14 +676,48 @@ void X26DockWidget::rowSelected(const QModelIndex ¤t, const QModelIndex &p
|
|||||||
{
|
{
|
||||||
Q_UNUSED(previous);
|
Q_UNUSED(previous);
|
||||||
|
|
||||||
updateAllRawTripletSpinBoxes(current);
|
if (current.isValid()) {
|
||||||
updateAllCookedTripletWidgets(current);
|
updateAllRawTripletSpinBoxes(current);
|
||||||
|
updateAllCookedTripletWidgets(current);
|
||||||
|
} else
|
||||||
|
disableTripletWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26DockWidget::disableTripletWidgets()
|
||||||
|
{
|
||||||
|
m_rawTripletAddressSpinBox->setEnabled(false);
|
||||||
|
m_rawTripletDataSpinBox->setEnabled(false);
|
||||||
|
m_rawTripletModeSpinBox->setEnabled(false);
|
||||||
|
m_rawTripletAddressSpinBox->blockSignals(true);
|
||||||
|
m_rawTripletModeSpinBox->blockSignals(true);
|
||||||
|
m_rawTripletDataSpinBox->blockSignals(true);
|
||||||
|
m_rawTripletAddressSpinBox->setValue(0);
|
||||||
|
m_rawTripletModeSpinBox->setValue(0);
|
||||||
|
m_rawTripletDataSpinBox->setValue(0);
|
||||||
|
m_rawTripletAddressSpinBox->blockSignals(false);
|
||||||
|
m_rawTripletModeSpinBox->blockSignals(false);
|
||||||
|
m_rawTripletDataSpinBox->blockSignals(false);
|
||||||
|
|
||||||
|
m_cookedRowSpinBox->setEnabled(false);
|
||||||
|
m_cookedColumnSpinBox->setEnabled(false);
|
||||||
|
m_cookedRowSpinBox->blockSignals(true);
|
||||||
|
m_cookedColumnSpinBox->blockSignals(true);
|
||||||
|
m_cookedRowSpinBox->setValue(1);
|
||||||
|
m_cookedColumnSpinBox->setValue(0);
|
||||||
|
m_cookedRowSpinBox->blockSignals(false);
|
||||||
|
m_cookedColumnSpinBox->blockSignals(false);
|
||||||
|
|
||||||
|
m_cookedModePushButton->setEnabled(false);
|
||||||
|
m_cookedModePushButton->setText(QString());
|
||||||
|
|
||||||
|
m_tripletParameterStackedLayout->setCurrentIndex(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void X26DockWidget::updateAllCookedTripletWidgets(const QModelIndex &index)
|
void X26DockWidget::updateAllCookedTripletWidgets(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
const int modeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
|
const int modeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
|
||||||
|
|
||||||
|
m_cookedModePushButton->setEnabled(true);
|
||||||
m_cookedModePushButton->setText(m_x26Model->modeTripletName(modeExt));
|
m_cookedModePushButton->setText(m_x26Model->modeTripletName(modeExt));
|
||||||
|
|
||||||
switch (modeExt) {
|
switch (modeExt) {
|
||||||
@@ -1000,9 +1036,11 @@ void X26DockWidget::insertTriplet(int modeExt, bool after)
|
|||||||
{
|
{
|
||||||
QModelIndex index = m_x26View->currentIndex();
|
QModelIndex index = m_x26View->currentIndex();
|
||||||
X26Triplet newTriplet(modeExt < 0x20 ? 41 : 0, modeExt & 0x1f, 0);
|
X26Triplet newTriplet(modeExt < 0x20 ? 41 : 0, modeExt & 0x1f, 0);
|
||||||
int row;
|
int newListRow;
|
||||||
|
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
|
newListRow = index.row()+after;
|
||||||
|
|
||||||
// If we're inserting a column triplet next to another column triplet,
|
// If we're inserting a column triplet next to another column triplet,
|
||||||
// duplicate the column number
|
// duplicate the column number
|
||||||
// Avoid the PDC and reserved mode triplets
|
// Avoid the PDC and reserved mode triplets
|
||||||
@@ -1012,9 +1050,26 @@ void X26DockWidget::insertTriplet(int modeExt, bool after)
|
|||||||
if (existingTripletModeExt >= 0x20 && existingTripletModeExt != 0x24 && existingTripletModeExt != 0x25 && existingTripletModeExt != 0x26 && existingTripletModeExt != 0x2a)
|
if (existingTripletModeExt >= 0x20 && existingTripletModeExt != 0x24 && existingTripletModeExt != 0x25 && existingTripletModeExt != 0x26 && existingTripletModeExt != 0x2a)
|
||||||
newTriplet.setAddress(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole).toInt());
|
newTriplet.setAddress(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole).toInt());
|
||||||
}
|
}
|
||||||
row = index.row()+after;
|
// If we're inserting a Set Active Position or Full Row Colour triplet,
|
||||||
|
// look for a previous row setting triplet and set this one to the row after
|
||||||
|
if (modeExt == 0x04 || modeExt == 0x01) {
|
||||||
|
for (int i=newListRow-1; i>=0; i--) {
|
||||||
|
const int scanTripletModeExt = index.model()->data(index.model()->index(i, 2), Qt::EditRole).toInt();
|
||||||
|
|
||||||
|
if (scanTripletModeExt == 0x04 || scanTripletModeExt == 0x01) {
|
||||||
|
const int scanActivePositionRow = index.model()->data(index.model()->index(i, 0), Qt::EditRole).toInt()+1;
|
||||||
|
|
||||||
|
if (scanActivePositionRow < 25)
|
||||||
|
newTriplet.setAddressRow(scanActivePositionRow);
|
||||||
|
else
|
||||||
|
newTriplet.setAddressRow(24);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
row = 0;
|
newListRow = 0;
|
||||||
|
|
||||||
// For character triplets, ensure Data is not reserved
|
// For character triplets, ensure Data is not reserved
|
||||||
if (modeExt == 0x21 || modeExt == 0x22 || modeExt == 0x29 || modeExt == 0x2b || modeExt >= 0x2f)
|
if (modeExt == 0x21 || modeExt == 0x22 || modeExt == 0x29 || modeExt == 0x2b || modeExt >= 0x2f)
|
||||||
@@ -1025,7 +1080,7 @@ void X26DockWidget::insertTriplet(int modeExt, bool after)
|
|||||||
newTriplet.setData(7);
|
newTriplet.setData(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_x26Model->insertRows(row, 1, QModelIndex(), newTriplet);
|
m_x26Model->insertRows(newListRow, 1, QModelIndex(), newTriplet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void X26DockWidget::insertTripletCopy()
|
void X26DockWidget::insertTripletCopy()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -117,6 +117,9 @@ private:
|
|||||||
QPushButton *m_insertBeforePushButton, *m_insertAfterPushButton, *m_insertCopyPushButton, *m_deletePushButton;
|
QPushButton *m_insertBeforePushButton, *m_insertAfterPushButton, *m_insertCopyPushButton, *m_deletePushButton;
|
||||||
|
|
||||||
TeletextWidget *m_parentMainWidget;
|
TeletextWidget *m_parentMainWidget;
|
||||||
|
|
||||||
|
void disableTripletWidgets();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
18
x26model.cpp
18
x26model.cpp
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -65,11 +65,19 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error colours from KDE Plasma Breeze (light) theme
|
// Error colours from KDE Plasma Breeze (light) theme
|
||||||
if (role == Qt::ForegroundRole && triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
if (role == Qt::ForegroundRole) {
|
||||||
return QColor(252, 252, 252);
|
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
||||||
|
return QColor(252, 252, 252);
|
||||||
|
else if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
||||||
|
return QColor(35, 38, 39);
|
||||||
|
}
|
||||||
|
|
||||||
if (role == Qt::BackgroundRole && triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
if (role == Qt::BackgroundRole) {
|
||||||
return QColor(218, 68, 63);
|
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
||||||
|
return QColor(218, 68, 63);
|
||||||
|
else if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
||||||
|
return QColor(246, 116, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (role == Qt::ToolTipRole && triplet.error() != X26Triplet::NoError)
|
if (role == Qt::ToolTipRole && triplet.error() != X26Triplet::NoError)
|
||||||
return m_tripletErrors[triplet.error()].message;
|
return m_tripletErrors[triplet.error()].message;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -77,30 +77,45 @@ void X26TripletList::updateInternalData()
|
|||||||
for (int i=0; i < m_list.size(); i++) {
|
for (int i=0; i < m_list.size(); i++) {
|
||||||
triplet = &m_list[i];
|
triplet = &m_list[i];
|
||||||
triplet->m_error = X26Triplet::NoError;
|
triplet->m_error = X26Triplet::NoError;
|
||||||
|
triplet->m_reservedMode = false;
|
||||||
|
triplet->m_reservedData = false;
|
||||||
|
|
||||||
if (triplet->isRowTriplet()) {
|
if (triplet->isRowTriplet()) {
|
||||||
switch (triplet->modeExt()) {
|
switch (triplet->modeExt()) {
|
||||||
case 0x00: // Full screen colour
|
case 0x00: // Full screen colour
|
||||||
if (activePosition.isDeployed())
|
if (activePosition.isDeployed())
|
||||||
// TODO more specific error needed
|
// TODO more specific error needed
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
if (triplet->m_data & 0x60)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
break;
|
break;
|
||||||
case 0x01: // Full row colour
|
case 0x01: // Full row colour
|
||||||
if (!activePosition.setRow(triplet->addressRow()))
|
if (!activePosition.setRow(triplet->addressRow()))
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
if ((triplet->m_data & 0x60) != 0x00 && (triplet->m_data & 0x60) != 0x60)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
break;
|
break;
|
||||||
case 0x04: // Set Active Position;
|
case 0x04: // Set Active Position;
|
||||||
if (!activePosition.setRow(triplet->addressRow()))
|
if (!activePosition.setRow(triplet->addressRow()))
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
else if (triplet->data() >= 40 || !activePosition.setColumn(triplet->data()))
|
else if (triplet->data() >= 40)
|
||||||
|
// FIXME data column highlighted?
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
else if (!activePosition.setColumn(triplet->data()))
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
||||||
break;
|
break;
|
||||||
case 0x07: // Address row 0
|
case 0x07: // Address row 0
|
||||||
if (activePosition.isDeployed())
|
if (triplet->m_address != 63)
|
||||||
|
// FIXME data column highlighted?
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
else if (activePosition.isDeployed())
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
else {
|
else {
|
||||||
activePosition.setRow(0);
|
activePosition.setRow(0);
|
||||||
activePosition.setColumn(8);
|
activePosition.setColumn(8);
|
||||||
}
|
}
|
||||||
|
if ((triplet->m_data & 0x60) != 0x00 && (triplet->m_data & 0x60) != 0x60)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
break;
|
break;
|
||||||
case 0x10: // Origin Modifier
|
case 0x10: // Origin Modifier
|
||||||
if (i == m_list.size()-1 ||
|
if (i == m_list.size()-1 ||
|
||||||
@@ -125,11 +140,45 @@ void X26TripletList::updateInternalData()
|
|||||||
// otherwise the object won't appear
|
// otherwise the object won't appear
|
||||||
triplet->setObjectLocalIndex(i);
|
triplet->setObjectLocalIndex(i);
|
||||||
break;
|
break;
|
||||||
|
case 0x18: // DRCS mode
|
||||||
|
if ((triplet->m_data & 0x30) == 0x00)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
case 0x1f: // Termination marker
|
||||||
|
case 0x08 ... 0x0d: // PDC
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
triplet->m_reservedMode = true;
|
||||||
};
|
};
|
||||||
// Column triplet: make sure that PDC and reserved triplets don't affect the Active Position
|
// Column triplet: all triplets modes except PDC and reserved move the Active Position
|
||||||
} else if (triplet->modeExt() != 0x24 && triplet->modeExt() != 0x25 && triplet->modeExt() != 0x26 && triplet->modeExt() != 0x2a)
|
} else if (triplet->modeExt() == 0x24 || triplet->modeExt() == 0x25 || triplet->modeExt() == 0x2a)
|
||||||
if (!activePosition.setColumn(triplet->addressColumn()))
|
triplet->m_reservedMode = true;
|
||||||
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
else if (triplet->modeExt() != 0x26 && !activePosition.setColumn(triplet->addressColumn()))
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
||||||
|
else
|
||||||
|
switch (triplet->modeExt()) {
|
||||||
|
case 0x20: // Foreground colour
|
||||||
|
case 0x23: // Foreground colour
|
||||||
|
if (triplet->m_data & 0x60)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
break;
|
||||||
|
case 0x21: // G1 mosaic character
|
||||||
|
case 0x22: // G3 mosaic character at level 1.5
|
||||||
|
case 0x29: // G0 character
|
||||||
|
case 0x2b: // G3 mosaic character at level >=2.5
|
||||||
|
case 0x2f ... 0x3f: // G2 character or G0 diacritical mark
|
||||||
|
if (triplet->m_data < 0x20)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
break;
|
||||||
|
case 0x27: // Additional flash functions
|
||||||
|
if (triplet->m_data >= 0x18)
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
break;
|
||||||
|
case 0x2d: // DRCS character
|
||||||
|
if ((triplet->m_data & 0x3f) >= 48)
|
||||||
|
// Should really be an error?
|
||||||
|
triplet->m_reservedData = true;
|
||||||
|
}
|
||||||
|
|
||||||
triplet->m_activePositionRow = activePosition.row();
|
triplet->m_activePositionRow = activePosition.row();
|
||||||
triplet->m_activePositionColumn = activePosition.column();
|
triplet->m_activePositionColumn = activePosition.column();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2020-2022 Gavin MacGregor
|
* Copyright (C) 2020-2023 Gavin MacGregor
|
||||||
*
|
*
|
||||||
* This file is part of QTeletextMaker.
|
* This file is part of QTeletextMaker.
|
||||||
*
|
*
|
||||||
@@ -62,6 +62,8 @@ public:
|
|||||||
int activePositionRow() const { return m_activePositionRow; }
|
int activePositionRow() const { return m_activePositionRow; }
|
||||||
int activePositionColumn() const { return m_activePositionColumn; }
|
int activePositionColumn() const { return m_activePositionColumn; }
|
||||||
X26TripletError error() const { return m_error; }
|
X26TripletError error() const { return m_error; }
|
||||||
|
bool reservedMode() const { return m_reservedMode; }
|
||||||
|
bool reservedData() const { return m_reservedData; }
|
||||||
|
|
||||||
friend class X26TripletList;
|
friend class X26TripletList;
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@ private:
|
|||||||
int m_activePositionRow = -1;
|
int m_activePositionRow = -1;
|
||||||
int m_activePositionColumn = -1;
|
int m_activePositionColumn = -1;
|
||||||
X26TripletError m_error = NoError;
|
X26TripletError m_error = NoError;
|
||||||
|
bool m_reservedMode = false;
|
||||||
|
bool m_reservedData = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class X26TripletList
|
class X26TripletList
|
||||||
|
|||||||
Reference in New Issue
Block a user