Move document class
This commit is contained in:
387
src/qteletextmaker/document.cpp
Normal file
387
src/qteletextmaker/document.cpp
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2025 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
|
||||
#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), QList<int>(Qt::DecorationRole));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TeletextDocument::TeletextDocument()
|
||||
{
|
||||
m_pageNumber = 0x199;
|
||||
m_description.clear();
|
||||
m_pageFunction = PFLevelOnePage;
|
||||
m_packetCoding = Coding7bit;
|
||||
m_subPages.append(new LevelOnePage);
|
||||
m_currentSubPageIndex = 0;
|
||||
m_undoStack = new QUndoStack(this);
|
||||
m_cursorRow = 1;
|
||||
m_cursorColumn = 0;
|
||||
m_rowZeroAllowed = false;
|
||||
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)
|
||||
delete(recycleSubPage);
|
||||
}
|
||||
|
||||
bool TeletextDocument::isEmpty() const
|
||||
{
|
||||
for (auto &subPage : m_subPages)
|
||||
if (!subPage->isEmpty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TeletextDocument::clear()
|
||||
{
|
||||
m_subPages.prepend(new LevelOnePage);
|
||||
|
||||
emit aboutToChangeSubPage();
|
||||
m_currentSubPageIndex = 0;
|
||||
m_clutModel->setSubPage(m_subPages[0]);
|
||||
emit subPageSelected();
|
||||
cancelSelection();
|
||||
m_undoStack->clear();
|
||||
|
||||
for (int i=m_subPages.size()-1; i>0; i--) {
|
||||
delete(m_subPages[i]);
|
||||
m_subPages.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void TeletextDocument::setPageFunction(PageFunctionEnum newPageFunction)
|
||||
{
|
||||
m_pageFunction = newPageFunction;
|
||||
}
|
||||
|
||||
void TeletextDocument::setPacketCoding(PacketCodingEnum newPacketEncoding)
|
||||
{
|
||||
m_packetCoding = newPacketEncoding;
|
||||
}
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextDocument::selectSubPagePrevious()
|
||||
{
|
||||
if (m_currentSubPageIndex > 0) {
|
||||
emit aboutToChangeSubPage();
|
||||
|
||||
m_currentSubPageIndex--;
|
||||
|
||||
m_clutModel->setSubPage(m_subPages[m_currentSubPageIndex]);
|
||||
emit subPageSelected();
|
||||
emit selectionMoved();
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextDocument::insertSubPage(int beforeSubPageIndex, bool copySubPage)
|
||||
{
|
||||
LevelOnePage *insertedSubPage;
|
||||
|
||||
if (copySubPage)
|
||||
insertedSubPage = new LevelOnePage(*m_subPages.at(beforeSubPageIndex));
|
||||
else
|
||||
insertedSubPage = new LevelOnePage;
|
||||
|
||||
if (beforeSubPageIndex == m_subPages.size())
|
||||
m_subPages.append(insertedSubPage);
|
||||
else
|
||||
m_subPages.insert(beforeSubPageIndex, insertedSubPage);
|
||||
}
|
||||
|
||||
void TeletextDocument::deleteSubPage(int subPageToDelete)
|
||||
{
|
||||
m_clutModel->setSubPage(nullptr);
|
||||
|
||||
delete(m_subPages[subPageToDelete]);
|
||||
m_subPages.remove(subPageToDelete);
|
||||
}
|
||||
|
||||
void TeletextDocument::deleteSubPageToRecycle(int subPageToRecycle)
|
||||
{
|
||||
m_recycleSubPages.append(m_subPages[subPageToRecycle]);
|
||||
m_subPages.remove(subPageToRecycle);
|
||||
}
|
||||
|
||||
void TeletextDocument::unDeleteSubPageFromRecycle(int subPage)
|
||||
{
|
||||
m_subPages.insert(subPage, m_recycleSubPages.last());
|
||||
m_recycleSubPages.removeLast();
|
||||
}
|
||||
|
||||
void TeletextDocument::setPageNumber(int pageNumber)
|
||||
{
|
||||
// If the magazine number was changed, we need to update the relative magazine numbers in FastText
|
||||
// and page enhancement links
|
||||
int oldMagazine = (m_pageNumber & 0xf00);
|
||||
int newMagazine = (pageNumber & 0xf00);
|
||||
// Fix magazine 0 to 8
|
||||
if (oldMagazine == 0x800)
|
||||
oldMagazine = 0x000;
|
||||
if (newMagazine == 0x800)
|
||||
newMagazine = 0x000;
|
||||
int magazineFlip = oldMagazine ^ newMagazine;
|
||||
|
||||
m_pageNumber = pageNumber;
|
||||
|
||||
for (auto &subPage : m_subPages)
|
||||
if (magazineFlip) {
|
||||
for (int i=0; i<6; i++)
|
||||
subPage->setFastTextLinkPageNumber(i, subPage->fastTextLinkPageNumber(i) ^ magazineFlip);
|
||||
for (int i=0; i<8; i++)
|
||||
subPage->setComposeLinkPageNumber(i, subPage->composeLinkPageNumber(i) ^ magazineFlip);
|
||||
}
|
||||
}
|
||||
|
||||
void TeletextDocument::setPageNumberFromString(QString pageNumberString)
|
||||
{
|
||||
bool pageNumberOk;
|
||||
int pageNumberRead = pageNumberString.toInt(&pageNumberOk, 16);
|
||||
|
||||
if ((!pageNumberOk) || pageNumberRead < 0x100 || pageNumberRead > 0x8ff)
|
||||
return;
|
||||
|
||||
setPageNumber(pageNumberRead);
|
||||
}
|
||||
|
||||
void TeletextDocument::setDescription(QString newDescription)
|
||||
{
|
||||
m_description = newDescription;
|
||||
}
|
||||
|
||||
void TeletextDocument::setFastTextLinkPageNumberOnAllSubPages(int linkNumber, int pageNumber)
|
||||
{
|
||||
for (auto &subPage : m_subPages)
|
||||
subPage->setFastTextLinkPageNumber(linkNumber, pageNumber);
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorUp(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (--m_cursorRow == 0 - (int)m_rowZeroAllowed)
|
||||
m_cursorRow = 24;
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorDown(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (++m_cursorRow == 25)
|
||||
m_cursorRow = (int)!m_rowZeroAllowed;
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorLeft(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (--m_cursorColumn == -1) {
|
||||
m_cursorColumn = 39;
|
||||
cursorUp(shiftKey);
|
||||
}
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
void TeletextDocument::cursorRight(bool shiftKey)
|
||||
{
|
||||
if (shiftKey && !selectionActive())
|
||||
setSelectionCorner(m_cursorRow, m_cursorColumn);
|
||||
|
||||
if (++m_cursorColumn == 40) {
|
||||
m_cursorColumn = 0;
|
||||
cursorDown(shiftKey);
|
||||
}
|
||||
|
||||
if (shiftKey)
|
||||
emit selectionMoved();
|
||||
else
|
||||
cancelSelection();
|
||||
|
||||
emit cursorMoved();
|
||||
}
|
||||
|
||||
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::setRowZeroAllowed(bool allowed)
|
||||
{
|
||||
m_rowZeroAllowed = allowed;
|
||||
if (m_cursorRow == 0 && !allowed)
|
||||
cursorDown();
|
||||
}
|
||||
|
||||
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 (selectionTopRow() != topRow || selectionBottomRow() != bottomRow || selectionLeftColumn() != leftColumn || selectionRightColumn() != rightColumn) {
|
||||
m_selectionSubPage = currentSubPage();
|
||||
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
|
||||
{
|
||||
int levelSeen = 0;
|
||||
|
||||
for (auto &subPage : m_subPages) {
|
||||
levelSeen = qMax(levelSeen, subPage->levelRequired());
|
||||
if (levelSeen == 3)
|
||||
break;
|
||||
}
|
||||
|
||||
return levelSeen;
|
||||
}
|
||||
133
src/qteletextmaker/document.h
Normal file
133
src/qteletextmaker/document.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2020-2025 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DOCUMENT_H
|
||||
#define DOCUMENT_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QUndoStack>
|
||||
|
||||
#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
|
||||
|
||||
public:
|
||||
// Available Page Functions according to 9.4.2.1 of the spec
|
||||
enum PageFunctionEnum { PFLevelOnePage, PFDataBroadcasting, PFGlobalPOP, PFNormalPOP, PFGlobalDRCS, PFNormalDRCS, PFMOT, PFMIP, PFBasicTOPTable, PFAdditionalInformationTable, PFMultiPageTable, PFMultiPageExtensionTable, PFTriggerMessages };
|
||||
// Available Page Codings of X/1 to X/25 according to 9.4.2.1 of the spec
|
||||
enum PacketCodingEnum { Coding7bit, Coding8bit, Coding18bit, Coding4bit, Coding4bitThen7bit, CodingPerPacket };
|
||||
|
||||
TeletextDocument();
|
||||
~TeletextDocument();
|
||||
|
||||
bool isEmpty() const;
|
||||
void clear();
|
||||
|
||||
PageFunctionEnum pageFunction() const { return m_pageFunction; }
|
||||
// void setPageFunction(PageFunctionEnum);
|
||||
PacketCodingEnum packetCoding() const { return m_packetCoding; }
|
||||
// void setPacketCoding(PacketCodingEnum);
|
||||
|
||||
int numberOfSubPages() const { return m_subPages.size(); }
|
||||
LevelOnePage* subPage(int p) const { return m_subPages[p]; }
|
||||
LevelOnePage* currentSubPage() const { return m_subPages[m_currentSubPageIndex]; }
|
||||
int currentSubPageIndex() const { return m_currentSubPageIndex; }
|
||||
void selectSubPageIndex(int newSubPageIndex, bool refresh=false);
|
||||
void selectSubPageNext();
|
||||
void selectSubPagePrevious();
|
||||
void insertSubPage(int beforeSubPageIndex, bool copySubPage);
|
||||
void deleteSubPage(int subPageToDelete);
|
||||
void deleteSubPageToRecycle(int subPageToRecycle);
|
||||
void unDeleteSubPageFromRecycle(int subPage);
|
||||
int pageNumber() const { return m_pageNumber; }
|
||||
void setPageNumber(int pageNumber);
|
||||
void setPageNumberFromString(QString pageNumberString);
|
||||
QString description() const { return m_description; }
|
||||
void setDescription(QString newDescription);
|
||||
void setFastTextLinkPageNumberOnAllSubPages(int linkNumber, int pageNumber);
|
||||
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(bool shiftKey=false);
|
||||
void cursorDown(bool shiftKey=false);
|
||||
void cursorLeft(bool shiftKey=false);
|
||||
void cursorRight(bool shiftKey=false);
|
||||
void moveCursor(int cursorRow, int cursorColumn, bool selectionInProgress=false);
|
||||
bool rowZeroAllowed() const { return m_rowZeroAllowed; };
|
||||
void setRowZeroAllowed(bool allowed);
|
||||
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 row, int column);
|
||||
void setSelection(int topRow, int leftColumn, int bottomRow, int rightColumn);
|
||||
void cancelSelection();
|
||||
int levelRequired() const;
|
||||
|
||||
signals:
|
||||
void cursorMoved();
|
||||
void selectionMoved();
|
||||
void colourChanged(int i);
|
||||
void pageOptionsChanged();
|
||||
void aboutToChangeSubPage();
|
||||
void subPageSelected();
|
||||
void contentsChanged();
|
||||
|
||||
void tripletCommandHighlight(int tripletNumber);
|
||||
|
||||
private:
|
||||
QString m_description;
|
||||
int m_pageNumber, m_currentSubPageIndex;
|
||||
PageFunctionEnum m_pageFunction;
|
||||
PacketCodingEnum m_packetCoding;
|
||||
QList<LevelOnePage *> m_subPages;
|
||||
QList<LevelOnePage *> m_recycleSubPages;
|
||||
QUndoStack *m_undoStack;
|
||||
int m_cursorRow, m_cursorColumn, m_selectionCornerRow, m_selectionCornerColumn;
|
||||
bool m_rowZeroAllowed;
|
||||
LevelOnePage *m_selectionSubPage;
|
||||
ClutModel *m_clutModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user