Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2ae42701c | ||
|
|
9edaa2fda7 | ||
|
|
2ec4039393 | ||
|
|
69b6ad1976 | ||
|
|
d5a9469df1 | ||
|
|
e7f6a54d8d | ||
|
|
e1a1bcf070 | ||
|
|
cd531bd0a5 | ||
|
|
a54385b8f5 | ||
|
|
38746c7f38 | ||
|
|
f256e4ed28 | ||
|
|
06970fd448 | ||
|
|
a3d4783796 | ||
|
|
c8e57150eb | ||
|
|
23c2623bcf | ||
|
|
72dbe94dc2 | ||
|
|
8d415f1a0f | ||
|
|
2c04c898ab | ||
|
|
a66474b7cf | ||
|
|
3906bfde80 | ||
|
|
19f74a1761 | ||
|
|
8903703064 | ||
|
|
56e7b0500c | ||
|
|
9fa86f8c4c | ||
|
|
551172aed3 | ||
|
|
7a0dbcca2b | ||
|
|
1a7e5aff5f | ||
|
|
c24a6b1fa1 | ||
|
|
4387e9ffbd | ||
|
|
f258c6e095 | ||
|
|
5739474957 | ||
|
|
8bc0c2c886 | ||
|
|
4584ba668d | ||
|
|
d3607f5b00 | ||
|
|
2ad5d45153 | ||
|
|
690f340922 | ||
|
|
dc93fe856d |
@@ -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.
|
||||
|
||||
140
document.cpp
140
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;
|
||||
@@ -34,11 +68,17 @@ TeletextDocument::TeletextDocument()
|
||||
m_undoStack = new QUndoStack(this);
|
||||
m_cursorRow = 1;
|
||||
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)
|
||||
@@ -71,8 +111,12 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -81,8 +125,12 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +138,12 @@ void TeletextDocument::selectSubPagePrevious()
|
||||
{
|
||||
if (m_currentSubPageIndex > 0) {
|
||||
emit aboutToChangeSubPage();
|
||||
|
||||
m_currentSubPageIndex--;
|
||||
|
||||
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||
emit subPageSelected();
|
||||
emit selectionMoved();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,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);
|
||||
}
|
||||
@@ -168,62 +222,124 @@ void TeletextDocument::setFastTextLinkPageNumberOnAllSubPages(int linkNumber, in
|
||||
subPage->setFastTextLinkPageNumber(linkNumber, pageNumber);
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorUp()
|
||||
void TeletextDocument::cursorUp(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (--m_cursorRow == 0)
|
||||
m_cursorRow = 24;
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorDown()
|
||||
void TeletextDocument::cursorDown(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (++m_cursorRow == 25)
|
||||
m_cursorRow = 1;
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorLeft()
|
||||
void TeletextDocument::cursorLeft(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (--m_cursorColumn == -1) {
|
||||
m_cursorColumn = 39;
|
||||
cursorUp();
|
||||
cursorUp(shiftKey);
|
||||
}
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorRight()
|
||||
void TeletextDocument::cursorRight(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (++m_cursorColumn == 40) {
|
||||
m_cursorColumn = 0;
|
||||
cursorDown();
|
||||
cursorDown(shiftKey);
|
||||
}
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::moveCursor(int cursorRow, int cursorColumn)
|
||||
void TeletextDocument::moveCursor(int cursorRow, int cursorColumn, bool selectionInProgress)
|
||||
{
|
||||
if (selectionInProgress && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (cursorRow != -1)
|
||||
m_cursorRow = cursorRow;
|
||||
if (cursorColumn != -1)
|
||||
m_cursorColumn = cursorColumn;
|
||||
|
||||
if (selectionInProgress)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::setSelectionCorner(int row, int column)
|
||||
{
|
||||
if (m_selectionCornerRow != row || m_selectionCornerColumn != column) {
|
||||
m_selectionSubPage = currentSubPage();
|
||||
m_selectionCornerRow = row;
|
||||
m_selectionCornerColumn = column;
|
||||
|
||||
// emit selectionMoved();
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextDocument::setSelection(int topRow, int leftColumn, int bottomRow, int rightColumn)
|
||||
{
|
||||
if (m_selectionTopRow != topRow || m_selectionBottomRow != bottomRow || m_selectionLeftColumn != leftColumn || m_selectionRightColumn != rightColumn) {
|
||||
if (selectionTopRow() != topRow || selectionBottomRow() != bottomRow || selectionLeftColumn() != leftColumn || selectionRightColumn() != rightColumn) {
|
||||
m_selectionSubPage = currentSubPage();
|
||||
m_selectionTopRow = topRow;
|
||||
m_selectionBottomRow = bottomRow;
|
||||
m_selectionLeftColumn = leftColumn;
|
||||
m_selectionRightColumn = rightColumn;
|
||||
m_selectionCornerRow = topRow;
|
||||
m_cursorRow = bottomRow;
|
||||
m_selectionCornerColumn = leftColumn;
|
||||
m_cursorColumn = rightColumn;
|
||||
|
||||
emit selectionMoved();
|
||||
emit cursorMoved();
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextDocument::cancelSelection()
|
||||
{
|
||||
if (m_selectionSubPage != nullptr) {
|
||||
m_selectionSubPage = nullptr;
|
||||
emit selectionMoved();
|
||||
m_selectionCornerRow = m_selectionCornerColumn = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int TeletextDocument::levelRequired() const
|
||||
|
||||
46
document.h
46
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,20 +79,24 @@ 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();
|
||||
void cursorDown();
|
||||
void cursorLeft();
|
||||
void cursorRight();
|
||||
void moveCursor(int, int);
|
||||
int selectionTopRow() const { return m_selectionTopRow; }
|
||||
int selectionBottomRow() const { return m_selectionBottomRow; }
|
||||
int selectionLeftColumn() const { return m_selectionLeftColumn; }
|
||||
int selectionRightColumn() const { return m_selectionRightColumn; }
|
||||
int selectionWidth() const { return m_selectionRightColumn - m_selectionLeftColumn + 1; }
|
||||
int selectionHeight() const { return m_selectionBottomRow - m_selectionTopRow + 1; }
|
||||
void cursorUp(bool shiftKey=false);
|
||||
void cursorDown(bool shiftKey=false);
|
||||
void cursorLeft(bool shiftKey=false);
|
||||
void cursorRight(bool shiftKey=false);
|
||||
void moveCursor(int, int, bool selectionInProgress=false);
|
||||
int selectionTopRow() const { return m_selectionCornerRow == -1 ? m_cursorRow : qMin(m_selectionCornerRow, m_cursorRow); }
|
||||
int selectionBottomRow() const { return qMax(m_selectionCornerRow, m_cursorRow); }
|
||||
int selectionLeftColumn() const { return m_selectionCornerColumn == -1 ? m_cursorColumn : qMin(m_selectionCornerColumn, m_cursorColumn); }
|
||||
int selectionRightColumn() const { return qMax(m_selectionCornerColumn, m_cursorColumn); }
|
||||
int selectionWidth() const { return m_selectionCornerColumn == -1 ? 1 : selectionRightColumn() - selectionLeftColumn() + 1; }
|
||||
int selectionHeight() const { return m_selectionCornerRow == -1 ? 1 : selectionBottomRow() - selectionTopRow() + 1; }
|
||||
bool selectionActive() const { return m_selectionSubPage == currentSubPage(); }
|
||||
int selectionCornerRow() const { return m_selectionCornerRow == -1 ? m_cursorRow : m_selectionCornerRow; }
|
||||
int selectionCornerColumn() const { return m_selectionCornerColumn == -1 ? m_cursorColumn : m_selectionCornerColumn; }
|
||||
void setSelectionCorner(int, int);
|
||||
void setSelection(int, int, int, int);
|
||||
void cancelSelection();
|
||||
int levelRequired() const;
|
||||
@@ -99,8 +120,9 @@ private:
|
||||
std::vector<LevelOnePage *> m_subPages;
|
||||
std::vector<LevelOnePage *> m_recycleSubPages;
|
||||
QUndoStack *m_undoStack;
|
||||
int m_cursorRow, m_cursorColumn, m_selectionTopRow, m_selectionBottomRow, m_selectionLeftColumn, m_selectionRightColumn;
|
||||
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
|
||||
LevelOnePage *m_selectionSubPage;
|
||||
ClutModel *m_clutModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
23
examples/Level1p5-Defcon.tti
Normal file
23
examples/Level1p5-Defcon.tti
Normal file
@@ -0,0 +1,23 @@
|
||||
PN,11600
|
||||
SC,0000
|
||||
PS,8000
|
||||
CT,8,T
|
||||
OL,26,@lD@Ib\UbTXb\ZbT]b\`B]mD@HB{VBsWB{[Bs\B{
|
||||
OL,26,AaBRnD@`BraB]oD@HBXVBPWBX[BP\BX`B}pD@Iby
|
||||
OL,26,BUbqXbyZbq]byaBruD@KBYLBSRbTVb\ZbT^b\cbY
|
||||
OL,26,CdBSvD@JBYKBSLBywD@Fb\JBTKByLBsVB^ZbX[bU
|
||||
OL,26,D\bS^bxxD@[bx\bu]bsyD@BbqFbyRbqVbyCC
|
||||
OL,3,W <,,,,,,,,,,,,,,,,,,,,,,,,,,,,,l
|
||||
OL,4,W 5t xt xt 5 j
|
||||
OL,5,W 5 " !"/ !" j
|
||||
OL,6,W 5 j5j
|
||||
OL,7,W 5 ` 0`| 0` j
|
||||
OL,8,W 5' +' +' jj
|
||||
OL,9,W -,,,,,,,,,,,,,,,,,,,,,,,,,,,,,.
|
||||
OL,12,V<,,,,,lR<,,,,,lS<,,,,,lQ<,,,,,lW<,,,,,l
|
||||
OL,13,V5jR5 `5jS5xtjQ5xtjW5 `~5 j
|
||||
OL,14,V55 jR5 ~75jS5/% jjQ5/% jjW5 #5 j
|
||||
OL,15,V5tjR5z? 5jS5 j5jQ5`x~/jW5 5 j
|
||||
OL,16,V5|4 jjR5jS5|4 jjQ5?# jW5 5 j
|
||||
OL,17,V5+'jR5 5jS5+'jQ5jW5 5 j
|
||||
OL,18,V-,,,,,.R-,,,,,.S-,,,,,.Q-,,,,,.W-,,,,,.
|
||||
33
examples/SidePanels-PriceChop.tti
Normal file
33
examples/SidePanels-PriceChop.tti
Normal file
@@ -0,0 +1,33 @@
|
||||
PN,13200
|
||||
SC,0000
|
||||
PS,8000
|
||||
CT,8,T
|
||||
OL,28,@@@|H@p_@|wsA@@AfUrLs_w}ww]_}_wM@p
|
||||
OL,26,@rD@Ab|BBpKBxLbtsD@BB{CbqJbyKBstD@Db|EBp
|
||||
OL,26,AHBxIbtuD@EB{FbqGbyHBswD@AbqLbyxD@Bb|CBp
|
||||
OL,26,BJBxKbtyD@CB{DbqIbyJBszD@Eb|FBpGBxHbt{D@
|
||||
OL,26,CFB{GBs}aJ@cJhPThr]hP`hre~aI@cI_Cxv]@It
|
||||
OL,26,DAipBitCIwGi\iD@@@H@irAiyBIzCirDIyxve@Iy
|
||||
OL,26,EAIqCiqDItEipFitGIwiD@@@HAisBIvCipDiyEis
|
||||
OL,26,FFiwGi{BBBBBBBBBBB
|
||||
OL,1, calls cost #5 WZ`p0ppb`p0pp `p0up`p0pp
|
||||
OL,2, 0909 879 0100 WZjp55"jj 1=.(j 15jj 5uz
|
||||
OL,3, GB #9.99 p&p WZ* ! ""#!## "#!!""#!%
|
||||
OL,4, W Zn,h h
|
||||
OL,5,C]Dstart #89 \ W Zjp"d&
|
||||
OL,6,S############
|
||||
OL,7, LIVE
|
||||
OL,8,E]G now at \
|
||||
OL,9,Ussssssssssss
|
||||
OL,10,U+'
|
||||
OL,11,U^"o]G#3U?\!
|
||||
OL,12,U +'
|
||||
OL,13,U "o?!
|
||||
OL,14,Spppppppppppp
|
||||
OL,15,S+]D^leftS\'
|
||||
OL,16,S^"o]D24S?\!
|
||||
OL,17,S +'
|
||||
OL,18,S "o?!
|
||||
OL,19,S
|
||||
OL,21,E]G9ct gold curb chain 9ct gold curb c
|
||||
OL,22,C]DNEW BUYER JOHN london JANET manch
|
||||
@@ -17,16 +17,29 @@
|
||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QByteArrayList>
|
||||
#include <QClipboard>
|
||||
#include <QMimeData>
|
||||
|
||||
#include "levelonecommands.h"
|
||||
|
||||
#include "document.h"
|
||||
#include "keymap.h"
|
||||
|
||||
TypeCharacterCommand::TypeCharacterCommand(TeletextDocument *teletextDocument, unsigned char newCharacter, bool insertMode, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
LevelOneCommand::LevelOneCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
m_columnStart = m_columnEnd = teletextDocument->cursorColumn();
|
||||
m_column = teletextDocument->cursorColumn();
|
||||
m_firstDo = true;
|
||||
}
|
||||
|
||||
TypeCharacterCommand::TypeCharacterCommand(TeletextDocument *teletextDocument, unsigned char newCharacter, bool insertMode, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_columnStart = m_columnEnd = m_column;
|
||||
m_newCharacter = newCharacter;
|
||||
m_insertMode = insertMode;
|
||||
|
||||
@@ -37,7 +50,6 @@ TypeCharacterCommand::TypeCharacterCommand(TeletextDocument *teletextDocument, u
|
||||
setText(QObject::tr("insert character"));
|
||||
else
|
||||
setText(QObject::tr("overwrite character"));
|
||||
m_firstDo = true;
|
||||
}
|
||||
|
||||
void TypeCharacterCommand::redo()
|
||||
@@ -89,12 +101,8 @@ bool TypeCharacterCommand::mergeWith(const QUndoCommand *command)
|
||||
}
|
||||
|
||||
|
||||
ToggleMosaicBitCommand::ToggleMosaicBitCommand(TeletextDocument *teletextDocument, unsigned char bitToToggle, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
ToggleMosaicBitCommand::ToggleMosaicBitCommand(TeletextDocument *teletextDocument, unsigned char bitToToggle, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
m_column = teletextDocument->cursorColumn();
|
||||
m_oldCharacter = teletextDocument->currentSubPage()->character(m_row, m_column);
|
||||
if (bitToToggle == 0x20 || bitToToggle == 0x7f)
|
||||
m_newCharacter = bitToToggle;
|
||||
@@ -134,12 +142,10 @@ bool ToggleMosaicBitCommand::mergeWith(const QUndoCommand *command)
|
||||
}
|
||||
|
||||
|
||||
BackspaceKeyCommand::BackspaceKeyCommand(TeletextDocument *teletextDocument, bool insertMode, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
BackspaceKeyCommand::BackspaceKeyCommand(TeletextDocument *teletextDocument, bool insertMode, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
m_columnStart = teletextDocument->cursorColumn()-1;
|
||||
m_columnStart = m_column - 1;
|
||||
|
||||
if (m_columnStart == -1) {
|
||||
m_columnStart = 39;
|
||||
if (--m_row == 0)
|
||||
@@ -152,7 +158,6 @@ BackspaceKeyCommand::BackspaceKeyCommand(TeletextDocument *teletextDocument, boo
|
||||
m_oldRowContents[c] = m_newRowContents[c] = m_teletextDocument->currentSubPage()->character(m_row, c);
|
||||
|
||||
setText(QObject::tr("backspace"));
|
||||
m_firstDo = true;
|
||||
}
|
||||
|
||||
void BackspaceKeyCommand::redo()
|
||||
@@ -208,13 +213,8 @@ bool BackspaceKeyCommand::mergeWith(const QUndoCommand *command)
|
||||
}
|
||||
|
||||
|
||||
DeleteKeyCommand::DeleteKeyCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
DeleteKeyCommand::DeleteKeyCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
m_column = teletextDocument->cursorColumn();
|
||||
|
||||
for (int c=0; c<40; c++)
|
||||
m_oldRowContents[c] = m_newRowContents[c] = m_teletextDocument->currentSubPage()->character(m_row, c);
|
||||
|
||||
@@ -262,11 +262,8 @@ bool DeleteKeyCommand::mergeWith(const QUndoCommand *command)
|
||||
}
|
||||
|
||||
|
||||
InsertRowCommand::InsertRowCommand(TeletextDocument *teletextDocument, bool copyRow, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
InsertRowCommand::InsertRowCommand(TeletextDocument *teletextDocument, bool copyRow, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
m_copyRow = copyRow;
|
||||
|
||||
if (m_copyRow)
|
||||
@@ -310,12 +307,8 @@ void InsertRowCommand::undo()
|
||||
}
|
||||
|
||||
|
||||
DeleteRowCommand::DeleteRowCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
DeleteRowCommand::DeleteRowCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_row = teletextDocument->cursorRow();
|
||||
|
||||
setText(QObject::tr("delete row"));
|
||||
}
|
||||
|
||||
@@ -354,10 +347,233 @@ void DeleteRowCommand::undo()
|
||||
}
|
||||
|
||||
|
||||
InsertSubPageCommand::InsertSubPageCommand(TeletextDocument *teletextDocument, bool afterCurrentSubPage, bool copySubPage, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
#ifndef QT_NO_CLIPBOARD
|
||||
CutCommand::CutCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_newSubPageIndex = teletextDocument->currentSubPageIndex()+afterCurrentSubPage;
|
||||
m_selectionTopRow = m_teletextDocument->selectionTopRow();
|
||||
m_selectionBottomRow = m_teletextDocument->selectionBottomRow();
|
||||
m_selectionLeftColumn = m_teletextDocument->selectionLeftColumn();
|
||||
m_selectionRightColumn = m_teletextDocument->selectionRightColumn();
|
||||
|
||||
m_selectionCornerRow = m_teletextDocument->selectionCornerRow();
|
||||
m_selectionCornerColumn = m_teletextDocument->selectionCornerColumn();
|
||||
|
||||
// Store copy of the characters that we're about to blank
|
||||
for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) {
|
||||
QByteArray rowArray;
|
||||
|
||||
for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++)
|
||||
rowArray.append(m_teletextDocument->currentSubPage()->character(r, c));
|
||||
|
||||
m_deletedCharacters.append(rowArray);
|
||||
}
|
||||
|
||||
setText(QObject::tr("cut"));
|
||||
}
|
||||
|
||||
void CutCommand::redo()
|
||||
{
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex);
|
||||
|
||||
for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) {
|
||||
for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++)
|
||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, 0x20);
|
||||
emit m_teletextDocument->contentsChange(r);
|
||||
}
|
||||
}
|
||||
|
||||
void CutCommand::undo()
|
||||
{
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex);
|
||||
|
||||
int arrayR = 0;
|
||||
int arrayC;
|
||||
|
||||
for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) {
|
||||
arrayC = 0;
|
||||
for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++)
|
||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_deletedCharacters[arrayR].at(arrayC++));
|
||||
|
||||
emit m_teletextDocument->contentsChange(r);
|
||||
arrayR++;
|
||||
}
|
||||
|
||||
m_teletextDocument->setSelectionCorner(m_selectionCornerRow, m_selectionCornerColumn);
|
||||
m_teletextDocument->moveCursor(m_row, m_column, true);
|
||||
}
|
||||
|
||||
|
||||
PasteCommand::PasteCommand(TeletextDocument *teletextDocument, int pageCharSet, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
const QClipboard *clipboard = QApplication::clipboard();
|
||||
const QMimeData *mimeData = clipboard->mimeData();
|
||||
QByteArray nativeData;
|
||||
|
||||
m_selectionActive = m_teletextDocument->selectionActive();
|
||||
if (m_selectionActive) {
|
||||
m_selectionCornerRow = m_teletextDocument->selectionCornerRow();
|
||||
m_selectionCornerColumn = m_teletextDocument->selectionCornerColumn();
|
||||
}
|
||||
|
||||
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
||||
|
||||
// Try to get something from the clipboard
|
||||
// FIXME is this a correct "custom" mime type? Or should we use vnd?
|
||||
nativeData = mimeData->data("application/x-teletext");
|
||||
if (nativeData.size() > 2) {
|
||||
// Native clipboard data: we put it there ourselves
|
||||
m_clipboardDataHeight = nativeData.at(0);
|
||||
m_clipboardDataWidth = nativeData.at(1);
|
||||
|
||||
// Guard against invalid dimensions or total size not matching stated dimensions
|
||||
if (m_clipboardDataHeight > 0 && m_clipboardDataWidth > 0 && m_clipboardDataHeight <= 25 && m_clipboardDataWidth <= 40 && nativeData.size() == m_clipboardDataHeight * m_clipboardDataWidth + 2)
|
||||
for (int r=0; r<m_clipboardDataHeight; r++)
|
||||
m_pastingCharacters.append(nativeData.mid(2 + r * m_clipboardDataWidth, m_clipboardDataWidth));
|
||||
else
|
||||
// Invalidate
|
||||
m_clipboardDataHeight = m_clipboardDataWidth = 0;
|
||||
} else if (mimeData->hasText()) {
|
||||
// Plain text
|
||||
QStringList plainTextData = mimeData->text().split(QRegExp("\n|\r\n|\r"));
|
||||
|
||||
m_clipboardDataHeight = plainTextData.size();
|
||||
m_clipboardDataWidth = 0;
|
||||
|
||||
for (int r=0; r<m_clipboardDataHeight; r++) {
|
||||
m_pastingCharacters.append(QByteArray());
|
||||
for (int c=0; c<plainTextData.at(r).size(); c++) {
|
||||
// Try to map the unicode character to the current Level 1 character set of this page
|
||||
|
||||
char convertedChar;
|
||||
const QChar charToConvert = plainTextData.at(r).at(c);
|
||||
|
||||
if (keymapping[pageCharSet].contains(charToConvert))
|
||||
// Remapped character or non-Latin character converted successfully
|
||||
convertedChar = keymapping[pageCharSet].value(charToConvert);
|
||||
else {
|
||||
// Either a Latin character or non-Latin character that can't be converted
|
||||
// See if it's a Latin character
|
||||
convertedChar = charToConvert.toLatin1();
|
||||
if (convertedChar == 0)
|
||||
// Couldn't convert - make it a block character so it doesn't need to be inserted-between later on
|
||||
convertedChar = 0x7f;
|
||||
}
|
||||
|
||||
m_pastingCharacters[r].append(convertedChar);
|
||||
}
|
||||
m_clipboardDataWidth = qMax(m_pastingCharacters.at(r).size(), m_clipboardDataWidth);
|
||||
}
|
||||
// Pad short lines with spaces to make a box
|
||||
for (int r=0; r<m_clipboardDataHeight; r++)
|
||||
m_pastingCharacters[r] = m_pastingCharacters.at(r).leftJustified(m_clipboardDataWidth);
|
||||
}
|
||||
|
||||
if (m_clipboardDataWidth == 0 || m_clipboardDataHeight == 0)
|
||||
return;
|
||||
|
||||
if (m_selectionActive) {
|
||||
m_pasteTopRow = m_teletextDocument->selectionTopRow();
|
||||
m_pasteBottomRow = m_teletextDocument->selectionBottomRow();
|
||||
m_pasteLeftColumn = m_teletextDocument->selectionLeftColumn();
|
||||
m_pasteRightColumn = m_teletextDocument->selectionRightColumn();
|
||||
} else {
|
||||
m_pasteTopRow = m_row;
|
||||
m_pasteBottomRow = m_row + m_clipboardDataHeight - 1;
|
||||
m_pasteLeftColumn = m_column;
|
||||
m_pasteRightColumn = m_column + m_clipboardDataWidth - 1;
|
||||
}
|
||||
|
||||
// Store copy of the characters that we're about to overwrite
|
||||
for (int r=m_pasteTopRow; r<=m_pasteBottomRow; r++) {
|
||||
QByteArray rowArray;
|
||||
|
||||
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
||||
// Guard against size of pasted block going beyond last line or column
|
||||
if (r < 25 && c < 40)
|
||||
rowArray.append(m_teletextDocument->currentSubPage()->character(r, c));
|
||||
else
|
||||
// Gone beyond last line or column - store a filler character which we won't see
|
||||
// Not sure if this is really necessary as out-of-bounds access might not occur?
|
||||
rowArray.append(0x7f);
|
||||
|
||||
m_deletedCharacters.append(rowArray);
|
||||
}
|
||||
|
||||
setText(QObject::tr("paste"));
|
||||
}
|
||||
|
||||
void PasteCommand::redo()
|
||||
{
|
||||
if (m_clipboardDataWidth == 0 || m_clipboardDataHeight == 0)
|
||||
return;
|
||||
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex);
|
||||
|
||||
int arrayR = 0;
|
||||
int arrayC;
|
||||
|
||||
for (int r=m_pasteTopRow; r<=m_pasteBottomRow; r++) {
|
||||
arrayC = 0;
|
||||
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
||||
// Guard against size of pasted block going beyond last line or column
|
||||
if (r < 25 && c < 40) {
|
||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_pastingCharacters[arrayR].at(arrayC++));
|
||||
|
||||
// If paste area is wider than clipboard data, repeat the pattern
|
||||
if (arrayC == m_clipboardDataWidth)
|
||||
arrayC = 0;
|
||||
}
|
||||
|
||||
if (r < 25)
|
||||
emit m_teletextDocument->contentsChange(r);
|
||||
|
||||
arrayR++;
|
||||
// If paste area is taller than clipboard data, repeat the pattern
|
||||
if (arrayR == m_clipboardDataHeight)
|
||||
arrayR = 0;
|
||||
}
|
||||
|
||||
if (m_selectionActive) {
|
||||
m_teletextDocument->setSelectionCorner(m_selectionCornerRow, m_selectionCornerColumn);
|
||||
m_teletextDocument->moveCursor(m_row, m_column, true);
|
||||
} else {
|
||||
m_teletextDocument->moveCursor(m_row, qMin(m_column+m_clipboardDataWidth-1, 39));
|
||||
m_teletextDocument->cursorRight();
|
||||
}
|
||||
}
|
||||
|
||||
void PasteCommand::undo()
|
||||
{
|
||||
if (m_clipboardDataWidth == 0 || m_clipboardDataHeight == 0)
|
||||
return;
|
||||
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex);
|
||||
|
||||
int arrayR = 0;
|
||||
int arrayC;
|
||||
|
||||
for (int r=m_pasteTopRow; r<=m_pasteBottomRow; r++) {
|
||||
arrayC = 0;
|
||||
for (int c=m_pasteLeftColumn; c<=m_pasteRightColumn; c++)
|
||||
// Guard against size of pasted block going beyond last line or column
|
||||
if (r < 25 && c < 40)
|
||||
m_teletextDocument->currentSubPage()->setCharacter(r, c, m_deletedCharacters[arrayR].at(arrayC++));
|
||||
|
||||
if (r < 25)
|
||||
emit m_teletextDocument->contentsChange(r);
|
||||
|
||||
arrayR++;
|
||||
}
|
||||
|
||||
if (!m_selectionActive)
|
||||
m_teletextDocument->moveCursor(m_row, m_column);
|
||||
}
|
||||
#endif // !QT_NO_CLIPBOARD
|
||||
|
||||
|
||||
InsertSubPageCommand::InsertSubPageCommand(TeletextDocument *teletextDocument, bool afterCurrentSubPage, bool copySubPage, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_newSubPageIndex = m_subPageIndex + afterCurrentSubPage;
|
||||
m_copySubPage = copySubPage;
|
||||
|
||||
setText(QObject::tr("insert subpage"));
|
||||
@@ -380,30 +596,26 @@ void InsertSubPageCommand::undo()
|
||||
}
|
||||
|
||||
|
||||
DeleteSubPageCommand::DeleteSubPageCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
DeleteSubPageCommand::DeleteSubPageCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageToDelete = teletextDocument->currentSubPageIndex();
|
||||
setText(QObject::tr("delete subpage"));
|
||||
}
|
||||
|
||||
void DeleteSubPageCommand::redo()
|
||||
{
|
||||
m_teletextDocument->deleteSubPageToRecycle(m_subPageToDelete);
|
||||
m_teletextDocument->selectSubPageIndex(qMin(m_subPageToDelete, m_teletextDocument->numberOfSubPages()-1), true);
|
||||
m_teletextDocument->deleteSubPageToRecycle(m_subPageIndex);
|
||||
m_teletextDocument->selectSubPageIndex(qMin(m_subPageIndex, m_teletextDocument->numberOfSubPages()-1), true);
|
||||
}
|
||||
|
||||
void DeleteSubPageCommand::undo()
|
||||
{
|
||||
m_teletextDocument->unDeleteSubPageFromRecycle(m_subPageToDelete);
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageToDelete, true);
|
||||
m_teletextDocument->unDeleteSubPageFromRecycle(m_subPageIndex);
|
||||
m_teletextDocument->selectSubPageIndex(m_subPageIndex, true);
|
||||
}
|
||||
|
||||
|
||||
SetColourCommand::SetColourCommand(TeletextDocument *teletextDocument, int colourIndex, int newColour, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
SetColourCommand::SetColourCommand(TeletextDocument *teletextDocument, int colourIndex, int newColour, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_colourIndex = colourIndex;
|
||||
m_oldColour = teletextDocument->currentSubPage()->CLUT(colourIndex);
|
||||
m_newColour = newColour;
|
||||
@@ -431,10 +643,8 @@ void SetColourCommand::undo()
|
||||
}
|
||||
|
||||
|
||||
ResetCLUTCommand::ResetCLUTCommand(TeletextDocument *teletextDocument, int colourTable, QUndoCommand *parent) : QUndoCommand(parent)
|
||||
ResetCLUTCommand::ResetCLUTCommand(TeletextDocument *teletextDocument, int colourTable, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent)
|
||||
{
|
||||
m_teletextDocument = teletextDocument;
|
||||
m_subPageIndex = teletextDocument->currentSubPageIndex();
|
||||
m_colourTable = colourTable;
|
||||
for (int i=m_colourTable*8; i<m_colourTable*8+8; i++)
|
||||
m_oldColourEntry[i&7] = teletextDocument->currentSubPage()->CLUT(i);
|
||||
|
||||
@@ -20,11 +20,23 @@
|
||||
#ifndef LEVELONECOMMANDS_H
|
||||
#define LEVELONECOMMANDS_H
|
||||
|
||||
#include <QByteArrayList>
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include "document.h"
|
||||
|
||||
class TypeCharacterCommand : public QUndoCommand
|
||||
class LevelOneCommand : public QUndoCommand
|
||||
{
|
||||
public:
|
||||
LevelOneCommand(TeletextDocument *, QUndoCommand *parent = 0);
|
||||
|
||||
protected:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageIndex, m_row, m_column;
|
||||
bool m_firstDo;
|
||||
};
|
||||
|
||||
class TypeCharacterCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
enum { Id = 101 };
|
||||
@@ -37,13 +49,12 @@ public:
|
||||
int id() const override { return Id; }
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
unsigned char m_newCharacter, m_oldRowContents[40], m_newRowContents[40];
|
||||
int m_subPageIndex, m_row, m_columnStart, m_columnEnd;
|
||||
bool m_firstDo, m_insertMode;
|
||||
int m_columnStart, m_columnEnd;
|
||||
bool m_insertMode;
|
||||
};
|
||||
|
||||
class ToggleMosaicBitCommand : public QUndoCommand
|
||||
class ToggleMosaicBitCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
enum { Id = 102 };
|
||||
@@ -56,12 +67,10 @@ public:
|
||||
int id() const override { return Id; }
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
unsigned char m_oldCharacter, m_newCharacter;
|
||||
int m_subPageIndex, m_row, m_column;
|
||||
};
|
||||
|
||||
class BackspaceKeyCommand : public QUndoCommand
|
||||
class BackspaceKeyCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
enum { Id = 103 };
|
||||
@@ -74,13 +83,12 @@ public:
|
||||
int id() const override { return Id; }
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
unsigned char m_oldRowContents[40], m_newRowContents[40];
|
||||
int m_subPageIndex, m_row, m_columnStart, m_columnEnd;
|
||||
bool m_firstDo, m_insertMode;
|
||||
int m_columnStart, m_columnEnd;
|
||||
bool m_insertMode;
|
||||
};
|
||||
|
||||
class DeleteKeyCommand : public QUndoCommand
|
||||
class DeleteKeyCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
enum { Id = 104 };
|
||||
@@ -93,12 +101,10 @@ public:
|
||||
int id() const override { return Id; }
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
unsigned char m_oldRowContents[40], m_newRowContents[40];
|
||||
int m_subPageIndex, m_row, m_column;
|
||||
};
|
||||
|
||||
class InsertSubPageCommand : public QUndoCommand
|
||||
class InsertSubPageCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
InsertSubPageCommand(TeletextDocument *, bool, bool, QUndoCommand *parent = 0);
|
||||
@@ -107,25 +113,20 @@ public:
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_newSubPageIndex;
|
||||
bool m_copySubPage;
|
||||
};
|
||||
|
||||
class DeleteSubPageCommand : public QUndoCommand
|
||||
class DeleteSubPageCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
DeleteSubPageCommand(TeletextDocument *, QUndoCommand *parent = 0);
|
||||
|
||||
void redo() override;
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageToDelete;
|
||||
};
|
||||
|
||||
class InsertRowCommand : public QUndoCommand
|
||||
class InsertRowCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
InsertRowCommand(TeletextDocument *, bool, QUndoCommand *parent = 0);
|
||||
@@ -134,13 +135,11 @@ public:
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageIndex, m_row;
|
||||
bool m_copyRow;
|
||||
unsigned char m_deletedBottomRow[40];
|
||||
};
|
||||
|
||||
class DeleteRowCommand : public QUndoCommand
|
||||
class DeleteRowCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
DeleteRowCommand(TeletextDocument *, QUndoCommand *parent = 0);
|
||||
@@ -149,12 +148,42 @@ public:
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageIndex, m_row;
|
||||
unsigned char m_deletedRow[40];
|
||||
};
|
||||
|
||||
class SetColourCommand : public QUndoCommand
|
||||
#ifndef QT_NO_CLIPBOARD
|
||||
class CutCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
CutCommand(TeletextDocument *, QUndoCommand *parent = 0);
|
||||
|
||||
void redo() override;
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
QByteArrayList m_deletedCharacters;
|
||||
int m_selectionTopRow, m_selectionBottomRow, m_selectionLeftColumn, m_selectionRightColumn;
|
||||
int m_selectionCornerRow, m_selectionCornerColumn;
|
||||
};
|
||||
|
||||
class PasteCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
PasteCommand(TeletextDocument *, int, QUndoCommand *parent = 0);
|
||||
|
||||
void redo() override;
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
QByteArrayList m_deletedCharacters, m_pastingCharacters;
|
||||
int m_pasteTopRow, m_pasteBottomRow, m_pasteLeftColumn, m_pasteRightColumn;
|
||||
int m_clipboardDataHeight, m_clipboardDataWidth;
|
||||
int m_selectionCornerRow, m_selectionCornerColumn;
|
||||
bool m_selectionActive;
|
||||
};
|
||||
#endif // !QT_NO_CLIPBOARD
|
||||
|
||||
class SetColourCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
SetColourCommand(TeletextDocument *, int, int, QUndoCommand *parent = 0);
|
||||
@@ -163,11 +192,10 @@ public:
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageIndex, m_colourIndex, m_oldColour, m_newColour;
|
||||
int m_colourIndex, m_oldColour, m_newColour;
|
||||
};
|
||||
|
||||
class ResetCLUTCommand : public QUndoCommand
|
||||
class ResetCLUTCommand : public LevelOneCommand
|
||||
{
|
||||
public:
|
||||
ResetCLUTCommand(TeletextDocument *, int, QUndoCommand *parent = 0);
|
||||
@@ -176,8 +204,7 @@ public:
|
||||
void undo() override;
|
||||
|
||||
private:
|
||||
TeletextDocument *m_teletextDocument;
|
||||
int m_subPageIndex, m_colourTable;
|
||||
int m_colourTable;
|
||||
int m_oldColourEntry[8];
|
||||
};
|
||||
|
||||
|
||||
@@ -27,21 +27,21 @@
|
||||
|
||||
LevelOnePage::LevelOnePage()
|
||||
{
|
||||
m_enhancements.reserve(208);
|
||||
m_enhancements.reserve(maxEnhancements());
|
||||
clearPage();
|
||||
}
|
||||
|
||||
LevelOnePage::LevelOnePage(const PageBase &other)
|
||||
{
|
||||
m_enhancements.reserve(208);
|
||||
m_enhancements.reserve(maxEnhancements());
|
||||
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;
|
||||
|
||||
@@ -52,6 +52,8 @@ public:
|
||||
|
||||
void clearPage();
|
||||
|
||||
int maxEnhancements() const { return 208; };
|
||||
|
||||
/* void setSubPageNumber(int); */
|
||||
int cycleValue() const { return m_cycleValue; };
|
||||
void setCycleValue(int);
|
||||
|
||||
69
loadsave.cpp
69
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);
|
||||
@@ -193,13 +193,17 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
outLine.insert(c, 0x1b);
|
||||
c++;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << outLine << Qt::endl;
|
||||
#else
|
||||
outStream << outLine << endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
@@ -207,14 +211,22 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
outLine[0] = designationCode | 0x40;
|
||||
for (int c=1; c<outLine.size(); c++)
|
||||
outLine[c] = outLine.at(c) | 0x40;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << outLine << Qt::endl;
|
||||
#else
|
||||
outStream << outLine << endl;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
outStream.setCodec("ISO-8859-1");
|
||||
|
||||
if (!document.description().isEmpty())
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << "DE," << document.description() << Qt::endl;
|
||||
#else
|
||||
outStream << "DE," << document.description() << endl;
|
||||
#endif
|
||||
|
||||
// TODO DS and SP commands
|
||||
|
||||
@@ -224,23 +236,50 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
|
||||
for (p=0; p<document.numberOfSubPages(); p++) {
|
||||
|
||||
outStream << QString("PN,%1%2").arg(document.pageNumber(), 3, 16, QChar('0')).arg(subPageNumber & 0xff, 2, 16, QChar('0')) << Qt::endl;
|
||||
// Page number
|
||||
outStream << QString("PN,%1%2").arg(document.pageNumber(), 3, 16, QChar('0')).arg(subPageNumber & 0xff, 2, 16, QChar('0'));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << Qt::endl;
|
||||
#else
|
||||
outStream << endl;
|
||||
#endif
|
||||
|
||||
// Subpage
|
||||
// Magazine Organisation Table and Magazine Inventory Page don't have subpages
|
||||
if (document.pageFunction() != TeletextDocument::PFMOT && document.pageFunction() != TeletextDocument::PFMIP)
|
||||
outStream << QString("SC,%1").arg(subPageNumber, 4, 16, QChar('0')) << Qt::endl;
|
||||
if (document.pageFunction() != TeletextDocument::PFMOT && document.pageFunction() != TeletextDocument::PFMIP) {
|
||||
outStream << QString("SC,%1").arg(subPageNumber, 4, 16, QChar('0'));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << Qt::endl;
|
||||
#else
|
||||
outStream << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
outStream << QString("PS,%1").arg(0x8000 | controlBitsToPS(document.subPage(p)), 4, 16, QChar('0')) << Qt::endl;
|
||||
// Status bits
|
||||
outStream << QString("PS,%1").arg(0x8000 | controlBitsToPS(document.subPage(p)), 4, 16, QChar('0'));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << Qt::endl;
|
||||
#else
|
||||
outStream << endl;
|
||||
#endif
|
||||
|
||||
// Cycle time
|
||||
if (document.pageFunction() == TeletextDocument::PFLevelOnePage)
|
||||
// Assume that only Level One Pages have configurable cycle times
|
||||
outStream << QString("CT,%1,%2").arg(document.subPage(p)->cycleValue()).arg(document.subPage(p)->cycleType()==LevelOnePage::CTcycles ? 'C' : 'T') << Qt::endl;
|
||||
outStream << QString("CT,%1,%2").arg(document.subPage(p)->cycleValue()).arg(document.subPage(p)->cycleType()==LevelOnePage::CTcycles ? 'C' : 'T');
|
||||
else
|
||||
// X/28/0 specifies page function and coding but the PF command
|
||||
// should make it obvious to a human that this isn't a Level One Page
|
||||
outStream << QString("PF,%1,%2").arg(document.pageFunction()).arg(document.packetCoding()) << Qt::endl;
|
||||
outStream << QString("PF,%1,%2").arg(document.pageFunction()).arg(document.packetCoding());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << Qt::endl;
|
||||
#else
|
||||
outStream << endl;
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
@@ -285,7 +324,11 @@ void saveTTI(QSaveFile &file, const TeletextDocument &document)
|
||||
if (i<5)
|
||||
outStream << ',';
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
outStream << Qt::endl;
|
||||
#else
|
||||
outStream << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
subPageNumber++;
|
||||
@@ -294,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, ' ');
|
||||
@@ -345,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;
|
||||
@@ -356,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.1-alpha");
|
||||
QApplication::setApplicationVersion("0.4-alpha");
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
|
||||
275
mainwidget.cpp
275
mainwidget.cpp
@@ -17,13 +17,18 @@
|
||||
* along with QTeletextMaker. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBitmap>
|
||||
#include <QClipboard>
|
||||
#include <QFrame>
|
||||
#include <QGraphicsItem>
|
||||
#include <QGraphicsItemGroup>
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPair>
|
||||
#include <QUndoCommand>
|
||||
@@ -50,7 +55,6 @@ TeletextWidget::TeletextWidget(QFrame *parent) : QFrame(parent)
|
||||
m_pageRender.setTeletextPage(m_levelOnePage);
|
||||
m_insertMode = false;
|
||||
m_selectionInProgress = false;
|
||||
m_grid = false;
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
m_flashTiming = m_flashPhase = 0;
|
||||
connect(&m_pageRender, &TeletextPageRender::flashChanged, this, &TeletextWidget::updateFlashTimer);
|
||||
@@ -58,7 +62,6 @@ TeletextWidget::TeletextWidget(QFrame *parent) : QFrame(parent)
|
||||
connect(m_teletextDocument, &TeletextDocument::subPageSelected, this, &TeletextWidget::subPageSelected);
|
||||
connect(m_teletextDocument, &TeletextDocument::contentsChange, this, &TeletextWidget::refreshRow);
|
||||
connect(m_teletextDocument, &TeletextDocument::refreshNeeded, this, &TeletextWidget::refreshPage);
|
||||
connect(m_teletextDocument, &TeletextDocument::selectionMoved, this, QOverload<>::of(&TeletextWidget::update));
|
||||
}
|
||||
|
||||
TeletextWidget::~TeletextWidget()
|
||||
@@ -109,13 +112,6 @@ void TeletextWidget::paintEvent(QPaintEvent *event)
|
||||
widgetPainter.drawPixmap(0, 0, *m_pageRender.pagePixmap(m_flashPhase), 864-m_pageRender.leftSidePanelColumns()*12, 0, m_pageRender.leftSidePanelColumns()*12, 250);
|
||||
if (m_pageRender.rightSidePanelColumns())
|
||||
widgetPainter.drawPixmap(480+m_pageRender.leftSidePanelColumns()*12, 0, *m_pageRender.pagePixmap(m_flashPhase), 480, 0, m_pageRender.rightSidePanelColumns()*12, 250);
|
||||
if (this->hasFocus())
|
||||
widgetPainter.fillRect((m_teletextDocument->cursorColumn()+m_pageRender.leftSidePanelColumns())*12, m_teletextDocument->cursorRow()*10, 12, 10, QColor(128, 128, 128, 192));
|
||||
if (m_teletextDocument->selectionActive()) {
|
||||
widgetPainter.setPen(QPen(QColor(192, 192, 192, 224), 1, Qt::DashLine));
|
||||
widgetPainter.setBrush(QBrush(QColor(255, 255, 255, 64)));
|
||||
widgetPainter.drawRect((m_teletextDocument->selectionLeftColumn()+m_pageRender.leftSidePanelColumns())*12, m_teletextDocument->selectionTopRow()*10, m_teletextDocument->selectionWidth()*12-1, m_teletextDocument->selectionHeight()*10-1);
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextWidget::updateFlashTimer(int newFlashTimer)
|
||||
@@ -144,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;
|
||||
@@ -161,14 +167,6 @@ void TeletextWidget::toggleMix(bool mixOn)
|
||||
update();
|
||||
}
|
||||
|
||||
void TeletextWidget::toggleGrid(bool gridOn)
|
||||
{
|
||||
m_grid = gridOn;
|
||||
m_pageRender.setGrid(gridOn);
|
||||
m_pageRender.renderPage();
|
||||
update();
|
||||
}
|
||||
|
||||
void TeletextWidget::setControlBit(int bitNumber, bool active)
|
||||
{
|
||||
m_levelOnePage->setControlBit(bitNumber, active);
|
||||
@@ -354,32 +352,27 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
|
||||
break;
|
||||
|
||||
case Qt::Key_Up:
|
||||
m_teletextDocument->cursorUp();
|
||||
update();
|
||||
m_teletextDocument->cursorUp(event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
m_teletextDocument->cursorDown();
|
||||
update();
|
||||
m_teletextDocument->cursorDown(event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
m_teletextDocument->cursorLeft();
|
||||
update();
|
||||
m_teletextDocument->cursorLeft(event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
m_teletextDocument->cursorRight();
|
||||
update();
|
||||
m_teletextDocument->cursorRight(event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
m_teletextDocument->cursorDown();
|
||||
// fall through
|
||||
case Qt::Key_Home:
|
||||
m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 0);
|
||||
update();
|
||||
break;
|
||||
case Qt::Key_Home:
|
||||
m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 0, event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
case Qt::Key_End:
|
||||
m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 39);
|
||||
update();
|
||||
m_teletextDocument->moveCursor(m_teletextDocument->cursorRow(), 39, event->modifiers() & Qt::ShiftModifier);
|
||||
break;
|
||||
|
||||
case Qt::Key_PageUp:
|
||||
@@ -405,8 +398,62 @@ void TeletextWidget::setCharacter(unsigned char newCharacter)
|
||||
|
||||
void TeletextWidget::toggleCharacterBit(unsigned char bitToToggle)
|
||||
{
|
||||
QUndoCommand *toggleMosaicBitCommand = new ToggleMosaicBitCommand(m_teletextDocument, bitToToggle);
|
||||
m_teletextDocument->undoStack()->push(toggleMosaicBitCommand);
|
||||
m_teletextDocument->undoStack()->push(new ToggleMosaicBitCommand(m_teletextDocument, bitToToggle));
|
||||
}
|
||||
|
||||
void TeletextWidget::selectionToClipboard()
|
||||
{
|
||||
QByteArray nativeData;
|
||||
QString plainTextData;
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
|
||||
nativeData.resize(2 + m_teletextDocument->selectionWidth() * m_teletextDocument->selectionHeight());
|
||||
nativeData[0] = m_teletextDocument->selectionHeight();
|
||||
nativeData[1] = m_teletextDocument->selectionWidth();
|
||||
|
||||
plainTextData.reserve((m_teletextDocument->selectionWidth()+1) * m_teletextDocument->selectionHeight() - 1);
|
||||
|
||||
int i=2;
|
||||
|
||||
for (int r=m_teletextDocument->selectionTopRow(); r<=m_teletextDocument->selectionBottomRow(); r++) {
|
||||
for (int c=m_teletextDocument->selectionLeftColumn(); c<=m_teletextDocument->selectionRightColumn(); c++) {
|
||||
nativeData[i++] = m_teletextDocument->currentSubPage()->character(r, c);
|
||||
|
||||
if (m_teletextDocument->currentSubPage()->character(r, c) >= 0x20)
|
||||
plainTextData.append(keymapping[m_pageRender.level1CharSet(r, c)].key(m_teletextDocument->currentSubPage()->character(r, c), m_teletextDocument->currentSubPage()->character(r, c)));
|
||||
else
|
||||
plainTextData.append(' ');
|
||||
}
|
||||
|
||||
plainTextData.append('\n');
|
||||
}
|
||||
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
mimeData->setData("application/x-teletext", nativeData);
|
||||
mimeData->setText(plainTextData);
|
||||
clipboard->setMimeData(mimeData);
|
||||
}
|
||||
|
||||
void TeletextWidget::cut()
|
||||
{
|
||||
if (!m_teletextDocument->selectionActive())
|
||||
return;
|
||||
|
||||
selectionToClipboard();
|
||||
m_teletextDocument->undoStack()->push(new CutCommand(m_teletextDocument));
|
||||
}
|
||||
|
||||
void TeletextWidget::copy()
|
||||
{
|
||||
if (!m_teletextDocument->selectionActive())
|
||||
return;
|
||||
|
||||
selectionToClipboard();
|
||||
}
|
||||
|
||||
void TeletextWidget::paste()
|
||||
{
|
||||
m_teletextDocument->undoStack()->push(new PasteCommand(m_teletextDocument, m_pageRender.level1CharSet(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn())));
|
||||
}
|
||||
|
||||
QPair<int, int> TeletextWidget::mouseToRowAndColumn(const QPoint &mousePosition)
|
||||
@@ -438,25 +485,12 @@ void TeletextWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
QPair<int, int> position = mouseToRowAndColumn(event->pos());
|
||||
if (m_selectionInProgress || position.first != m_teletextDocument->cursorRow() || position.second != m_teletextDocument->cursorColumn()) {
|
||||
int topRow, bottomRow, leftColumn, rightColumn;
|
||||
|
||||
if (position.first != m_teletextDocument->cursorRow() || position.second != m_teletextDocument->cursorColumn()) {
|
||||
if (!m_selectionInProgress) {
|
||||
m_selectionInProgress = true;
|
||||
if (m_teletextDocument->cursorRow() < position.first) {
|
||||
topRow = m_teletextDocument->cursorRow();
|
||||
bottomRow = position.first;
|
||||
} else {
|
||||
topRow = position.first;
|
||||
bottomRow = m_teletextDocument->cursorRow();
|
||||
m_teletextDocument->setSelectionCorner(m_teletextDocument->cursorRow(), m_teletextDocument->cursorColumn());
|
||||
}
|
||||
if (m_teletextDocument->cursorColumn() < position.second) {
|
||||
leftColumn = m_teletextDocument->cursorColumn();
|
||||
rightColumn = position.second;
|
||||
} else {
|
||||
leftColumn = position.second;
|
||||
rightColumn = m_teletextDocument->cursorColumn();
|
||||
}
|
||||
m_teletextDocument->setSelection(topRow, leftColumn, bottomRow, rightColumn);
|
||||
m_teletextDocument->moveCursor(position.first, position.second, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,7 +514,12 @@ void TeletextWidget::focusOutEvent(QFocusEvent *event)
|
||||
|
||||
LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphicsScene(parent)
|
||||
{
|
||||
m_grid = false;
|
||||
|
||||
// These dimensions are scratch, setBorderDimensions will get called straight away to adjust them
|
||||
setSceneRect(0, 0, 600, 288);
|
||||
|
||||
// Full screen colours
|
||||
m_fullScreenTopRectItem = new QGraphicsRectItem(0, 0, 600, 19);
|
||||
m_fullScreenTopRectItem->setPen(Qt::NoPen);
|
||||
m_fullScreenTopRectItem->setBrush(QBrush(QColor(0, 0, 0)));
|
||||
@@ -490,6 +529,7 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi
|
||||
m_fullScreenBottomRectItem->setBrush(QBrush(QColor(0, 0, 0)));
|
||||
addItem(m_fullScreenBottomRectItem);
|
||||
|
||||
// Full row colours
|
||||
for (int r=0; r<25; r++) {
|
||||
m_fullRowLeftRectItem[r] = new QGraphicsRectItem(0, 19+r*10, 60, 10);
|
||||
m_fullRowLeftRectItem[r]->setPen(Qt::NoPen);
|
||||
@@ -501,16 +541,59 @@ LevelOneScene::LevelOneScene(QWidget *levelOneWidget, QObject *parent) : QGraphi
|
||||
addItem(m_fullRowRightRectItem[r]);
|
||||
}
|
||||
|
||||
// Main text widget
|
||||
m_levelOneProxyWidget = addWidget(levelOneWidget);
|
||||
m_levelOneProxyWidget->setPos(60, 19);
|
||||
m_levelOneProxyWidget->setAutoFillBackground(false);
|
||||
m_levelOneProxyWidget->setFocus();
|
||||
|
||||
// Selection
|
||||
m_selectionRectItem = new QGraphicsRectItem(0, 0, 12, 10);
|
||||
m_selectionRectItem->setVisible(false);
|
||||
m_selectionRectItem->setPen(QPen(QColor(192, 192, 192), 1, Qt::DashLine));
|
||||
m_selectionRectItem->setBrush(QBrush(QColor(255, 255, 255, 64)));
|
||||
addItem(m_selectionRectItem);
|
||||
|
||||
// Cursor
|
||||
m_cursorRectItem = new QGraphicsRectItem(0, 0, 12, 10);
|
||||
m_cursorRectItem->setPen(Qt::NoPen);
|
||||
m_cursorRectItem->setBrush(QBrush(QColor(128, 128, 128, 192)));
|
||||
addItem(m_cursorRectItem);
|
||||
|
||||
// Optional grid overlay for text widget
|
||||
m_mainGridItemGroup = new QGraphicsItemGroup;
|
||||
m_mainGridItemGroup->setVisible(false);
|
||||
addItem(m_mainGridItemGroup);
|
||||
// Additional vertical pieces of grid for side panels
|
||||
for (int i=0; i<32; i++) {
|
||||
m_sidePanelGridNeeded[i] = false;
|
||||
m_sidePanelGridItemGroup[i] = new QGraphicsItemGroup;
|
||||
m_sidePanelGridItemGroup[i]->setVisible(false);
|
||||
addItem(m_sidePanelGridItemGroup[i]);
|
||||
}
|
||||
for (int r=1; r<25; r++) {
|
||||
for (int c=0; c<40; c++) {
|
||||
QGraphicsRectItem *gridPiece = new QGraphicsRectItem(c*12, r*10, 12, 10);
|
||||
gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, r<24 ? 192 : 128)), 0));
|
||||
m_mainGridItemGroup->addToGroup(gridPiece);
|
||||
}
|
||||
|
||||
void LevelOneScene::setDimensions(int sceneWidth, int sceneHeight, int widgetWidth)
|
||||
if (r < 24)
|
||||
for (int c=0; c<32; c++) {
|
||||
QGraphicsRectItem *gridPiece = new QGraphicsRectItem(0, r*10, 12, 10);
|
||||
gridPiece->setPen(QPen(QBrush(QColor(128, 128, 128, 64)), 0));
|
||||
m_sidePanelGridItemGroup[c]->addToGroup(gridPiece);
|
||||
}
|
||||
}
|
||||
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
void LevelOneScene::setBorderDimensions(int sceneWidth, int sceneHeight, int widgetWidth, int leftSidePanelColumns, int rightSidePanelColumns)
|
||||
{
|
||||
setSceneRect(0, 0, sceneWidth, sceneHeight);
|
||||
|
||||
// Assume widget height is always 250
|
||||
// Assume text widget height is always 250
|
||||
int topBottomBorders = (sceneHeight-250) / 2;
|
||||
// Ideally we'd use m_levelOneProxyWidget->size() to discover the widget width ourselves
|
||||
// but this causes a stubborn segfault, so we have to receive the widgetWidth as a parameter
|
||||
@@ -518,15 +601,103 @@ void LevelOneScene::setDimensions(int sceneWidth, int sceneHeight, int widgetWid
|
||||
|
||||
m_levelOneProxyWidget->setPos(leftRightBorders, topBottomBorders);
|
||||
|
||||
// Position grid to cover central 40 columns
|
||||
m_mainGridItemGroup->setPos(leftRightBorders + leftSidePanelColumns*12, topBottomBorders);
|
||||
|
||||
updateCursor();
|
||||
updateSelection();
|
||||
|
||||
// Grid for right side panel
|
||||
for (int c=0; c<16; c++)
|
||||
if (rightSidePanelColumns > c) {
|
||||
m_sidePanelGridItemGroup[c]->setPos(leftRightBorders + leftSidePanelColumns*12 + 480 + c*12, topBottomBorders);
|
||||
m_sidePanelGridItemGroup[c]->setVisible(m_grid);
|
||||
m_sidePanelGridNeeded[c] = true;
|
||||
} else {
|
||||
m_sidePanelGridItemGroup[c]->setVisible(false);
|
||||
m_sidePanelGridNeeded[c] = false;
|
||||
}
|
||||
// Grid for left side panel
|
||||
for (int c=0; c<16; c++)
|
||||
if (c < leftSidePanelColumns) {
|
||||
m_sidePanelGridItemGroup[31-c]->setPos(leftRightBorders + (leftSidePanelColumns-c-1)*12, topBottomBorders);
|
||||
m_sidePanelGridItemGroup[31-c]->setVisible(m_grid);
|
||||
m_sidePanelGridNeeded[31-c] = true;
|
||||
} else {
|
||||
m_sidePanelGridItemGroup[31-c]->setVisible(false);
|
||||
m_sidePanelGridNeeded[31-c] = false;
|
||||
}
|
||||
|
||||
// Full screen colours
|
||||
m_fullScreenTopRectItem->setRect(0, 0, sceneWidth, topBottomBorders);
|
||||
m_fullScreenBottomRectItem->setRect(0, 250+topBottomBorders, sceneWidth, topBottomBorders);
|
||||
|
||||
// Full row colours
|
||||
for (int r=0; r<25; r++) {
|
||||
m_fullRowLeftRectItem[r]->setRect(0, topBottomBorders+r*10, leftRightBorders+1, 10);
|
||||
m_fullRowRightRectItem[r]->setRect(sceneWidth-leftRightBorders-1, topBottomBorders+r*10, leftRightBorders+1, 10);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelOneScene::updateCursor()
|
||||
{
|
||||
m_cursorRectItem->setPos(m_mainGridItemGroup->pos().x() + static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->cursorColumn()*12, m_mainGridItemGroup->pos().y() + static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->cursorRow()*10);
|
||||
}
|
||||
|
||||
void LevelOneScene::updateSelection()
|
||||
{
|
||||
if (!static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionActive()) {
|
||||
m_selectionRectItem->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_selectionRectItem->setRect(m_mainGridItemGroup->pos().x() + static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionLeftColumn()*12, m_mainGridItemGroup->pos().y() + static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionTopRow()*10, static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionWidth()*12-1, static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->document()->selectionHeight()*10-1);
|
||||
|
||||
m_selectionRectItem->setVisible(true);
|
||||
}
|
||||
|
||||
void LevelOneScene::toggleGrid(bool gridOn)
|
||||
{
|
||||
m_grid = gridOn;
|
||||
m_mainGridItemGroup->setVisible(gridOn);
|
||||
for (int i=0; i<32; i++)
|
||||
if (m_sidePanelGridNeeded[i])
|
||||
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);
|
||||
|
||||
if (event->type() == QEvent::GraphicsSceneWheel && static_cast<QGraphicsSceneWheelEvent *>(event)->modifiers() == Qt::ControlModifier) {
|
||||
if (static_cast<QGraphicsSceneWheelEvent *>(event)->delta() > 0)
|
||||
emit mouseZoomIn();
|
||||
else if (static_cast<QGraphicsSceneWheelEvent *>(event)->delta() < 0)
|
||||
emit mouseZoomOut();
|
||||
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LevelOneScene::setFullScreenColour(const QColor &newColor)
|
||||
{
|
||||
m_fullScreenTopRectItem->setBrush(QBrush(newColor));
|
||||
|
||||
29
mainwidget.h
29
mainwidget.h
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <QBasicTimer>
|
||||
#include <QFrame>
|
||||
#include <QGraphicsItemGroup>
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QGraphicsScene>
|
||||
#include <QPair>
|
||||
@@ -62,8 +63,8 @@ public slots:
|
||||
void refreshPage();
|
||||
void toggleReveal(bool);
|
||||
void toggleMix(bool);
|
||||
void toggleGrid(bool);
|
||||
void updateFlashTimer(int);
|
||||
void pauseFlash(bool);
|
||||
void refreshRow(int);
|
||||
|
||||
void setControlBit(int, bool);
|
||||
@@ -75,6 +76,11 @@ public slots:
|
||||
void setBlackBackgroundSubst(bool);
|
||||
void setSidePanelWidths(int, int);
|
||||
void setSidePanelAtL35Only(bool);
|
||||
|
||||
void cut();
|
||||
void copy();
|
||||
void paste();
|
||||
|
||||
void changeSize();
|
||||
|
||||
protected:
|
||||
@@ -91,13 +97,12 @@ protected:
|
||||
private:
|
||||
TeletextDocument* m_teletextDocument;
|
||||
LevelOnePage* m_levelOnePage;
|
||||
bool m_insertMode, m_grid, m_selectionInProgress;
|
||||
bool m_insertMode, m_selectionInProgress;
|
||||
QBasicTimer m_flashTimer;
|
||||
int m_flashTiming, m_flashPhase;
|
||||
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
void calculateDimensions();
|
||||
void selectionToClipboard();
|
||||
|
||||
QPair<int, int> mouseToRowAndColumn(const QPoint &);
|
||||
};
|
||||
@@ -108,16 +113,30 @@ class LevelOneScene : public QGraphicsScene
|
||||
|
||||
public:
|
||||
LevelOneScene(QWidget *, QObject *parent = nullptr);
|
||||
void setDimensions(int, int, int);
|
||||
void setBorderDimensions(int, int, int, int, int);
|
||||
QGraphicsRectItem *cursorRectItem() const { return m_cursorRectItem; }
|
||||
|
||||
public slots:
|
||||
void updateCursor();
|
||||
void updateSelection();
|
||||
void toggleGrid(bool);
|
||||
void hideGUIElements(bool);
|
||||
void setFullScreenColour(const QColor &);
|
||||
void setFullRowColour(int, const QColor &);
|
||||
|
||||
signals:
|
||||
void mouseZoomIn();
|
||||
void mouseZoomOut();
|
||||
|
||||
private:
|
||||
bool eventFilter(QObject *, QEvent *);
|
||||
|
||||
QGraphicsRectItem *m_fullScreenTopRectItem, *m_fullScreenBottomRectItem;
|
||||
QGraphicsRectItem *m_fullRowLeftRectItem[25], *m_fullRowRightRectItem[25];
|
||||
QGraphicsProxyWidget *m_levelOneProxyWidget;
|
||||
QGraphicsRectItem *m_cursorRectItem, *m_selectionRectItem;
|
||||
QGraphicsItemGroup *m_mainGridItemGroup, *m_sidePanelGridItemGroup[32];
|
||||
bool m_grid, m_sidePanelGridNeeded[32];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
135
mainwindow.cpp
135
mainwindow.cpp
@@ -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())));
|
||||
@@ -132,12 +184,12 @@ void MainWindow::exportEditTF()
|
||||
|
||||
void MainWindow::about()
|
||||
{
|
||||
QMessageBox::about(this, tr("About QTeletextMaker"), tr("<b>QTeletextMaker</b><br>"
|
||||
QMessageBox::about(this, tr("About"), QString("<b>%1</b><br>"
|
||||
"An open source Level 2.5 teletext page editor.<br>"
|
||||
"<i>Version 0.1-alpha</i><br><br>"
|
||||
"<i>Version %2</i><br><br>"
|
||||
"Copyright (C) 2020, 2021 Gavin MacGregor<br><br>"
|
||||
"Released under the GNU General Public License version 3<br>"
|
||||
"<a href=\"https://github.com/gkthemac/qteletextmaker\">https://github.com/gkthemac/qteletextmaker</a>"));
|
||||
"<a href=\"https://github.com/gkthemac/qteletextmaker\">https://github.com/gkthemac/qteletextmaker</a>").arg(QApplication::applicationDisplayName()).arg(QApplication::applicationVersion()));
|
||||
}
|
||||
|
||||
void MainWindow::init()
|
||||
@@ -157,21 +209,23 @@ void MainWindow::init()
|
||||
m_paletteDockWidget = new PaletteDockWidget(m_textWidget);
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_paletteDockWidget);
|
||||
|
||||
m_textScene = new LevelOneScene(m_textWidget, this);
|
||||
|
||||
createActions();
|
||||
createStatusBar();
|
||||
|
||||
readSettings();
|
||||
|
||||
m_textScene = new LevelOneScene(m_textWidget, this);
|
||||
|
||||
m_textView = new QGraphicsView(this);
|
||||
m_textView->setScene(m_textScene);
|
||||
if (m_viewSmoothTransform)
|
||||
m_textView->setRenderHints(QPainter::SmoothPixmapTransform);
|
||||
m_textView->setBackgroundBrush(QBrush(QColor(32, 48, 96)));
|
||||
setSceneDimensions();
|
||||
setCentralWidget(m_textView);
|
||||
|
||||
connect(m_textWidget->document(), &TeletextDocument::cursorMoved, this, &MainWindow::updateCursorPosition);
|
||||
connect(m_textWidget->document(), &TeletextDocument::selectionMoved, m_textScene, &LevelOneScene::updateSelection);
|
||||
connect(m_textWidget->document()->undoStack(), &QUndoStack::cleanChanged, this, [=]() { setWindowModified(!m_textWidget->document()->undoStack()->isClean()); } );
|
||||
connect(m_textWidget->document(), &TeletextDocument::aboutToChangeSubPage, m_x26DockWidget, &X26DockWidget::unloadX26List);
|
||||
connect(m_textWidget->document(), &TeletextDocument::subPageSelected, this, &MainWindow::updatePageWidgets);
|
||||
@@ -180,12 +234,17 @@ void MainWindow::init()
|
||||
connect(m_textWidget->pageRender(), &TeletextPageRender::fullRowColourChanged, m_textScene, &LevelOneScene::setFullRowColour);
|
||||
connect(m_textWidget, &TeletextWidget::insertKeyPressed, this, &MainWindow::toggleInsertMode);
|
||||
|
||||
connect(m_textScene, &LevelOneScene::mouseZoomIn, this, &MainWindow::zoomIn);
|
||||
connect(m_textScene, &LevelOneScene::mouseZoomOut, this, &MainWindow::zoomOut);
|
||||
|
||||
QShortcut *blockShortCut = new QShortcut(QKeySequence(Qt::Key_Escape, Qt::Key_J), m_textView);
|
||||
connect(blockShortCut, &QShortcut::activated, [=]() { m_textWidget->setCharacter(0x7f); });
|
||||
|
||||
setUnifiedTitleAndToolBarOnMac(true);
|
||||
|
||||
updatePageWidgets();
|
||||
|
||||
m_textView->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::tile(const QMainWindow *previous)
|
||||
@@ -251,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();
|
||||
@@ -291,36 +354,33 @@ void MainWindow::createActions()
|
||||
redoAction->setShortcuts(QKeySequence::Redo);
|
||||
|
||||
editMenu->addSeparator();
|
||||
|
||||
#ifndef QT_NO_CLIPBOARD
|
||||
/* const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png"));
|
||||
const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png"));
|
||||
QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this);
|
||||
cutAct->setShortcuts(QKeySequence::Cut);
|
||||
cutAct->setStatusTip(tr("Cut the current selection's contents to the "
|
||||
"clipboard"));
|
||||
connect(cutAct, &QAction::triggered, textWidget, &QTextEdit::cut);
|
||||
cutAct->setStatusTip(tr("Cut the current selection's contents to the clipboard"));
|
||||
connect(cutAct, &QAction::triggered, m_textWidget, &TeletextWidget::cut);
|
||||
editMenu->addAction(cutAct);
|
||||
editToolBar->addAction(cutAct);
|
||||
|
||||
const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png"));
|
||||
QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this);
|
||||
copyAct->setShortcuts(QKeySequence::Copy);
|
||||
copyAct->setStatusTip(tr("Copy the current selection's contents to the "
|
||||
"clipboard"));
|
||||
connect(copyAct, &QAction::triggered, textWidget, &QTextEdit::copy);
|
||||
copyAct->setStatusTip(tr("Copy the current selection's contents to the clipboard"));
|
||||
connect(copyAct, &QAction::triggered, m_textWidget, &TeletextWidget::copy);
|
||||
editMenu->addAction(copyAct);
|
||||
editToolBar->addAction(copyAct);
|
||||
|
||||
const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png"));
|
||||
QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
|
||||
pasteAct->setShortcuts(QKeySequence::Paste);
|
||||
pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
|
||||
"selection"));
|
||||
connect(pasteAct, &QAction::triggered, textWidget, &QTextEdit::paste);
|
||||
pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current selection"));
|
||||
connect(pasteAct, &QAction::triggered, m_textWidget, &TeletextWidget::paste);
|
||||
editMenu->addAction(pasteAct);
|
||||
editToolBar->addAction(pasteAct);
|
||||
|
||||
editMenu->addSeparator();
|
||||
*/
|
||||
#endif // !QT_NO_CLIPBOARD
|
||||
|
||||
QAction *insertBlankRowAct = editMenu->addAction(tr("Insert blank row"));
|
||||
@@ -374,7 +434,7 @@ void MainWindow::createActions()
|
||||
gridAct->setCheckable(true);
|
||||
gridAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_G));
|
||||
gridAct->setStatusTip(tr("Toggle the text grid"));
|
||||
connect(gridAct, &QAction::toggled, m_textWidget, &TeletextWidget::toggleGrid);
|
||||
connect(gridAct, &QAction::toggled, m_textScene, &LevelOneScene::toggleGrid);
|
||||
|
||||
QAction *showCodesAct = viewMenu->addAction(tr("Show codes"));
|
||||
showCodesAct->setCheckable(true);
|
||||
@@ -415,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"));
|
||||
@@ -552,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 };
|
||||
@@ -568,7 +634,7 @@ void MainWindow::setSceneDimensions()
|
||||
else
|
||||
newSceneWidth = m_textWidget->width() + leftRightBorders[m_viewBorder]*2;
|
||||
|
||||
m_textScene->setDimensions(newSceneWidth, 250+topBottomBorders[m_viewBorder]*2, m_textWidget->width());
|
||||
m_textScene->setBorderDimensions(newSceneWidth, 250+topBottomBorders[m_viewBorder]*2, m_textWidget->width(), m_textWidget->pageRender()->leftSidePanelColumns(), m_textWidget->pageRender()->rightSidePanelColumns());
|
||||
m_textView->setTransform(QTransform((1+(float)m_viewZoom/2)*aspectRatioHorizontalScaling[m_viewAspectRatio], 0, 0, 1+(float)m_viewZoom/2, 0, 0));
|
||||
}
|
||||
|
||||
@@ -576,20 +642,17 @@ void MainWindow::insertRow(bool copyRow)
|
||||
{
|
||||
if (m_textWidget->document()->cursorRow() == 24)
|
||||
return;
|
||||
QUndoCommand *insertRowCommand = new InsertRowCommand(m_textWidget->document(), copyRow);
|
||||
m_textWidget->document()->undoStack()->push(insertRowCommand);
|
||||
m_textWidget->document()->undoStack()->push(new InsertRowCommand(m_textWidget->document(), copyRow));
|
||||
}
|
||||
|
||||
void MainWindow::deleteRow()
|
||||
{
|
||||
QUndoCommand *deleteRowCommand = new DeleteRowCommand(m_textWidget->document());
|
||||
m_textWidget->document()->undoStack()->push(deleteRowCommand);
|
||||
m_textWidget->document()->undoStack()->push(new DeleteRowCommand(m_textWidget->document()));
|
||||
}
|
||||
|
||||
void MainWindow::insertSubPage(bool afterCurrentSubPage, bool copyCurrentSubPage)
|
||||
{
|
||||
QUndoCommand *insertSubPageCommand = new InsertSubPageCommand(m_textWidget->document(), afterCurrentSubPage, copyCurrentSubPage);
|
||||
m_textWidget->document()->undoStack()->push(insertSubPageCommand);
|
||||
m_textWidget->document()->undoStack()->push(new InsertSubPageCommand(m_textWidget->document(), afterCurrentSubPage, copyCurrentSubPage));
|
||||
}
|
||||
|
||||
void MainWindow::deleteSubPage()
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -926,6 +1003,8 @@ MainWindow *MainWindow::findMainWindow(const QString &fileName) const
|
||||
void MainWindow::updateCursorPosition()
|
||||
{
|
||||
m_cursorPositionLabel->setText(QString("Row %1 Column %2").arg(m_textWidget->document()->cursorRow()).arg(m_textWidget->document()->cursorColumn()));
|
||||
m_textScene->updateCursor();
|
||||
m_textView->ensureVisible(m_textScene->cursorRectItem(), 16, 24);
|
||||
}
|
||||
|
||||
void MainWindow::updatePageWidgets()
|
||||
|
||||
@@ -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,14 +31,15 @@ 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:
|
||||
QByteArray packetFromEnhancementList(int) const;
|
||||
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 };
|
||||
};
|
||||
|
||||
|
||||
@@ -108,14 +108,11 @@ void PaletteDockWidget::selectColour(int colourIndex)
|
||||
{
|
||||
const QColor newColour = QColorDialog::getColor(m_parentMainWidget->document()->currentSubPage()->CLUTtoQColor(colourIndex), this, "Select Colour");
|
||||
|
||||
if (newColour.isValid()) {
|
||||
QUndoCommand *setColourCommand = new SetColourCommand(m_parentMainWidget->document(), colourIndex, ((newColour.red() & 0xf0) << 4) | (newColour.green() & 0xf0) | ((newColour.blue() & 0xf0) >> 4));
|
||||
m_parentMainWidget->document()->undoStack()->push(setColourCommand);
|
||||
}
|
||||
if (newColour.isValid())
|
||||
m_parentMainWidget->document()->undoStack()->push(new SetColourCommand(m_parentMainWidget->document(), colourIndex, ((newColour.red() & 0xf0) << 4) | (newColour.green() & 0xf0) | ((newColour.blue() & 0xf0) >> 4)));
|
||||
}
|
||||
|
||||
void PaletteDockWidget::resetCLUT(int colourTable)
|
||||
{
|
||||
QUndoCommand *resetCLUTCommand = new ResetCLUTCommand(m_parentMainWidget->document(), colourTable);
|
||||
m_parentMainWidget->document()->undoStack()->push(resetCLUTCommand);
|
||||
m_parentMainWidget->document()->undoStack()->push(new ResetCLUTCommand(m_parentMainWidget->document(), colourTable));
|
||||
}
|
||||
|
||||
48
render.cpp
48
render.cpp
@@ -26,15 +26,30 @@
|
||||
|
||||
#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);
|
||||
m_grid = m_mix = m_reveal = m_showCodes = false;
|
||||
m_mix = m_reveal = m_showCodes = false;
|
||||
m_renderLevel = 0;
|
||||
m_flashRequired = 0;
|
||||
m_finalFullScreenColour = 0;
|
||||
@@ -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)
|
||||
@@ -92,7 +106,6 @@ void TeletextPageRender::setRenderLevel(int newRenderLevel)
|
||||
renderPage();
|
||||
}
|
||||
|
||||
void TeletextPageRender::setGrid(bool newGrid) { m_grid = newGrid; }
|
||||
void TeletextPageRender::setShowCodes(bool newShowCodes)
|
||||
{
|
||||
m_showCodes = newShowCodes;
|
||||
@@ -164,7 +177,7 @@ void TeletextPageRender::buildEnhanceMap(TextLayer *enhanceLayer, int tripletNum
|
||||
if ((x26Triplet->address() & 0x18) == 0x08) {
|
||||
// Local Object
|
||||
// Check if the pointer in the Invocation triplet is valid
|
||||
// Can't point to triplets 13-15; only 12 triplets per packet
|
||||
// Can't point to triplets 13-15; only triplets 0-12 per packet
|
||||
if ((x26Triplet->data() & 0x0f) > 12)
|
||||
break;
|
||||
int tripletPointer = ((x26Triplet->data() >> 4) | ((x26Triplet->address() & 1) << 3)) * 13 + (x26Triplet->data() & 0x0f);
|
||||
@@ -174,12 +187,15 @@ void TeletextPageRender::buildEnhanceMap(TextLayer *enhanceLayer, int tripletNum
|
||||
// Check if we're pointing to an actual Object Definition of the same type
|
||||
if ((x26Triplet->mode() | 0x04) != m_levelOnePage->enhancements()->at(tripletPointer).mode())
|
||||
break;
|
||||
// The Object Definition can't declare it's at triplet 13-15; only 12 triplets per packet
|
||||
// The Object Definition can't declare it's at triplet 13-15; only triplets 0-12 per packet
|
||||
if ((m_levelOnePage->enhancements()->at(tripletPointer).data() & 0x0f) > 12)
|
||||
break;
|
||||
// Check if the Object Definition triplet is where it declares it is
|
||||
if ((((m_levelOnePage->enhancements()->at(tripletPointer).data() >> 4) | ((m_levelOnePage->enhancements()->at(tripletPointer).address() & 1) << 3)) * 13 + (m_levelOnePage->enhancements()->at(tripletPointer).data() & 0x0f)) != tripletPointer)
|
||||
break;
|
||||
// Check if (sub)Object type can be invoked by Object type we're within
|
||||
if (enhanceLayer->objectType() >= (x26Triplet->mode() & 0x03))
|
||||
break;
|
||||
// Is the object required at the current presentation Level?
|
||||
if (m_renderLevel == 2 && (m_levelOnePage->enhancements()->at(tripletPointer).address() & 0x08) == 0x00)
|
||||
break;
|
||||
@@ -188,7 +204,7 @@ void TeletextPageRender::buildEnhanceMap(TextLayer *enhanceLayer, int tripletNum
|
||||
EnhanceLayer *newLayer = new EnhanceLayer;
|
||||
m_textLayer.push_back(newLayer);
|
||||
newLayer->setObjectType(x26Triplet->mode() & 0x03);
|
||||
newLayer->setOrigin(activePosition.row()+originModifierR, activePosition.column()+originModifierC);
|
||||
newLayer->setOrigin(enhanceLayer->originR() + activePosition.row() + originModifierR, enhanceLayer->originC() + activePosition.column() + originModifierC);
|
||||
buildEnhanceMap(newLayer, tripletPointer+1);
|
||||
} else
|
||||
qDebug("POP or GPOP");
|
||||
@@ -257,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);
|
||||
@@ -346,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);
|
||||
}
|
||||
}
|
||||
@@ -360,12 +376,6 @@ void TeletextPageRender::renderPage(int r)
|
||||
else
|
||||
pixmapPainter.drawRect(c*12, r*10+18, charWidth-1, 1);
|
||||
}
|
||||
|
||||
if (m_grid) {
|
||||
pixmapPainter.setPen(QColor(128, 128, 128, (c <= 39) ? 192 : 64));
|
||||
pixmapPainter.drawRect(c*12, r*10, 11, 9);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
pixmapPainter.begin(m_pagePixmap[0]);
|
||||
@@ -487,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)
|
||||
|
||||
23
render.h
23
render.h
@@ -107,6 +107,8 @@ public:
|
||||
virtual int fullRowColour(int) const =0;
|
||||
virtual bool fullRowDownwards(int) const =0;
|
||||
virtual int objectType() const =0;
|
||||
virtual int originR() const { return 0; };
|
||||
virtual int originC() const { return 0; };
|
||||
void setFullScreenColour(int);
|
||||
void setFullRowColour(int, int, bool);
|
||||
|
||||
@@ -131,6 +133,8 @@ public:
|
||||
int fullRowColour(int r) const { return m_layerFullRowColour[r]; };
|
||||
bool fullRowDownwards(int r) const { return m_layerFullRowDownwards[r]; };
|
||||
int objectType() const { return m_objectType; };
|
||||
int originR() const { return m_originR; };
|
||||
int originC() const { return m_originC; };
|
||||
void setObjectType(int);
|
||||
void setOrigin(int, int);
|
||||
|
||||
@@ -175,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
|
||||
@@ -185,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);
|
||||
@@ -193,7 +211,6 @@ public:
|
||||
int level1CharSet(int r, int c) const { return m_cell[r][c].level1CharSet; };
|
||||
int leftSidePanelColumns() const { return m_leftSidePanelColumns; };
|
||||
int rightSidePanelColumns() const { return m_rightSidePanelColumns; };
|
||||
void setGrid(bool);
|
||||
|
||||
public slots:
|
||||
void setReveal(bool);
|
||||
@@ -212,12 +229,12 @@ 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;
|
||||
int m_leftSidePanelColumns, m_rightSidePanelColumns;
|
||||
bool m_reveal, m_mix, m_grid, m_showCodes;
|
||||
bool m_reveal, m_mix, m_showCodes;
|
||||
Level1Layer m_level1Layer;
|
||||
std::vector<TextLayer *> m_textLayer;
|
||||
const int m_foregroundRemap[8] = { 0, 0, 0, 8, 8, 16, 16, 16 };
|
||||
|
||||
@@ -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);
|
||||
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;
|
||||
|
||||
718
x26model.cpp
718
x26model.cpp
File diff suppressed because it is too large
Load Diff
17
x26model.h
17
x26model.h
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include "mainwidget.h"
|
||||
#include "render.h"
|
||||
|
||||
class X26Model : public QAbstractListModel
|
||||
{
|
||||
@@ -49,9 +50,9 @@ public:
|
||||
private:
|
||||
TeletextWidget *m_parentMainWidget;
|
||||
bool m_listLoaded;
|
||||
};
|
||||
TeletextFontBitmap m_fontBitmap;
|
||||
|
||||
static const QString modeTripletName[64] {
|
||||
const QString m_modeTripletName[64] {
|
||||
// Row triplet modes
|
||||
"Full screen colour",
|
||||
"Full row colour",
|
||||
@@ -132,4 +133,16 @@ static const QString modeTripletName[64] {
|
||||
"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