The saving code is an attempt to be page function and page coding agnostic so the same code can save (G)POP, (G)DRCS and MOT pages in the future. loadsave will be the home of page loading, URL exporting and also for importing and exporting of other teletext file formats.
297 lines
8.8 KiB
C++
297 lines
8.8 KiB
C++
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <vector>
|
|
|
|
#include "document.h"
|
|
|
|
#include "levelonepage.h"
|
|
|
|
TeletextDocument::TeletextDocument()
|
|
{
|
|
m_pageNumber = 0x198;
|
|
m_description.clear();
|
|
m_pageFunction = PFLevelOnePage;
|
|
m_packetCoding = Coding7bit;
|
|
m_empty = true;
|
|
m_subPages.push_back(new LevelOnePage);
|
|
m_currentSubPageIndex = 0;
|
|
m_undoStack = new QUndoStack(this);
|
|
m_cursorRow = 1;
|
|
m_cursorColumn = 0;
|
|
m_selectionSubPage = nullptr;
|
|
}
|
|
|
|
TeletextDocument::~TeletextDocument()
|
|
{
|
|
for (auto &subPage : m_subPages)
|
|
delete(subPage);
|
|
}
|
|
|
|
/*
|
|
void TeletextDocument::setPageFunction(PageFunctionEnum newPageFunction)
|
|
{
|
|
m_pageFunction = newPageFunction;
|
|
}
|
|
|
|
void TeletextDocument::setPacketCoding(PacketCodingEnum newPacketEncoding)
|
|
{
|
|
m_packetCoding = newPacketEncoding;
|
|
}
|
|
*/
|
|
|
|
void TeletextDocument::loadDocument(QFile *inFile)
|
|
{
|
|
QByteArray inLine;
|
|
bool firstSubPageFound = false;
|
|
int cycleCommandsFound = 0;
|
|
int mostRecentCycleValue = -1;
|
|
LevelOnePage::CycleTypeEnum mostRecentCycleType;
|
|
|
|
LevelOnePage* loadingPage = m_subPages[0];
|
|
|
|
for (;;) {
|
|
inLine = inFile->readLine(160).trimmed();
|
|
if (inLine.isEmpty())
|
|
break;
|
|
if (inLine.startsWith("DE,"))
|
|
m_description = QString(inLine.remove(0, 3));
|
|
if (inLine.startsWith("PN,")) {
|
|
bool pageNumberOk;
|
|
int pageNumberRead = inLine.mid(3, 3).toInt(&pageNumberOk, 16);
|
|
if ((!pageNumberOk) || pageNumberRead < 0x100 || pageNumberRead > 0x8ff)
|
|
pageNumberRead = 0x199;
|
|
// When second and subsequent PN commands are found, firstSubPageFound==true at this point
|
|
// This assumes that PN is the first command of a new subpage...
|
|
if (firstSubPageFound) {
|
|
m_subPages.push_back(new LevelOnePage);
|
|
loadingPage = m_subPages.back();
|
|
}
|
|
m_pageNumber = pageNumberRead;
|
|
firstSubPageFound = true;
|
|
}
|
|
/* if (lineType == "SC,") {
|
|
bool subPageNumberOk;
|
|
int subPageNumberRead = inLine.mid(3, 4).toInt(&subPageNumberOk, 16);
|
|
if ((!subPageNumberOk) || subPageNumberRead > 0x3f7f)
|
|
subPageNumberRead = 0;
|
|
loadingPage->setSubPageNumber(subPageNumberRead);
|
|
}*/
|
|
if (inLine.startsWith("PS,")) {
|
|
bool pageStatusOk;
|
|
int pageStatusRead = inLine.mid(3, 4).toInt(&pageStatusOk, 16);
|
|
if (pageStatusOk) {
|
|
loadingPage->setControlBit(PageBase::C4ErasePage, pageStatusRead & 0x4000);
|
|
for (int i=PageBase::C5Newsflash, pageStatusBit=0x0001; i<=PageBase::C11SerialMagazine; i++, pageStatusBit<<=1)
|
|
loadingPage->setControlBit(i, pageStatusRead & pageStatusBit);
|
|
loadingPage->setDefaultNOS(((pageStatusRead & 0x0200) >> 9) | ((pageStatusRead & 0x0100) >> 7) | ((pageStatusRead & 0x0080) >> 5));
|
|
}
|
|
}
|
|
if (inLine.startsWith("CT,") && (inLine.endsWith(",C") || inLine.endsWith(",T"))) {
|
|
bool cycleValueOk;
|
|
int cycleValueRead = inLine.mid(3, inLine.size()-5).toInt(&cycleValueOk);
|
|
if (cycleValueOk) {
|
|
cycleCommandsFound++;
|
|
// House-keep CT command values, in case it's the only one within multiple subpages
|
|
mostRecentCycleValue = cycleValueRead;
|
|
loadingPage->setCycleValue(cycleValueRead);
|
|
mostRecentCycleType = inLine.endsWith("C") ? LevelOnePage::CTcycles : LevelOnePage::CTseconds;
|
|
loadingPage->setCycleType(mostRecentCycleType);
|
|
}
|
|
}
|
|
if (inLine.startsWith("FL,")) {
|
|
bool fastTextLinkOk;
|
|
int fastTextLinkRead;
|
|
QString flLine = QString(inLine.remove(0, 3));
|
|
if (flLine.count(',') == 5)
|
|
for (int i=0; i<6; i++) {
|
|
fastTextLinkRead = flLine.section(',', i, i).toInt(&fastTextLinkOk, 16);
|
|
if (fastTextLinkOk) {
|
|
if (fastTextLinkRead == 0)
|
|
fastTextLinkRead = 0x8ff;
|
|
// Stored as page link with relative magazine number, convert from absolute page number that was read
|
|
fastTextLinkRead ^= m_pageNumber & 0x700;
|
|
fastTextLinkRead &= 0x7ff; // Fixes magazine 8 to 0
|
|
loadingPage->setFastTextLinkPageNumber(i, fastTextLinkRead);
|
|
}
|
|
}
|
|
}
|
|
if (inLine.startsWith("OL,"))
|
|
loadingPage->loadPagePacket(inLine);
|
|
}
|
|
// If there's more than one subpage but only one valid CT command was found, apply it to all subpages
|
|
// I don't know if this is correct
|
|
if (cycleCommandsFound == 1 && m_subPages.size()>1)
|
|
for (auto &subPage : m_subPages) {
|
|
subPage->setCycleValue(mostRecentCycleValue);
|
|
subPage->setCycleType(mostRecentCycleType);
|
|
}
|
|
subPageSelected();
|
|
}
|
|
|
|
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()-1)) {
|
|
emit aboutToChangeSubPage();
|
|
m_currentSubPageIndex = newSubPageIndex;
|
|
emit subPageSelected();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TeletextDocument::selectSubPageNext()
|
|
{
|
|
if (m_currentSubPageIndex < m_subPages.size()-1) {
|
|
emit aboutToChangeSubPage();
|
|
m_currentSubPageIndex++;
|
|
emit subPageSelected();
|
|
}
|
|
}
|
|
|
|
void TeletextDocument::selectSubPagePrevious()
|
|
{
|
|
if (m_currentSubPageIndex > 0) {
|
|
emit aboutToChangeSubPage();
|
|
m_currentSubPageIndex--;
|
|
emit subPageSelected();
|
|
}
|
|
}
|
|
|
|
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.push_back(insertedSubPage);
|
|
else
|
|
m_subPages.insert(m_subPages.begin()+beforeSubPageIndex, insertedSubPage);
|
|
}
|
|
|
|
void TeletextDocument::deleteSubPage(int subPageToDelete)
|
|
{
|
|
delete(m_subPages[subPageToDelete]);
|
|
m_subPages.erase(m_subPages.begin()+subPageToDelete);
|
|
}
|
|
|
|
void TeletextDocument::setPageNumber(QString pageNumberString)
|
|
{
|
|
// The LineEdit should check if a valid hex number was entered, but just in case...
|
|
bool pageNumberOk;
|
|
int pageNumberRead = pageNumberString.toInt(&pageNumberOk, 16);
|
|
if ((!pageNumberOk) || pageNumberRead < 0x100 || pageNumberRead > 0x8fe)
|
|
return;
|
|
|
|
// 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 = (pageNumberRead & 0xf00);
|
|
// Fix magazine 0 to 8
|
|
if (oldMagazine == 0x800)
|
|
oldMagazine = 0x000;
|
|
if (newMagazine == 0x800)
|
|
newMagazine = 0x000;
|
|
int magazineFlip = oldMagazine ^ newMagazine;
|
|
|
|
m_pageNumber = pageNumberRead;
|
|
|
|
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::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()
|
|
{
|
|
if (--m_cursorRow == 0)
|
|
m_cursorRow = 24;
|
|
emit cursorMoved();
|
|
}
|
|
|
|
void TeletextDocument::cursorDown()
|
|
{
|
|
if (++m_cursorRow == 25)
|
|
m_cursorRow = 1;
|
|
emit cursorMoved();
|
|
}
|
|
|
|
void TeletextDocument::cursorLeft()
|
|
{
|
|
if (--m_cursorColumn == -1) {
|
|
m_cursorColumn = 39;
|
|
cursorUp();
|
|
}
|
|
emit cursorMoved();
|
|
}
|
|
|
|
void TeletextDocument::cursorRight()
|
|
{
|
|
if (++m_cursorColumn == 40) {
|
|
m_cursorColumn = 0;
|
|
cursorDown();
|
|
}
|
|
emit cursorMoved();
|
|
}
|
|
|
|
void TeletextDocument::moveCursor(int cursorRow, int cursorColumn)
|
|
{
|
|
if (cursorRow != -1)
|
|
m_cursorRow = cursorRow;
|
|
if (cursorColumn != -1)
|
|
m_cursorColumn = cursorColumn;
|
|
emit cursorMoved();
|
|
}
|
|
|
|
void TeletextDocument::setSelection(int topRow, int leftColumn, int bottomRow, int rightColumn)
|
|
{
|
|
if (m_selectionTopRow != topRow || m_selectionBottomRow != bottomRow || m_selectionLeftColumn != leftColumn || m_selectionRightColumn != rightColumn) {
|
|
m_selectionSubPage = currentSubPage();
|
|
m_selectionTopRow = topRow;
|
|
m_selectionBottomRow = bottomRow;
|
|
m_selectionLeftColumn = leftColumn;
|
|
m_selectionRightColumn = rightColumn;
|
|
emit selectionMoved();
|
|
}
|
|
}
|
|
|
|
void TeletextDocument::cancelSelection()
|
|
{
|
|
m_selectionSubPage = nullptr;
|
|
}
|