Implement DRCS rendering
External pages with DRCS definitions can be loaded using the options in the "DRCS pages" submenu within the "View" menu. Two DRCS pages can be loaded, one for Global DRCS definitions and the other for Normal DRCS definitions. Level 2.5 mode 0 PTUs are fully supported. Partial support for Level 3.5 mode 1, 2 and 3 PTUs. DCLUTs defined in X/28/1 on the main page are not yet implemented; the characters currently appear in the default DCLUTs described in D.1.6 and D.2.2 of the ETSI spec.
This commit is contained in:
@@ -19,9 +19,16 @@
|
|||||||
|
|
||||||
#include "decode.h"
|
#include "decode.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QImage>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
|
||||||
|
#include "drcspage.h"
|
||||||
|
#include "levelonepage.h"
|
||||||
|
#include "pagebase.h"
|
||||||
|
|
||||||
|
|
||||||
TeletextPageDecode::Invocation::Invocation()
|
TeletextPageDecode::Invocation::Invocation()
|
||||||
{
|
{
|
||||||
m_tripletList = nullptr;
|
m_tripletList = nullptr;
|
||||||
@@ -113,6 +120,7 @@ void TeletextPageDecode::Invocation::buildMap(int level)
|
|||||||
if (targetRow == 0)
|
if (targetRow == 0)
|
||||||
m_fullRowCLUTMap.insert(targetRow, triplet);
|
m_fullRowCLUTMap.insert(targetRow, triplet);
|
||||||
break;
|
break;
|
||||||
|
case 0x18: // DRCS mode
|
||||||
case 0x20: // Foreground colour
|
case 0x20: // Foreground colour
|
||||||
case 0x23: // Background colour
|
case 0x23: // Background colour
|
||||||
case 0x27: // Additional flash functions
|
case 0x27: // Additional flash functions
|
||||||
@@ -128,6 +136,7 @@ void TeletextPageDecode::Invocation::buildMap(int level)
|
|||||||
case 0x22: // G3 character at Level 1.5
|
case 0x22: // G3 character at Level 1.5
|
||||||
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 0x2d: // DRCS character
|
||||||
case 0x2f: // G2 character
|
case 0x2f: // G2 character
|
||||||
m_characterMap.insert(qMakePair(targetRow, targetColumn), triplet);
|
m_characterMap.insert(qMakePair(targetRow, targetColumn), triplet);
|
||||||
m_rightMostColumn.insert(targetRow, targetColumn);
|
m_rightMostColumn.insert(targetRow, targetColumn);
|
||||||
@@ -184,6 +193,9 @@ TeletextPageDecode::TeletextPageDecode()
|
|||||||
m_fullRowQColor[r].setRgb(0, 0, 0);
|
m_fullRowQColor[r].setRgb(0, 0, 0);
|
||||||
}
|
}
|
||||||
m_leftSidePanelColumns = m_rightSidePanelColumns = 0;
|
m_leftSidePanelColumns = m_rightSidePanelColumns = 0;
|
||||||
|
|
||||||
|
m_drcsPage[GlobalDRCSPage] = nullptr;
|
||||||
|
m_drcsPage[NormalDRCSPage] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TeletextPageDecode::~TeletextPageDecode()
|
TeletextPageDecode::~TeletextPageDecode()
|
||||||
@@ -203,6 +215,28 @@ void TeletextPageDecode::setTeletextPage(LevelOnePage *newCurrentPage)
|
|||||||
updateSidePanels();
|
updateSidePanels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeletextPageDecode::setDRCSPage(DRCSPageType pageType, QList<DRCSPage> *pages)
|
||||||
|
{
|
||||||
|
m_drcsPage[pageType] = pages;
|
||||||
|
|
||||||
|
bool refreshRequired = false;
|
||||||
|
|
||||||
|
for (int r=0; r<25; r++)
|
||||||
|
for (int c=0; c<72; c++)
|
||||||
|
if (m_cell[r][c].character.drcsSource != NoDRCS) {
|
||||||
|
m_refresh[r][c] = true;
|
||||||
|
refreshRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshRequired)
|
||||||
|
decodePage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TeletextPageDecode::clearDRCSPage(DRCSPageType pageType)
|
||||||
|
{
|
||||||
|
setDRCSPage(pageType, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void TeletextPageDecode::setLevel(int level)
|
void TeletextPageDecode::setLevel(int level)
|
||||||
{
|
{
|
||||||
if (level == m_level)
|
if (level == m_level)
|
||||||
@@ -218,6 +252,116 @@ void TeletextPageDecode::setLevel(int level)
|
|||||||
decodePage();
|
decodePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage TeletextPageDecode::drcsImage(DRCSSource pageType, int subTable, int chr, bool flashPhOn)
|
||||||
|
{
|
||||||
|
if (pageType == NoDRCS)
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
// Check if page is loaded and if the subpage exists
|
||||||
|
const QList<DRCSPage>* drcsPage = m_drcsPage[pageType-1];
|
||||||
|
if (drcsPage == nullptr || subTable >= drcsPage->size())
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
// Level 2.5: only and always mode 0 (12x10x1) and doesn't use X/28/3
|
||||||
|
// Level 3.5: if X/28/3 is absent, drcsMode below returns mode 0
|
||||||
|
if (m_level == 2 || drcsPage->at(subTable).drcsMode(chr) == 0) {
|
||||||
|
uchar rawData[20];
|
||||||
|
|
||||||
|
if (!drcsPage->at(subTable).ptu(chr, rawData))
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QImage result = QImage(rawData, 12, 10, 2, QImage::Format_Mono);
|
||||||
|
return result.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level 3.5: obey X/28/3 "subsequent PTU" and "no data" values, ignore reserved values
|
||||||
|
const int drcsMode = drcsPage->at(subTable).drcsMode(chr);
|
||||||
|
if (drcsMode > 3)
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
uchar rawData[120];
|
||||||
|
|
||||||
|
if (drcsMode != 3) {
|
||||||
|
// mode 1 (12x10x2) or mode 2 (12x10x4)
|
||||||
|
// Each complete bitplane stored sequentially across multiple PTUs
|
||||||
|
|
||||||
|
uchar bitplaneArr[4][20] = { };
|
||||||
|
|
||||||
|
// Get the PTUs for each bitplane
|
||||||
|
drcsPage->at(subTable).ptu(chr, bitplaneArr[0]);
|
||||||
|
if (chr < 47)
|
||||||
|
drcsPage->at(subTable).ptu(chr+1, bitplaneArr[1]);
|
||||||
|
if (drcsMode == 2) {
|
||||||
|
if (chr < 46)
|
||||||
|
drcsPage->at(subTable).ptu(chr+2, bitplaneArr[2]);
|
||||||
|
if (chr < 45)
|
||||||
|
drcsPage->at(subTable).ptu(chr+3, bitplaneArr[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now assemble the bitplanes into byte-per-pixel data
|
||||||
|
for (int x=0; x<12; x++)
|
||||||
|
for (int y=0; y<10; y++) {
|
||||||
|
const int scanByte = y*2 + (x > 7);
|
||||||
|
const int scanBit = 7 - x%8;
|
||||||
|
|
||||||
|
rawData[x + y*12] = bitplaneArr[0][scanByte] >> scanBit & 1;
|
||||||
|
rawData[x + y*12] |= (bitplaneArr[1][scanByte] >> scanBit & 1) << 1;
|
||||||
|
if (drcsMode == 2) {
|
||||||
|
rawData[x + y*12] |= (bitplaneArr[2][scanByte] >> scanBit & 1) << 2;
|
||||||
|
rawData[x + y*12] |= (bitplaneArr[3][scanByte] >> scanBit & 1) << 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// mode 3 (6x5x4)
|
||||||
|
// Interleaved: First row of six pixels is stored four times sequentially, one for
|
||||||
|
// each bitplane, then second row of pixels four times, and so on
|
||||||
|
const int pktNo = (chr+2)/2;
|
||||||
|
|
||||||
|
if (!drcsPage->at(subTable).packetExists(pktNo))
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QByteArray pkt;
|
||||||
|
|
||||||
|
if (chr % 2 == 0)
|
||||||
|
pkt = drcsPage->at(subTable).packet(pktNo).first(20);
|
||||||
|
else
|
||||||
|
pkt = drcsPage->at(subTable).packet(pktNo).last(20);
|
||||||
|
|
||||||
|
for (int x=0; x<6; x++)
|
||||||
|
for (int y=0; y<5; y++) {
|
||||||
|
const int scanByte = y * 4;
|
||||||
|
const int scanBit = 5 - x;
|
||||||
|
uchar pixel;
|
||||||
|
|
||||||
|
pixel = pkt.at(scanByte) >> scanBit & 1;
|
||||||
|
pixel |= (pkt.at(scanByte+1) >> scanBit & 1) << 1;
|
||||||
|
pixel |= (pkt.at(scanByte+2) >> scanBit & 1) << 2;
|
||||||
|
pixel |= (pkt.at(scanByte+3) >> scanBit & 1) << 3;
|
||||||
|
|
||||||
|
rawData[x*2 + y*24 ] = pixel;
|
||||||
|
rawData[x*2+1 + y*24 ] = pixel;
|
||||||
|
rawData[x*2 + y*24+12] = pixel;
|
||||||
|
rawData[x*2+1 + y*24+12] = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage result = QImage(rawData, 12, 10, 12, QImage::Format_Indexed8);
|
||||||
|
|
||||||
|
// Now put in the colours
|
||||||
|
// TODO read colours from X/28/1, for now we put in the default colours
|
||||||
|
for (int i=0; i<16; i++) {
|
||||||
|
if (flashPhOn)
|
||||||
|
result.setColor(i, m_levelOnePage->CLUTtoQColor(i).rgb());
|
||||||
|
else
|
||||||
|
result.setColor(i, m_levelOnePage->CLUTtoQColor(i ^ 8).rgb());
|
||||||
|
|
||||||
|
if (i == 3 && drcsMode == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.copy();
|
||||||
|
}
|
||||||
|
|
||||||
void TeletextPageDecode::updateSidePanels()
|
void TeletextPageDecode::updateSidePanels()
|
||||||
{
|
{
|
||||||
int oldLeftSidePanelColumns = m_leftSidePanelColumns;
|
int oldLeftSidePanelColumns = m_leftSidePanelColumns;
|
||||||
@@ -315,7 +459,8 @@ TeletextPageDecode::textCharacter TeletextPageDecode::characterFromTriplets(cons
|
|||||||
for (int a=triplets.size()-1; a>=0; a--) {
|
for (int a=triplets.size()-1; a>=0; a--) {
|
||||||
const X26Triplet triplet = triplets.at(a);
|
const X26Triplet triplet = triplets.at(a);
|
||||||
|
|
||||||
if (triplet.data() < 0x20)
|
// Data values below 0x20 are reserved, except for DRCS character
|
||||||
|
if (triplet.data() < 0x20 && triplet.modeExt() != 0x2d)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const unsigned char charCode = triplet.data();
|
const unsigned char charCode = triplet.data();
|
||||||
@@ -353,6 +498,9 @@ TeletextPageDecode::textCharacter TeletextPageDecode::characterFromTriplets(cons
|
|||||||
case 0x2b: // G3 character at Level 2.5
|
case 0x2b: // G3 character at Level 2.5
|
||||||
result = { charCode, 26, 0 };
|
result = { charCode, 26, 0 };
|
||||||
break;
|
break;
|
||||||
|
case 0x2d: // DRCS character
|
||||||
|
result.drcsSource = (charCode & 0x40) == 0x40 ? NormalDRCS : GlobalDRCS;
|
||||||
|
result.drcsChar = charCode & 0x3f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,6 +779,7 @@ void TeletextPageDecode::decodeRow(int r)
|
|||||||
|
|
||||||
bool applyAdapt = false;
|
bool applyAdapt = false;
|
||||||
|
|
||||||
|
drcsMode *drcsModePtr;
|
||||||
// Adaptive Invocation that is applying an attribute
|
// Adaptive Invocation that is applying an attribute
|
||||||
// If we're not tracking an Adaptive Invocation yet, start tracking this one
|
// If we're not tracking an Adaptive Invocation yet, start tracking this one
|
||||||
// Otherwise check if this Invocation is the the same one as we are tracking
|
// Otherwise check if this Invocation is the the same one as we are tracking
|
||||||
@@ -645,6 +794,16 @@ void TeletextPageDecode::decodeRow(int r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (triplet.modeExt()) {
|
switch (triplet.modeExt()) {
|
||||||
|
case 0x18: // DRCS mode
|
||||||
|
drcsModePtr = (triplet.data() & 0x40) == 0x40 ? &painter->nDrcs : &painter->gDrcs;
|
||||||
|
if ((triplet.data() & 0x30) != 0x00) {
|
||||||
|
drcsModePtr->level2p5 = triplet.data() & 0x10;
|
||||||
|
drcsModePtr->level3p5 = triplet.data() & 0x20;
|
||||||
|
// "used" is never set to true on Level 3.5, to allow all 16 sub-tables
|
||||||
|
if (!drcsModePtr->used)
|
||||||
|
drcsModePtr->subTable = triplet.data() & 0x0f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 0x20: // Foreground colour
|
case 0x20: // Foreground colour
|
||||||
if (applyAdapt)
|
if (applyAdapt)
|
||||||
adapForeground = true;
|
adapForeground = true;
|
||||||
@@ -733,6 +892,7 @@ void TeletextPageDecode::decodeRow(int r)
|
|||||||
|
|
||||||
if (c < 40 && m_rowHeight[r] != BottomHalf) {
|
if (c < 40 && m_rowHeight[r] != BottomHalf) {
|
||||||
m_level1ActivePainter.result.character.diacritical = 0;
|
m_level1ActivePainter.result.character.diacritical = 0;
|
||||||
|
m_level1ActivePainter.result.character.drcsSource = NoDRCS;
|
||||||
if (m_levelOnePage->character(r, c) >= 0x20) {
|
if (m_levelOnePage->character(r, c) >= 0x20) {
|
||||||
m_level1ActivePainter.result.character.code = m_levelOnePage->character(r, c);
|
m_level1ActivePainter.result.character.code = m_levelOnePage->character(r, c);
|
||||||
if (m_cellLevel1MosaicChar[r][c]) {
|
if (m_cellLevel1MosaicChar[r][c]) {
|
||||||
@@ -773,7 +933,36 @@ void TeletextPageDecode::decodeRow(int r)
|
|||||||
for (int i=0; i<m_invocations[t].size(); i++) {
|
for (int i=0; i<m_invocations[t].size(); i++) {
|
||||||
painter = (t == 0) ? &m_level1ActivePainter : &m_adapPassPainter[t-1][i];
|
painter = (t == 0) ? &m_level1ActivePainter : &m_adapPassPainter[t-1][i];
|
||||||
|
|
||||||
const textCharacter result = characterFromTriplets(m_invocations[t].at(i).charactersMappedAt(r, c));
|
textCharacter result = characterFromTriplets(m_invocations[t].at(i).charactersMappedAt(r, c));
|
||||||
|
|
||||||
|
if (result.drcsSource) {
|
||||||
|
drcsMode *drcsModePtr = result.drcsSource == NormalDRCS ? &painter->nDrcs : &painter->gDrcs;
|
||||||
|
|
||||||
|
if ((m_level == 2 && drcsModePtr->level2p5) || (m_level == 3 && drcsModePtr->level3p5)) {
|
||||||
|
// "code" is zero if an X/26 character is NOT invoked in the same cell
|
||||||
|
if (result.code == 0x00)
|
||||||
|
result.code = 0x20;
|
||||||
|
result.drcsSubTable = drcsModePtr->subTable;
|
||||||
|
if (m_level < 3)
|
||||||
|
drcsModePtr->used = true;
|
||||||
|
} else
|
||||||
|
// DRCS character not required at the current level
|
||||||
|
result.drcsSource = NoDRCS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the DRCS character in question is not downloaded, scrap all that hard work
|
||||||
|
// looking it up.
|
||||||
|
// Ideally we'd leave it in case somebody wants to find which character was meant
|
||||||
|
// to be invoked, but things like underlying Level 1 characters still needing to
|
||||||
|
// appear when the DRCS characters are not (yet) downloaded are too complex to
|
||||||
|
// figure out with this too complex decoder.
|
||||||
|
if (result.drcsSource) {
|
||||||
|
const QList<DRCSPage>* drcsPage = m_drcsPage[result.drcsSource-1];
|
||||||
|
if (drcsPage == nullptr || result.drcsSubTable >= drcsPage->size() || !drcsPage->at(result.drcsSubTable).ptu(result.drcsChar, nullptr)) {
|
||||||
|
result.drcsSource = NoDRCS;
|
||||||
|
result.code = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (t == 0 && result.code == 0x00)
|
if (t == 0 && result.code == 0x00)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -20,11 +20,14 @@
|
|||||||
#ifndef DECODE_H
|
#ifndef DECODE_H
|
||||||
#define DECODE_H
|
#define DECODE_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMultiMap>
|
#include <QMultiMap>
|
||||||
|
|
||||||
|
#include "drcspage.h"
|
||||||
#include "levelonepage.h"
|
#include "levelonepage.h"
|
||||||
|
#include "pagebase.h"
|
||||||
|
|
||||||
class TeletextPageDecode : public QObject
|
class TeletextPageDecode : public QObject
|
||||||
{
|
{
|
||||||
@@ -32,6 +35,9 @@ class TeletextPageDecode : public QObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
enum CharacterFragment { NormalSize, DoubleHeightTopHalf, DoubleHeightBottomHalf, DoubleWidthLeftHalf, DoubleWidthRightHalf, DoubleSizeTopLeftQuarter, DoubleSizeTopRightQuarter, DoubleSizeBottomLeftQuarter, DoubleSizeBottomRightQuarter };
|
enum CharacterFragment { NormalSize, DoubleHeightTopHalf, DoubleHeightBottomHalf, DoubleWidthLeftHalf, DoubleWidthRightHalf, DoubleSizeTopLeftQuarter, DoubleSizeTopRightQuarter, DoubleSizeBottomLeftQuarter, DoubleSizeBottomRightQuarter };
|
||||||
|
enum DRCSPageType { NormalDRCSPage, GlobalDRCSPage };
|
||||||
|
// enum ObjectPageType { NormalPOPage = 2, GlobalPOPage };
|
||||||
|
enum DRCSSource { NoDRCS, NormalDRCS, GlobalDRCS };
|
||||||
enum RowHeight { NormalHeight, TopHalf, BottomHalf };
|
enum RowHeight { NormalHeight, TopHalf, BottomHalf };
|
||||||
|
|
||||||
TeletextPageDecode();
|
TeletextPageDecode();
|
||||||
@@ -42,6 +48,9 @@ public:
|
|||||||
void decodePage();
|
void decodePage();
|
||||||
LevelOnePage *teletextPage() const { return m_levelOnePage; };
|
LevelOnePage *teletextPage() const { return m_levelOnePage; };
|
||||||
void setTeletextPage(LevelOnePage *newCurrentPage);
|
void setTeletextPage(LevelOnePage *newCurrentPage);
|
||||||
|
QList<DRCSPage> *drcsPage(DRCSPageType pageType) const { return m_drcsPage[pageType]; };
|
||||||
|
void setDRCSPage(DRCSPageType pageType, QList<DRCSPage> *pages);
|
||||||
|
void clearDRCSPage(DRCSPageType pageType);
|
||||||
void updateSidePanels();
|
void updateSidePanels();
|
||||||
|
|
||||||
unsigned char cellCharacterCode(int r, int c) const { return m_cell[r][c].character.code; };
|
unsigned char cellCharacterCode(int r, int c) const { return m_cell[r][c].character.code; };
|
||||||
@@ -49,6 +58,13 @@ public:
|
|||||||
int cellCharacterDiacritical(int r, int c) const { return m_cell[r][c].character.diacritical; };
|
int cellCharacterDiacritical(int r, int c) const { return m_cell[r][c].character.diacritical; };
|
||||||
int cellG0CharacterSet(int r, int c) const { return m_cell[r][c].g0Set; };
|
int cellG0CharacterSet(int r, int c) const { return m_cell[r][c].g0Set; };
|
||||||
int cellG2CharacterSet(int r, int c) const { return m_cell[r][c].g2Set; };
|
int cellG2CharacterSet(int r, int c) const { return m_cell[r][c].g2Set; };
|
||||||
|
|
||||||
|
DRCSSource cellDrcsSource(int r, int c) const { return m_cell[r][c].character.drcsSource; };
|
||||||
|
int cellDrcsSubTable(int r, int c) const { return m_cell[r][c].character.drcsSubTable; };
|
||||||
|
int cellDrcsCharacter(int r, int c) const { return m_cell[r][c].character.drcsChar; };
|
||||||
|
|
||||||
|
QImage drcsImage(DRCSSource pageType, int subTable, int chr, bool flashPhOn = true);
|
||||||
|
|
||||||
int cellForegroundCLUT(int r, int c) const { return m_cell[r][c].attribute.foregroundCLUT; };
|
int cellForegroundCLUT(int r, int c) const { return m_cell[r][c].attribute.foregroundCLUT; };
|
||||||
int cellBackgroundCLUT(int r, int c) const { return m_cell[r][c].attribute.backgroundCLUT; };
|
int cellBackgroundCLUT(int r, int c) const { return m_cell[r][c].attribute.backgroundCLUT; };
|
||||||
QColor cellForegroundQColor(int r, int c);
|
QColor cellForegroundQColor(int r, int c);
|
||||||
@@ -103,13 +119,19 @@ private:
|
|||||||
unsigned char code=0x20;
|
unsigned char code=0x20;
|
||||||
int set=0;
|
int set=0;
|
||||||
int diacritical=0;
|
int diacritical=0;
|
||||||
|
DRCSSource drcsSource=NoDRCS;
|
||||||
|
int drcsSubTable=0;
|
||||||
|
int drcsChar=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
friend inline bool operator!=(const textCharacter &lhs, const textCharacter &rhs)
|
friend inline bool operator!=(const textCharacter &lhs, const textCharacter &rhs)
|
||||||
{
|
{
|
||||||
return lhs.code != rhs.code ||
|
return lhs.code != rhs.code ||
|
||||||
lhs.set != rhs.set ||
|
lhs.set != rhs.set ||
|
||||||
lhs.diacritical != rhs.diacritical;
|
lhs.diacritical != rhs.diacritical ||
|
||||||
|
lhs.drcsSource != rhs.drcsSource ||
|
||||||
|
lhs.drcsSubTable != rhs.drcsSubTable ||
|
||||||
|
lhs.drcsChar != rhs.drcsChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct flashFunctions {
|
struct flashFunctions {
|
||||||
@@ -179,12 +201,21 @@ private:
|
|||||||
lhs.fragment != rhs.fragment;
|
lhs.fragment != rhs.fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct drcsMode {
|
||||||
|
bool level2p5=true;
|
||||||
|
bool level3p5=true;
|
||||||
|
bool used=false;
|
||||||
|
int subTable=0;
|
||||||
|
};
|
||||||
|
|
||||||
struct textPainter {
|
struct textPainter {
|
||||||
textAttributes attribute;
|
textAttributes attribute;
|
||||||
textCell result;
|
textCell result;
|
||||||
textCell rightHalfCell;
|
textCell rightHalfCell;
|
||||||
textCell bottomHalfCell[72];
|
textCell bottomHalfCell[72];
|
||||||
|
|
||||||
|
drcsMode gDrcs, nDrcs;
|
||||||
|
|
||||||
int styleSpreadRows=0;
|
int styleSpreadRows=0;
|
||||||
int setProportionalRows[72], clearProportionalRows[72];
|
int setProportionalRows[72], clearProportionalRows[72];
|
||||||
int setBoldRows[72], clearBoldRows[72];
|
int setBoldRows[72], clearBoldRows[72];
|
||||||
@@ -267,6 +298,7 @@ private:
|
|||||||
bool m_cellLevel1MosaicChar[25][40];
|
bool m_cellLevel1MosaicChar[25][40];
|
||||||
int m_cellLevel1CharSet[25][40];
|
int m_cellLevel1CharSet[25][40];
|
||||||
LevelOnePage* m_levelOnePage;
|
LevelOnePage* m_levelOnePage;
|
||||||
|
QList<DRCSPage>* m_drcsPage[2];
|
||||||
int m_fullRowColour[25];
|
int m_fullRowColour[25];
|
||||||
QColor m_fullRowQColor[25];
|
QColor m_fullRowQColor[25];
|
||||||
QList<Invocation> m_invocations[3];
|
QList<Invocation> m_invocations[3];
|
||||||
|
|||||||
82
src/qteletextdecoder/drcspage.cpp
Normal file
82
src/qteletextdecoder/drcspage.cpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2025 Gavin MacGregor
|
||||||
|
*
|
||||||
|
* This file is part of QTeletextMaker.
|
||||||
|
*
|
||||||
|
* QTeletextMaker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QTeletextMaker is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include "drcspage.h"
|
||||||
|
|
||||||
|
DRCSPage::DRCSPage(const PageBase &other)
|
||||||
|
{
|
||||||
|
for (int y=0; y<26; y++)
|
||||||
|
if (other.packetExists(y))
|
||||||
|
setPacket(y, other.packet(y));
|
||||||
|
|
||||||
|
for (int y=26; y<29; y++)
|
||||||
|
for (int d=0; d<16; d++)
|
||||||
|
if (other.packetExists(y, d))
|
||||||
|
setPacket(y, d, other.packet(y, d));
|
||||||
|
|
||||||
|
for (int b=PageBase::C4ErasePage; b<=PageBase::C14NOS; b++)
|
||||||
|
setControlBit(b, other.controlBit(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
int DRCSPage::drcsMode(int c) const
|
||||||
|
{
|
||||||
|
if (!packetExists(28, 3))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const QByteArray pkt = packet(28, 3);
|
||||||
|
|
||||||
|
// Some tricky bit juggling to extract 4 bits from part of a 6-bit triplet
|
||||||
|
switch (c % 3) {
|
||||||
|
case 0:
|
||||||
|
return pkt.at(c/3*2 + 4) & 0xf;
|
||||||
|
case 1:
|
||||||
|
return ((pkt.at((c-1)/3*2 + 4) & 0x30) >> 4) | ((pkt.at((c-1)/3*2 + 5) & 0x3) << 2);
|
||||||
|
case 2:
|
||||||
|
return pkt.at(((c-2)/3*2 + 5) & 0x3f) >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Won't get here; used to suppress a compiler warning
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DRCSPage::ptu(int c, uchar *data) const
|
||||||
|
{
|
||||||
|
const int pktNo = (c+2)/2;
|
||||||
|
|
||||||
|
if (!packetExists(pktNo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int start = c%2 * 20;
|
||||||
|
|
||||||
|
// FIXME should we check all 20 D-bytes for SPACE instead of just the first D-byte?
|
||||||
|
if (packet(pktNo).at(start) < 0x40)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (data != nullptr) {
|
||||||
|
const int end = start + 20;
|
||||||
|
|
||||||
|
for (int i=start, j=0; i<end; i+=2, j+=2) {
|
||||||
|
data[j] = ((packet(pktNo).at(i) & 0x3f) << 2) | ((packet(pktNo).at(i+1) & 0x30) >> 4);
|
||||||
|
data[j+1] = (packet(pktNo).at(i+1) & 0x0f) << 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
40
src/qteletextdecoder/drcspage.h
Normal file
40
src/qteletextdecoder/drcspage.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020-2025 Gavin MacGregor
|
||||||
|
*
|
||||||
|
* This file is part of QTeletextMaker.
|
||||||
|
*
|
||||||
|
* QTeletextMaker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QTeletextMaker is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRCSPAGE_H
|
||||||
|
#define DRCSPAGE_H
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
#include "pagebase.h"
|
||||||
|
|
||||||
|
class DRCSPage : public PageBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DRCSPage(const PageBase &other);
|
||||||
|
|
||||||
|
// TODO PFNormalPOP as well?
|
||||||
|
PageFunctionEnum pageFunction() const { return PFGlobalPOP; }
|
||||||
|
PacketCodingEnum packetCoding() const override { return Coding7bit; }
|
||||||
|
|
||||||
|
int drcsMode(int c) const;
|
||||||
|
bool ptu(int c, uchar *data) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -191,6 +191,23 @@ inline void TeletextPageRender::drawCharacter(QPainter &painter, int r, int c, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool TeletextPageRender::drawDRCSCharacter(QPainter &painter, int r, int c, TeletextPageDecode::DRCSSource drcsSource, int drcsSubTable, int drcsChar, TeletextPageDecode::CharacterFragment characterFragment, bool flashPhOn)
|
||||||
|
{
|
||||||
|
QImage drcsImage = m_decoder->drcsImage(drcsSource, drcsSubTable, drcsChar, flashPhOn);
|
||||||
|
|
||||||
|
if (drcsImage.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (drcsImage.format() == QImage::Format_Mono)
|
||||||
|
// mode 0 (12x10x1) returned here has no colours of its own
|
||||||
|
// so apply the foreground and background colours of the cell it appears in
|
||||||
|
drcsImage.setColorTable(QVector<QRgb>{m_backgroundQColor.rgba(), m_foregroundQColor.rgba()});
|
||||||
|
|
||||||
|
drawFromBitmap(painter, r, c, drcsImage, characterFragment);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
inline void TeletextPageRender::drawBoldOrItalicCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
|
inline void TeletextPageRender::drawBoldOrItalicCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment)
|
||||||
{
|
{
|
||||||
QImage styledImage = QImage(12, 10, QImage::Format_Mono);
|
QImage styledImage = QImage(12, 10, QImage::Format_Mono);
|
||||||
@@ -306,7 +323,7 @@ void TeletextPageRender::renderRow(int r, int ph, bool force)
|
|||||||
drawCharacter(painter, r, c, 0x00, 0, 0, m_decoder->cellCharacterFragment(r, c));
|
drawCharacter(painter, r, c, 0x00, 0, 0, m_decoder->cellCharacterFragment(r, c));
|
||||||
else if (concealed)
|
else if (concealed)
|
||||||
drawCharacter(painter, r, c, 0x20, 0, 0, m_decoder->cellCharacterFragment(r, c));
|
drawCharacter(painter, r, c, 0x20, 0, 0, m_decoder->cellCharacterFragment(r, c));
|
||||||
else
|
else if (m_decoder->cellDrcsSource(r, c) == TeletextPageDecode::NoDRCS || !drawDRCSCharacter(painter, r, c, m_decoder->cellDrcsSource(r, c), m_decoder->cellDrcsSubTable(r, c), m_decoder->cellDrcsCharacter(r, c), m_decoder->cellCharacterFragment(r, c), flashPhOn))
|
||||||
drawCharacter(painter, r, c, m_decoder->cellCharacterCode(r, c), m_decoder->cellCharacterSet(r, c), m_decoder->cellCharacterDiacritical(r, c), m_decoder->cellCharacterFragment(r, c));
|
drawCharacter(painter, r, c, m_decoder->cellCharacterCode(r, c), m_decoder->cellCharacterSet(r, c), m_decoder->cellCharacterDiacritical(r, c), m_decoder->cellCharacterFragment(r, c));
|
||||||
|
|
||||||
if (m_showControlCodes && c < 40 && m_decoder->teletextPage()->character(r, c) < 0x20) {
|
if (m_showControlCodes && c < 40 && m_decoder->teletextPage()->character(r, c) < 0x20) {
|
||||||
@@ -422,6 +439,13 @@ void TeletextPageRender::colourChanged(int index)
|
|||||||
if (m_decoder->cellFlashMode(r, c) == 3 && ((m_decoder->cellForegroundCLUT(r, c) ^ 8) == index || (m_decoder->cellForegroundCLUT(r, c) ^ 8) == 8))
|
if (m_decoder->cellFlashMode(r, c) == 3 && ((m_decoder->cellForegroundCLUT(r, c) ^ 8) == index || (m_decoder->cellForegroundCLUT(r, c) ^ 8) == 8))
|
||||||
m_decoder->setRefresh(r, c, true);
|
m_decoder->setRefresh(r, c, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_decoder->level() == 3)
|
||||||
|
// TODO don't refresh mode 0 DRCS
|
||||||
|
for (int r=0; r<25; r++)
|
||||||
|
for (int c=0; c<72; c++)
|
||||||
|
if (m_decoder->cellDrcsSource(r, c) != TeletextPageDecode::NoDRCS)
|
||||||
|
m_decoder->setRefresh(r, c, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeletextPageRender::setReveal(bool reveal)
|
void TeletextPageRender::setReveal(bool reveal)
|
||||||
|
|||||||
@@ -79,9 +79,10 @@ protected:
|
|||||||
int m_flashingRow[25];
|
int m_flashingRow[25];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void drawFromBitmap(QPainter &, int, int, const QImage, TeletextPageDecode::CharacterFragment);
|
inline void drawFromBitmap(QPainter &painter, int r, int c, const QImage image, TeletextPageDecode::CharacterFragment characterFragment);
|
||||||
inline void drawFromFontBitmap(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment);
|
inline void drawFromFontBitmap(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment);
|
||||||
inline void drawCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, int characterDiacritical, TeletextPageDecode::CharacterFragment characterFragment);
|
inline void drawCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, int characterDiacritical, TeletextPageDecode::CharacterFragment characterFragment);
|
||||||
|
inline bool drawDRCSCharacter(QPainter &painter, int r, int c, TeletextPageDecode::DRCSSource drcsSource, int drcsSubTable, int drcsChar, TeletextPageDecode::CharacterFragment characterFragment, bool flashPhOn = true);
|
||||||
inline void drawBoldOrItalicCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment);
|
inline void drawBoldOrItalicCharacter(QPainter &painter, int r, int c, unsigned char characterCode, int characterSet, TeletextPageDecode::CharacterFragment characterFragment);
|
||||||
void renderRow(int r, int ph, bool force=false);
|
void renderRow(int r, int ph, bool force=false);
|
||||||
void setRowFlashStatus(int r, int rowFlashHz);
|
void setRowFlashStatus(int r, int rowFlashHz);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include "drcspage.h"
|
||||||
#include "hashformats.h"
|
#include "hashformats.h"
|
||||||
#include "levelonecommands.h"
|
#include "levelonecommands.h"
|
||||||
#include "loadformats.h"
|
#include "loadformats.h"
|
||||||
@@ -378,6 +380,8 @@ void MainWindow::init()
|
|||||||
connect(m_textScene, &LevelOneScene::mouseZoomIn, this, &MainWindow::zoomIn);
|
connect(m_textScene, &LevelOneScene::mouseZoomIn, this, &MainWindow::zoomIn);
|
||||||
connect(m_textScene, &LevelOneScene::mouseZoomOut, this, &MainWindow::zoomOut);
|
connect(m_textScene, &LevelOneScene::mouseZoomOut, this, &MainWindow::zoomOut);
|
||||||
|
|
||||||
|
connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &MainWindow::updateWatchedFile);
|
||||||
|
|
||||||
QShortcut *blockShortCut = new QShortcut(QKeySequence(Qt::Key_Escape, Qt::Key_J), m_textView);
|
QShortcut *blockShortCut = new QShortcut(QKeySequence(Qt::Key_Escape, Qt::Key_J), m_textView);
|
||||||
connect(blockShortCut, &QShortcut::activated, [=]() { m_textWidget->setCharacter(0x7f); });
|
connect(blockShortCut, &QShortcut::activated, [=]() { m_textWidget->setCharacter(0x7f); });
|
||||||
|
|
||||||
@@ -678,6 +682,35 @@ void MainWindow::createActions()
|
|||||||
zoomResetAct->setStatusTip(tr("Reset zoom level"));
|
zoomResetAct->setStatusTip(tr("Reset zoom level"));
|
||||||
connect(zoomResetAct, &QAction::triggered, this, &MainWindow::zoomReset);
|
connect(zoomResetAct, &QAction::triggered, this, &MainWindow::zoomReset);
|
||||||
|
|
||||||
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
|
QMenu *drcsSubMenu = viewMenu->addMenu(tr("DRCS pages"));
|
||||||
|
m_drcsSeparator[1] = drcsSubMenu->addSeparator();
|
||||||
|
m_drcsSeparator[1]->setText("Global DRCS");
|
||||||
|
QAction *gDrcsFileSelect = drcsSubMenu->addAction(tr("Load file..."));
|
||||||
|
gDrcsFileSelect->setStatusTip(tr("Load a file to use for Global DRCS definitions"));
|
||||||
|
connect(gDrcsFileSelect, &QAction::triggered, [=]() { loadDRCSFile(1); });
|
||||||
|
m_drcsClear[1] = drcsSubMenu->addAction(tr("Clear"));
|
||||||
|
m_drcsClear[1]->setStatusTip(tr("Clear Global DRCS definitions"));
|
||||||
|
m_drcsClear[1]->setEnabled(false);
|
||||||
|
connect(m_drcsClear[1], &QAction::triggered, [=]() { clearDRCSFile(1); });
|
||||||
|
|
||||||
|
m_drcsSeparator[0] = drcsSubMenu->addSeparator();
|
||||||
|
m_drcsSeparator[0]->setText("Normal DRCS");
|
||||||
|
QAction *nDrcsFileSelect = drcsSubMenu->addAction(tr("Load file..."));
|
||||||
|
nDrcsFileSelect->setStatusTip(tr("Load a file to use for Normal DRCS definitions"));
|
||||||
|
connect(nDrcsFileSelect, &QAction::triggered, [=]() { loadDRCSFile(0); });
|
||||||
|
m_drcsClear[0] = drcsSubMenu->addAction(tr("Clear"));
|
||||||
|
m_drcsClear[0]->setStatusTip(tr("Clear Normal DRCS definitions"));
|
||||||
|
m_drcsClear[0]->setEnabled(false);
|
||||||
|
connect(m_drcsClear[0], &QAction::triggered, [=]() { clearDRCSFile(0); });
|
||||||
|
|
||||||
|
drcsSubMenu->addSeparator();
|
||||||
|
m_drcsSwap = drcsSubMenu->addAction(tr("Swap Global and Normal"));
|
||||||
|
m_drcsSwap->setStatusTip(tr("Swap the files used for Global and Normal DRCS definitions"));
|
||||||
|
m_drcsSwap->setEnabled(false);
|
||||||
|
connect(m_drcsSwap, &QAction::triggered, this, &MainWindow::swapDRCS);
|
||||||
|
|
||||||
QMenu *insertMenu = menuBar()->addMenu(tr("&Insert"));
|
QMenu *insertMenu = menuBar()->addMenu(tr("&Insert"));
|
||||||
|
|
||||||
QMenu *alphaColourSubMenu = insertMenu->addMenu(tr("Alphanumeric colour"));
|
QMenu *alphaColourSubMenu = insertMenu->addMenu(tr("Alphanumeric colour"));
|
||||||
@@ -900,6 +933,119 @@ void MainWindow::zoomReset()
|
|||||||
m_zoomSlider->setValue(2);
|
m_zoomSlider->setValue(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::loadDRCSFile(int drcsType, QString fileName)
|
||||||
|
{
|
||||||
|
const QString drcsTypeName = drcsType == 1 ? "Global DRCS" : "Normal DRCS";
|
||||||
|
|
||||||
|
const bool updatingWatched = !fileName.isEmpty();
|
||||||
|
|
||||||
|
if (!updatingWatched)
|
||||||
|
fileName = QFileDialog::getOpenFileName(this, tr("Select %1 file").arg(drcsTypeName), m_drcsFileName[drcsType], m_loadFormats.filters());
|
||||||
|
|
||||||
|
if (!fileName.isEmpty()) {
|
||||||
|
QFile file(fileName);
|
||||||
|
|
||||||
|
LoadFormat *loadingFormat = m_loadFormats.findFormat(QFileInfo(fileName).suffix());
|
||||||
|
if (loadingFormat == nullptr) {
|
||||||
|
if (updatingWatched)
|
||||||
|
clearDRCSFile(drcsType);
|
||||||
|
else
|
||||||
|
QMessageBox::warning(this, QApplication::applicationDisplayName(), tr("Cannot load file %1:\nUnknown file format or extension").arg(QDir::toNativeSeparators(fileName)));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.open(QFile::ReadOnly)) {
|
||||||
|
if (updatingWatched)
|
||||||
|
clearDRCSFile(drcsType);
|
||||||
|
else
|
||||||
|
QMessageBox::warning(this, QApplication::applicationDisplayName(), tr("Cannot read file %1:\n%2.").arg(QDir::toNativeSeparators(fileName), file.errorString()));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<PageBase> loadedPages;
|
||||||
|
|
||||||
|
if (loadingFormat->load(&file, loadedPages, nullptr)) {
|
||||||
|
if (!m_drcsFileName[drcsType].isEmpty())
|
||||||
|
m_fileWatcher.removePath(m_drcsFileName[drcsType]);
|
||||||
|
|
||||||
|
m_textWidget->pageDecode()->clearDRCSPage((TeletextPageDecode::DRCSPageType)drcsType);
|
||||||
|
m_drcsPage[drcsType].clear();
|
||||||
|
|
||||||
|
for (int i=0; i<loadedPages.size(); i++)
|
||||||
|
m_drcsPage[drcsType].append(loadedPages.at(i));
|
||||||
|
|
||||||
|
m_textWidget->pageDecode()->setDRCSPage((TeletextPageDecode::DRCSPageType)drcsType, &m_drcsPage[drcsType]);
|
||||||
|
m_textWidget->refreshPage();
|
||||||
|
|
||||||
|
m_fileWatcher.addPath(fileName);
|
||||||
|
m_drcsFileName[drcsType] = fileName;
|
||||||
|
m_drcsSeparator[drcsType]->setText(QString("%1: %2").arg(drcsTypeName).arg(QFileInfo(fileName).fileName()));
|
||||||
|
m_drcsClear[drcsType]->setEnabled(true);
|
||||||
|
m_drcsSwap->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
if (updatingWatched)
|
||||||
|
clearDRCSFile(drcsType);
|
||||||
|
else
|
||||||
|
QMessageBox::warning(this, QApplication::applicationDisplayName(), tr("Cannot load file %1\n%2").arg(QDir::toNativeSeparators(fileName), loadingFormat->errorString()));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::clearDRCSFile(int drcsType)
|
||||||
|
{
|
||||||
|
m_fileWatcher.removePath(m_drcsFileName[drcsType]);
|
||||||
|
|
||||||
|
m_textWidget->pageDecode()->clearDRCSPage((TeletextPageDecode::DRCSPageType)drcsType);
|
||||||
|
m_drcsPage[drcsType].clear();
|
||||||
|
|
||||||
|
m_textWidget->refreshPage();
|
||||||
|
|
||||||
|
m_drcsFileName[drcsType].clear();
|
||||||
|
m_drcsSeparator[drcsType]->setText(drcsType == 1 ? "Global DRCS" : "Normal DRCS");
|
||||||
|
m_drcsClear[drcsType]->setEnabled(false);
|
||||||
|
m_drcsSwap->setEnabled(m_drcsClear[0]->isEnabled() || m_drcsClear[1]->isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::swapDRCS()
|
||||||
|
{
|
||||||
|
m_drcsPage[0].swap(m_drcsPage[1]);
|
||||||
|
m_drcsFileName[0].swap(m_drcsFileName[1]);
|
||||||
|
|
||||||
|
for (int i=0; i<2; i++) {
|
||||||
|
const QString drcsTypeName = i == 1 ? "Global DRCS" : "Normal DRCS";
|
||||||
|
|
||||||
|
if (m_drcsPage[i].isEmpty()) {
|
||||||
|
m_textWidget->pageDecode()->clearDRCSPage((TeletextPageDecode::DRCSPageType)i);
|
||||||
|
m_drcsSeparator[i]->setText(drcsTypeName);
|
||||||
|
} else {
|
||||||
|
m_textWidget->pageDecode()->setDRCSPage((TeletextPageDecode::DRCSPageType)i, &m_drcsPage[i]);
|
||||||
|
m_drcsSeparator[i]->setText(QString("%1: %2").arg(drcsTypeName).arg(QFileInfo(m_drcsFileName[i]).fileName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_drcsClear[i]->setEnabled(!m_drcsPage[i].isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_textWidget->refreshPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateWatchedFile(const QString &path)
|
||||||
|
{
|
||||||
|
int drcsType;
|
||||||
|
|
||||||
|
if (path == m_drcsFileName[1])
|
||||||
|
drcsType = 1;
|
||||||
|
else if (path == m_drcsFileName[0])
|
||||||
|
drcsType = 0;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadDRCSFile(drcsType, path);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::toggleInsertMode()
|
void MainWindow::toggleInsertMode()
|
||||||
{
|
{
|
||||||
m_textWidget->setInsertMode(!m_textWidget->insertMode());
|
m_textWidget->setInsertMode(!m_textWidget->insertMode());
|
||||||
|
|||||||
@@ -21,15 +21,18 @@
|
|||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QGraphicsProxyWidget>
|
#include <QGraphicsProxyWidget>
|
||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
#include <QGraphicsView>
|
#include <QGraphicsView>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QList>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSlider>
|
#include <QSlider>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include "drcspage.h"
|
||||||
#include "loadformats.h"
|
#include "loadformats.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "pagecomposelinksdockwidget.h"
|
#include "pagecomposelinksdockwidget.h"
|
||||||
@@ -89,6 +92,12 @@ private slots:
|
|||||||
void zoomSet(int viewZoom);
|
void zoomSet(int viewZoom);
|
||||||
void zoomReset();
|
void zoomReset();
|
||||||
|
|
||||||
|
void loadDRCSFile(int drcsType, QString fileName = "");
|
||||||
|
void clearDRCSFile(int drcsType);
|
||||||
|
void swapDRCS();
|
||||||
|
|
||||||
|
void updateWatchedFile(const QString &path);
|
||||||
|
|
||||||
void toggleInsertMode();
|
void toggleInsertMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -115,6 +124,10 @@ private:
|
|||||||
LevelOneScene *m_textScene;
|
LevelOneScene *m_textScene;
|
||||||
QGraphicsView *m_textView;
|
QGraphicsView *m_textView;
|
||||||
|
|
||||||
|
QList<DRCSPage> m_drcsPage[2];
|
||||||
|
QString m_drcsFileName[2];
|
||||||
|
QFileSystemWatcher m_fileWatcher;
|
||||||
|
|
||||||
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
||||||
bool m_viewSmoothTransform;
|
bool m_viewSmoothTransform;
|
||||||
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
||||||
@@ -128,10 +141,11 @@ private:
|
|||||||
QAction *m_recentFileSubMenuAct;
|
QAction *m_recentFileSubMenuAct;
|
||||||
QAction *m_exportAutoAct;
|
QAction *m_exportAutoAct;
|
||||||
QAction *m_deleteSubPageAction;
|
QAction *m_deleteSubPageAction;
|
||||||
|
QAction *m_rowZeroAct;
|
||||||
QAction *m_borderActs[3];
|
QAction *m_borderActs[3];
|
||||||
QAction *m_aspectRatioActs[4];
|
QAction *m_aspectRatioActs[4];
|
||||||
QAction *m_smoothTransformAction;
|
QAction *m_smoothTransformAction;
|
||||||
QAction *m_rowZeroAct;
|
QAction *m_drcsSeparator[2], *m_drcsClear[2], *m_drcsSwap;
|
||||||
|
|
||||||
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
||||||
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
||||||
|
|||||||
Reference in New Issue
Block a user