Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2ae42701c | ||
|
|
9edaa2fda7 | ||
|
|
2ec4039393 | ||
|
|
69b6ad1976 | ||
|
|
d5a9469df1 | ||
|
|
e7f6a54d8d | ||
|
|
e1a1bcf070 | ||
|
|
cd531bd0a5 | ||
|
|
a54385b8f5 | ||
|
|
38746c7f38 | ||
|
|
f256e4ed28 | ||
|
|
06970fd448 | ||
|
|
a3d4783796 | ||
|
|
c8e57150eb | ||
|
|
23c2623bcf |
@@ -5,6 +5,8 @@ Features
|
|||||||
- Load and save teletext pages in .tti format.
|
- Load and save teletext pages in .tti format.
|
||||||
- Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5
|
- Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5
|
||||||
- Rendering of Local Objects and side panels.
|
- Rendering of Local Objects and side panels.
|
||||||
|
- Export PNG images of teletext pages.
|
||||||
|
- Undo and redo of editing actions.
|
||||||
- Interactive X/26 Local Enhancement Data triplet editor.
|
- Interactive X/26 Local Enhancement Data triplet editor.
|
||||||
- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages.
|
- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages.
|
||||||
- Palette editor.
|
- Palette editor.
|
||||||
@@ -49,11 +51,9 @@ The Active Position, whether set explicitly by a "Set Active Position" triplet o
|
|||||||
- The Active Position can never be moved up to a lesser numbered row.
|
- The Active Position can never be moved up to a lesser numbered row.
|
||||||
- The Active Position can never be moved left *within the same row* to a lesser numbered column, but it can be moved left at the same time as it is moved down to a greater numbered row.
|
- The Active Position can never be moved left *within the same row* to a lesser numbered column, but it can be moved left at the same time as it is moved down to a greater numbered row.
|
||||||
|
|
||||||
If this rule is not followed then triplets in earlier screen addresses will be ignored.
|
If this rule is not followed then triplets in earlier screen addresses will be ignored. Triplets that break this rule will be highlighted red in the X/26 triplet editor.
|
||||||
|
|
||||||
### Objects
|
### Objects
|
||||||
"Define ... Object" triplets need to declare that they are in the correct place in the triplet list e.g. if the Define Object triplet is at `d1 t3` in the list then the data field must show `Local: d1 t3`, otherwise the Object won't appear.
|
Insert and deleting triplets from the list will upset the Object pointers on "Invoke" triplets and will need to be corrected afterwards. A future version of the editor may adjust these pointers automatically.
|
||||||
|
|
||||||
Insert and deleting triplets from the list will upset the Object pointers on both "Define" and "Invoke" triplets and will need to be corrected afterwards. A future version of the editor may adjust these pointers automatically.
|
|
||||||
|
|
||||||
"Invoke ... Object" triplets must point to a "Define ... Object" of the same type e.g. "Invoke *Active* Object" must point to a "Define *Active* Object", otherwise the Object won't appear.
|
"Invoke ... Object" triplets must point to a "Define ... Object" of the same type e.g. "Invoke *Active* Object" must point to a "Define *Active* Object", otherwise the Object won't appear.
|
||||||
|
|||||||
50
document.cpp
50
document.cpp
@@ -17,12 +17,46 @@
|
|||||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
|
|
||||||
#include "levelonepage.h"
|
#include "levelonepage.h"
|
||||||
|
|
||||||
|
ClutModel::ClutModel(QObject *parent): QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
m_subPage = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClutModel::rowCount(const QModelIndex & /*parent*/) const
|
||||||
|
{
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ClutModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return QString("CLUT %1:%2").arg(index.row() >> 3).arg(index.row() & 0x07);
|
||||||
|
|
||||||
|
if (role == Qt::DecorationRole && m_subPage != nullptr)
|
||||||
|
return m_subPage->CLUTtoQColor(index.row());
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClutModel::setSubPage(LevelOnePage *subPage)
|
||||||
|
{
|
||||||
|
if (subPage != m_subPage) {
|
||||||
|
m_subPage = subPage;
|
||||||
|
emit dataChanged(createIndex(0, 0), createIndex(31, 0), QVector<int>(Qt::DecorationRole));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TeletextDocument::TeletextDocument()
|
TeletextDocument::TeletextDocument()
|
||||||
{
|
{
|
||||||
m_pageNumber = 0x199;
|
m_pageNumber = 0x199;
|
||||||
@@ -36,10 +70,15 @@ TeletextDocument::TeletextDocument()
|
|||||||
m_cursorColumn = 0;
|
m_cursorColumn = 0;
|
||||||
m_selectionCornerRow = m_selectionCornerColumn = -1;
|
m_selectionCornerRow = m_selectionCornerColumn = -1;
|
||||||
m_selectionSubPage = nullptr;
|
m_selectionSubPage = nullptr;
|
||||||
|
|
||||||
|
m_clutModel = new ClutModel;
|
||||||
|
m_clutModel->setSubPage(m_subPages[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TeletextDocument::~TeletextDocument()
|
TeletextDocument::~TeletextDocument()
|
||||||
{
|
{
|
||||||
|
delete m_clutModel;
|
||||||
|
|
||||||
for (auto &subPage : m_subPages)
|
for (auto &subPage : m_subPages)
|
||||||
delete(subPage);
|
delete(subPage);
|
||||||
for (auto &recycleSubPage : m_recycleSubPages)
|
for (auto &recycleSubPage : m_recycleSubPages)
|
||||||
@@ -72,7 +111,10 @@ void TeletextDocument::selectSubPageIndex(int newSubPageIndex, bool forceRefresh
|
|||||||
// forceRefresh overrides "beyond the last subpage" check, so inserting a subpage after the last one still shows - dangerous workaround?
|
// forceRefresh overrides "beyond the last subpage" check, so inserting a subpage after the last one still shows - dangerous workaround?
|
||||||
if (forceRefresh || (newSubPageIndex != m_currentSubPageIndex && newSubPageIndex < m_subPages.size())) {
|
if (forceRefresh || (newSubPageIndex != m_currentSubPageIndex && newSubPageIndex < m_subPages.size())) {
|
||||||
emit aboutToChangeSubPage();
|
emit aboutToChangeSubPage();
|
||||||
|
|
||||||
m_currentSubPageIndex = newSubPageIndex;
|
m_currentSubPageIndex = newSubPageIndex;
|
||||||
|
|
||||||
|
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||||
emit subPageSelected();
|
emit subPageSelected();
|
||||||
emit selectionMoved();
|
emit selectionMoved();
|
||||||
return;
|
return;
|
||||||
@@ -83,7 +125,10 @@ void TeletextDocument::selectSubPageNext()
|
|||||||
{
|
{
|
||||||
if (m_currentSubPageIndex < m_subPages.size()-1) {
|
if (m_currentSubPageIndex < m_subPages.size()-1) {
|
||||||
emit aboutToChangeSubPage();
|
emit aboutToChangeSubPage();
|
||||||
|
|
||||||
m_currentSubPageIndex++;
|
m_currentSubPageIndex++;
|
||||||
|
|
||||||
|
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||||
emit subPageSelected();
|
emit subPageSelected();
|
||||||
emit selectionMoved();
|
emit selectionMoved();
|
||||||
}
|
}
|
||||||
@@ -93,7 +138,10 @@ void TeletextDocument::selectSubPagePrevious()
|
|||||||
{
|
{
|
||||||
if (m_currentSubPageIndex > 0) {
|
if (m_currentSubPageIndex > 0) {
|
||||||
emit aboutToChangeSubPage();
|
emit aboutToChangeSubPage();
|
||||||
|
|
||||||
m_currentSubPageIndex--;
|
m_currentSubPageIndex--;
|
||||||
|
|
||||||
|
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||||
emit subPageSelected();
|
emit subPageSelected();
|
||||||
emit selectionMoved();
|
emit selectionMoved();
|
||||||
}
|
}
|
||||||
@@ -116,6 +164,8 @@ void TeletextDocument::insertSubPage(int beforeSubPageIndex, bool copySubPage)
|
|||||||
|
|
||||||
void TeletextDocument::deleteSubPage(int subPageToDelete)
|
void TeletextDocument::deleteSubPage(int subPageToDelete)
|
||||||
{
|
{
|
||||||
|
m_clutModel->setSubPage(nullptr);
|
||||||
|
|
||||||
delete(m_subPages[subPageToDelete]);
|
delete(m_subPages[subPageToDelete]);
|
||||||
m_subPages.erase(m_subPages.begin()+subPageToDelete);
|
m_subPages.erase(m_subPages.begin()+subPageToDelete);
|
||||||
}
|
}
|
||||||
|
|||||||
19
document.h
19
document.h
@@ -20,11 +20,28 @@
|
|||||||
#ifndef DOCUMENT_H
|
#ifndef DOCUMENT_H
|
||||||
#define DOCUMENT_H
|
#define DOCUMENT_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QUndoStack>
|
#include <QUndoStack>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "levelonepage.h"
|
#include "levelonepage.h"
|
||||||
|
|
||||||
|
class ClutModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClutModel(QObject *parent = 0);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
void setSubPage(LevelOnePage *page);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LevelOnePage *m_subPage;
|
||||||
|
};
|
||||||
|
|
||||||
class TeletextDocument : public QObject
|
class TeletextDocument : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -62,6 +79,7 @@ public:
|
|||||||
void setDescription(QString);
|
void setDescription(QString);
|
||||||
void setFastTextLinkPageNumberOnAllSubPages(int, int);
|
void setFastTextLinkPageNumberOnAllSubPages(int, int);
|
||||||
QUndoStack *undoStack() const { return m_undoStack; }
|
QUndoStack *undoStack() const { return m_undoStack; }
|
||||||
|
ClutModel *clutModel() const { return m_clutModel; }
|
||||||
int cursorRow() const { return m_cursorRow; }
|
int cursorRow() const { return m_cursorRow; }
|
||||||
int cursorColumn() const { return m_cursorColumn; }
|
int cursorColumn() const { return m_cursorColumn; }
|
||||||
void cursorUp(bool shiftKey=false);
|
void cursorUp(bool shiftKey=false);
|
||||||
@@ -104,6 +122,7 @@ private:
|
|||||||
QUndoStack *m_undoStack;
|
QUndoStack *m_undoStack;
|
||||||
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
|
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
|
||||||
LevelOnePage *m_selectionSubPage;
|
LevelOnePage *m_selectionSubPage;
|
||||||
|
ClutModel *m_clutModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ LevelOnePage::LevelOnePage(const PageBase &other)
|
|||||||
clearPage();
|
clearPage();
|
||||||
|
|
||||||
for (int i=0; i<26; i++)
|
for (int i=0; i<26; i++)
|
||||||
if (other.packetNeeded(i))
|
if (other.packetExists(i))
|
||||||
setPacket(i, other.packet(i));
|
setPacket(i, other.packet(i));
|
||||||
for (int i=26; i<30; i++)
|
for (int i=26; i<30; i++)
|
||||||
for (int j=0; j<16; j++)
|
for (int j=0; j<16; j++)
|
||||||
if (other.packetNeeded(i, j))
|
if (other.packetExists(i, j))
|
||||||
setPacket(i, j, other.packet(i));
|
setPacket(i, j, other.packet(i));
|
||||||
|
|
||||||
for (int i=PageBase::C4ErasePage; i<=PageBase::C14NOS; i++)
|
for (int i=PageBase::C4ErasePage; i<=PageBase::C14NOS; i++)
|
||||||
@@ -254,7 +254,7 @@ bool LevelOnePage::setPacket(int packetNumber, int designationCode, QByteArray p
|
|||||||
return PageBase::setPacket(packetNumber, designationCode, packetContents);
|
return PageBase::setPacket(packetNumber, designationCode, packetContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LevelOnePage::packetNeeded(int packetNumber) const
|
bool LevelOnePage::packetExists(int packetNumber) const
|
||||||
{
|
{
|
||||||
if (packetNumber <= 24) {
|
if (packetNumber <= 24) {
|
||||||
for (int c=0; c<40; c++)
|
for (int c=0; c<40; c++)
|
||||||
@@ -263,10 +263,10 @@ bool LevelOnePage::packetNeeded(int packetNumber) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PageBase::packetNeeded(packetNumber);
|
return PageBase::packetExists(packetNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LevelOnePage::packetNeeded(int packetNumber, int designationCode) const
|
bool LevelOnePage::packetExists(int packetNumber, int designationCode) const
|
||||||
{
|
{
|
||||||
if (packetNumber == 26)
|
if (packetNumber == 26)
|
||||||
return packetFromEnhancementListNeeded(designationCode);
|
return packetFromEnhancementListNeeded(designationCode);
|
||||||
@@ -298,7 +298,7 @@ bool LevelOnePage::packetNeeded(int packetNumber, int designationCode) const
|
|||||||
return !isPaletteDefault(0,15);
|
return !isPaletteDefault(0,15);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PageBase::packetNeeded(packetNumber, designationCode);
|
return PageBase::packetExists(packetNumber, designationCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LevelOnePage::controlBit(int bitNumber) const
|
bool LevelOnePage::controlBit(int bitNumber) const
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ public:
|
|||||||
|
|
||||||
QByteArray packet(int) const override;
|
QByteArray packet(int) const override;
|
||||||
QByteArray packet(int, int) const override;
|
QByteArray packet(int, int) const override;
|
||||||
bool packetNeeded(int) const override;
|
bool packetExists(int) const override;
|
||||||
bool packetNeeded(int, int) const override;
|
bool packetExists(int, int) const override;
|
||||||
bool setPacket(int, QByteArray) override;
|
bool setPacket(int, QByteArray) override;
|
||||||
bool setPacket(int, int, QByteArray) override;
|
bool setPacket(int, int, QByteArray) override;
|
||||||
|
|
||||||
|
|||||||
14
loadsave.cpp
14
loadsave.cpp
@@ -182,7 +182,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
|||||||
|
|
||||||
auto write7bitPacket=[&](int packetNumber)
|
auto write7bitPacket=[&](int packetNumber)
|
||||||
{
|
{
|
||||||
if (document.subPage(p)->packetNeeded(packetNumber)) {
|
if (document.subPage(p)->packetExists(packetNumber)) {
|
||||||
QByteArray outLine = document.subPage(p)->packet(packetNumber);
|
QByteArray outLine = document.subPage(p)->packet(packetNumber);
|
||||||
|
|
||||||
outStream << QString("OL,%1,").arg(packetNumber);
|
outStream << QString("OL,%1,").arg(packetNumber);
|
||||||
@@ -203,7 +203,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
|||||||
|
|
||||||
auto writeHammingPacket=[&](int packetNumber, int designationCode=0)
|
auto writeHammingPacket=[&](int packetNumber, int designationCode=0)
|
||||||
{
|
{
|
||||||
if (document.subPage(p)->packetNeeded(packetNumber, designationCode)) {
|
if (document.subPage(p)->packetExists(packetNumber, designationCode)) {
|
||||||
QByteArray outLine = document.subPage(p)->packet(packetNumber, designationCode);
|
QByteArray outLine = document.subPage(p)->packet(packetNumber, designationCode);
|
||||||
|
|
||||||
outStream << QString("OL,%1,").arg(packetNumber);
|
outStream << QString("OL,%1,").arg(packetNumber);
|
||||||
@@ -279,7 +279,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
|||||||
|
|
||||||
// FastText links
|
// FastText links
|
||||||
bool writeFLCommand = false;
|
bool writeFLCommand = false;
|
||||||
if (document.pageFunction() == TeletextDocument::PFLevelOnePage && document.subPage(p)->packetNeeded(27,0)) {
|
if (document.pageFunction() == TeletextDocument::PFLevelOnePage && document.subPage(p)->packetExists(27,0)) {
|
||||||
// Subpage has FastText links - if any link to a specific subpage, we need to write X/27/0 as raw
|
// Subpage has FastText links - if any link to a specific subpage, we need to write X/27/0 as raw
|
||||||
// otherwise we write the links as a human-readable FL command later on
|
// otherwise we write the links as a human-readable FL command later on
|
||||||
writeFLCommand = true;
|
writeFLCommand = true;
|
||||||
@@ -337,7 +337,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
|||||||
|
|
||||||
QByteArray rowPacketAlways(PageBase *subPage, int packetNumber)
|
QByteArray rowPacketAlways(PageBase *subPage, int packetNumber)
|
||||||
{
|
{
|
||||||
if (subPage->packetNeeded(packetNumber))
|
if (subPage->packetExists(packetNumber))
|
||||||
return subPage->packet(packetNumber);
|
return subPage->packet(packetNumber);
|
||||||
else
|
else
|
||||||
return QByteArray(40, ' ');
|
return QByteArray(40, ' ');
|
||||||
@@ -388,7 +388,7 @@ QString exportHashStringPackets(LevelOnePage *subPage)
|
|||||||
const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
QString result;
|
QString result;
|
||||||
|
|
||||||
if (subPage->packetNeeded(28,0) || subPage->packetNeeded(28,4)) {
|
if (subPage->packetExists(28,0) || subPage->packetExists(28,4)) {
|
||||||
// X/28/0 and X/28/4 are duplicates apart from the CLUT definitions
|
// X/28/0 and X/28/4 are duplicates apart from the CLUT definitions
|
||||||
// Assemble the duplicate beginning and ending of both packets
|
// Assemble the duplicate beginning and ending of both packets
|
||||||
QString x28StringBegin, x28StringEnd;
|
QString x28StringBegin, x28StringEnd;
|
||||||
@@ -399,9 +399,9 @@ QString exportHashStringPackets(LevelOnePage *subPage)
|
|||||||
|
|
||||||
x28StringEnd = QString("%1%2%3%4").arg(subPage->defaultScreenColour(), 2, 16, QChar('0')).arg(subPage->defaultRowColour(), 2, 16, QChar('0')).arg(subPage->blackBackgroundSubst(), 1, 10).arg(subPage->colourTableRemap(), 1, 10);
|
x28StringEnd = QString("%1%2%3%4").arg(subPage->defaultScreenColour(), 2, 16, QChar('0')).arg(subPage->defaultRowColour(), 2, 16, QChar('0')).arg(subPage->blackBackgroundSubst(), 1, 10).arg(subPage->colourTableRemap(), 1, 10);
|
||||||
|
|
||||||
if (subPage->packetNeeded(28,0))
|
if (subPage->packetExists(28,0))
|
||||||
result.append(":X280=" + x28StringBegin + colourToHexString(2) + colourToHexString(3) + x28StringEnd);
|
result.append(":X280=" + x28StringBegin + colourToHexString(2) + colourToHexString(3) + x28StringEnd);
|
||||||
if (subPage->packetNeeded(28,4))
|
if (subPage->packetExists(28,4))
|
||||||
result.append(":X284=" + x28StringBegin + colourToHexString(0) + colourToHexString(1) + x28StringEnd);
|
result.append(":X284=" + x28StringBegin + colourToHexString(0) + colourToHexString(1) + x28StringEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
main.cpp
2
main.cpp
@@ -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.2-alpha");
|
QApplication::setApplicationVersion("0.4-alpha");
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QApplication::applicationName());
|
parser.setApplicationDescription(QApplication::applicationName());
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
|
|||||||
@@ -140,6 +140,16 @@ void TeletextWidget::timerEvent(QTimerEvent *event)
|
|||||||
QWidget::timerEvent(event);
|
QWidget::timerEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeletextWidget::pauseFlash(bool pauseNow)
|
||||||
|
{
|
||||||
|
if (pauseNow && m_flashTiming != 0) {
|
||||||
|
m_flashTimer.stop();
|
||||||
|
m_flashPhase = 0;
|
||||||
|
update();
|
||||||
|
} else if (m_flashTiming != 0)
|
||||||
|
m_flashTimer.start((m_flashTiming == 1) ? 500 : 167, this);
|
||||||
|
}
|
||||||
|
|
||||||
void TeletextWidget::setInsertMode(bool insertMode)
|
void TeletextWidget::setInsertMode(bool insertMode)
|
||||||
{
|
{
|
||||||
m_insertMode = insertMode;
|
m_insertMode = insertMode;
|
||||||
@@ -654,6 +664,24 @@ void LevelOneScene::toggleGrid(bool gridOn)
|
|||||||
m_sidePanelGridItemGroup[i]->setVisible(gridOn);
|
m_sidePanelGridItemGroup[i]->setVisible(gridOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LevelOneScene::hideGUIElements(bool hidden)
|
||||||
|
{
|
||||||
|
if (hidden) {
|
||||||
|
m_mainGridItemGroup->setVisible(false);
|
||||||
|
m_cursorRectItem->setVisible(false);
|
||||||
|
m_selectionRectItem->setVisible(false);
|
||||||
|
for (int i=0; i<32; i++)
|
||||||
|
if (m_sidePanelGridNeeded[i])
|
||||||
|
m_sidePanelGridItemGroup[i]->setVisible(false);
|
||||||
|
} else {
|
||||||
|
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionActive())
|
||||||
|
m_selectionRectItem->setVisible(true);
|
||||||
|
|
||||||
|
m_cursorRectItem->setVisible(true);
|
||||||
|
toggleGrid(m_grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool LevelOneScene::eventFilter(QObject *object, QEvent *event)
|
bool LevelOneScene::eventFilter(QObject *object, QEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(object);
|
Q_UNUSED(object);
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public slots:
|
|||||||
void toggleReveal(bool);
|
void toggleReveal(bool);
|
||||||
void toggleMix(bool);
|
void toggleMix(bool);
|
||||||
void updateFlashTimer(int);
|
void updateFlashTimer(int);
|
||||||
|
void pauseFlash(bool);
|
||||||
void refreshRow(int);
|
void refreshRow(int);
|
||||||
|
|
||||||
void setControlBit(int, bool);
|
void setControlBit(int, bool);
|
||||||
@@ -119,6 +120,7 @@ public slots:
|
|||||||
void updateCursor();
|
void updateCursor();
|
||||||
void updateSelection();
|
void updateSelection();
|
||||||
void toggleGrid(bool);
|
void toggleGrid(bool);
|
||||||
|
void hideGUIElements(bool);
|
||||||
void setFullScreenColour(const QColor &);
|
void setFullScreenColour(const QColor &);
|
||||||
void setFullRowColour(int, const QColor &);
|
void setFullRowColour(int, const QColor &);
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,11 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QImage>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QPainter>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
@@ -120,6 +122,56 @@ bool MainWindow::saveAs()
|
|||||||
return saveFile(fileName);
|
return saveFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::exportPNG()
|
||||||
|
{
|
||||||
|
QString exportFileName = QFileDialog::getSaveFileName(this, tr("Export PNG"), QString(), "PNG image (*.png)");
|
||||||
|
if (exportFileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Prepare widget image for extraction
|
||||||
|
m_textWidget->pauseFlash(true);
|
||||||
|
m_textScene->hideGUIElements(true);
|
||||||
|
bool reshowCodes = m_textWidget->pageRender()->showCodes();
|
||||||
|
if (reshowCodes)
|
||||||
|
m_textWidget->pageRender()->setShowCodes(false);
|
||||||
|
// Disable exporting in Mix mode as it corrupts the background
|
||||||
|
bool reMix = m_textWidget->pageRender()->mix();
|
||||||
|
if (reMix)
|
||||||
|
m_textWidget->pageRender()->setMix(false);
|
||||||
|
|
||||||
|
// Extract the image from the scene
|
||||||
|
QImage interImage = QImage(m_textScene->sceneRect().size().toSize(), QImage::Format_RGB32);
|
||||||
|
// This ought to make the background transparent in Mix mode, but it doesn't
|
||||||
|
// if (m_textWidget->pageRender()->mix())
|
||||||
|
// interImage.fill(QColor(0, 0, 0, 0));
|
||||||
|
QPainter interPainter(&interImage);
|
||||||
|
m_textScene->render(&interPainter);
|
||||||
|
|
||||||
|
// Now we've extracted the image we can put the GUI things back
|
||||||
|
m_textScene->hideGUIElements(false);
|
||||||
|
if (reshowCodes)
|
||||||
|
m_textWidget->pageRender()->setShowCodes(true);
|
||||||
|
if (reMix)
|
||||||
|
m_textWidget->pageRender()->setMix(true);
|
||||||
|
m_textWidget->pauseFlash(false);
|
||||||
|
|
||||||
|
// Now scale the extracted image to the selected aspect ratio
|
||||||
|
// We do this in two steps so that anti-aliasing only occurs on vertical lines
|
||||||
|
|
||||||
|
// Double the vertical height first
|
||||||
|
const QImage doubleHeightImage = interImage.scaled(interImage.width(), interImage.height()*2, Qt::IgnoreAspectRatio, Qt::FastTransformation);
|
||||||
|
|
||||||
|
// If aspect ratio is Pixel 1:2 we're already at the correct scale
|
||||||
|
if (m_viewAspectRatio != 3) {
|
||||||
|
// Scale it horizontally to the selected aspect ratio
|
||||||
|
const QImage scaledImage = doubleHeightImage.scaled((int)((float)doubleHeightImage.width() * aspectRatioHorizontalScaling[m_viewAspectRatio] * 2), doubleHeightImage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
if (!scaledImage.save(exportFileName, "PNG"))
|
||||||
|
QMessageBox::warning(this, tr("QTeletextMaker"), tr("Cannot export file %1.").arg(QDir::toNativeSeparators(exportFileName)));
|
||||||
|
} else if (!doubleHeightImage.save(exportFileName, "PNG"))
|
||||||
|
QMessageBox::warning(this, tr("QTeletextMaker"), tr("Cannot export file %1.").arg(QDir::toNativeSeparators(exportFileName)));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::exportZXNet()
|
void MainWindow::exportZXNet()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage())));
|
QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage())));
|
||||||
@@ -166,6 +218,7 @@ void MainWindow::init()
|
|||||||
|
|
||||||
m_textView = new QGraphicsView(this);
|
m_textView = new QGraphicsView(this);
|
||||||
m_textView->setScene(m_textScene);
|
m_textView->setScene(m_textScene);
|
||||||
|
if (m_viewSmoothTransform)
|
||||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||||
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
||||||
setSceneDimensions();
|
setSceneDimensions();
|
||||||
@@ -257,14 +310,18 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
setRecentFilesVisible(MainWindow::hasRecentFiles());
|
||||||
|
|
||||||
QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export to online editor"));
|
QAction *exportPNGAct = fileMenu->addAction(tr("Export subpage as PNG..."));
|
||||||
|
exportPNGAct->setStatusTip("Export a PNG image of this subpage");
|
||||||
|
connect(exportPNGAct, &QAction::triggered, this, &MainWindow::exportPNG);
|
||||||
|
|
||||||
|
QMenu *exportHashStringSubMenu = fileMenu->addMenu(tr("Export subpage to online editor"));
|
||||||
|
|
||||||
QAction *exportZXNetAct = exportHashStringSubMenu->addAction(tr("Open in zxnet.co.uk"));
|
QAction *exportZXNetAct = exportHashStringSubMenu->addAction(tr("Open in zxnet.co.uk"));
|
||||||
exportZXNetAct->setStatusTip("Export and open page in zxnet.co.uk online editor");
|
exportZXNetAct->setStatusTip("Export and open this subpage in the zxnet.co.uk online editor");
|
||||||
connect(exportZXNetAct, &QAction::triggered, this, &MainWindow::exportZXNet);
|
connect(exportZXNetAct, &QAction::triggered, this, &MainWindow::exportZXNet);
|
||||||
|
|
||||||
QAction *exportEditTFAct = exportHashStringSubMenu->addAction(tr("Open in edit.tf"));
|
QAction *exportEditTFAct = exportHashStringSubMenu->addAction(tr("Open in edit.tf"));
|
||||||
exportEditTFAct->setStatusTip("Export and open page in edit.tf online editor");
|
exportEditTFAct->setStatusTip("Export and open this subpage in the edit.tf online editor");
|
||||||
connect(exportEditTFAct, &QAction::triggered, this, &MainWindow::exportEditTF);
|
connect(exportEditTFAct, &QAction::triggered, this, &MainWindow::exportEditTF);
|
||||||
|
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
@@ -418,6 +475,13 @@ void MainWindow::createActions()
|
|||||||
borderGroup->addAction(m_borderActs[i]);
|
borderGroup->addAction(m_borderActs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
|
m_smoothTransformAction = viewMenu->addAction(tr("Smooth font scaling"));
|
||||||
|
m_smoothTransformAction->setCheckable(true);
|
||||||
|
m_smoothTransformAction->setStatusTip(tr("Toggle smooth font scaling"));
|
||||||
|
connect(m_smoothTransformAction, &QAction::toggled, this, &MainWindow::setSmoothTransform);
|
||||||
|
|
||||||
QAction *zoomInAct = viewMenu->addAction(tr("Zoom In"));
|
QAction *zoomInAct = viewMenu->addAction(tr("Zoom In"));
|
||||||
zoomInAct->setShortcuts(QKeySequence::ZoomIn);
|
zoomInAct->setShortcuts(QKeySequence::ZoomIn);
|
||||||
zoomInAct->setStatusTip(tr("Zoom in"));
|
zoomInAct->setStatusTip(tr("Zoom in"));
|
||||||
@@ -555,7 +619,6 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
void MainWindow::setSceneDimensions()
|
void MainWindow::setSceneDimensions()
|
||||||
{
|
{
|
||||||
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
|
||||||
const int topBottomBorders[3] = { 0, 10, 19 };
|
const int topBottomBorders[3] = { 0, 10, 19 };
|
||||||
const int pillarBoxSizes[3] = { 672, 720, 854 };
|
const int pillarBoxSizes[3] = { 672, 720, 854 };
|
||||||
const int leftRightBorders[3] = { 0, 24, 77 };
|
const int leftRightBorders[3] = { 0, 24, 77 };
|
||||||
@@ -612,6 +675,15 @@ void MainWindow::setAspectRatio(int newViewAspectRatio)
|
|||||||
setSceneDimensions();
|
setSceneDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setSmoothTransform(bool smoothTransform)
|
||||||
|
{
|
||||||
|
m_viewSmoothTransform = smoothTransform;
|
||||||
|
if (smoothTransform)
|
||||||
|
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||||
|
else
|
||||||
|
m_textView->setRenderHints({ });
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::zoomIn()
|
void MainWindow::zoomIn()
|
||||||
{
|
{
|
||||||
if (m_viewZoom < 4)
|
if (m_viewZoom < 4)
|
||||||
@@ -705,6 +777,10 @@ void MainWindow::readSettings()
|
|||||||
m_viewAspectRatio = settings.value("aspectratio", 0).toInt();
|
m_viewAspectRatio = settings.value("aspectratio", 0).toInt();
|
||||||
m_viewAspectRatio = (m_viewAspectRatio < 0 || m_viewAspectRatio > 2) ? 0 : m_viewAspectRatio;
|
m_viewAspectRatio = (m_viewAspectRatio < 0 || m_viewAspectRatio > 2) ? 0 : m_viewAspectRatio;
|
||||||
m_aspectRatioActs[m_viewAspectRatio]->setChecked(true);
|
m_aspectRatioActs[m_viewAspectRatio]->setChecked(true);
|
||||||
|
m_viewSmoothTransform = settings.value("smoothTransform", 0).toBool();
|
||||||
|
m_smoothTransformAction->blockSignals(true);
|
||||||
|
m_smoothTransformAction->setChecked(m_viewSmoothTransform);
|
||||||
|
m_smoothTransformAction->blockSignals(false);
|
||||||
m_viewZoom = settings.value("zoom", 2).toInt();
|
m_viewZoom = settings.value("zoom", 2).toInt();
|
||||||
m_viewZoom = (m_viewZoom < 0 || m_viewZoom > 4) ? 2 : m_viewZoom;
|
m_viewZoom = (m_viewZoom < 0 || m_viewZoom > 4) ? 2 : m_viewZoom;
|
||||||
|
|
||||||
@@ -743,6 +819,7 @@ void MainWindow::writeSettings()
|
|||||||
settings.setValue("windowState", saveState());
|
settings.setValue("windowState", saveState());
|
||||||
settings.setValue("border", m_viewBorder);
|
settings.setValue("border", m_viewBorder);
|
||||||
settings.setValue("aspectratio", m_viewAspectRatio);
|
settings.setValue("aspectratio", m_viewAspectRatio);
|
||||||
|
settings.setValue("smoothTransform", m_viewSmoothTransform);
|
||||||
settings.setValue("zoom", m_viewZoom);
|
settings.setValue("zoom", m_viewZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ private slots:
|
|||||||
void open();
|
void open();
|
||||||
bool save();
|
bool save();
|
||||||
bool saveAs();
|
bool saveAs();
|
||||||
|
void exportPNG();
|
||||||
void exportZXNet();
|
void exportZXNet();
|
||||||
void exportEditTF();
|
void exportEditTF();
|
||||||
void updateRecentFileActions();
|
void updateRecentFileActions();
|
||||||
@@ -74,6 +75,7 @@ private slots:
|
|||||||
void setSceneDimensions();
|
void setSceneDimensions();
|
||||||
void setBorder(int);
|
void setBorder(int);
|
||||||
void setAspectRatio(int);
|
void setAspectRatio(int);
|
||||||
|
void setSmoothTransform(bool);
|
||||||
void zoomIn();
|
void zoomIn();
|
||||||
void zoomOut();
|
void zoomOut();
|
||||||
void zoomReset();
|
void zoomReset();
|
||||||
@@ -82,6 +84,7 @@ private slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
enum { m_MaxRecentFiles = 10 };
|
enum { m_MaxRecentFiles = 10 };
|
||||||
|
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void createActions();
|
void createActions();
|
||||||
@@ -104,6 +107,7 @@ private:
|
|||||||
QGraphicsView *m_textView;
|
QGraphicsView *m_textView;
|
||||||
|
|
||||||
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
||||||
|
bool m_viewSmoothTransform;
|
||||||
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
||||||
PageEnhancementsDockWidget *m_pageEnhancementsDockWidget;
|
PageEnhancementsDockWidget *m_pageEnhancementsDockWidget;
|
||||||
X26DockWidget *m_x26DockWidget;
|
X26DockWidget *m_x26DockWidget;
|
||||||
@@ -115,6 +119,7 @@ private:
|
|||||||
QAction *m_deleteSubPageAction;
|
QAction *m_deleteSubPageAction;
|
||||||
QAction *m_borderActs[3];
|
QAction *m_borderActs[3];
|
||||||
QAction *m_aspectRatioActs[4];
|
QAction *m_aspectRatioActs[4];
|
||||||
|
QAction *m_smoothTransformAction;
|
||||||
|
|
||||||
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
||||||
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ public:
|
|||||||
|
|
||||||
virtual QByteArray packet(int) const;
|
virtual QByteArray packet(int) const;
|
||||||
virtual QByteArray packet(int, int) const;
|
virtual QByteArray packet(int, int) const;
|
||||||
virtual bool packetNeeded(int i) const { return m_displayPackets[i] != nullptr; }
|
virtual bool packetExists(int i) const { return m_displayPackets[i] != nullptr; }
|
||||||
virtual bool packetNeeded(int i, int j) const { return m_designationPackets[i-26][j] != nullptr; }
|
virtual bool packetExists(int i, int j) const { return m_designationPackets[i-26][j] != nullptr; }
|
||||||
virtual bool setPacket(int, QByteArray);
|
virtual bool setPacket(int, QByteArray);
|
||||||
virtual bool setPacket(int, int, QByteArray);
|
virtual bool setPacket(int, int, QByteArray);
|
||||||
// bool deletePacket(int);
|
// bool deletePacket(int);
|
||||||
|
|||||||
@@ -47,12 +47,9 @@ PageEnhancementsDockWidget::PageEnhancementsDockWidget(TeletextWidget *parent):
|
|||||||
x28Layout->addWidget(new QLabel(tr("Default screen colour")), 0, 0, 1, 1);
|
x28Layout->addWidget(new QLabel(tr("Default screen colour")), 0, 0, 1, 1);
|
||||||
x28Layout->addWidget(new QLabel(tr("Default row colour")), 1, 0, 1, 1);
|
x28Layout->addWidget(new QLabel(tr("Default row colour")), 1, 0, 1, 1);
|
||||||
m_defaultScreenColourCombo = new QComboBox;
|
m_defaultScreenColourCombo = new QComboBox;
|
||||||
|
m_defaultScreenColourCombo->setModel(m_parentMainWidget->document()->clutModel());
|
||||||
m_defaultRowColourCombo = new QComboBox;
|
m_defaultRowColourCombo = new QComboBox;
|
||||||
for (int r=0; r<=3; r++)
|
m_defaultRowColourCombo->setModel(m_parentMainWidget->document()->clutModel());
|
||||||
for (int c=0; c<=7; c++) {
|
|
||||||
m_defaultScreenColourCombo->addItem(tr("CLUT %1:%2").arg(r).arg(c));
|
|
||||||
m_defaultRowColourCombo->addItem(tr("CLUT %1:%2").arg(r).arg(c));
|
|
||||||
}
|
|
||||||
x28Layout->addWidget(m_defaultScreenColourCombo, 0, 1, 1, 1, Qt::AlignTop);
|
x28Layout->addWidget(m_defaultScreenColourCombo, 0, 1, 1, 1, Qt::AlignTop);
|
||||||
connect(m_defaultScreenColourCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setDefaultScreenColour(index); });
|
connect(m_defaultScreenColourCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setDefaultScreenColour(index); });
|
||||||
x28Layout->addWidget(m_defaultRowColourCombo, 1, 1, 1, 1, Qt::AlignTop);
|
x28Layout->addWidget(m_defaultRowColourCombo, 1, 1, 1, 1, Qt::AlignTop);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ void PageX26Base::setEnhancementListFromPacket(int packetNumber, QByteArray pack
|
|||||||
newX26Triplet.setAddress(packetContents.at(i*3+1) & 0x3f);
|
newX26Triplet.setAddress(packetContents.at(i*3+1) & 0x3f);
|
||||||
newX26Triplet.setMode(packetContents.at(i*3+2) & 0x1f);
|
newX26Triplet.setMode(packetContents.at(i*3+2) & 0x1f);
|
||||||
newX26Triplet.setData(((packetContents.at(i*3+3) & 0x3f) << 1) | ((packetContents.at(i*3+2) & 0x20) >> 5));
|
newX26Triplet.setData(((packetContents.at(i*3+3) & 0x3f) << 1) | ((packetContents.at(i*3+2) & 0x20) >> 5));
|
||||||
m_enhancements[enhanceListPointer] = newX26Triplet;
|
m_enhancements.replace(enhanceListPointer, newX26Triplet);
|
||||||
}
|
}
|
||||||
if (newX26Triplet.mode() == 0x1f && newX26Triplet.address() == 0x3f && newX26Triplet.data() & 0x01)
|
if (newX26Triplet.mode() == 0x1f && newX26Triplet.address() == 0x3f && newX26Triplet.data() & 0x01)
|
||||||
// Last triplet was a Termination Marker (without ..follows) so clean up the repeated ones
|
// Last triplet was a Termination Marker (without ..follows) so clean up the repeated ones
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class PageX26Base : public PageBase //: public QObject
|
|||||||
//Q_OBJECT
|
//Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QList<X26Triplet> *enhancements() { return &m_enhancements; };
|
X26TripletList *enhancements() { return &m_enhancements; };
|
||||||
virtual int maxEnhancements() const =0;
|
virtual int maxEnhancements() const =0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -39,7 +39,7 @@ protected:
|
|||||||
void setEnhancementListFromPacket(int, QByteArray);
|
void setEnhancementListFromPacket(int, QByteArray);
|
||||||
bool packetFromEnhancementListNeeded(int n) const { return ((m_enhancements.size()+12) / 13) > n; };
|
bool packetFromEnhancementListNeeded(int n) const { return ((m_enhancements.size()+12) / 13) > n; };
|
||||||
|
|
||||||
QList<X26Triplet> m_enhancements;
|
X26TripletList m_enhancements;
|
||||||
const X26Triplet m_paddingX26Triplet { 41, 0x1e, 0 };
|
const X26Triplet m_paddingX26Triplet { 41, 0x1e, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
30
render.cpp
30
render.cpp
@@ -26,11 +26,26 @@
|
|||||||
|
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
|
||||||
|
int TeletextFontBitmap::s_instances = 0;
|
||||||
|
|
||||||
|
QBitmap *TeletextFontBitmap::s_fontBitmap = nullptr;
|
||||||
|
|
||||||
|
TeletextFontBitmap::TeletextFontBitmap()
|
||||||
|
{
|
||||||
|
if (s_instances == 0)
|
||||||
|
s_fontBitmap = new QBitmap(":/images/teletextfont.png");
|
||||||
|
s_instances++;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeletextFontBitmap::~TeletextFontBitmap()
|
||||||
|
{
|
||||||
|
s_instances--;
|
||||||
|
if (s_instances == 0)
|
||||||
|
delete s_fontBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
TeletextPageRender::TeletextPageRender()
|
TeletextPageRender::TeletextPageRender()
|
||||||
{
|
{
|
||||||
QPainter pixmapPainter;
|
|
||||||
|
|
||||||
m_fontBitmap = new QBitmap(":/images/teletextfont.png");
|
|
||||||
for (int i=0; i<6; i++)
|
for (int i=0; i<6; i++)
|
||||||
m_pagePixmap[i] = new QPixmap(864, 250);
|
m_pagePixmap[i] = new QPixmap(864, 250);
|
||||||
m_pagePixmap[0]->fill(Qt::transparent);
|
m_pagePixmap[0]->fill(Qt::transparent);
|
||||||
@@ -58,7 +73,6 @@ TeletextPageRender::~TeletextPageRender()
|
|||||||
}
|
}
|
||||||
for (int i=0; i<6; i++)
|
for (int i=0; i<6; i++)
|
||||||
delete m_pagePixmap[i];
|
delete m_pagePixmap[i];
|
||||||
delete m_fontBitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeletextPageRender::setTeletextPage(LevelOnePage *newCurrentPage)
|
void TeletextPageRender::setTeletextPage(LevelOnePage *newCurrentPage)
|
||||||
@@ -259,7 +273,7 @@ void TeletextPageRender::decodePage()
|
|||||||
setFullRowColour(r ,downwardsFullRowColour);
|
setFullRowColour(r ,downwardsFullRowColour);
|
||||||
|
|
||||||
m_textLayer[1]->enhanceMap.clear();
|
m_textLayer[1]->enhanceMap.clear();
|
||||||
if (m_renderLevel == 0 || m_levelOnePage->enhancements()->empty())
|
if (m_renderLevel == 0 || m_levelOnePage->enhancements()->isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_textLayer[1]->setFullScreenColour(-1);
|
m_textLayer[1]->setFullScreenColour(-1);
|
||||||
@@ -348,10 +362,10 @@ void TeletextPageRender::renderPage(int r)
|
|||||||
pixmapPainter.setBackground(QBrush(backQColour));
|
pixmapPainter.setBackground(QBrush(backQColour));
|
||||||
}
|
}
|
||||||
pixmapPainter.setPen(foreQColour);
|
pixmapPainter.setPen(foreQColour);
|
||||||
pixmapPainter.drawPixmap(c*12, r*10, charWidth, charHeight, *m_fontBitmap, (resultCharacter.code-32)*12, resultCharacter.set*10, 12, 10);
|
pixmapPainter.drawPixmap(c*12, r*10, charWidth, charHeight, *m_fontBitmap.rawBitmap(), (resultCharacter.code-32)*12, resultCharacter.set*10, 12, 10);
|
||||||
if (resultCharacter.diacritical) {
|
if (resultCharacter.diacritical) {
|
||||||
pixmapPainter.setBackgroundMode(Qt::TransparentMode);
|
pixmapPainter.setBackgroundMode(Qt::TransparentMode);
|
||||||
pixmapPainter.drawPixmap(c*12, r*10, charWidth, charHeight, *m_fontBitmap, 384+resultCharacter.diacritical*12, 70, 12, 10);
|
pixmapPainter.drawPixmap(c*12, r*10, charWidth, charHeight, *m_fontBitmap.rawBitmap(), 384+resultCharacter.diacritical*12, 70, 12, 10);
|
||||||
pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
|
pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,7 +497,7 @@ void TeletextPageRender::renderPage(int r)
|
|||||||
if (m_showCodes && c < 40 && m_levelOnePage->character(r, c)<0x20 && !m_level1Layer.isRowBottomHalf(r) && !m_cell[r][c].bottomHalf) {
|
if (m_showCodes && c < 40 && m_levelOnePage->character(r, c)<0x20 && !m_level1Layer.isRowBottomHalf(r) && !m_cell[r][c].bottomHalf) {
|
||||||
pixmapPainter.setBackground(QBrush(QColor(0, 0, 0, 128)));
|
pixmapPainter.setBackground(QBrush(QColor(0, 0, 0, 128)));
|
||||||
pixmapPainter.setPen(QColor(255, 255, 255, 224));
|
pixmapPainter.setPen(QColor(255, 255, 255, 224));
|
||||||
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, *m_fontBitmap, (m_levelOnePage->character(r, c)+32)*12, 250, 12, 10);
|
pixmapPainter.drawPixmap(c*12, r*10, 12, 10, *m_fontBitmap.rawBitmap(), (m_levelOnePage->character(r, c)+32)*12, 250, 12, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resultAttributes.display.doubleHeight)
|
if (resultAttributes.display.doubleHeight)
|
||||||
|
|||||||
16
render.h
16
render.h
@@ -179,6 +179,18 @@ private:
|
|||||||
enum rowHeightEnum { RHnormal=-1, RHtophalf, RHbottomhalf } m_rowHeight[25];
|
enum rowHeightEnum { RHnormal=-1, RHtophalf, RHbottomhalf } m_rowHeight[25];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TeletextFontBitmap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TeletextFontBitmap();
|
||||||
|
~TeletextFontBitmap();
|
||||||
|
QBitmap *rawBitmap() const { return s_fontBitmap; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int s_instances;
|
||||||
|
static QBitmap* s_fontBitmap;
|
||||||
|
};
|
||||||
|
|
||||||
class TeletextPageRender : public QObject
|
class TeletextPageRender : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -189,6 +201,8 @@ public:
|
|||||||
void decodePage();
|
void decodePage();
|
||||||
void renderPage();
|
void renderPage();
|
||||||
void renderPage(int r);
|
void renderPage(int r);
|
||||||
|
bool mix() const { return m_mix; };
|
||||||
|
bool showCodes() const { return m_showCodes; };
|
||||||
void setTeletextPage(LevelOnePage *);
|
void setTeletextPage(LevelOnePage *);
|
||||||
void updateSidePanels();
|
void updateSidePanels();
|
||||||
void buildEnhanceMap(TextLayer *, int=0);
|
void buildEnhanceMap(TextLayer *, int=0);
|
||||||
@@ -215,7 +229,7 @@ protected:
|
|||||||
inline void setFullScreenColour(int);
|
inline void setFullScreenColour(int);
|
||||||
inline void setFullRowColour(int, int);
|
inline void setFullRowColour(int, int);
|
||||||
|
|
||||||
QBitmap* m_fontBitmap;
|
TeletextFontBitmap m_fontBitmap;
|
||||||
QPixmap* m_pagePixmap[6];
|
QPixmap* m_pagePixmap[6];
|
||||||
int m_finalFullScreenColour, m_renderLevel;
|
int m_finalFullScreenColour, m_renderLevel;
|
||||||
QColor m_finalFullScreenQColor;
|
QColor m_finalFullScreenQColor;
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ void EditTripletCommand::redo()
|
|||||||
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
||||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex, true);
|
m_teletextDocument->selectSubPageIndex(m_subPageIndex, true);
|
||||||
|
|
||||||
m_teletextDocument->currentSubPage()->enhancements()->operator[](m_row) = m_newTriplet;
|
m_teletextDocument->currentSubPage()->enhancements()->replace(m_row, m_newTriplet);
|
||||||
m_x26Model->emit dataChanged(m_x26Model->createIndex(m_row, 0), m_x26Model->createIndex(m_row, 3), {m_role});
|
m_x26Model->emit dataChanged(m_x26Model->createIndex(m_row, 0), m_x26Model->createIndex(m_row, 3), {m_role});
|
||||||
m_teletextDocument->emit refreshNeeded();
|
m_teletextDocument->emit refreshNeeded();
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ void EditTripletCommand::undo()
|
|||||||
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
||||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex, true);
|
m_teletextDocument->selectSubPageIndex(m_subPageIndex, true);
|
||||||
|
|
||||||
m_teletextDocument->currentSubPage()->enhancements()->operator[](m_row) = m_oldTriplet;
|
m_teletextDocument->currentSubPage()->enhancements()->replace(m_row, m_oldTriplet);
|
||||||
m_x26Model->emit dataChanged(m_x26Model->createIndex(m_row, 0), m_x26Model->createIndex(m_row, 3), {m_role});
|
m_x26Model->emit dataChanged(m_x26Model->createIndex(m_row, 0), m_x26Model->createIndex(m_row, 3), {m_role});
|
||||||
m_teletextDocument->emit refreshNeeded();
|
m_teletextDocument->emit refreshNeeded();
|
||||||
m_teletextDocument->emit tripletCommandHighlight(m_row);
|
m_teletextDocument->emit tripletCommandHighlight(m_row);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QActionGroup>
|
#include <QActionGroup>
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
@@ -34,8 +35,42 @@
|
|||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "render.h"
|
||||||
#include "x26dockwidget.h"
|
#include "x26dockwidget.h"
|
||||||
|
|
||||||
|
CharacterListModel::CharacterListModel(QObject *parent): QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
m_characterSet = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CharacterListModel::rowCount(const QModelIndex & /*parent*/) const
|
||||||
|
{
|
||||||
|
return 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CharacterListModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role == Qt::DisplayRole)
|
||||||
|
return QString("0x%1").arg(index.row()+0x20, 2, 16);
|
||||||
|
|
||||||
|
if (role == Qt::DecorationRole)
|
||||||
|
return m_fontBitmap.rawBitmap()->copy(index.row()*12, m_characterSet*10, 12, 10);
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterListModel::setCharacterSet(int characterSet)
|
||||||
|
{
|
||||||
|
if (characterSet != m_characterSet) {
|
||||||
|
m_characterSet = characterSet;
|
||||||
|
emit dataChanged(createIndex(0, 0), createIndex(95, 0), QVector<int>(Qt::DecorationRole));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
||||||
{
|
{
|
||||||
QVBoxLayout *x26Layout = new QVBoxLayout;
|
QVBoxLayout *x26Layout = new QVBoxLayout;
|
||||||
@@ -161,9 +196,7 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
|||||||
QHBoxLayout *colourAndRowLayout = new QHBoxLayout;
|
QHBoxLayout *colourAndRowLayout = new QHBoxLayout;
|
||||||
|
|
||||||
m_colourComboBox = new QComboBox;
|
m_colourComboBox = new QComboBox;
|
||||||
for (int c=0; c<=3; c++)
|
m_colourComboBox->setModel(m_parentMainWidget->document()->clutModel());
|
||||||
for (int e=0; e<=7; e++)
|
|
||||||
m_colourComboBox->addItem(tr("CLUT %1:%2").arg(c).arg(e));
|
|
||||||
colourAndRowLayout->addWidget(m_colourComboBox);
|
colourAndRowLayout->addWidget(m_colourComboBox);
|
||||||
connect(m_colourComboBox, QOverload<int>::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
|
connect(m_colourComboBox, QOverload<int>::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
|
||||||
|
|
||||||
@@ -181,8 +214,7 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
|||||||
QHBoxLayout *characterCodeLayout = new QHBoxLayout;
|
QHBoxLayout *characterCodeLayout = new QHBoxLayout;
|
||||||
|
|
||||||
m_characterCodeComboBox = new QComboBox;
|
m_characterCodeComboBox = new QComboBox;
|
||||||
for (int i=32; i<128; i++)
|
m_characterCodeComboBox->setModel(&m_characterListModel);
|
||||||
m_characterCodeComboBox->addItem(QString("0x%1").arg(i, 2, 16), i);
|
|
||||||
characterCodeLayout->addWidget(m_characterCodeComboBox);
|
characterCodeLayout->addWidget(m_characterCodeComboBox);
|
||||||
connect(m_characterCodeComboBox, QOverload<int>::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value+32, Qt::UserRole+1); } );
|
connect(m_characterCodeComboBox, QOverload<int>::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value+32, Qt::UserRole+1); } );
|
||||||
|
|
||||||
@@ -253,13 +285,15 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
|||||||
// Invoke Local Objects
|
// Invoke Local Objects
|
||||||
QHBoxLayout *invokeLocalObjectLayout = new QHBoxLayout;
|
QHBoxLayout *invokeLocalObjectLayout = new QHBoxLayout;
|
||||||
|
|
||||||
invokeLocalObjectLayout->addWidget(new QLabel(tr("Designation")));
|
m_invokeLocalObjectDesignationCodeLabel = new QLabel(tr("Designation"));
|
||||||
|
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeLabel);
|
||||||
m_invokeLocalObjectDesignationCodeSpinBox = new QSpinBox;
|
m_invokeLocalObjectDesignationCodeSpinBox = new QSpinBox;
|
||||||
m_invokeLocalObjectDesignationCodeSpinBox->setMaximum(15);
|
m_invokeLocalObjectDesignationCodeSpinBox->setMaximum(15);
|
||||||
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeSpinBox);
|
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeSpinBox);
|
||||||
connect(m_invokeLocalObjectDesignationCodeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
|
connect(m_invokeLocalObjectDesignationCodeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
|
||||||
|
|
||||||
invokeLocalObjectLayout->addWidget(new QLabel(tr("Triplet")));
|
m_invokeLocalObjectTripletNumberLabel = new QLabel(tr("Triplet"));
|
||||||
|
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberLabel);
|
||||||
m_invokeLocalObjectTripletNumberSpinBox = new QSpinBox;
|
m_invokeLocalObjectTripletNumberSpinBox = new QSpinBox;
|
||||||
m_invokeLocalObjectTripletNumberSpinBox->setMaximum(12);
|
m_invokeLocalObjectTripletNumberSpinBox->setMaximum(12);
|
||||||
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberSpinBox);
|
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberSpinBox);
|
||||||
@@ -705,7 +739,16 @@ void X26DockWidget::updateCookedTripletParameters(const QModelIndex &index)
|
|||||||
case 0x29: // G0 character
|
case 0x29: // G0 character
|
||||||
case 0x2b: // G3 character at Level 2.5
|
case 0x2b: // G3 character at Level 2.5
|
||||||
case 0x2f ... 0x3f: // G2 character, G0 character with diacritical
|
case 0x2f ... 0x3f: // G2 character, G0 character with diacritical
|
||||||
|
// TODO non-Latin G0 and G2 character sets
|
||||||
m_characterCodeComboBox->blockSignals(true);
|
m_characterCodeComboBox->blockSignals(true);
|
||||||
|
if (modeExt == 0x22 || modeExt == 0x2b)
|
||||||
|
m_characterListModel.setCharacterSet(26);
|
||||||
|
else if (modeExt == 0x2f)
|
||||||
|
m_characterListModel.setCharacterSet(7);
|
||||||
|
else if (modeExt == 0x21)
|
||||||
|
m_characterListModel.setCharacterSet(24);
|
||||||
|
else
|
||||||
|
m_characterListModel.setCharacterSet(0);
|
||||||
m_characterCodeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt()-32);
|
m_characterCodeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt()-32);
|
||||||
m_characterCodeComboBox->blockSignals(false);
|
m_characterCodeComboBox->blockSignals(false);
|
||||||
m_tripletParameterStackedLayout->setCurrentIndex(2);
|
m_tripletParameterStackedLayout->setCurrentIndex(2);
|
||||||
@@ -756,14 +799,22 @@ void X26DockWidget::updateCookedTripletParameters(const QModelIndex &index)
|
|||||||
// BUG we're only dealing with Local Object Definitions at the moment!
|
// BUG we're only dealing with Local Object Definitions at the moment!
|
||||||
if (index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt() == 0 || (index.model()->data(index.model()->index(index.row(), 1), Qt::UserRole).toInt() & 0x04)) {
|
if (index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt() == 0 || (index.model()->data(index.model()->index(index.row(), 1), Qt::UserRole).toInt() & 0x04)) {
|
||||||
// if (triplet.objectSource() == X26Triplet::LocalObjectSource) {
|
// if (triplet.objectSource() == X26Triplet::LocalObjectSource) {
|
||||||
|
const bool tripletLocationWidgetsVisible = (modeExt & 0x04) != 0x04;
|
||||||
|
|
||||||
|
m_invokeLocalObjectDesignationCodeLabel->setVisible(tripletLocationWidgetsVisible);
|
||||||
|
m_invokeLocalObjectDesignationCodeSpinBox->setVisible(tripletLocationWidgetsVisible);
|
||||||
|
m_invokeLocalObjectTripletNumberLabel->setVisible(tripletLocationWidgetsVisible);
|
||||||
|
m_invokeLocalObjectTripletNumberSpinBox->setVisible(tripletLocationWidgetsVisible);
|
||||||
m_objectSourceComboBox->setCurrentIndex(0);
|
m_objectSourceComboBox->setCurrentIndex(0);
|
||||||
m_invokeObjectSourceStackedLayout->setCurrentIndex(0);
|
m_invokeObjectSourceStackedLayout->setCurrentIndex(0);
|
||||||
|
if (tripletLocationWidgetsVisible) {
|
||||||
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(true);
|
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(true);
|
||||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(true);
|
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(true);
|
||||||
m_invokeLocalObjectDesignationCodeSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
|
m_invokeLocalObjectDesignationCodeSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
|
||||||
m_invokeLocalObjectTripletNumberSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toInt());
|
m_invokeLocalObjectTripletNumberSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toInt());
|
||||||
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(false);
|
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(false);
|
||||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(false);
|
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(false);
|
||||||
|
}
|
||||||
} else { // if (triplet.objectSource() != X26Triplet::IllegalObjectSource) {
|
} else { // if (triplet.objectSource() != X26Triplet::IllegalObjectSource) {
|
||||||
m_objectSourceComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
|
m_objectSourceComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
|
||||||
m_invokeObjectSourceStackedLayout->setCurrentIndex(1);
|
m_invokeObjectSourceStackedLayout->setCurrentIndex(1);
|
||||||
@@ -849,28 +900,36 @@ void X26DockWidget::updateCookedTripletParameters(const QModelIndex &index)
|
|||||||
// Now deal with cooked row and column spinboxes
|
// Now deal with cooked row and column spinboxes
|
||||||
m_cookedRowSpinBox->blockSignals(true);
|
m_cookedRowSpinBox->blockSignals(true);
|
||||||
m_cookedColumnSpinBox->blockSignals(true);
|
m_cookedColumnSpinBox->blockSignals(true);
|
||||||
QVariant rowVariant = index.model()->data(index.model()->index(index.row(), 0), Qt::EditRole);
|
const QVariant rowVariant = index.model()->data(index.model()->index(index.row(), 0), Qt::EditRole);
|
||||||
if (rowVariant.isNull()) {
|
if (rowVariant.isNull()) {
|
||||||
m_cookedRowSpinBox->setEnabled(false);
|
m_cookedRowSpinBox->setEnabled(false);
|
||||||
m_cookedRowSpinBox->setValue(0);
|
m_cookedRowSpinBox->setValue(0);
|
||||||
|
m_cookedRowSpinBox->setPrefix("");
|
||||||
} else {
|
} else {
|
||||||
m_cookedRowSpinBox->setEnabled(true);
|
m_cookedRowSpinBox->setEnabled(true);
|
||||||
if (index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole) == 0x10)
|
if (modeExt == 0x10) {
|
||||||
m_cookedRowSpinBox->setRange(0, 23);
|
m_cookedRowSpinBox->setRange(0, 23);
|
||||||
else
|
m_cookedRowSpinBox->setPrefix("+");
|
||||||
|
} else {
|
||||||
m_cookedRowSpinBox->setRange(1, 24);
|
m_cookedRowSpinBox->setRange(1, 24);
|
||||||
|
m_cookedRowSpinBox->setPrefix("");
|
||||||
|
}
|
||||||
m_cookedRowSpinBox->setValue(rowVariant.toInt());
|
m_cookedRowSpinBox->setValue(rowVariant.toInt());
|
||||||
}
|
}
|
||||||
QVariant columnVariant = index.model()->data(index.model()->index(index.row(), 1), Qt::EditRole);
|
const QVariant columnVariant = index.model()->data(index.model()->index(index.row(), 1), Qt::EditRole);
|
||||||
if (columnVariant.isNull()) {
|
if (columnVariant.isNull()) {
|
||||||
m_cookedColumnSpinBox->setEnabled(false);
|
m_cookedColumnSpinBox->setEnabled(false);
|
||||||
m_cookedColumnSpinBox->setValue(0);
|
m_cookedColumnSpinBox->setValue(0);
|
||||||
|
m_cookedColumnSpinBox->setPrefix("");
|
||||||
} else {
|
} else {
|
||||||
m_cookedColumnSpinBox->setEnabled(true);
|
m_cookedColumnSpinBox->setEnabled(true);
|
||||||
if (index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole) == 0x10)
|
if (modeExt == 0x10) {
|
||||||
m_cookedColumnSpinBox->setMaximum(71);
|
m_cookedColumnSpinBox->setMaximum(71);
|
||||||
else
|
m_cookedColumnSpinBox->setPrefix("+");
|
||||||
|
} else {
|
||||||
m_cookedColumnSpinBox->setMaximum(39);
|
m_cookedColumnSpinBox->setMaximum(39);
|
||||||
|
m_cookedColumnSpinBox->setPrefix("");
|
||||||
|
}
|
||||||
m_cookedColumnSpinBox->setValue(columnVariant.toInt());
|
m_cookedColumnSpinBox->setValue(columnVariant.toInt());
|
||||||
}
|
}
|
||||||
m_cookedRowSpinBox->blockSignals(false);
|
m_cookedRowSpinBox->blockSignals(false);
|
||||||
|
|||||||
@@ -20,10 +20,12 @@
|
|||||||
#ifndef X26DOCKWIDGET_H
|
#ifndef X26DOCKWIDGET_H
|
||||||
#define X26DOCKWIDGET_H
|
#define X26DOCKWIDGET_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
@@ -31,8 +33,25 @@
|
|||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
|
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
#include "render.h"
|
||||||
#include "x26model.h"
|
#include "x26model.h"
|
||||||
|
|
||||||
|
class CharacterListModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CharacterListModel(QObject *parent = 0);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
void setCharacterSet(int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TeletextFontBitmap m_fontBitmap;
|
||||||
|
int m_characterSet;
|
||||||
|
};
|
||||||
|
|
||||||
class X26DockWidget : public QDockWidget
|
class X26DockWidget : public QDockWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -63,6 +82,7 @@ public slots:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
CharacterListModel m_characterListModel;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTableView *m_x26View;
|
QTableView *m_x26View;
|
||||||
@@ -79,6 +99,7 @@ private:
|
|||||||
QComboBox *m_textSizeComboBox;
|
QComboBox *m_textSizeComboBox;
|
||||||
QCheckBox *m_displayAttributeBoxingCheckBox, *m_displayAttributeConcealCheckBox, *m_displayAttributeInvertCheckBox, *m_displayAttributeUnderlineCheckBox;
|
QCheckBox *m_displayAttributeBoxingCheckBox, *m_displayAttributeConcealCheckBox, *m_displayAttributeInvertCheckBox, *m_displayAttributeUnderlineCheckBox;
|
||||||
QComboBox *m_objectSourceComboBox, *m_objectRequiredAtLevelsComboBox;
|
QComboBox *m_objectSourceComboBox, *m_objectRequiredAtLevelsComboBox;
|
||||||
|
QLabel *m_invokeLocalObjectDesignationCodeLabel, *m_invokeLocalObjectTripletNumberLabel;
|
||||||
QSpinBox *m_invokeLocalObjectDesignationCodeSpinBox, *m_invokeLocalObjectTripletNumberSpinBox;
|
QSpinBox *m_invokeLocalObjectDesignationCodeSpinBox, *m_invokeLocalObjectTripletNumberSpinBox;
|
||||||
QSpinBox *m_invokePOPSubPageSpinBox, *m_invokePOPPacketNumberSpinBox;
|
QSpinBox *m_invokePOPSubPageSpinBox, *m_invokePOPPacketNumberSpinBox;
|
||||||
QComboBox *m_invokePOPTripletNumberComboBox, *m_invokePOPPointerBitsComboBox;
|
QComboBox *m_invokePOPTripletNumberComboBox, *m_invokePOPPointerBitsComboBox;
|
||||||
|
|||||||
715
x26model.cpp
715
x26model.cpp
File diff suppressed because it is too large
Load Diff
17
x26model.h
17
x26model.h
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
class X26Model : public QAbstractListModel
|
class X26Model : public QAbstractListModel
|
||||||
{
|
{
|
||||||
@@ -49,9 +50,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
TeletextWidget *m_parentMainWidget;
|
TeletextWidget *m_parentMainWidget;
|
||||||
bool m_listLoaded;
|
bool m_listLoaded;
|
||||||
};
|
TeletextFontBitmap m_fontBitmap;
|
||||||
|
|
||||||
static const QString modeTripletName[64] {
|
const QString m_modeTripletName[64] {
|
||||||
// Row triplet modes
|
// Row triplet modes
|
||||||
"Full screen colour",
|
"Full screen colour",
|
||||||
"Full row colour",
|
"Full row colour",
|
||||||
@@ -132,4 +133,16 @@ static const QString modeTripletName[64] {
|
|||||||
"G0 character diacritical F"
|
"G0 character diacritical F"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tripletErrorShow {
|
||||||
|
QString message;
|
||||||
|
int columnHighlight;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tripletErrorShow m_tripletErrors[3] {
|
||||||
|
{ "", 0 }, // No error
|
||||||
|
{ "Active Position can't move up", 0 },
|
||||||
|
{ "Active Position can't move left within row", 1 }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
123
x26triplets.cpp
123
x26triplets.cpp
@@ -50,3 +50,126 @@ void X26Triplet::setAddressColumn(int addressColumn)
|
|||||||
{
|
{
|
||||||
m_address = addressColumn;
|
m_address = addressColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void X26TripletList::updateInternalData(int r)
|
||||||
|
{
|
||||||
|
ActivePosition activePosition;
|
||||||
|
X26Triplet *triplet;
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
|
activePosition.setRow(m_list[r-1].m_activePositionRow);
|
||||||
|
activePosition.setColumn(m_list[r-1].m_activePositionColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = r; i < m_list.size(); i++) {
|
||||||
|
triplet = &m_list[i];
|
||||||
|
triplet->m_error = X26Triplet::NoError;
|
||||||
|
if (triplet->isRowTriplet()) {
|
||||||
|
switch (m_list.at(i).modeExt()) {
|
||||||
|
case 0x00: // Full screen colour
|
||||||
|
if (activePosition.isDeployed())
|
||||||
|
// TODO more specific error needed
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
break;
|
||||||
|
case 0x01: // Full row colour
|
||||||
|
if (!activePosition.setRow(triplet->addressRow()))
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
break;
|
||||||
|
case 0x04: // Set Active Position;
|
||||||
|
if (!activePosition.setRow(triplet->addressRow()))
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
else if (triplet->data() >= 40 || !activePosition.setColumn(triplet->data()))
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
||||||
|
break;
|
||||||
|
case 0x07: // Address row 0
|
||||||
|
if (activePosition.isDeployed())
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedUp;
|
||||||
|
else {
|
||||||
|
activePosition.setRow(0);
|
||||||
|
activePosition.setColumn(8);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x15 ... 0x17: // Define Object
|
||||||
|
activePosition.reset();
|
||||||
|
// Make sure data field holds correct place of triplet
|
||||||
|
// otherwise the object won't appear
|
||||||
|
triplet->m_address &= 0x3c;
|
||||||
|
if (i >= 104) // Triplet 8
|
||||||
|
triplet->m_address |= 0x01;
|
||||||
|
triplet->m_data = (((i / 13) & 0x07) << 4) | (i % 13);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
// Column triplet: make sure that PDC and reserved triplets don't affect the Active Position
|
||||||
|
} else if (triplet->modeExt() != 0x24 && triplet->modeExt() != 0x25 && triplet->modeExt() != 0x26 && triplet->modeExt() != 0x2a)
|
||||||
|
if (!activePosition.setColumn(triplet->addressColumn()))
|
||||||
|
triplet->m_error = X26Triplet::ActivePositionMovedLeft;
|
||||||
|
triplet->m_activePositionRow = activePosition.row();
|
||||||
|
triplet->m_activePositionColumn = activePosition.column();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26TripletList::append(const X26Triplet &value)
|
||||||
|
{
|
||||||
|
m_list.append(value);
|
||||||
|
updateInternalData(m_list.size()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26TripletList::insert(int i, const X26Triplet &value)
|
||||||
|
{
|
||||||
|
m_list.insert(i, value);
|
||||||
|
updateInternalData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26TripletList::removeAt(int i)
|
||||||
|
{
|
||||||
|
m_list.removeAt(i);
|
||||||
|
if (m_list.size() != 0 && i < m_list.size())
|
||||||
|
updateInternalData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26TripletList::replace(int i, const X26Triplet &value)
|
||||||
|
{
|
||||||
|
m_list.replace(i, value);
|
||||||
|
updateInternalData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
X26TripletList::ActivePosition::ActivePosition()
|
||||||
|
{
|
||||||
|
m_row = m_column = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X26TripletList::ActivePosition::reset()
|
||||||
|
{
|
||||||
|
m_row = m_column = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool X26TripletList::ActivePosition::setRow(int row)
|
||||||
|
{
|
||||||
|
if (row < m_row)
|
||||||
|
return false;
|
||||||
|
if (row > m_row) {
|
||||||
|
m_row = row;
|
||||||
|
m_column = -1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool X26TripletList::ActivePosition::setColumn(int column)
|
||||||
|
{
|
||||||
|
if (column < m_column)
|
||||||
|
return false;
|
||||||
|
if (m_row == -1 and column >= 0)
|
||||||
|
m_row = 0;
|
||||||
|
m_column = column;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
bool X26TripletList::ActivePosition::setRowAndColumn(int newRow, int newColumn)
|
||||||
|
{
|
||||||
|
if (!setRow(newRow))
|
||||||
|
return false;
|
||||||
|
return setColumn(newColumn);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -20,9 +20,13 @@
|
|||||||
#ifndef X26TRIPLETS_H
|
#ifndef X26TRIPLETS_H
|
||||||
#define X26TRIPLETS_H
|
#define X26TRIPLETS_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
class X26Triplet
|
class X26Triplet
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum X26TripletError { NoError, ActivePositionMovedUp, ActivePositionMovedLeft };
|
||||||
|
|
||||||
X26Triplet() {}
|
X26Triplet() {}
|
||||||
// X26Triplet(const X26Triplet &other);
|
// X26Triplet(const X26Triplet &other);
|
||||||
|
|
||||||
@@ -44,9 +48,56 @@ public:
|
|||||||
void setAddressRow(int);
|
void setAddressRow(int);
|
||||||
void setAddressColumn(int);
|
void setAddressColumn(int);
|
||||||
|
|
||||||
|
int activePositionRow() const { return m_activePositionRow; }
|
||||||
|
int activePositionColumn() const { return m_activePositionColumn; }
|
||||||
|
X26TripletError error() const { return m_error; }
|
||||||
|
|
||||||
|
friend class X26TripletList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_address, m_mode, m_data;
|
int m_address, m_mode, m_data;
|
||||||
|
int m_activePositionRow = -1;
|
||||||
|
int m_activePositionColumn = -1;
|
||||||
|
X26TripletError m_error = NoError;
|
||||||
|
};
|
||||||
|
|
||||||
|
class X26TripletList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void append(const X26Triplet &);
|
||||||
|
void insert(int, const X26Triplet &);
|
||||||
|
void removeAt(int);
|
||||||
|
void replace(int, const X26Triplet &);
|
||||||
|
|
||||||
|
void removeLast() { m_list.removeLast(); }
|
||||||
|
|
||||||
|
const X26Triplet &at(int i) const { return m_list.at(i); }
|
||||||
|
bool isEmpty() const { return m_list.isEmpty(); }
|
||||||
|
void reserve(int alloc) { m_list.reserve(alloc); }
|
||||||
|
int size() const { return m_list.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateInternalData(int);
|
||||||
|
|
||||||
|
QList<X26Triplet> m_list;
|
||||||
|
|
||||||
|
class ActivePosition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ActivePosition();
|
||||||
|
void reset();
|
||||||
|
// int row() const { return (m_row == -1) ? 0 : m_row; }
|
||||||
|
// int column() const { return (m_column == -1) ? 0 : m_column; }
|
||||||
|
int row() const { return m_row; }
|
||||||
|
int column() const { return m_column; }
|
||||||
|
bool isDeployed() const { return m_row != -1; }
|
||||||
|
bool setRow(int);
|
||||||
|
bool setColumn(int);
|
||||||
|
// bool setRowAndColumn(int, int);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_row, m_column;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user