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.
|
||||
- Rendering of teletext pages in Levels 1, 1.5, 2.5 and 3.5
|
||||
- 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.
|
||||
- Editing of X/27/4 and X/27/5 compositional links to enhancement data pages.
|
||||
- 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 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
|
||||
"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 both "Define" and "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 "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.
|
||||
|
||||
50
document.cpp
50
document.cpp
@@ -17,12 +17,46 @@
|
||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <vector>
|
||||
|
||||
#include "document.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()
|
||||
{
|
||||
m_pageNumber = 0x199;
|
||||
@@ -36,10 +70,15 @@ TeletextDocument::TeletextDocument()
|
||||
m_cursorColumn = 0;
|
||||
m_selectionCornerRow = m_selectionCornerColumn = -1;
|
||||
m_selectionSubPage = nullptr;
|
||||
|
||||
m_clutModel = new ClutModel;
|
||||
m_clutModel->setSubPage(m_subPages[0]);
|
||||
}
|
||||
|
||||
TeletextDocument::~TeletextDocument()
|
||||
{
|
||||
delete m_clutModel;
|
||||
|
||||
for (auto &subPage : m_subPages)
|
||||
delete(subPage);
|
||||
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?
|
||||
if (forceRefresh || (newSubPageIndex != m_currentSubPageIndex && newSubPageIndex < m_subPages.size())) {
|
||||
emit aboutToChangeSubPage();
|
||||
|
||||
m_currentSubPageIndex = newSubPageIndex;
|
||||
|
||||
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||
emit subPageSelected();
|
||||
emit selectionMoved();
|
||||
return;
|
||||
@@ -83,7 +125,10 @@ void TeletextDocument::selectSubPageNext()
|
||||
{
|
||||
if (m_currentSubPageIndex < m_subPages.size()-1) {
|
||||
emit aboutToChangeSubPage();
|
||||
|
||||
m_currentSubPageIndex++;
|
||||
|
||||
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||
emit subPageSelected();
|
||||
emit selectionMoved();
|
||||
}
|
||||
@@ -93,7 +138,10 @@ void TeletextDocument::selectSubPagePrevious()
|
||||
{
|
||||
if (m_currentSubPageIndex > 0) {
|
||||
emit aboutToChangeSubPage();
|
||||
|
||||
m_currentSubPageIndex--;
|
||||
|
||||
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||
emit subPageSelected();
|
||||
emit selectionMoved();
|
||||
}
|
||||
@@ -116,6 +164,8 @@ void TeletextDocument::insertSubPage(int beforeSubPageIndex, bool copySubPage)
|
||||
|
||||
void TeletextDocument::deleteSubPage(int subPageToDelete)
|
||||
{
|
||||
m_clutModel->setSubPage(nullptr);
|
||||
|
||||
delete(m_subPages[subPageToDelete]);
|
||||
m_subPages.erase(m_subPages.begin()+subPageToDelete);
|
||||
}
|
||||
|
||||
19
document.h
19
document.h
@@ -20,11 +20,28 @@
|
||||
#ifndef DOCUMENT_H
|
||||
#define DOCUMENT_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QUndoStack>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -62,6 +79,7 @@ public:
|
||||
void setDescription(QString);
|
||||
void setFastTextLinkPageNumberOnAllSubPages(int, int);
|
||||
QUndoStack *undoStack() const { return m_undoStack; }
|
||||
ClutModel *clutModel() const { return m_clutModel; }
|
||||
int cursorRow() const { return m_cursorRow; }
|
||||
int cursorColumn() const { return m_cursorColumn; }
|
||||
void cursorUp(bool shiftKey=false);
|
||||
@@ -104,6 +122,7 @@ private:
|
||||
QUndoStack *m_undoStack;
|
||||
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
|
||||
LevelOnePage *m_selectionSubPage;
|
||||
ClutModel *m_clutModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,11 +37,11 @@ LevelOnePage::LevelOnePage(const PageBase &other)
|
||||
clearPage();
|
||||
|
||||
for (int i=0; i<26; i++)
|
||||
if (other.packetNeeded(i))
|
||||
if (other.packetExists(i))
|
||||
setPacket(i, other.packet(i));
|
||||
for (int i=26; i<30; i++)
|
||||
for (int j=0; j<16; j++)
|
||||
if (other.packetNeeded(i, j))
|
||||
if (other.packetExists(i, j))
|
||||
setPacket(i, j, other.packet(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);
|
||||
}
|
||||
|
||||
bool LevelOnePage::packetNeeded(int packetNumber) const
|
||||
bool LevelOnePage::packetExists(int packetNumber) const
|
||||
{
|
||||
if (packetNumber <= 24) {
|
||||
for (int c=0; c<40; c++)
|
||||
@@ -263,10 +263,10 @@ bool LevelOnePage::packetNeeded(int packetNumber) const
|
||||
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)
|
||||
return packetFromEnhancementListNeeded(designationCode);
|
||||
@@ -298,7 +298,7 @@ bool LevelOnePage::packetNeeded(int packetNumber, int designationCode) const
|
||||
return !isPaletteDefault(0,15);
|
||||
}
|
||||
|
||||
return PageBase::packetNeeded(packetNumber, designationCode);
|
||||
return PageBase::packetExists(packetNumber, designationCode);
|
||||
}
|
||||
|
||||
bool LevelOnePage::controlBit(int bitNumber) const
|
||||
|
||||
@@ -42,8 +42,8 @@ public:
|
||||
|
||||
QByteArray packet(int) const override;
|
||||
QByteArray packet(int, int) const override;
|
||||
bool packetNeeded(int) const override;
|
||||
bool packetNeeded(int, int) const override;
|
||||
bool packetExists(int) const override;
|
||||
bool packetExists(int, int) const override;
|
||||
bool setPacket(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)
|
||||
{
|
||||
if (document.subPage(p)->packetNeeded(packetNumber)) {
|
||||
if (document.subPage(p)->packetExists(packetNumber)) {
|
||||
QByteArray outLine = document.subPage(p)->packet(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)
|
||||
{
|
||||
if (document.subPage(p)->packetNeeded(packetNumber, designationCode)) {
|
||||
if (document.subPage(p)->packetExists(packetNumber, designationCode)) {
|
||||
QByteArray outLine = document.subPage(p)->packet(packetNumber, designationCode);
|
||||
|
||||
outStream << QString("OL,%1,").arg(packetNumber);
|
||||
@@ -279,7 +279,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
|
||||
// FastText links
|
||||
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
|
||||
// otherwise we write the links as a human-readable FL command later on
|
||||
writeFLCommand = true;
|
||||
@@ -337,7 +337,7 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
|
||||
QByteArray rowPacketAlways(PageBase *subPage, int packetNumber)
|
||||
{
|
||||
if (subPage->packetNeeded(packetNumber))
|
||||
if (subPage->packetExists(packetNumber))
|
||||
return subPage->packet(packetNumber);
|
||||
else
|
||||
return QByteArray(40, ' ');
|
||||
@@ -388,7 +388,7 @@ QString exportHashStringPackets(LevelOnePage *subPage)
|
||||
const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
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
|
||||
// Assemble the duplicate beginning and ending of both packets
|
||||
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);
|
||||
|
||||
if (subPage->packetNeeded(28,0))
|
||||
if (subPage->packetExists(28,0))
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
2
main.cpp
2
main.cpp
@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
|
||||
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
||||
QApplication::setOrganizationName("gkmac.co.uk");
|
||||
QApplication::setOrganizationDomain("gkmac.co.uk");
|
||||
QApplication::setApplicationVersion("0.2-alpha");
|
||||
QApplication::setApplicationVersion("0.4-alpha");
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
|
||||
@@ -140,6 +140,16 @@ void TeletextWidget::timerEvent(QTimerEvent *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)
|
||||
{
|
||||
m_insertMode = insertMode;
|
||||
@@ -654,6 +664,24 @@ void LevelOneScene::toggleGrid(bool 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)
|
||||
{
|
||||
Q_UNUSED(object);
|
||||
|
||||
@@ -64,6 +64,7 @@ public slots:
|
||||
void toggleReveal(bool);
|
||||
void toggleMix(bool);
|
||||
void updateFlashTimer(int);
|
||||
void pauseFlash(bool);
|
||||
void refreshRow(int);
|
||||
|
||||
void setControlBit(int, bool);
|
||||
@@ -119,6 +120,7 @@ public slots:
|
||||
void updateCursor();
|
||||
void updateSelection();
|
||||
void toggleGrid(bool);
|
||||
void hideGUIElements(bool);
|
||||
void setFullScreenColour(const QColor &);
|
||||
void setFullRowColour(int, const QColor &);
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QImage>
|
||||
#include <QList>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSaveFile>
|
||||
@@ -120,6 +122,56 @@ bool MainWindow::saveAs()
|
||||
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()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("http://zxnet.co.uk/teletext/editor/" + exportHashStringPage(m_textWidget->document()->currentSubPage()) + exportHashStringPackets(m_textWidget->document()->currentSubPage())));
|
||||
@@ -166,7 +218,8 @@ void MainWindow::init()
|
||||
|
||||
m_textView = new QGraphicsView(this);
|
||||
m_textView->setScene(m_textScene);
|
||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||
if (m_viewSmoothTransform)
|
||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
||||
setSceneDimensions();
|
||||
setCentralWidget(m_textView);
|
||||
@@ -257,14 +310,18 @@ void MainWindow::createActions()
|
||||
|
||||
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"));
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
fileMenu->addSeparator();
|
||||
@@ -418,6 +475,13 @@ void MainWindow::createActions()
|
||||
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"));
|
||||
zoomInAct->setShortcuts(QKeySequence::ZoomIn);
|
||||
zoomInAct->setStatusTip(tr("Zoom in"));
|
||||
@@ -555,7 +619,6 @@ void MainWindow::createActions()
|
||||
|
||||
void MainWindow::setSceneDimensions()
|
||||
{
|
||||
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
||||
const int topBottomBorders[3] = { 0, 10, 19 };
|
||||
const int pillarBoxSizes[3] = { 672, 720, 854 };
|
||||
const int leftRightBorders[3] = { 0, 24, 77 };
|
||||
@@ -612,6 +675,15 @@ void MainWindow::setAspectRatio(int newViewAspectRatio)
|
||||
setSceneDimensions();
|
||||
}
|
||||
|
||||
void MainWindow::setSmoothTransform(bool smoothTransform)
|
||||
{
|
||||
m_viewSmoothTransform = smoothTransform;
|
||||
if (smoothTransform)
|
||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||
else
|
||||
m_textView->setRenderHints({ });
|
||||
}
|
||||
|
||||
void MainWindow::zoomIn()
|
||||
{
|
||||
if (m_viewZoom < 4)
|
||||
@@ -705,6 +777,10 @@ void MainWindow::readSettings()
|
||||
m_viewAspectRatio = settings.value("aspectratio", 0).toInt();
|
||||
m_viewAspectRatio = (m_viewAspectRatio < 0 || m_viewAspectRatio > 2) ? 0 : m_viewAspectRatio;
|
||||
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 = (m_viewZoom < 0 || m_viewZoom > 4) ? 2 : m_viewZoom;
|
||||
|
||||
@@ -743,6 +819,7 @@ void MainWindow::writeSettings()
|
||||
settings.setValue("windowState", saveState());
|
||||
settings.setValue("border", m_viewBorder);
|
||||
settings.setValue("aspectratio", m_viewAspectRatio);
|
||||
settings.setValue("smoothTransform", m_viewSmoothTransform);
|
||||
settings.setValue("zoom", m_viewZoom);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ private slots:
|
||||
void open();
|
||||
bool save();
|
||||
bool saveAs();
|
||||
void exportPNG();
|
||||
void exportZXNet();
|
||||
void exportEditTF();
|
||||
void updateRecentFileActions();
|
||||
@@ -74,6 +75,7 @@ private slots:
|
||||
void setSceneDimensions();
|
||||
void setBorder(int);
|
||||
void setAspectRatio(int);
|
||||
void setSmoothTransform(bool);
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void zoomReset();
|
||||
@@ -82,6 +84,7 @@ private slots:
|
||||
|
||||
private:
|
||||
enum { m_MaxRecentFiles = 10 };
|
||||
const float aspectRatioHorizontalScaling[4] = { 0.6, 0.6, 0.8, 0.5 };
|
||||
|
||||
void init();
|
||||
void createActions();
|
||||
@@ -104,6 +107,7 @@ private:
|
||||
QGraphicsView *m_textView;
|
||||
|
||||
int m_viewBorder, m_viewAspectRatio, m_viewZoom;
|
||||
bool m_viewSmoothTransform;
|
||||
PageOptionsDockWidget *m_pageOptionsDockWidget;
|
||||
PageEnhancementsDockWidget *m_pageEnhancementsDockWidget;
|
||||
X26DockWidget *m_x26DockWidget;
|
||||
@@ -115,6 +119,7 @@ private:
|
||||
QAction *m_deleteSubPageAction;
|
||||
QAction *m_borderActs[3];
|
||||
QAction *m_aspectRatioActs[4];
|
||||
QAction *m_smoothTransformAction;
|
||||
|
||||
QLabel *m_subPageLabel, *m_cursorPositionLabel;
|
||||
QToolButton *m_previousSubPageButton, *m_nextSubPageButton;
|
||||
|
||||
@@ -37,8 +37,8 @@ public:
|
||||
|
||||
virtual QByteArray packet(int) const;
|
||||
virtual QByteArray packet(int, int) const;
|
||||
virtual bool packetNeeded(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) const { return m_displayPackets[i] != 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, int, QByteArray);
|
||||
// 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 row colour")), 1, 0, 1, 1);
|
||||
m_defaultScreenColourCombo = new QComboBox;
|
||||
m_defaultScreenColourCombo->setModel(m_parentMainWidget->document()->clutModel());
|
||||
m_defaultRowColourCombo = new QComboBox;
|
||||
for (int r=0; r<=3; r++)
|
||||
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));
|
||||
}
|
||||
m_defaultRowColourCombo->setModel(m_parentMainWidget->document()->clutModel());
|
||||
x28Layout->addWidget(m_defaultScreenColourCombo, 0, 1, 1, 1, Qt::AlignTop);
|
||||
connect(m_defaultScreenColourCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index){ m_parentMainWidget->setDefaultScreenColour(index); });
|
||||
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.setMode(packetContents.at(i*3+2) & 0x1f);
|
||||
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)
|
||||
// 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
|
||||
|
||||
public:
|
||||
QList<X26Triplet> *enhancements() { return &m_enhancements; };
|
||||
X26TripletList *enhancements() { return &m_enhancements; };
|
||||
virtual int maxEnhancements() const =0;
|
||||
|
||||
protected:
|
||||
@@ -39,7 +39,7 @@ protected:
|
||||
void setEnhancementListFromPacket(int, QByteArray);
|
||||
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 };
|
||||
};
|
||||
|
||||
|
||||
30
render.cpp
30
render.cpp
@@ -26,11 +26,26 @@
|
||||
|
||||
#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()
|
||||
{
|
||||
QPainter pixmapPainter;
|
||||
|
||||
m_fontBitmap = new QBitmap(":/images/teletextfont.png");
|
||||
for (int i=0; i<6; i++)
|
||||
m_pagePixmap[i] = new QPixmap(864, 250);
|
||||
m_pagePixmap[0]->fill(Qt::transparent);
|
||||
@@ -58,7 +73,6 @@ TeletextPageRender::~TeletextPageRender()
|
||||
}
|
||||
for (int i=0; i<6; i++)
|
||||
delete m_pagePixmap[i];
|
||||
delete m_fontBitmap;
|
||||
}
|
||||
|
||||
void TeletextPageRender::setTeletextPage(LevelOnePage *newCurrentPage)
|
||||
@@ -259,7 +273,7 @@ void TeletextPageRender::decodePage()
|
||||
setFullRowColour(r ,downwardsFullRowColour);
|
||||
|
||||
m_textLayer[1]->enhanceMap.clear();
|
||||
if (m_renderLevel == 0 || m_levelOnePage->enhancements()->empty())
|
||||
if (m_renderLevel == 0 || m_levelOnePage->enhancements()->isEmpty())
|
||||
return;
|
||||
|
||||
m_textLayer[1]->setFullScreenColour(-1);
|
||||
@@ -348,10 +362,10 @@ void TeletextPageRender::renderPage(int r)
|
||||
pixmapPainter.setBackground(QBrush(backQColour));
|
||||
}
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
pixmapPainter.setBackground(QBrush(QColor(0, 0, 0, 128)));
|
||||
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)
|
||||
|
||||
16
render.h
16
render.h
@@ -179,6 +179,18 @@ private:
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -189,6 +201,8 @@ public:
|
||||
void decodePage();
|
||||
void renderPage();
|
||||
void renderPage(int r);
|
||||
bool mix() const { return m_mix; };
|
||||
bool showCodes() const { return m_showCodes; };
|
||||
void setTeletextPage(LevelOnePage *);
|
||||
void updateSidePanels();
|
||||
void buildEnhanceMap(TextLayer *, int=0);
|
||||
@@ -215,7 +229,7 @@ protected:
|
||||
inline void setFullScreenColour(int);
|
||||
inline void setFullRowColour(int, int);
|
||||
|
||||
QBitmap* m_fontBitmap;
|
||||
TeletextFontBitmap m_fontBitmap;
|
||||
QPixmap* m_pagePixmap[6];
|
||||
int m_finalFullScreenColour, m_renderLevel;
|
||||
QColor m_finalFullScreenQColor;
|
||||
|
||||
@@ -164,7 +164,7 @@ void EditTripletCommand::redo()
|
||||
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
||||
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_teletextDocument->emit refreshNeeded();
|
||||
|
||||
@@ -179,7 +179,7 @@ void EditTripletCommand::undo()
|
||||
if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex)
|
||||
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_teletextDocument->emit refreshNeeded();
|
||||
m_teletextDocument->emit tripletCommandHighlight(m_row);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QActionGroup>
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
@@ -34,8 +35,42 @@
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "render.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)
|
||||
{
|
||||
QVBoxLayout *x26Layout = new QVBoxLayout;
|
||||
@@ -161,9 +196,7 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
|
||||
QHBoxLayout *colourAndRowLayout = new QHBoxLayout;
|
||||
|
||||
m_colourComboBox = new QComboBox;
|
||||
for (int c=0; c<=3; c++)
|
||||
for (int e=0; e<=7; e++)
|
||||
m_colourComboBox->addItem(tr("CLUT %1:%2").arg(c).arg(e));
|
||||
m_colourComboBox->setModel(m_parentMainWidget->document()->clutModel());
|
||||
colourAndRowLayout->addWidget(m_colourComboBox);
|
||||
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;
|
||||
|
||||
m_characterCodeComboBox = new QComboBox;
|
||||
for (int i=32; i<128; i++)
|
||||
m_characterCodeComboBox->addItem(QString("0x%1").arg(i, 2, 16), i);
|
||||
m_characterCodeComboBox->setModel(&m_characterListModel);
|
||||
characterCodeLayout->addWidget(m_characterCodeComboBox);
|
||||
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
|
||||
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->setMaximum(15);
|
||||
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeSpinBox);
|
||||
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->setMaximum(12);
|
||||
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberSpinBox);
|
||||
@@ -705,7 +739,16 @@ void X26DockWidget::updateCookedTripletParameters(const QModelIndex &index)
|
||||
case 0x29: // G0 character
|
||||
case 0x2b: // G3 character at Level 2.5
|
||||
case 0x2f ... 0x3f: // G2 character, G0 character with diacritical
|
||||
// TODO non-Latin G0 and G2 character sets
|
||||
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->blockSignals(false);
|
||||
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!
|
||||
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) {
|
||||
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_invokeObjectSourceStackedLayout->setCurrentIndex(0);
|
||||
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(true);
|
||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(true);
|
||||
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_invokeLocalObjectDesignationCodeSpinBox->blockSignals(false);
|
||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(false);
|
||||
if (tripletLocationWidgetsVisible) {
|
||||
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(true);
|
||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(true);
|
||||
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_invokeLocalObjectDesignationCodeSpinBox->blockSignals(false);
|
||||
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(false);
|
||||
}
|
||||
} else { // if (triplet.objectSource() != X26Triplet::IllegalObjectSource) {
|
||||
m_objectSourceComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
|
||||
m_invokeObjectSourceStackedLayout->setCurrentIndex(1);
|
||||
@@ -849,28 +900,36 @@ void X26DockWidget::updateCookedTripletParameters(const QModelIndex &index)
|
||||
// Now deal with cooked row and column spinboxes
|
||||
m_cookedRowSpinBox->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()) {
|
||||
m_cookedRowSpinBox->setEnabled(false);
|
||||
m_cookedRowSpinBox->setValue(0);
|
||||
m_cookedRowSpinBox->setPrefix("");
|
||||
} else {
|
||||
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);
|
||||
else
|
||||
m_cookedRowSpinBox->setPrefix("+");
|
||||
} else {
|
||||
m_cookedRowSpinBox->setRange(1, 24);
|
||||
m_cookedRowSpinBox->setPrefix("");
|
||||
}
|
||||
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()) {
|
||||
m_cookedColumnSpinBox->setEnabled(false);
|
||||
m_cookedColumnSpinBox->setValue(0);
|
||||
m_cookedColumnSpinBox->setPrefix("");
|
||||
} else {
|
||||
m_cookedColumnSpinBox->setEnabled(true);
|
||||
if (index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole) == 0x10)
|
||||
if (modeExt == 0x10) {
|
||||
m_cookedColumnSpinBox->setMaximum(71);
|
||||
else
|
||||
m_cookedColumnSpinBox->setPrefix("+");
|
||||
} else {
|
||||
m_cookedColumnSpinBox->setMaximum(39);
|
||||
m_cookedColumnSpinBox->setPrefix("");
|
||||
}
|
||||
m_cookedColumnSpinBox->setValue(columnVariant.toInt());
|
||||
}
|
||||
m_cookedRowSpinBox->blockSignals(false);
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
#ifndef X26DOCKWIDGET_H
|
||||
#define X26DOCKWIDGET_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDockWidget>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
@@ -31,8 +33,25 @@
|
||||
#include <QTableView>
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "render.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
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -63,6 +82,7 @@ public slots:
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
CharacterListModel m_characterListModel;
|
||||
|
||||
private:
|
||||
QTableView *m_x26View;
|
||||
@@ -79,6 +99,7 @@ private:
|
||||
QComboBox *m_textSizeComboBox;
|
||||
QCheckBox *m_displayAttributeBoxingCheckBox, *m_displayAttributeConcealCheckBox, *m_displayAttributeInvertCheckBox, *m_displayAttributeUnderlineCheckBox;
|
||||
QComboBox *m_objectSourceComboBox, *m_objectRequiredAtLevelsComboBox;
|
||||
QLabel *m_invokeLocalObjectDesignationCodeLabel, *m_invokeLocalObjectTripletNumberLabel;
|
||||
QSpinBox *m_invokeLocalObjectDesignationCodeSpinBox, *m_invokeLocalObjectTripletNumberSpinBox;
|
||||
QSpinBox *m_invokePOPSubPageSpinBox, *m_invokePOPPacketNumberSpinBox;
|
||||
QComboBox *m_invokePOPTripletNumberComboBox, *m_invokePOPPointerBitsComboBox;
|
||||
|
||||
725
x26model.cpp
725
x26model.cpp
File diff suppressed because it is too large
Load Diff
149
x26model.h
149
x26model.h
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "mainwidget.h"
|
||||
#include "render.h"
|
||||
|
||||
class X26Model : public QAbstractListModel
|
||||
{
|
||||
@@ -49,87 +50,99 @@ public:
|
||||
private:
|
||||
TeletextWidget *m_parentMainWidget;
|
||||
bool m_listLoaded;
|
||||
};
|
||||
TeletextFontBitmap m_fontBitmap;
|
||||
|
||||
static const QString modeTripletName[64] {
|
||||
// Row triplet modes
|
||||
"Full screen colour",
|
||||
"Full row colour",
|
||||
"Reserved 0x02",
|
||||
"Reserved 0x03",
|
||||
const QString m_modeTripletName[64] {
|
||||
// Row triplet modes
|
||||
"Full screen colour",
|
||||
"Full row colour",
|
||||
"Reserved 0x02",
|
||||
"Reserved 0x03",
|
||||
|
||||
"Set Active Position",
|
||||
"Reserved 0x05",
|
||||
"Reserved 0x06",
|
||||
"Address row 0",
|
||||
"Set Active Position",
|
||||
"Reserved 0x05",
|
||||
"Reserved 0x06",
|
||||
"Address row 0",
|
||||
|
||||
"PDC origin, source",
|
||||
"PDC month and day",
|
||||
"PDC cursor and start hour",
|
||||
"PDC cursor and end hour",
|
||||
"PDC origin, source",
|
||||
"PDC month and day",
|
||||
"PDC cursor and start hour",
|
||||
"PDC cursor and end hour",
|
||||
|
||||
"PDC cursor local offset",
|
||||
"PDC series ID and code",
|
||||
"Reserved 0x0e",
|
||||
"Reserved 0x0f",
|
||||
"PDC cursor local offset",
|
||||
"PDC series ID and code",
|
||||
"Reserved 0x0e",
|
||||
"Reserved 0x0f",
|
||||
|
||||
"Origin modifier",
|
||||
"Invoke active object",
|
||||
"Invoke adaptive object",
|
||||
"Invoke passive object",
|
||||
"Origin modifier",
|
||||
"Invoke active object",
|
||||
"Invoke adaptive object",
|
||||
"Invoke passive object",
|
||||
|
||||
"Reserved 0x14",
|
||||
"Define active object",
|
||||
"Define adaptive object",
|
||||
"Define passive object",
|
||||
"Reserved 0x14",
|
||||
"Define active object",
|
||||
"Define adaptive object",
|
||||
"Define passive object",
|
||||
|
||||
"DRCS mode",
|
||||
"Reserved 0x19",
|
||||
"Reserved 0x1a",
|
||||
"Reserved 0x1b",
|
||||
"DRCS mode",
|
||||
"Reserved 0x19",
|
||||
"Reserved 0x1a",
|
||||
"Reserved 0x1b",
|
||||
|
||||
"Reserved 0x1c",
|
||||
"Reserved 0x1d",
|
||||
"Reserved 0x1e",
|
||||
"Termination marker",
|
||||
"Reserved 0x1c",
|
||||
"Reserved 0x1d",
|
||||
"Reserved 0x1e",
|
||||
"Termination marker",
|
||||
|
||||
// Column triplet modes
|
||||
"Foreground colour",
|
||||
"G1 character",
|
||||
"G3 character, level 1.5",
|
||||
"Background colour",
|
||||
// Column triplet modes
|
||||
"Foreground colour",
|
||||
"G1 character",
|
||||
"G3 character, level 1.5",
|
||||
"Background colour",
|
||||
|
||||
"Reserved 0x04",
|
||||
"Reserved 0x05",
|
||||
"PDC cursor, start end min",
|
||||
"Additional flash functions",
|
||||
"Reserved 0x04",
|
||||
"Reserved 0x05",
|
||||
"PDC cursor, start end min",
|
||||
"Additional flash functions",
|
||||
|
||||
"Modified G0/G2 character set",
|
||||
"G0 character",
|
||||
"Reserved 0x0a",
|
||||
"G3 character, level 2.5",
|
||||
"Modified G0/G2 character set",
|
||||
"G0 character",
|
||||
"Reserved 0x0a",
|
||||
"G3 character, level 2.5",
|
||||
|
||||
"Display attributes",
|
||||
"DRCS character",
|
||||
"Font style",
|
||||
"G2 character",
|
||||
"Display attributes",
|
||||
"DRCS character",
|
||||
"Font style",
|
||||
"G2 character",
|
||||
|
||||
"G0 character no diacritical",
|
||||
"G0 character diacritical 1",
|
||||
"G0 character diacritical 2",
|
||||
"G0 character diacritical 3",
|
||||
"G0 character diacritical 4",
|
||||
"G0 character diacritical 5",
|
||||
"G0 character diacritical 6",
|
||||
"G0 character diacritical 7",
|
||||
"G0 character diacritical 8",
|
||||
"G0 character diacritical 9",
|
||||
"G0 character diacritical A",
|
||||
"G0 character diacritical B",
|
||||
"G0 character diacritical C",
|
||||
"G0 character diacritical D",
|
||||
"G0 character diacritical E",
|
||||
"G0 character diacritical F"
|
||||
"G0 character no diacritical",
|
||||
"G0 character diacritical 1",
|
||||
"G0 character diacritical 2",
|
||||
"G0 character diacritical 3",
|
||||
"G0 character diacritical 4",
|
||||
"G0 character diacritical 5",
|
||||
"G0 character diacritical 6",
|
||||
"G0 character diacritical 7",
|
||||
"G0 character diacritical 8",
|
||||
"G0 character diacritical 9",
|
||||
"G0 character diacritical A",
|
||||
"G0 character diacritical B",
|
||||
"G0 character diacritical C",
|
||||
"G0 character diacritical D",
|
||||
"G0 character diacritical E",
|
||||
"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
|
||||
|
||||
123
x26triplets.cpp
123
x26triplets.cpp
@@ -50,3 +50,126 @@ void X26Triplet::setAddressColumn(int 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
|
||||
#define X26TRIPLETS_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
class X26Triplet
|
||||
{
|
||||
public:
|
||||
enum X26TripletError { NoError, ActivePositionMovedUp, ActivePositionMovedLeft };
|
||||
|
||||
X26Triplet() {}
|
||||
// X26Triplet(const X26Triplet &other);
|
||||
|
||||
@@ -44,9 +48,56 @@ public:
|
||||
void setAddressRow(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:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user