diff --git a/README.md b/README.md index 14394c7..593a5a6 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,6 @@ Spacing attributes are only accessible through an "Insert" menu. A toolbar to in There is no visible widget for switching between subpages, use the `PageUp` and `PageDown` keys for this. -Undo only undoes keypresses, insertion of spacing attributes and CLUT changes. Other changes (most notably triplet editing) can't be undone, nor will they cause the "unsaved page" dialog to appear if the editor window is closed. - Keymapping between ASCII and Teletext character sets is not yet implemented e.g. in English pressing the hash key will type a pound sign instead, and in other languages the square and curly brackets keys need to be pressed to obtain the accented characters. The following X/26 enhancement triplets are not rendered by the editor, although the list is fully aware of them. diff --git a/document.h b/document.h index fb1efb1..0b52b01 100644 --- a/document.h +++ b/document.h @@ -86,6 +86,8 @@ signals: void subPageSelected(); void refreshNeeded(); + void tripletCommandHighlight(int); + private: QString m_description; int m_pageNumber, m_currentSubPageIndex; diff --git a/qteletextmaker.pro b/qteletextmaker.pro index 49dc390..4183a74 100644 --- a/qteletextmaker.pro +++ b/qteletextmaker.pro @@ -12,6 +12,7 @@ HEADERS = document.h \ pageoptionsdockwidget.h \ palettedockwidget.h \ render.h \ + x26commands.h \ x26dockwidget.h \ x26model.h \ x26triplets.h @@ -27,6 +28,7 @@ SOURCES = document.cpp \ pageoptionsdockwidget.cpp \ palettedockwidget.cpp \ render.cpp \ + x26commands.cpp \ x26dockwidget.cpp \ x26model.cpp \ x26triplets.cpp diff --git a/x26commands.cpp b/x26commands.cpp new file mode 100644 index 0000000..33c583a --- /dev/null +++ b/x26commands.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2020 Gavin MacGregor + * + * This file is part of QTeletextMaker. + * + * QTeletextMaker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QTeletextMaker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QTeletextMaker. If not, see . + */ + +#include "x26commands.h" + +#include + +#include "document.h" +#include "x26model.h" +#include "x26triplets.h" + +InsertTripletCommand::InsertTripletCommand(TeletextDocument *teletextDocument, X26Model *x26Model, int row, int count, X26Triplet newTriplet, QUndoCommand *parent) : QUndoCommand(parent) +{ + m_teletextDocument = teletextDocument; + m_subPageIndex = teletextDocument->currentSubPageIndex(); + m_x26Model = x26Model; + m_row = row; + m_count = count; + m_insertedTriplet = newTriplet; + setText(QString("insert triplet d%1 t%2").arg(m_row / 13).arg(m_row % 13));; + m_firstDo = true; +} + +void InsertTripletCommand::redo() +{ + bool changingSubPage = (m_teletextDocument->currentSubPageIndex() != m_subPageIndex); + + if (changingSubPage) { + m_teletextDocument->emit aboutToChangeSubPage(); + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + } else + m_x26Model->beginInsertRows(QModelIndex(), m_row, m_row+m_count-1); + + for (int i=0; icurrentSubPage()->localEnhance.insert(m_row+i, m_insertedTriplet); + + if (changingSubPage) + m_teletextDocument->emit subPageSelected(); + else { + m_x26Model->endInsertRows(); + m_teletextDocument->emit refreshNeeded(); + } + + if (m_firstDo) + m_firstDo = false; + else + m_teletextDocument->emit tripletCommandHighlight(m_row+1); +} + +void InsertTripletCommand::undo() +{ + bool changingSubPage = (m_teletextDocument->currentSubPageIndex() != m_subPageIndex); + + if (changingSubPage) { + m_teletextDocument->emit aboutToChangeSubPage(); + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + } else + m_x26Model->beginRemoveRows(QModelIndex(), m_row, m_row+m_count-1); + + for (int i=0; icurrentSubPage()->localEnhance.removeAt(m_row); + + if (changingSubPage) + m_teletextDocument->emit subPageSelected(); + else { + m_x26Model->endRemoveRows(); + m_teletextDocument->emit refreshNeeded(); + } +} + + +DeleteTripletCommand::DeleteTripletCommand(TeletextDocument *teletextDocument, X26Model *x26Model, int row, int count, QUndoCommand *parent) : QUndoCommand(parent) +{ + m_teletextDocument = teletextDocument; + m_subPageIndex = teletextDocument->currentSubPageIndex(); + m_x26Model = x26Model; + m_row = row; + m_count = count; + m_deletedTriplet = m_teletextDocument->subPage(m_subPageIndex)->localEnhance.at(m_row); + setText(QString("delete triplet d%1 t%2").arg(m_row / 13).arg(m_row % 13));; + // BUG we only store one of the deleted triplets + if (m_count > 1) + qDebug("More than one triplet row deleted, undo won't put the lost triplets back!"); +} + +void DeleteTripletCommand::redo() +{ + m_teletextDocument->emit aboutToChangeSubPage(); + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + + m_x26Model->beginRemoveRows(QModelIndex(), m_row, m_row+m_count-1); + for (int i=0; icurrentSubPage()->localEnhance.removeAt(m_row); + m_x26Model->endRemoveRows(); + + m_teletextDocument->emit subPageSelected(); +} + +void DeleteTripletCommand::undo() +{ + bool changingSubPage = (m_teletextDocument->currentSubPageIndex() != m_subPageIndex); + + if (changingSubPage) { + m_teletextDocument->emit aboutToChangeSubPage(); + m_teletextDocument->selectSubPageIndex(m_subPageIndex); + } else + m_x26Model->beginInsertRows(QModelIndex(), m_row, m_row+m_count-1); + + for (int i=0; icurrentSubPage()->localEnhance.insert(m_row+i, m_deletedTriplet); + + if (changingSubPage) + m_teletextDocument->emit subPageSelected(); + else { + m_x26Model->endInsertRows(); + m_teletextDocument->emit refreshNeeded(); + } + + m_teletextDocument->emit tripletCommandHighlight(m_row); +} + + +EditTripletCommand::EditTripletCommand(TeletextDocument *teletextDocument, X26Model *x26Model, int row, int tripletPart, int bitsToKeep, int newValue, int role, QUndoCommand *parent) : QUndoCommand(parent) +{ + m_teletextDocument = teletextDocument; + m_subPageIndex = teletextDocument->currentSubPageIndex(); + m_x26Model = x26Model; + m_row = row; + m_role = role; + m_oldTriplet = m_newTriplet = teletextDocument->currentSubPage()->localEnhance.at(m_row); + setText(QString("edit triplet d%1 t%2").arg(m_row / 13).arg(m_row % 13));; + switch (tripletPart) { + case ETaddress: + m_newTriplet.setAddress((m_newTriplet.address() & bitsToKeep) | newValue); + break; + case ETmode: + m_newTriplet.setMode((m_newTriplet.mode() & bitsToKeep) | newValue); + break; + case ETdata: + m_newTriplet.setData((m_newTriplet.data() & bitsToKeep) | newValue); + break; + }; + m_firstDo = true; +} + +void EditTripletCommand::redo() +{ + if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex) + m_teletextDocument->selectSubPageIndex(m_subPageIndex, true); + + m_teletextDocument->currentSubPage()->localEnhance[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(); + + if (m_firstDo) + m_firstDo = false; + else + m_teletextDocument->emit tripletCommandHighlight(m_row); +} + +void EditTripletCommand::undo() +{ + if (m_teletextDocument->currentSubPageIndex() != m_subPageIndex) + m_teletextDocument->selectSubPageIndex(m_subPageIndex, true); + + m_teletextDocument->currentSubPage()->localEnhance[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); +} + +bool EditTripletCommand::mergeWith(const QUndoCommand *command) +{ + const EditTripletCommand *previousCommand = static_cast(command); + + if (m_row != previousCommand->m_row) + return false; + m_newTriplet = previousCommand->m_newTriplet; + return true; +} diff --git a/x26commands.h b/x26commands.h new file mode 100644 index 0000000..8b40dc3 --- /dev/null +++ b/x26commands.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Gavin MacGregor + * + * This file is part of QTeletextMaker. + * + * QTeletextMaker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QTeletextMaker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QTeletextMaker. If not, see . + */ + +#ifndef X26COMMANDS_H +#define X26COMMANDS_H + +#include + +#include "document.h" +#include "x26model.h" +#include "x26triplets.h" + +class InsertTripletCommand : public QUndoCommand +{ +public: + InsertTripletCommand(TeletextDocument *, X26Model *, int, int, const X26Triplet, QUndoCommand *parent = 0); + + void redo() override; + void undo() override; + +private: + TeletextDocument *m_teletextDocument; + X26Model *m_x26Model; + int m_subPageIndex, m_row, m_count; + X26Triplet m_insertedTriplet; + bool m_firstDo; +}; + +class DeleteTripletCommand : public QUndoCommand +{ +public: + DeleteTripletCommand(TeletextDocument *, X26Model *, int, int, QUndoCommand *parent = 0); + + void redo() override; + void undo() override; + +private: + TeletextDocument *m_teletextDocument; + X26Model *m_x26Model; + int m_subPageIndex, m_row, m_count; + X26Triplet m_deletedTriplet; +}; + +class EditTripletCommand : public QUndoCommand +{ +public: + enum { Id = 1 }; + enum EditTripletEnum { ETaddress, ETmode, ETdata }; + + EditTripletCommand(TeletextDocument *, X26Model *, int, int, int, int, int, QUndoCommand *parent = 0); + + void redo() override; + void undo() override; + bool mergeWith(const QUndoCommand *) override; + int id() const override { return Id; } + +private: + TeletextDocument *m_teletextDocument; + X26Model *m_x26Model; + int m_subPageIndex, m_row, m_role; + X26Triplet m_oldTriplet, m_newTriplet; + bool m_firstDo; +}; + +#endif diff --git a/x26dockwidget.cpp b/x26dockwidget.cpp index d8d30f1..b3324f9 100644 --- a/x26dockwidget.cpp +++ b/x26dockwidget.cpp @@ -51,12 +51,15 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent) m_x26View->setModel(m_x26Model); m_x26View->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); m_x26View->setSelectionBehavior(QAbstractItemView::SelectRows); + m_x26View->setSelectionMode(QAbstractItemView::SingleSelection); m_x26View->setColumnWidth(0, 50); m_x26View->setColumnWidth(1, 50); m_x26View->setColumnWidth(2, 200); m_x26View->setColumnWidth(3, 200); x26Layout->addWidget(m_x26View); + connect(m_parentMainWidget->document(), &TeletextDocument::tripletCommandHighlight, this, &X26DockWidget::selectX26ListRow); + // Triplet type and mode selection, with row and column spinboxes QHBoxLayout *tripletSelectLayout = new QHBoxLayout; @@ -469,6 +472,17 @@ X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent) connect(deleteShortcut, &QShortcut::activated, this, &X26DockWidget::deleteTriplet); } +void X26DockWidget::selectX26ListRow(int row) +{ + if (m_x26Model->rowCount() <= 0) + return; + if (row >= m_x26Model->rowCount()) + row = m_x26Model->rowCount() - 1; + + m_x26View->selectRow(row); + rowClicked(m_x26View->currentIndex()); +} + void X26DockWidget::loadX26List() { m_x26Model->setX26ListLoaded(true); diff --git a/x26dockwidget.h b/x26dockwidget.h index 5303b35..39d7e56 100644 --- a/x26dockwidget.h +++ b/x26dockwidget.h @@ -59,6 +59,7 @@ public slots: void cookedColumnSpinBoxChanged(const int); void cookedModeComboBoxChanged(const int); void updateModelFromCookedWidget(const int, const int); + void selectX26ListRow(int); private: QTableView *m_x26View; diff --git a/x26model.cpp b/x26model.cpp index 77f043c..8300f94 100644 --- a/x26model.cpp +++ b/x26model.cpp @@ -17,9 +17,11 @@ * along with QTeletextMaker. If not, see . */ +#include "x26model.h" + #include -#include "x26model.h" +#include "x26commands.h" X26Model::X26Model(TeletextWidget *parent): QAbstractListModel(parent) { @@ -461,69 +463,56 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role // Raw address, mode and data values if (role == Qt::UserRole && value.canConvert() && index.column() <= 2) { - int intValue = value.toInt(); - switch (index.column()) { - case 0: - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress(intValue); - break; - case 1: - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setMode(intValue); - break; - case 2: - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(intValue); - break; - } - emit dataChanged(createIndex(index.row(), 0), createIndex(index.row(), 3), {role}); - m_parentMainWidget->refreshPage(); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), index.column(), 0x00, value.toInt(), role)); + return true; } int mode = m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).modeExt(); - // Row, column and triplet mode + // Cooked row, column and triplet mode if (role == Qt::EditRole && value.canConvert()) { int intValue = value.toInt(); if (intValue < 0) return false; switch (index.column()) { - case 0: + case 0: // Cooked row + // Maximum row is 24 if (intValue > 24) return false; + // Set Active Position and Full Row Colour can't select row 0 if (((mode == 0x04) || (mode == 0x01)) && intValue == 0) return false; - if (mode == 0x10) - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress(intValue + 40); - else - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddressRow(intValue); - emit dataChanged(index, index, {role}); - m_parentMainWidget->refreshPage(); + // For Origin Modifier address of 40 refers to same row + if (mode == 0x10 && intValue == 24) { + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, 40, role)); + return true; + } + // Others use address 40 for row 24 + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, (intValue == 24) ? 40 : intValue+40, role)); return true; - case 1: + case 1: // Cooked column // Origin modifier allows columns up to 71 if (intValue > (mode == 0x10 ? 71 : 39)) return false; // For Set Active Position and Origin Modifier, data is the column if (mode == 0x04 || mode == 0x10) - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, intValue, role)); else - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddressColumn(intValue); - emit dataChanged(index, index, {role}); - m_parentMainWidget->refreshPage(); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, intValue, role)); return true; - case 2: + case 2: // Cooked triplet mode if (intValue < 0x20 && !m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).isRowTriplet()) { // Changing mode from column triplet to row triplet - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddressRow(1); - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(0); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, 41, role)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role)); } if (intValue >= 0x20 && m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).isRowTriplet()) { // Changing mode from row triplet to column triplet - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddressColumn(0); - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(0); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x00, 0, role)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, 0, role)); } - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setMode(intValue & 0x1f); - emit dataChanged(createIndex(index.row(), 0), createIndex(index.row(), 3), {role}); - m_parentMainWidget->refreshPage(); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETmode, 0x00, intValue & 0x1f, role)); return true; } return false; @@ -546,45 +535,42 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role case 0x20: // Foreground colour case 0x23: // Background colour // Colour index - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x60) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x60, value.toInt(), role)); break; -// case 0x04: // Set Active Position - data is the column -// m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(intValue); -// break; case 0x11 ... 0x13: // Invoke object // Object source: Local, POP or GPOP - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x27) | ((intValue+1) << 3)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x27, (value.toInt()+1) << 3, role)); break; case 0x15 ... 0x17: // Define object // Required at level 2.5 - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x37) | (intValue << 2)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x37, value.toInt() << 2, role)); break; case 0x18: // DRCS Mode // Required at level 2.5 - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x6f) | (intValue << 3)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x6f, value.toInt() << 3, role)); break; case 0x1f: // Termination // Intermed POP subpage|Last POP subpage|Local Object|Local enhance - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x01) | (intValue << 1)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x01, value.toInt() << 1, role)); break; case 0x27: // Flash functions // Flash mode - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x1c) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x1c, value.toInt(), role)); break; case 0x2c: // Display attributes // Text size - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x36) | ((intValue & 0x02) << 5) | (intValue & 0x01)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x36, ((value.toInt() & 0x02) << 5) | (value.toInt() & 0x01), role)); break; case 0x2d: // DRCS character // Normal or Global - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x3f) | (intValue << 6)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x3f, value.toInt() << 6, role)); break; case 0x2e: // Font style // Proportional - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x7e) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x7e, value.toInt(), role)); break; default: - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData(intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x00, value.toInt(), role)); } break; @@ -594,45 +580,45 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role case 0x01: // Full row colour case 0x07: // Address row 0 // "this row only" or "down to bottom" - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x1f) | (intValue * 0x60)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x1f, value.toInt() * 0x60, role)); break; case 0x11 ... 0x13: // Invoke object if ((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x08) == 0x08) { // Local object: Designation code - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x0f) | ((intValue & 0x07) << 4)); - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x3e) | (intValue >> 3)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x07, value.toInt() << 4, role)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x3e, value.toInt() >> 3, role)); } else // (G)POP object: Subpage - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x30) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x30, value.toInt(), role)); break; case 0x15 ... 0x17: // Define object // Local object: Designation code - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x0f) | ((intValue & 0x07) << 4)); - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x3e) | (intValue >> 3)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x0f, (value.toInt() & 0x07) << 4, role)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x3e, value.toInt() >> 3, role)); break; case 0x18: // DRCS Mode // Required at level 3.5 - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x5f) | (intValue << 4)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x5f, value.toInt() << 4, role)); break; case 0x1f: // Termination // More follows/Last - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x06) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x06, value.toInt(), role)); break; case 0x27: // Flash functions // Flash rate and phase - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x03) | (intValue << 2)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x03, value.toInt() << 2, role)); break; case 0x2c: // Display attributes // Boxing/window - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x7d) | (intValue << 1)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x7d, value.toInt() << 1, role)); break; case 0x2d: // DRCS character // Character number - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x40) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x40, value.toInt(), role)); break; case 0x2e: // Font style // Bold - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x7d) | (intValue << 1)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x7d, value.toInt() << 1, role)); break; } break; @@ -642,26 +628,26 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role case 0x11 ... 0x13: // Invoke object if ((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x08) == 0x08) // Local object: triplet number - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x70) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x70, value.toInt(), role)); else // (G)POP object: Pointer location - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x7c) | (intValue - 1)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x7c, value.toInt() - 1, role)); break; case 0x15 ... 0x17: // Define object // Local object: triplet number - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x70) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x70, value.toInt(), role)); break; case 0x18: // DRCS Mode // Normal or Global - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x3f) | (intValue << 6)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x3f, value.toInt() << 6, role)); break; case 0x2c: // Display attributes // Conceal - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x7b) | (intValue << 2)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x7b, value.toInt() << 2, role)); break; case 0x2e: // Font style // Italics - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x7b) | (intValue << 2)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x7b, value.toInt() << 2, role)); break; } break; @@ -670,23 +656,23 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role switch (mode) { case 0x11 ... 0x13: // Invoke object // (G)POP object: Triplet number - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x1f) | (intValue << 5)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x1f, value.toInt() << 5, role)); break; case 0x15 ... 0x17: // Define object // Required at level 3.5 - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setAddress((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).address() & 0x2f) | (intValue << 3)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETaddress, 0x2f, value.toInt() << 3, role)); break; case 0x18: // DRCS Mode // Subpage - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x70) | intValue); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x70, value.toInt(), role)); break; case 0x2c: // Display attributes // Invert - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x6f) | (intValue << 4)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x6f, value.toInt() << 4, role)); break; case 0x2e: // Font style // Number of rows - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x07) | (intValue << 4)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x07, value.toInt() << 4, role)); break; } break; @@ -695,18 +681,16 @@ bool X26Model::setData(const QModelIndex &index, const QVariant &value, int role switch (mode) { case 0x11 ... 0x13: // Invoke object // (G)POP object: Pointer position - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x6f) | (intValue << 4)); + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x6f, value.toInt() << 4, role)); break; case 0x2c: // Display attributes - // Invert - m_parentMainWidget->document()->currentSubPage()->localEnhance[index.row()].setData((m_parentMainWidget->document()->currentSubPage()->localEnhance.at(index.row()).data() & 0x5f) | (intValue << 5)); + // Underline/Separated + m_parentMainWidget->document()->undoStack()->push(new EditTripletCommand(m_parentMainWidget->document(), this, index.row(), EditTripletCommand::ETdata, 0x5f, value.toInt() << 5, role)); break; } break; } - emit dataChanged(createIndex(index.row(), 0), createIndex(index.row(), 3), {role}); - m_parentMainWidget->refreshPage(); return true; } @@ -734,47 +718,27 @@ QVariant X26Model::headerData(int section, Qt::Orientation orientation, int role bool X26Model::insertFirstRow() { - X26Triplet firstTriplet; + m_parentMainWidget->document()->undoStack()->push(new InsertTripletCommand(m_parentMainWidget->document(), this, 0, 1, X26Triplet(63, 31, 7))); - firstTriplet.setAddress(63); - firstTriplet.setMode(31); - firstTriplet.setData(7); - - beginInsertRows(QModelIndex(), 0, 0); - m_parentMainWidget->document()->currentSubPage()->localEnhance.insert(0, firstTriplet); - endInsertRows(); - - // Since we always insert a Termination Marker there's no need to refresh the page return true; } -bool X26Model::insertRows(int position, int rows, const QModelIndex &parent) +bool X26Model::insertRows(int row, int count, const QModelIndex &parent) { Q_UNUSED(parent); - X26Triplet copyTriplet = m_parentMainWidget->document()->currentSubPage()->localEnhance.at(position); - beginInsertRows(QModelIndex(), position, position+rows-1); - for (int row=0; rowdocument()->currentSubPage()->localEnhance.insert(position+row, copyTriplet); - endInsertRows(); - - // Since we always insert duplicates of the selected triplet there's no need to refresh the page + m_parentMainWidget->document()->undoStack()->push(new InsertTripletCommand(m_parentMainWidget->document(), this, row, count, m_parentMainWidget->document()->currentSubPage()->localEnhance.at(row))); return true; } -bool X26Model::removeRows(int position, int rows, const QModelIndex &index) +bool X26Model::removeRows(int row, int count, const QModelIndex &index) { Q_UNUSED(index); - beginRemoveRows(QModelIndex(), position, position+rows-1); - for (int row=0; rowdocument()->currentSubPage()->localEnhance.removeAt(position+row); - - endRemoveRows(); - - m_parentMainWidget->refreshPage(); + m_parentMainWidget->document()->undoStack()->push(new DeleteTripletCommand(m_parentMainWidget->document(), this, row, count)); return true; } + /* Qt::ItemFlags X26Model::flags(const QModelIndex &index) const { diff --git a/x26model.h b/x26model.h index 231f10d..7aa8052 100644 --- a/x26model.h +++ b/x26model.h @@ -40,6 +40,12 @@ public: bool removeRows(int position, int rows, const QModelIndex &index); // Qt::ItemFlags flags(const QModelIndex &index) const; + // The x26commands classes manipulate the model but beginInsertRows and endInsertRows + // are protected methods, so we need to friend them + friend class InsertTripletCommand; + friend class DeleteTripletCommand; + friend class EditTripletCommand; + private: TeletextWidget *m_parentMainWidget; bool m_listLoaded;