diff --git a/document.h b/document.h index f542827..c22df18 100644 --- a/document.h +++ b/document.h @@ -76,6 +76,8 @@ public: 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(); diff --git a/levelonecommands.cpp b/levelonecommands.cpp index 4905b92..85b1603 100644 --- a/levelonecommands.cpp +++ b/levelonecommands.cpp @@ -17,6 +17,12 @@ * along with QTeletextMaker. If not, see . */ +#include +#include +#include +#include +#include + #include "levelonecommands.h" #include "document.h" @@ -340,6 +346,192 @@ void DeleteRowCommand::undo() } +#ifndef QT_NO_CLIPBOARD +CutCommand::CutCommand(TeletextDocument *teletextDocument, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent) +{ + 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, 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 + 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; rselectionTopRow(); + 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) + 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, m_column+m_clipboardDataWidth-1); + m_teletextDocument->cursorRight(); + } +} + +void PasteCommand::undo() +{ + if (m_clipboardDataWidth == 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++; + } +} +#endif // !QT_NO_CLIPBOARD + + InsertSubPageCommand::InsertSubPageCommand(TeletextDocument *teletextDocument, bool afterCurrentSubPage, bool copySubPage, QUndoCommand *parent) : LevelOneCommand(teletextDocument, parent) { m_newSubPageIndex = m_subPageIndex + afterCurrentSubPage; diff --git a/levelonecommands.h b/levelonecommands.h index 9c0f32e..30e13d5 100644 --- a/levelonecommands.h +++ b/levelonecommands.h @@ -20,6 +20,7 @@ #ifndef LEVELONECOMMANDS_H #define LEVELONECOMMANDS_H +#include #include #include "document.h" @@ -150,6 +151,38 @@ private: unsigned char m_deletedRow[40]; }; +#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 *, 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: diff --git a/mainwidget.cpp b/mainwidget.cpp index ed91490..8cae394 100644 --- a/mainwidget.cpp +++ b/mainwidget.cpp @@ -17,7 +17,9 @@ * along with QTeletextMaker. If not, see . */ +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -387,6 +390,48 @@ void TeletextWidget::toggleCharacterBit(unsigned char bitToToggle) m_teletextDocument->undoStack()->push(new ToggleMosaicBitCommand(m_teletextDocument, bitToToggle)); } +void TeletextWidget::selectionToClipboard() +{ + QByteArray nativeData; + QClipboard *clipboard = QApplication::clipboard(); + + nativeData.resize(2 + m_teletextDocument->selectionWidth() * m_teletextDocument->selectionHeight()); + nativeData[0] = m_teletextDocument->selectionHeight(); + nativeData[1] = m_teletextDocument->selectionWidth(); + + 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); + + QMimeData *mimeData = new QMimeData(); + mimeData->setData("application/x-teletext", nativeData); + 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)); +} + QPair TeletextWidget::mouseToRowAndColumn(const QPoint &mousePosition) { int row = mousePosition.y() / 10; diff --git a/mainwidget.h b/mainwidget.h index b4b1316..2971534 100644 --- a/mainwidget.h +++ b/mainwidget.h @@ -75,6 +75,11 @@ public slots: void setBlackBackgroundSubst(bool); void setSidePanelWidths(int, int); void setSidePanelAtL35Only(bool); + + void cut(); + void copy(); + void paste(); + void changeSize(); protected: @@ -96,6 +101,7 @@ private: int m_flashTiming, m_flashPhase; void timerEvent(QTimerEvent *event) override; + void selectionToClipboard(); QPair mouseToRowAndColumn(const QPoint &); }; diff --git a/mainwindow.cpp b/mainwindow.cpp index 83f816a..cdd32cc 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -292,36 +292,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"));