diff --git a/levelonecommands.cpp b/levelonecommands.cpp index 89ceb28..9936605 100644 --- a/levelonecommands.cpp +++ b/levelonecommands.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "levelonecommands.h" @@ -318,6 +319,145 @@ bool DeleteKeyCommand::mergeWith(const QUndoCommand *command) } +ShiftMosaicsCommand::ShiftMosaicsCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent) +{ + m_selectionTopRow = m_teletextDocument->selectionTopRow(); + m_selectionLeftColumn = m_teletextDocument->selectionLeftColumn(); + m_selectionBottomRow = m_teletextDocument->selectionBottomRow(); + m_selectionRightColumn = m_teletextDocument->selectionRightColumn(); + + m_selectionCornerRow = m_teletextDocument->selectionCornerRow(); + m_selectionCornerColumn = m_teletextDocument->selectionCornerColumn(); + + m_mosaicList = mosaicList; + + m_oldCharacters = storeCharacters(m_selectionTopRow, m_selectionLeftColumn, m_selectionBottomRow, m_selectionRightColumn); + m_newCharacters = m_oldCharacters; +} + +void ShiftMosaicsCommand::redo() +{ + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + retrieveCharacters(m_selectionTopRow, m_selectionLeftColumn, m_newCharacters); + + emit m_teletextDocument->contentsChanged(); + + m_teletextDocument->setSelectionCorner(m_selectionCornerRow, m_selectionCornerColumn); + m_teletextDocument->moveCursor(m_row, m_column, true); +} + +void ShiftMosaicsCommand::undo() +{ + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + retrieveCharacters(m_selectionTopRow, m_selectionLeftColumn, m_oldCharacters); + + emit m_teletextDocument->contentsChanged(); + + m_teletextDocument->setSelectionCorner(m_selectionCornerRow, m_selectionCornerColumn); + m_teletextDocument->moveCursor(m_row, m_column, true); +} + +bool ShiftMosaicsCommand::mergeWith(const QUndoCommand *command) +{ + const ShiftMosaicsCommand *newerCommand = static_cast(command); + + if (m_subPageIndex != newerCommand->m_subPageIndex || m_selectionTopRow != newerCommand->m_selectionTopRow || m_selectionLeftColumn != newerCommand->m_selectionLeftColumn || m_selectionBottomRow != newerCommand->m_selectionBottomRow || m_selectionRightColumn != newerCommand->m_selectionRightColumn) + return false; + + m_newCharacters = newerCommand->m_newCharacters; + + return true; +} + +ShiftMosaicsUpCommand::ShiftMosaicsUpCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent) : ShiftMosaicsCommand(teletextDocument, mosaicList, parent) +{ + for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) + for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++) + if (m_mosaicList.contains(qMakePair(r, c))) { + const int lr = r - m_selectionTopRow; + const int lc = c - m_selectionLeftColumn; + unsigned char mosaicWrap = 0x00; + + for (int sr=r+1; sr<=m_selectionBottomRow; sr++) + if (m_mosaicList.contains(qMakePair(sr, c))) { + mosaicWrap = m_newCharacters[sr - m_selectionTopRow][lc]; + mosaicWrap = ((mosaicWrap & 0x01) << 4) | ((mosaicWrap & 0x02) << 5); + break; + } + + m_newCharacters[lr][lc] = ((m_newCharacters[lr][lc] >> 2) & 0x07) | ((m_newCharacters[lr][lc] & 0x40) >> 3) | mosaicWrap | 0x20; + } + + setText(QObject::tr("shift mosaics up")); +} + +ShiftMosaicsDownCommand::ShiftMosaicsDownCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent) : ShiftMosaicsCommand(teletextDocument, mosaicList, parent) +{ + for (int r=m_selectionBottomRow; r>=m_selectionTopRow; r--) + for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++) + if (m_mosaicList.contains(qMakePair(r, c))) { + const int lr = r - m_selectionTopRow; + const int lc = c - m_selectionLeftColumn; + unsigned char mosaicWrap = 0x00; + + for (int sr=r-1; sr>=m_selectionTopRow; sr--) + if (m_mosaicList.contains(qMakePair(sr, c))) { + mosaicWrap = m_newCharacters[sr - m_selectionTopRow][lc]; + mosaicWrap = ((mosaicWrap & 0x10) >> 4) | ((mosaicWrap & 0x40) >> 5); + break; + } + + m_newCharacters[lr][lc] = ((m_newCharacters[lr][lc] & 0x07) << 2) | ((m_newCharacters[lr][lc] & 0x08) << 3) | mosaicWrap | 0x20; + } + + setText(QObject::tr("shift mosaics down")); +} + +ShiftMosaicsLeftCommand::ShiftMosaicsLeftCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent) : ShiftMosaicsCommand(teletextDocument, mosaicList, parent) +{ + for (int c=m_selectionLeftColumn; c<=m_selectionRightColumn; c++) + for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) + if (m_mosaicList.contains(qMakePair(r, c))) { + const int lr = r - m_selectionTopRow; + const int lc = c - m_selectionLeftColumn; + unsigned char mosaicWrap = 0x00; + + for (int sc=c+1; sc<=m_selectionRightColumn; sc++) + if (m_mosaicList.contains(qMakePair(r, sc))) { + mosaicWrap = m_newCharacters[lr][sc - m_selectionLeftColumn]; + mosaicWrap = ((mosaicWrap & 0x05) << 1) | ((mosaicWrap & 0x10) << 2); + break; + } + + m_newCharacters[lr][lc] = ((m_newCharacters[lr][lc] & 0x0a) >> 1) | ((m_newCharacters[lr][lc] & 0x40) >> 2) | mosaicWrap | 0x20; + } + + setText(QObject::tr("shift mosaics left")); +} + +ShiftMosaicsRightCommand::ShiftMosaicsRightCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent) : ShiftMosaicsCommand(teletextDocument, mosaicList, parent) +{ + for (int c=m_selectionRightColumn; c>=m_selectionLeftColumn; c--) + for (int r=m_selectionTopRow; r<=m_selectionBottomRow; r++) + if (m_mosaicList.contains(qMakePair(r, c))) { + const int lr = r - m_selectionTopRow; + const int lc = c - m_selectionLeftColumn; + unsigned char mosaicWrap = 0x00; + + for (int sc=c-1; sc>=m_selectionLeftColumn; sc--) + if (m_mosaicList.contains(qMakePair(r, sc))) { + mosaicWrap = m_newCharacters[lr][sc - m_selectionLeftColumn]; + mosaicWrap = ((mosaicWrap & 0x0a) >> 1) | ((mosaicWrap & 0x40) >> 2); + break; + } + + m_newCharacters[lr][lc] = ((m_newCharacters[lr][lc] & 0x05) << 1) | ((m_newCharacters[lr][lc] & 0x10) << 2) | mosaicWrap | 0x20; + } + + setText(QObject::tr("shift mosaics right")); +} + + InsertRowCommand::InsertRowCommand(TeletextDocument *teletextDocument, bool copyRow, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent) { m_copyRow = copyRow; diff --git a/levelonecommands.h b/levelonecommands.h index 0074dac..cf2e1ac 100644 --- a/levelonecommands.h +++ b/levelonecommands.h @@ -21,6 +21,7 @@ #define LEVELONECOMMANDS_H #include +#include #include #include "document.h" @@ -107,6 +108,62 @@ private: unsigned char m_oldRowContents[40], m_newRowContents[40]; }; +class ShiftMosaicsCommand : public LevelOneCommand +{ +public: + ShiftMosaicsCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent = 0); + + void redo() override; + void undo() override; + bool mergeWith(const QUndoCommand *command) override; + +protected: + QByteArrayList m_oldCharacters, m_newCharacters; + QSet> m_mosaicList; + int m_selectionTopRow, m_selectionBottomRow, m_selectionLeftColumn, m_selectionRightColumn; + int m_selectionCornerRow, m_selectionCornerColumn; +}; + +class ShiftMosaicsUpCommand : public ShiftMosaicsCommand +{ +public: + enum { Id = 110 }; + + ShiftMosaicsUpCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent = 0); + + int id() const override { return Id; } +}; + +class ShiftMosaicsDownCommand : public ShiftMosaicsCommand +{ +public: + enum { Id = 111 }; + + ShiftMosaicsDownCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent = 0); + + int id() const override { return Id; } +}; + +class ShiftMosaicsLeftCommand : public ShiftMosaicsCommand +{ +public: + enum { Id = 112 }; + + ShiftMosaicsLeftCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent = 0); + + int id() const override { return Id; } +}; + +class ShiftMosaicsRightCommand : public ShiftMosaicsCommand +{ +public: + enum { Id = 113 }; + + ShiftMosaicsRightCommand(TeletextDocument *teletextDocument, const QSet> &mosaicList, QUndoCommand *parent = 0); + + int id() const override { return Id; } +}; + class InsertSubPageCommand : public LevelOneCommand { public: diff --git a/mainwidget.cpp b/mainwidget.cpp index 18f1527..ac13f40 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -318,6 +319,7 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event) setCharacter(mappedKeyPress); return; } + switch (event->key()) { case Qt::Key_Backspace: m_teletextDocument->undoStack()->push(new BackspaceKeyCommand(m_teletextDocument, m_insertMode)); @@ -333,16 +335,28 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event) break; case Qt::Key_Up: - m_teletextDocument->cursorUp(event->modifiers() & Qt::ShiftModifier); + if (event->modifiers() & Qt::ControlModifier) + shiftMosaics(event->key()); + else + m_teletextDocument->cursorUp(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Down: - m_teletextDocument->cursorDown(event->modifiers() & Qt::ShiftModifier); + if (event->modifiers() & Qt::ControlModifier) + shiftMosaics(event->key()); + else + m_teletextDocument->cursorDown(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Left: - m_teletextDocument->cursorLeft(event->modifiers() & Qt::ShiftModifier); + if (event->modifiers() & Qt::ControlModifier) + shiftMosaics(event->key()); + else + m_teletextDocument->cursorLeft(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Right: - m_teletextDocument->cursorRight(event->modifiers() & Qt::ShiftModifier); + if (event->modifiers() & Qt::ControlModifier) + shiftMosaics(event->key()); + else + m_teletextDocument->cursorRight(event->modifiers() & Qt::ShiftModifier); break; case Qt::Key_Return: case Qt::Key_Enter: @@ -377,6 +391,35 @@ void TeletextWidget::toggleCharacterBit(unsigned char bitToToggle) m_teletextDocument->undoStack()->push(new ToggleMosaicBitCommand(m_teletextDocument, bitToToggle)); } +void TeletextWidget::shiftMosaics(int key) +{ + if (!m_teletextDocument->selectionActive()) + return; + + QSet> mosaicList; + + for (int r=m_teletextDocument->selectionTopRow(); r<=m_teletextDocument->selectionBottomRow(); r++) + for (int c=m_teletextDocument->selectionLeftColumn(); c<=m_teletextDocument->selectionRightColumn(); c++) + if (m_pageDecode.level1MosaicChar(r, c)) + mosaicList.insert(qMakePair(r, c)); + + if (!mosaicList.isEmpty()) + switch (key) { + case Qt::Key_Up: + m_teletextDocument->undoStack()->push(new ShiftMosaicsUpCommand(m_teletextDocument, mosaicList)); + break; + case Qt::Key_Down: + m_teletextDocument->undoStack()->push(new ShiftMosaicsDownCommand(m_teletextDocument, mosaicList)); + break; + case Qt::Key_Left: + m_teletextDocument->undoStack()->push(new ShiftMosaicsLeftCommand(m_teletextDocument, mosaicList)); + break; + case Qt::Key_Right: + m_teletextDocument->undoStack()->push(new ShiftMosaicsRightCommand(m_teletextDocument, mosaicList)); + break; + } +} + void TeletextWidget::selectionToClipboard() { QByteArray nativeData; diff --git a/mainwidget.h b/mainwidget.h index 883b626..7fe654b 100644 --- a/mainwidget.h +++ b/mainwidget.h @@ -102,6 +102,7 @@ private: int m_flashTiming, m_flashPhase; void timerEvent(QTimerEvent *event) override; + void shiftMosaics(int key); void selectionToClipboard(); QPair mouseToRowAndColumn(const QPoint &mousePosition);