Implement mosaic shifting

When a selection box is dragged, holding Ctrl while pressing the arrow
keys will shift the mosaics one sixel at a time.

If a shifting sixel encounters a non-mosaic cell it will pass through to
the other side of it.
This commit is contained in:
G.K.MacGregor
2024-07-10 21:13:54 +01:00
parent 9045160d3a
commit d184c50246
4 changed files with 245 additions and 4 deletions

View File

@@ -24,6 +24,7 @@
#include <QImage>
#include <QMimeData>
#include <QRegularExpression>
#include <QSet>
#include "levelonecommands.h"
@@ -318,6 +319,145 @@ bool DeleteKeyCommand::mergeWith(const QUndoCommand *command)
}
ShiftMosaicsCommand::ShiftMosaicsCommand(TeletextDocument *teletextDocument, const QSet<QPair<int, int>> &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<const ShiftMosaicsCommand *>(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<QPair<int, int>> &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<QPair<int, int>> &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<QPair<int, int>> &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<QPair<int, int>> &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;

View File

@@ -21,6 +21,7 @@
#define LEVELONECOMMANDS_H
#include <QByteArrayList>
#include <QSet>
#include <QUndoCommand>
#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<QPair<int, int>> &mosaicList, QUndoCommand *parent = 0);
void redo() override;
void undo() override;
bool mergeWith(const QUndoCommand *command) override;
protected:
QByteArrayList m_oldCharacters, m_newCharacters;
QSet<QPair<int, int>> 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<QPair<int, int>> &mosaicList, QUndoCommand *parent = 0);
int id() const override { return Id; }
};
class ShiftMosaicsDownCommand : public ShiftMosaicsCommand
{
public:
enum { Id = 111 };
ShiftMosaicsDownCommand(TeletextDocument *teletextDocument, const QSet<QPair<int, int>> &mosaicList, QUndoCommand *parent = 0);
int id() const override { return Id; }
};
class ShiftMosaicsLeftCommand : public ShiftMosaicsCommand
{
public:
enum { Id = 112 };
ShiftMosaicsLeftCommand(TeletextDocument *teletextDocument, const QSet<QPair<int, int>> &mosaicList, QUndoCommand *parent = 0);
int id() const override { return Id; }
};
class ShiftMosaicsRightCommand : public ShiftMosaicsCommand
{
public:
enum { Id = 113 };
ShiftMosaicsRightCommand(TeletextDocument *teletextDocument, const QSet<QPair<int, int>> &mosaicList, QUndoCommand *parent = 0);
int id() const override { return Id; }
};
class InsertSubPageCommand : public LevelOneCommand
{
public:

View File

@@ -32,6 +32,7 @@
#include <QMimeData>
#include <QPainter>
#include <QPair>
#include <QSet>
#include <QUndoCommand>
#include <QWidget>
#include <vector>
@@ -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,15 +335,27 @@ void TeletextWidget::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up:
if (event->modifiers() & Qt::ControlModifier)
shiftMosaics(event->key());
else
m_teletextDocument->cursorUp(event->modifiers() & Qt::ShiftModifier);
break;
case Qt::Key_Down:
if (event->modifiers() & Qt::ControlModifier)
shiftMosaics(event->key());
else
m_teletextDocument->cursorDown(event->modifiers() & Qt::ShiftModifier);
break;
case Qt::Key_Left:
if (event->modifiers() & Qt::ControlModifier)
shiftMosaics(event->key());
else
m_teletextDocument->cursorLeft(event->modifiers() & Qt::ShiftModifier);
break;
case Qt::Key_Right:
if (event->modifiers() & Qt::ControlModifier)
shiftMosaics(event->key());
else
m_teletextDocument->cursorRight(event->modifiers() & Qt::ShiftModifier);
break;
case Qt::Key_Return:
@@ -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<QPair<int, int>> 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;

View File

@@ -102,6 +102,7 @@ private:
int m_flashTiming, m_flashPhase;
void timerEvent(QTimerEvent *event) override;
void shiftMosaics(int key);
void selectionToClipboard();
QPair<int, int> mouseToRowAndColumn(const QPoint &mousePosition);