diff --git a/document.cpp b/document.cpp
index eed8c20..f8f128a 100644
--- a/document.cpp
+++ b/document.cpp
@@ -147,34 +147,6 @@ void TeletextDocument::loadDocument(QFile *inFile)
subPageSelected();
}
-void TeletextDocument::saveDocument(QTextStream *outStream)
-{
- if (!m_description.isEmpty())
- *outStream << "DE," << m_description << endl;
- //TODO DS and SP commands
-
- int subPageNumber = m_subPages.size()>1;
-
- for (auto &subPage : m_subPages) {
- subPage->savePage(outStream, m_pageNumber, subPageNumber++);
- if ((subPage->fastTextLinkPageNumber(0) & 0x0ff) != 0x0ff) {
- *outStream << "FL,";
- for (int i=0; i<6; i++) {
- // Stored as page link with relative magazine number, convert to absolute page number for display
- int absoluteLinkPageNumber = subPage->fastTextLinkPageNumber(i) ^ (m_pageNumber & 0x700);
- // Fix magazine 0 to 8
- if ((absoluteLinkPageNumber & 0x700) == 0x000)
- absoluteLinkPageNumber |= 0x800;
-
- *outStream << QString("%1").arg(absoluteLinkPageNumber, 3, 16, QChar('0'));
- if (i<5)
- *outStream << ',';
- }
- *outStream << endl;
- }
- }
-}
-
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?
diff --git a/document.h b/document.h
index 491e8c4..db78a86 100644
--- a/document.h
+++ b/document.h
@@ -48,8 +48,8 @@ public:
// void setPacketCoding(PacketCodingEnum);
void loadDocument(QFile *);
- void saveDocument(QTextStream *);
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, bool=false);
diff --git a/levelonepage.cpp b/levelonepage.cpp
index f15782a..8cc9704 100644
--- a/levelonepage.cpp
+++ b/levelonepage.cpp
@@ -42,7 +42,7 @@ LevelOnePage::LevelOnePage(const PageBase &other)
localEnhance.reserve(208);
clearPage();
- for (int i=PageBase::C4ErasePage; i<=PageBase::C11SerialMagazine; i++)
+ for (int i=PageBase::C4ErasePage; i<=PageBase::C14NOS; i++)
setControlBit(i, other.controlBit(i));
for (int i=0; i<90; i++)
if (other.packetNeededArrayIndex(i))
@@ -55,12 +55,12 @@ void LevelOnePage::clearPage()
for (int r=0; r<25; r++)
for (int c=0; c<40; c++)
m_level1Page[r][c] = 0x20;
- for (int i=0; i<8; i++) {
+ for (int i=C4ErasePage; i<=C14NOS; i++)
setControlBit(i, false);
+ for (int i=0; i<8; i++)
m_composeLink[i] = { (i<4) ? i : 0, false, i>=4, 0x0ff, 0x0000 };
- }
for (int i=0; i<6; i++)
- m_fastTextLink[i] = { 0x0ff, 0x37f7 };
+ m_fastTextLink[i] = { 0x0ff, 0x3f7f };
/* m_subPageNumber = 0x0000; */
m_cycleValue = 8;
@@ -256,7 +256,8 @@ bool LevelOnePage::setPacket(int packetNumber, int designationCode, QByteArray p
int CLUToffset = (designationCode == 0) ? 16 : 0;
m_defaultCharSet = ((packetContents.at(2) >> 4) & 0x3) | ((packetContents.at(3) << 2) & 0xc);
- m_defaultNOS = (packetContents.at(2) >> 1) & 0x7;
+ // Don't set m_defaultNOS directly as we need to keep control bits in subclass in sync
+ setDefaultNOS((packetContents.at(2) >> 1) & 0x7);
m_secondCharSet = ((packetContents.at(3) >> 5) & 0x1) | ((packetContents.at(4) << 1) & 0xe);
m_secondNOS = (packetContents.at(3) >> 2) & 0x7;
@@ -292,18 +293,13 @@ bool LevelOnePage::packetNeeded(int packetNumber, int designationCode) const
if (packetNumber == 26)
return ((localEnhance.size()+12) / 13) > designationCode;
- // FIXME don't save this raw packet yet as TeletextDocument::savePage currently uses fastTextLinkPageNumber
- // to put the FL commands into the .tti file
- // When we separate out loading and saving into its own cpp file, that will then become responsible for
- // converting this packet into an FL command itself
-
-/* if (packetNumber == 27 && designationCode == 0) {
+ if (packetNumber == 27 && designationCode == 0) {
for (int i=0; i<6; i++)
if ((m_fastTextLink[i].pageNumber & 0x0ff) != 0xff)
return true;
return false;
- }*/
+ }
if (packetNumber == 27 && (designationCode == 4 || designationCode == 5)) {
for (int i=0; i<(designationCode == 4 ? 6 : 2); i++) {
@@ -363,7 +359,7 @@ void LevelOnePage::loadPagePacket(QByteArray &inLine)
setPacket(lineNumber, inLine);
} else {
int designationCode = inLine.at(0) & 0x3f;
- if (inLine.size() < 40)
+ if (inLine.size() < 40) {
// OL is too short!
if (lineNumber == 26) {
// For a too-short enhancement triplets OL, first trim the line down to nearest whole triplet
@@ -374,6 +370,7 @@ void LevelOnePage::loadPagePacket(QByteArray &inLine)
} else
// For other triplet OLs and Hamming 8/4 OLs, just pad with zero data
inLine.leftJustified(40, '@');
+ }
for (int i=1; i<=39; i++)
inLine[i] = inLine.at(i) & 0x3f;
setPacket(lineNumber, designationCode, inLine);
@@ -381,48 +378,6 @@ void LevelOnePage::loadPagePacket(QByteArray &inLine)
}
}
-void LevelOnePage::savePage(QTextStream *outStream, int pageNumber, int subPageNumber)
-{
- auto writePacketsWithDesignationCodes=[&](int packetNumber)
- {
- for (int i=0; i<=16; i++)
- if (packetNeeded(packetNumber, i)) {
- QByteArray outLine = packet(packetNumber, i);
-
- *outStream << QString("OL,%1,").arg(packetNumber);
- outLine[0] = i | 0x40;
- for (int c=1; c.
+ */
+
+#include "loadsave.h"
+
+#include
+
+#include "document.h"
+#include "levelonepage.h"
+#include "pagebase.h"
+
+// Used by TTI and hashstring
+int controlBitsToPS(PageBase *subPage)
+{
+ // C4 Erase page is stored in bit 14
+ int pageStatus = 0x8000 | (subPage->controlBit(PageBase::C4ErasePage) << 14);
+ // C5 to C11 stored in order from bits 1 to 6
+ for (int i=PageBase::C5Newsflash; i<=PageBase::C11SerialMagazine; i++)
+ pageStatus |= subPage->controlBit(i) << (i-1);
+ // Apparently the TTI format stores the NOS bits backwards
+ pageStatus |= subPage->controlBit(PageBase::C12NOS) << 9;
+ pageStatus |= subPage->controlBit(PageBase::C13NOS) << 8;
+ pageStatus |= subPage->controlBit(PageBase::C14NOS) << 7;
+ return pageStatus;
+}
+
+void saveTTI(QSaveFile &file, const TeletextDocument &document)
+{
+ int p;
+ QTextStream outStream(&file);
+
+ auto write7bitPacket=[&](int packetNumber)
+ {
+ if (document.subPage(p)->packetNeeded(packetNumber)) {
+ QByteArray outLine = document.subPage(p)->packet(packetNumber);
+
+ outStream << QString("OL,%1,").arg(packetNumber);
+ for (int c=0; cpacketNeeded(packetNumber, designationCode)) {
+ QByteArray outLine = document.subPage(p)->packet(packetNumber, designationCode);
+
+ outStream << QString("OL,%1,").arg(packetNumber);
+ // TTI stores raw values with bit 7 set, doesn't do Hamming encoding
+ outLine[0] = designationCode | 0x40;
+ for (int c=1; c 1;
+
+ for (p=0; pcycleValue()).arg(document.subPage(p)->cycleType()==LevelOnePage::CTcycles ? 'C' : 'T') << Qt::endl;
+ else
+ // X/28/0 specifies page function and coding but the PF command
+ // should make it obvious to a human that this isn't a Level One Page
+ outStream << QString("PF,%1,%2").arg(document.pageFunction()).arg(document.packetCoding()) << Qt::endl;
+
+ bool writeFLCommand = false;
+ if (document.pageFunction() == TeletextDocument::PFLevelOnePage && document.subPage(p)->packetNeeded(27,0)) {
+ // Subpage has FastText links - if any link to a specific subpage, we need to write X/27/0 as raw
+ // otherwise we write the links as a human-readable FL command later on
+ writeFLCommand = true;
+ // TODO uncomment this when we can edit FastText subpage links
+ /*for (int i=0; i<6; i++)
+ if (document.subPage(p)->fastTextLinkSubPageNumber(i) != 0x3f7f) {
+ writeFLCommand = false;
+ break;
+ }*/
+ }
+
+ // X27 then X28 always come first
+ for (int i=(writeFLCommand ? 1 : 0); i<16; i++)
+ writeHammingPacket(27, i);
+ for (int i=0; i<16; i++)
+ writeHammingPacket(28, i);
+
+ if (document.packetCoding() == TeletextDocument::Coding7bit) {
+ // For 7 bit coding i.e. Level One Pages, X/26 are written before X/1 to X/25
+ for (int i=0; i<16; i++)
+ writeHammingPacket(26, i);
+ for (int i=1; i<=24; i++)
+ write7bitPacket(i);
+ } else {
+ // For others (especially (G)POP pages) X/1 to X/25 are written before X/26
+ for (int i=1; i<=25; i++)
+ writeHammingPacket(i);
+ for (int i=0; i<16; i++)
+ writeHammingPacket(26, i);
+ }
+
+ if (writeFLCommand) {
+ outStream << "FL,";
+ for (int i=0; i<6; i++) {
+ // Stored as page link with relative magazine number, convert to absolute page number for display
+ int absoluteLinkPageNumber = document.subPage(p)->fastTextLinkPageNumber(i) ^ (document.pageNumber() & 0x700);
+ // Fix magazine 0 to 8
+ if ((absoluteLinkPageNumber & 0x700) == 0x000)
+ absoluteLinkPageNumber |= 0x800;
+
+ outStream << QString("%1").arg(absoluteLinkPageNumber, 3, 16, QChar('0'));
+ if (i<5)
+ outStream << ',';
+ }
+ outStream << Qt::endl;
+ }
+
+ subPageNumber++;
+ }
+}
diff --git a/loadsave.h b/loadsave.h
new file mode 100644
index 0000000..3e1ba6b
--- /dev/null
+++ b/loadsave.h
@@ -0,0 +1,31 @@
+/*
+ * 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 LOADSAVE_H
+#define LOADSAVE_H
+
+#include
+
+#include "document.h"
+#include "pagebase.h"
+
+int controlBitsToPS(PageBase *);
+void saveTTI(QSaveFile &, const TeletextDocument &);
+
+#endif
diff --git a/mainwindow.cpp b/mainwindow.cpp
index d427330..43c1bfc 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -33,6 +34,7 @@
#include "mainwindow.h"
#include "levelonecommands.h"
+#include "loadsave.h"
#include "mainwidget.h"
#include "pageenhancementsdockwidget.h"
#include "pageoptionsdockwidget.h"
@@ -762,18 +764,23 @@ void MainWindow::openRecentFile()
bool MainWindow::saveFile(const QString &fileName)
{
- QFile file(fileName);
- if (!file.open(QFile::WriteOnly | QFile::Text)) {
- QMessageBox::warning(this, tr("QTeletextMaker"), tr("Cannot write file %1:\n%2.").arg(QDir::toNativeSeparators(fileName), file.errorString()));
+ QString errorMessage;
+
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ QSaveFile file(fileName);
+ if (file.open(QFile::WriteOnly | QFile::Text)) {
+ saveTTI(file, *m_textWidget->document());
+ if (!file.commit())
+ errorMessage = tr("Cannot write file %1:\n%2.") .arg(QDir::toNativeSeparators(fileName), file.errorString());
+ } else
+ errorMessage = tr("Cannot open file %1 for writing:\n%2.").arg(QDir::toNativeSeparators(fileName), file.errorString());
+ QApplication::restoreOverrideCursor();
+
+ if (!errorMessage.isEmpty()) {
+ QMessageBox::warning(this, tr("QTeletextMaker"), errorMessage);
return false;
}
- QTextStream out(&file);
- out.setCodec("ISO-8859-1");
- QApplication::setOverrideCursor(Qt::WaitCursor);
- m_textWidget->document()->saveDocument(&out);
- QApplication::restoreOverrideCursor();
-
setCurrentFile(fileName);
statusBar()->showMessage(tr("File saved"), 2000);
return true;
diff --git a/pagebase.cpp b/pagebase.cpp
index 1536dfc..bcd1ba3 100644
--- a/pagebase.cpp
+++ b/pagebase.cpp
@@ -26,13 +26,13 @@ PageBase::PageBase()
// We use nullptrs to keep track of allocated packets, so initialise them this way
for (int i=0; i<90; i++)
m_packets[i] = nullptr;
- for (int i=PageBase::C4ErasePage; i<=PageBase::C11SerialMagazine; i++)
+ for (int i=PageBase::C4ErasePage; i<=PageBase::C14NOS; i++)
m_controlBits[i] = false;
}
PageBase::PageBase(const PageBase &other)
{
- for (int i=PageBase::C4ErasePage; i<=PageBase::C11SerialMagazine; i++)
+ for (int i=PageBase::C4ErasePage; i<=PageBase::C14NOS; i++)
setControlBit(i, other.controlBit(i));
for (int i=0; i<90; i++)
if (other.packetNeededArrayIndex(i))
diff --git a/pagebase.h b/pagebase.h
index 25ebcd1..4b1a513 100644
--- a/pagebase.h
+++ b/pagebase.h
@@ -28,7 +28,7 @@ class PageBase //: public QObject
//Q_OBJECT
public:
- enum ControlBitsEnum { C4ErasePage, C5Newsflash, C6Subtitle, C7SuppressHeader, C8Update, C9InterruptedSequence, C10InhibitDisplay, C11SerialMagazine };
+ enum ControlBitsEnum { C4ErasePage, C5Newsflash, C6Subtitle, C7SuppressHeader, C8Update, C9InterruptedSequence, C10InhibitDisplay, C11SerialMagazine, C12NOS, C13NOS, C14NOS };
PageBase();
PageBase(const PageBase &);
@@ -49,7 +49,7 @@ public:
bool setControlBit(int, bool);
private:
- bool m_controlBits[8];
+ bool m_controlBits[11];
QByteArray *m_packets[90]; // X/0 to X/25, plus 16 packets for X/26, another 16 for X/27, for X28 and for X/29
};
diff --git a/qteletextmaker.pro b/qteletextmaker.pro
index caefd60..49dc390 100644
--- a/qteletextmaker.pro
+++ b/qteletextmaker.pro
@@ -4,6 +4,7 @@ requires(qtConfig(filedialog))
HEADERS = document.h \
levelonecommands.h \
levelonepage.h \
+ loadsave.h \
mainwidget.h \
mainwindow.h \
pagebase.h \
@@ -17,6 +18,7 @@ HEADERS = document.h \
SOURCES = document.cpp \
levelonecommands.cpp \
levelonepage.cpp \
+ loadsave.cpp \
main.cpp \
mainwidget.cpp \
mainwindow.cpp \