Compare commits
8 Commits
0.6.4-beta
...
0.6.5-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf4f85cc51 | ||
|
|
f96b973ff3 | ||
|
|
1d889ab724 | ||
|
|
48a2b48964 | ||
|
|
26b5974421 | ||
|
|
4743b26400 | ||
|
|
40fc1e38d8 | ||
|
|
cf6c4855ce |
8
.gitignore
vendored
@@ -1,6 +1,4 @@
|
|||||||
.qmake.stash
|
|
||||||
moc_*
|
|
||||||
qrc_*
|
|
||||||
*.o
|
|
||||||
Makefile
|
Makefile
|
||||||
qteletextmaker
|
build*/
|
||||||
|
cmake-build-*/
|
||||||
|
.idea
|
||||||
|
|||||||
60
CMakeLists.txt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
project(qteletextmaker VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
|
||||||
|
|
||||||
|
qt_standard_project_setup()
|
||||||
|
|
||||||
|
add_subdirectory(src/qteletextdecoder)
|
||||||
|
|
||||||
|
file (GLOB SOURCES src/qteletextmaker/*.cpp)
|
||||||
|
qt_add_executable(qteletextmaker ${SOURCES} src/qteletextmaker/actionicons.qrc)
|
||||||
|
|
||||||
|
target_link_libraries(qteletextmaker PRIVATE qteletextdecoder Qt::Widgets)
|
||||||
|
|
||||||
|
set_target_properties(qteletextmaker PROPERTIES
|
||||||
|
WIN32_EXECUTABLE ON
|
||||||
|
)
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
||||||
|
set(DOC_INSTALL_DIR "${CMAKE_INSTALL_DOCDIR}")
|
||||||
|
set(EXAMPLES_INSTALL_DIR "${DOC_INSTALL_DIR}/examples")
|
||||||
|
else()
|
||||||
|
set(BIN_INSTALL_DIR ".")
|
||||||
|
set(DOC_INSTALL_DIR ".")
|
||||||
|
set(EXAMPLES_INSTALL_DIR "./examples")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS qteletextmaker
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/README.md
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/LICENSE
|
||||||
|
DESTINATION ${DOC_INSTALL_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(DIRECTORY
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/examples/
|
||||||
|
DESTINATION ${EXAMPLES_INSTALL_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_generate_deploy_app_script(
|
||||||
|
TARGET qteletextmaker
|
||||||
|
FILENAME_VARIABLE deploy_script
|
||||||
|
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||||
|
)
|
||||||
|
install(SCRIPT ${deploy_script})
|
||||||
18
README.md
@@ -1,7 +1,7 @@
|
|||||||
# QTeletextMaker
|
# QTeletextMaker
|
||||||
QTeletextMaker is a teletext page editor with an emphasis on Level 2.5 enhancement editing, released under the GNU General Public License v3.0
|
QTeletextMaker is a teletext page editor with an emphasis on Level 2.5 enhancement editing, released under the GNU General Public License v3.0
|
||||||
|
|
||||||
It is written in C++ using the Qt 5 widget libraries but should also compile with Qt 6.
|
It is written in C++ using the Qt 6 widget libraries.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
- Load and save teletext pages in .tti format.
|
- Load and save teletext pages in .tti format.
|
||||||
@@ -16,15 +16,23 @@ Features
|
|||||||
- Configurable zoom.
|
- Configurable zoom.
|
||||||
- View teletext pages in 4:3, 16:9 pillar-box and 16:9 stretch aspect ratios.
|
- View teletext pages in 4:3, 16:9 pillar-box and 16:9 stretch aspect ratios.
|
||||||
|
|
||||||
Although designed on and developed for Linux, the Qt libraries are cross platform so a Windows executable can be built. A Windows executable can be found within the "Releases" link, compiled on a Linux host using [MXE](https://github.com/mxe/mxe) based on [these instructions](https://blog.8bitbuddhism.com/2018/08/22/cross-compiling-windows-applications-with-mxe/). After MXE is installed `make qtbase` should build and install the required dependencies to build QTeletextMaker.
|
Although designed on and developed for Linux, the Qt libraries are cross platform so a Windows executable can be built. A Windows executable can be found within the "Releases" link, compiled on a Linux host using [MXE](https://github.com/mxe/mxe) based on [these instructions](https://web.archive.org/web/20230606021352/https://blog.8bitbuddhism.com/2018/08/22/cross-compiling-windows-applications-with-mxe/). After MXE is installed `make qt6-qtbase` should build and install the required dependencies to build QTeletextMaker.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
### Linux
|
### Linux
|
||||||
Install version 5 or 6 of the QtCore, QtGui and QtWidgets libraries and build headers, along with the qmake tool. Depending on how qmake is installed, type `qmake && make -j3` or `qmake5 && make -j3` or `qmake6 && make -j3` in a terminal to build, you can replace -j3 with the number of processor cores used for the compile process.
|
Install version 6 of the QtCore, QtGui and QtWidgets libraries and build headers, along with CMake.
|
||||||
|
|
||||||
The above should place the qteletextmaker executable in the same directory as the source, type `./qteletextmaker` in the terminal to launch. Some Qt installs may place the executable into a "release" directory.
|
Change into the source directory and run the following commands. -j8 can be replaced with the number of processor cores used for the compile process
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
make -j8
|
||||||
|
```
|
||||||
|
|
||||||
Optionally, type `make install` afterwards to place the executable into /usr/local/bin.
|
The above should place the qteletextmaker executable into the build directory created by the above commands. Within the build directory type `./qteletextmaker` in the terminal to launch.
|
||||||
|
|
||||||
|
Optionally, type `cmake --install .` to place the executable into /usr/local/bin and the example .tti files into /usr/local/share/doc/qteletextmaker.
|
||||||
|
|
||||||
## Current limitations
|
## Current limitations
|
||||||
The following X/26 enhancement triplets are not rendered by the editor, although the list is fully aware of them.
|
The following X/26 enhancement triplets are not rendered by the editor, although the list is fully aware of them.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ PS,8000
|
|||||||
CT,8,T
|
CT,8,T
|
||||||
OL,26,@iD@@kfjD@@kfkD@@kflD@@kfmD@@kfnD@@kfoD@
|
OL,26,@iD@@kfjD@@kfkD@@kflD@@kfmD@@kfnD@@kfoD@
|
||||||
OL,26,A@kfpD@@kfqD@@kfrD@@kfsD@@kftD@@kfuD@@kf
|
OL,26,A@kfpD@@kfqD@@kfrD@@kfsD@@kftD@@kfuD@@kf
|
||||||
OL,26,BvD@@kfwD@@kfxD@@kfxD@@kfyD@@kfzD@@kf{D@
|
OL,26,BvD@@kfwD@@kfxD@@kfyD@@kfzD@@kf{D@@kf|D@
|
||||||
OL,26,C@kf|D@@kf}D@@kf~D@@kfD@@kfCCCC
|
OL,26,C@kf}D@@kf~D@@kfD@@kfCCCCCC
|
||||||
OL,1, Active Position across every row
|
OL,1, Active Position across every row
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
QT += widgets
|
|
||||||
requires(qtConfig(filedialog))
|
|
||||||
|
|
||||||
HEADERS = decode.h \
|
|
||||||
document.h \
|
|
||||||
hamming.h \
|
|
||||||
keymap.h \
|
|
||||||
levelonecommands.h \
|
|
||||||
levelonepage.h \
|
|
||||||
loadsave.h \
|
|
||||||
mainwidget.h \
|
|
||||||
mainwindow.h \
|
|
||||||
pagebase.h \
|
|
||||||
pagex26base.h \
|
|
||||||
pagecomposelinksdockwidget.h \
|
|
||||||
pageenhancementsdockwidget.h \
|
|
||||||
pageoptionsdockwidget.h \
|
|
||||||
palettedockwidget.h \
|
|
||||||
render.h \
|
|
||||||
x26commands.h \
|
|
||||||
x26dockwidget.h \
|
|
||||||
x26menus.h \
|
|
||||||
x26model.h \
|
|
||||||
x26triplets.h \
|
|
||||||
x28commands.h
|
|
||||||
SOURCES = decode.cpp \
|
|
||||||
document.cpp \
|
|
||||||
levelonecommands.cpp \
|
|
||||||
levelonepage.cpp \
|
|
||||||
loadsave.cpp \
|
|
||||||
main.cpp \
|
|
||||||
mainwidget.cpp \
|
|
||||||
mainwindow.cpp \
|
|
||||||
pagebase.cpp \
|
|
||||||
pagex26base.cpp \
|
|
||||||
pagecomposelinksdockwidget.cpp \
|
|
||||||
pageenhancementsdockwidget.cpp \
|
|
||||||
pageoptionsdockwidget.cpp \
|
|
||||||
palettedockwidget.cpp \
|
|
||||||
render.cpp \
|
|
||||||
x26commands.cpp \
|
|
||||||
x26dockwidget.cpp \
|
|
||||||
x26menus.cpp \
|
|
||||||
x26model.cpp \
|
|
||||||
x26triplets.cpp \
|
|
||||||
x28commands.cpp
|
|
||||||
RESOURCES = qteletextmaker.qrc
|
|
||||||
|
|
||||||
# install
|
|
||||||
target.path = /usr/local/bin
|
|
||||||
INSTALLS += target
|
|
||||||
7
src/qteletextdecoder/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
file (GLOB SOURCES *.cpp)
|
||||||
|
|
||||||
|
add_library(qteletextdecoder ${SOURCES} teletextfonts.qrc)
|
||||||
|
|
||||||
|
target_include_directories(qteletextdecoder PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(qteletextdecoder Qt::Widgets)
|
||||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <QBitmap>
|
#include <QBitmap>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QDir>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
@@ -34,8 +35,10 @@ QImage *TeletextFontBitmap::s_fontImage = nullptr;
|
|||||||
|
|
||||||
TeletextFontBitmap::TeletextFontBitmap()
|
TeletextFontBitmap::TeletextFontBitmap()
|
||||||
{
|
{
|
||||||
|
Q_INIT_RESOURCE(teletextfonts);
|
||||||
|
|
||||||
if (s_instances == 0) {
|
if (s_instances == 0) {
|
||||||
s_fontBitmap = new QBitmap(":/images/teletextfont.png");
|
s_fontBitmap = new QBitmap(":/fontimages/teletextfont.png");
|
||||||
s_fontImage = new QImage(s_fontBitmap->toImage());
|
s_fontImage = new QImage(s_fontBitmap->toImage());
|
||||||
}
|
}
|
||||||
s_instances++;
|
s_instances++;
|
||||||
@@ -57,7 +60,7 @@ TeletextPageRender::TeletextPageRender()
|
|||||||
m_pageImage[i] = new QImage(864, 250, QImage::Format_ARGB32_Premultiplied);
|
m_pageImage[i] = new QImage(864, 250, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
|
||||||
m_reveal = false;
|
m_reveal = false;
|
||||||
m_mix = false;
|
m_renderMode = RenderNormal;
|
||||||
m_showControlCodes = false;
|
m_showControlCodes = false;
|
||||||
m_flashBuffersHz = 0;
|
m_flashBuffersHz = 0;
|
||||||
|
|
||||||
@@ -223,6 +226,13 @@ inline void TeletextPageRender::drawBoldOrItalicCharacter(QPainter &painter, int
|
|||||||
|
|
||||||
void TeletextPageRender::renderPage(bool force)
|
void TeletextPageRender::renderPage(bool force)
|
||||||
{
|
{
|
||||||
|
if (m_renderMode == RenderWhiteOnBlack) {
|
||||||
|
m_foregroundQColor = Qt::white;
|
||||||
|
m_backgroundQColor = Qt::black;
|
||||||
|
} else if (m_renderMode == RenderBlackOnWhite) {
|
||||||
|
m_foregroundQColor = Qt::black;
|
||||||
|
m_backgroundQColor = Qt::white;
|
||||||
|
}
|
||||||
for (int r=0; r<25; r++)
|
for (int r=0; r<25; r++)
|
||||||
renderRow(r, 0, force);
|
renderRow(r, 0, force);
|
||||||
}
|
}
|
||||||
@@ -250,9 +260,11 @@ void TeletextPageRender::renderRow(int r, int ph, bool force)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ph == 0) {
|
// Second part of "if" suppresses all flashing on monochrome render modes
|
||||||
|
if (ph == 0 && m_renderMode < RenderWhiteOnBlack) {
|
||||||
if (m_decoder->cellFlashMode(r, c) != 0)
|
if (m_decoder->cellFlashMode(r, c) != 0)
|
||||||
flashingRow = qMax(flashingRow, (m_decoder->cellFlashRatePhase(r, c) == 0) ? 1 : 2);
|
flashingRow = qMax(flashingRow, (m_decoder->cellFlashRatePhase(r, c) == 0) ? 1 : 2);
|
||||||
|
// } else if (!force)
|
||||||
} else
|
} else
|
||||||
force = m_decoder->cellFlashMode(r, c) != 0;
|
force = m_decoder->cellFlashMode(r, c) != 0;
|
||||||
|
|
||||||
@@ -275,36 +287,38 @@ void TeletextPageRender::renderRow(int r, int ph, bool force)
|
|||||||
characterDiacritical = m_decoder->cellCharacterDiacritical(r, c);
|
characterDiacritical = m_decoder->cellCharacterDiacritical(r, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder->cellFlashMode(r, c) == 0)
|
if (m_renderMode < RenderWhiteOnBlack) {
|
||||||
m_foregroundQColor = m_decoder->cellForegroundQColor(r, c);
|
if (m_decoder->cellFlashMode(r, c) == 0)
|
||||||
else {
|
|
||||||
// Flashing cell, decide if phase in this cycle is on or off
|
|
||||||
bool phaseOn;
|
|
||||||
|
|
||||||
if (m_decoder->cellFlashRatePhase(r, c) == 0)
|
|
||||||
phaseOn = (ph < 3) ^ (m_decoder->cellFlashMode(r, c) == 2);
|
|
||||||
else
|
|
||||||
phaseOn = ((ph == m_decoder->cellFlash2HzPhaseNumber(r, c)-1) || (ph == m_decoder->cellFlash2HzPhaseNumber(r, c)+2)) ^ (m_decoder->cellFlashMode(r, c) == 2);
|
|
||||||
|
|
||||||
// If flashing to adjacent CLUT select the appropriate foreground colour
|
|
||||||
if (m_decoder->cellFlashMode(r, c) == 3 && !phaseOn)
|
|
||||||
m_foregroundQColor = m_decoder->cellFlashForegroundQColor(r, c);
|
|
||||||
else
|
|
||||||
m_foregroundQColor = m_decoder->cellForegroundQColor(r, c);
|
m_foregroundQColor = m_decoder->cellForegroundQColor(r, c);
|
||||||
|
else {
|
||||||
|
// Flashing cell, decide if phase in this cycle is on or off
|
||||||
|
bool phaseOn;
|
||||||
|
|
||||||
// If flashing mode is Normal or Invert, draw a space instead of a character on phase
|
if (m_decoder->cellFlashRatePhase(r, c) == 0)
|
||||||
if ((m_decoder->cellFlashMode(r, c) == 1 || m_decoder->cellFlashMode(r, c) == 2) && !phaseOn) {
|
phaseOn = (ph < 3) ^ (m_decoder->cellFlashMode(r, c) == 2);
|
||||||
// Character 0x00 draws space without underline
|
else
|
||||||
characterCode = 0x00;
|
phaseOn = ((ph == m_decoder->cellFlash2HzPhaseNumber(r, c)-1) || (ph == m_decoder->cellFlash2HzPhaseNumber(r, c)+2)) ^ (m_decoder->cellFlashMode(r, c) == 2);
|
||||||
characterSet = 0;
|
|
||||||
characterDiacritical = 0;
|
// If flashing to adjacent CLUT select the appropriate foreground colour
|
||||||
|
if (m_decoder->cellFlashMode(r, c) == 3 && !phaseOn)
|
||||||
|
m_foregroundQColor = m_decoder->cellFlashForegroundQColor(r, c);
|
||||||
|
else
|
||||||
|
m_foregroundQColor = m_decoder->cellForegroundQColor(r, c);
|
||||||
|
|
||||||
|
// If flashing mode is Normal or Invert, draw a space instead of a character on phase
|
||||||
|
if ((m_decoder->cellFlashMode(r, c) == 1 || m_decoder->cellFlashMode(r, c) == 2) && !phaseOn) {
|
||||||
|
// Character 0x00 draws space without underline
|
||||||
|
characterCode = 0x00;
|
||||||
|
characterSet = 0;
|
||||||
|
characterDiacritical = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_mix || m_decoder->cellBoxed(r, c))
|
if (m_renderMode != RenderMix || m_decoder->cellBoxed(r, c))
|
||||||
m_backgroundQColor = m_decoder->cellBackgroundQColor(r, c);
|
m_backgroundQColor = m_decoder->cellBackgroundQColor(r, c);
|
||||||
else
|
else
|
||||||
m_backgroundQColor = Qt::transparent;
|
m_backgroundQColor = Qt::transparent;
|
||||||
|
}
|
||||||
|
|
||||||
drawCharacter(painter, r, c, characterCode, characterSet, characterDiacritical, m_decoder->cellCharacterFragment(r, c));
|
drawCharacter(painter, r, c, characterCode, characterSet, characterDiacritical, m_decoder->cellCharacterFragment(r, c));
|
||||||
|
|
||||||
@@ -436,17 +450,16 @@ void TeletextPageRender::setReveal(bool reveal)
|
|||||||
m_decoder->setRefresh(r, c, true);
|
m_decoder->setRefresh(r, c, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeletextPageRender::setMix(bool mix)
|
void TeletextPageRender::setRenderMode(RenderMode renderMode)
|
||||||
{
|
{
|
||||||
if (mix == m_mix)
|
if (renderMode == m_renderMode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_mix = mix;
|
m_renderMode = renderMode;
|
||||||
|
|
||||||
for (int r=0; r<25; r++)
|
for (int r=0; r<25; r++)
|
||||||
for (int c=0; c<72; c++)
|
for (int c=0; c<72; c++)
|
||||||
if (!m_decoder->cellBoxed(r, c))
|
m_decoder->setRefresh(r, c, true);
|
||||||
m_decoder->setRefresh(r, c, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,11 +49,13 @@ class TeletextPageRender : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum RenderMode { RenderNormal, RenderMix, RenderWhiteOnBlack, RenderBlackOnWhite };
|
||||||
|
|
||||||
TeletextPageRender();
|
TeletextPageRender();
|
||||||
~TeletextPageRender();
|
~TeletextPageRender();
|
||||||
|
|
||||||
QImage* image(int i) const { return m_pageImage[i]; };
|
QImage* image(int i) const { return m_pageImage[i]; };
|
||||||
bool mix() const { return m_mix; };
|
RenderMode renderMode() const { return m_renderMode; };
|
||||||
void setDecoder(TeletextPageDecode *decoder);
|
void setDecoder(TeletextPageDecode *decoder);
|
||||||
void renderPage(bool force=false);
|
void renderPage(bool force=false);
|
||||||
bool showControlCodes() const { return m_showControlCodes; };
|
bool showControlCodes() const { return m_showControlCodes; };
|
||||||
@@ -61,7 +63,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void colourChanged(int index);
|
void colourChanged(int index);
|
||||||
void setReveal(bool reveal);
|
void setReveal(bool reveal);
|
||||||
void setMix(bool mix);
|
void setRenderMode(RenderMode renderMode);
|
||||||
void setShowControlCodes(bool showControlCodes);
|
void setShowControlCodes(bool showControlCodes);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -71,7 +73,8 @@ protected:
|
|||||||
TeletextFontBitmap m_fontBitmap;
|
TeletextFontBitmap m_fontBitmap;
|
||||||
QImage* m_pageImage[6];
|
QImage* m_pageImage[6];
|
||||||
unsigned char m_controlCodeCache[25][40];
|
unsigned char m_controlCodeCache[25][40];
|
||||||
bool m_reveal, m_mix, m_showControlCodes;
|
RenderMode m_renderMode;
|
||||||
|
bool m_reveal, m_showControlCodes;
|
||||||
int m_flashBuffersHz;
|
int m_flashBuffersHz;
|
||||||
int m_flashingRow[25];
|
int m_flashingRow[25];
|
||||||
|
|
||||||
5
src/qteletextdecoder/teletextfonts.qrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file>fontimages/teletextfont.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -238,11 +238,18 @@ void X26TripletList::updateInternalData()
|
|||||||
case 0x22: // G3 mosaic character at level 1.5
|
case 0x22: // G3 mosaic character at level 1.5
|
||||||
case 0x2f: // G2 character
|
case 0x2f: // G2 character
|
||||||
activePosition.setColumn(triplet->addressColumn());
|
activePosition.setColumn(triplet->addressColumn());
|
||||||
|
|
||||||
|
if (activePosition.row() != triplet->m_activePositionRow || activePosition.column() != triplet->m_activePositionColumn)
|
||||||
|
triplet->m_activePosition1p5Differs = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (triplet->modeExt() >= 0x30 && triplet->modeExt() <= 0x3f)
|
if (triplet->modeExt() >= 0x30 && triplet->modeExt() <= 0x3f) {
|
||||||
// G0 diacritical mark
|
// G0 diacritical mark
|
||||||
activePosition.setColumn(triplet->addressColumn());
|
activePosition.setColumn(triplet->addressColumn());
|
||||||
|
|
||||||
|
if (activePosition.row() != triplet->m_activePositionRow || activePosition.column() != triplet->m_activePositionColumn)
|
||||||
|
triplet->m_activePosition1p5Differs = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
triplet->m_activePositionRow1p5 = activePosition.row();
|
triplet->m_activePositionRow1p5 = activePosition.row();
|
||||||
@@ -66,6 +66,7 @@ public:
|
|||||||
X26TripletError error() const { return m_error; }
|
X26TripletError error() const { return m_error; }
|
||||||
bool reservedMode() const { return m_reservedMode; }
|
bool reservedMode() const { return m_reservedMode; }
|
||||||
bool reservedData() const { return m_reservedData; }
|
bool reservedData() const { return m_reservedData; }
|
||||||
|
bool activePosition1p5Differs() const { return m_activePosition1p5Differs; }
|
||||||
|
|
||||||
friend class X26TripletList;
|
friend class X26TripletList;
|
||||||
|
|
||||||
@@ -77,6 +78,7 @@ private:
|
|||||||
int m_activePositionColumn = -1;
|
int m_activePositionColumn = -1;
|
||||||
int m_activePositionRow1p5 = -1;
|
int m_activePositionRow1p5 = -1;
|
||||||
int m_activePositionColumn1p5 = -1;
|
int m_activePositionColumn1p5 = -1;
|
||||||
|
bool m_activePosition1p5Differs = false;
|
||||||
X26TripletError m_error = NoError;
|
X26TripletError m_error = NoError;
|
||||||
bool m_reservedMode = false;
|
bool m_reservedMode = false;
|
||||||
bool m_reservedData = false;
|
bool m_reservedData = false;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>images/teletextfont.png</file>
|
|
||||||
<file>images/copy.png</file>
|
<file>images/copy.png</file>
|
||||||
<file>images/cut.png</file>
|
<file>images/cut.png</file>
|
||||||
<file>images/new.png</file>
|
<file>images/new.png</file>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -24,13 +24,13 @@
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Q_INIT_RESOURCE(qteletextmaker);
|
Q_INIT_RESOURCE(actionicons);
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
QApplication::setApplicationName("QTeletextMaker");
|
QApplication::setApplicationName("QTeletextMaker");
|
||||||
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
QApplication::setApplicationDisplayName(QApplication::applicationName());
|
||||||
QApplication::setOrganizationName("gkmac.co.uk");
|
QApplication::setOrganizationName("gkmac.co.uk");
|
||||||
QApplication::setOrganizationDomain("gkmac.co.uk");
|
QApplication::setOrganizationDomain("gkmac.co.uk");
|
||||||
QApplication::setApplicationVersion("0.6.4-beta");
|
QApplication::setApplicationVersion("0.6.5-beta");
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QApplication::applicationName());
|
parser.setApplicationDescription(QApplication::applicationName());
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
@@ -161,18 +161,18 @@ void TeletextWidget::setReveal(bool reveal)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeletextWidget::setMix(bool mix)
|
|
||||||
{
|
|
||||||
m_pageRender.setMix(mix);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TeletextWidget::setShowControlCodes(bool showControlCodes)
|
void TeletextWidget::setShowControlCodes(bool showControlCodes)
|
||||||
{
|
{
|
||||||
m_pageRender.setShowControlCodes(showControlCodes);
|
m_pageRender.setShowControlCodes(showControlCodes);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeletextWidget::setRenderMode(TeletextPageRender::RenderMode renderMode)
|
||||||
|
{
|
||||||
|
m_pageRender.setRenderMode(renderMode);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void TeletextWidget::setControlBit(int bitNumber, bool active)
|
void TeletextWidget::setControlBit(int bitNumber, bool active)
|
||||||
{
|
{
|
||||||
m_levelOnePage->setControlBit(bitNumber, active);
|
m_levelOnePage->setControlBit(bitNumber, active);
|
||||||
@@ -705,19 +705,34 @@ void LevelOneScene::updateSelection()
|
|||||||
m_selectionRectItem->setVisible(true);
|
m_selectionRectItem->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelOneScene::setMix(bool mix)
|
void LevelOneScene::setRenderMode(TeletextPageRender::RenderMode renderMode)
|
||||||
{
|
{
|
||||||
if (mix) {
|
static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->setRenderMode(renderMode);
|
||||||
m_fullScreenTopRectItem->setBrush(Qt::transparent);
|
|
||||||
m_fullScreenBottomRectItem->setBrush(Qt::transparent);
|
QColor fullColour;
|
||||||
for (int r=0; r<25; r++) {
|
|
||||||
m_fullRowLeftRectItem[r]->setBrush(Qt::transparent);
|
switch (renderMode) {
|
||||||
m_fullRowRightRectItem[r]->setBrush(Qt::transparent);
|
case TeletextPageRender::RenderNormal:
|
||||||
}
|
setFullScreenColour(static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageDecode()->fullScreenQColor());
|
||||||
} else {
|
for (int r=0; r<25; r++)
|
||||||
setFullScreenColour(static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageDecode()->fullScreenQColor());
|
setFullRowColour(r, static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageDecode()->fullRowQColor(r));
|
||||||
for (int r=0; r<25; r++)
|
return;
|
||||||
setFullRowColour(r, static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageDecode()->fullRowQColor(r));
|
case TeletextPageRender::RenderMix:
|
||||||
|
fullColour = Qt::transparent;
|
||||||
|
break;
|
||||||
|
case TeletextPageRender::RenderWhiteOnBlack:
|
||||||
|
fullColour = Qt::black;
|
||||||
|
break;
|
||||||
|
case TeletextPageRender::RenderBlackOnWhite:
|
||||||
|
fullColour = Qt::white;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fullScreenTopRectItem->setBrush(fullColour);
|
||||||
|
m_fullScreenBottomRectItem->setBrush(fullColour);
|
||||||
|
for (int r=0; r<25; r++) {
|
||||||
|
m_fullRowLeftRectItem[r]->setBrush(fullColour);
|
||||||
|
m_fullRowRightRectItem[r]->setBrush(fullColour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,7 +797,7 @@ void LevelOneScene::keyReleaseEvent(QKeyEvent *keyEvent)
|
|||||||
|
|
||||||
void LevelOneScene::setFullScreenColour(const QColor &newColor)
|
void LevelOneScene::setFullScreenColour(const QColor &newColor)
|
||||||
{
|
{
|
||||||
if (!static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageRender()->mix()) {
|
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageRender()->renderMode() == TeletextPageRender::RenderNormal) {
|
||||||
m_fullScreenTopRectItem->setBrush(newColor);
|
m_fullScreenTopRectItem->setBrush(newColor);
|
||||||
m_fullScreenBottomRectItem->setBrush(newColor);
|
m_fullScreenBottomRectItem->setBrush(newColor);
|
||||||
}
|
}
|
||||||
@@ -790,7 +805,7 @@ void LevelOneScene::setFullScreenColour(const QColor &newColor)
|
|||||||
|
|
||||||
void LevelOneScene::setFullRowColour(int row, const QColor &newColor)
|
void LevelOneScene::setFullRowColour(int row, const QColor &newColor)
|
||||||
{
|
{
|
||||||
if (!static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageRender()->mix()) {
|
if (static_cast<TeletextWidget *>(m_levelOneProxyWidget->widget())->pageRender()->renderMode() == TeletextPageRender::RenderNormal) {
|
||||||
m_fullRowLeftRectItem[row]->setBrush(newColor);
|
m_fullRowLeftRectItem[row]->setBrush(newColor);
|
||||||
m_fullRowRightRectItem[row]->setBrush(newColor);
|
m_fullRowRightRectItem[row]->setBrush(newColor);
|
||||||
}
|
}
|
||||||
@@ -65,8 +65,8 @@ public slots:
|
|||||||
void subPageSelected();
|
void subPageSelected();
|
||||||
void refreshPage();
|
void refreshPage();
|
||||||
void setReveal(bool reveal);
|
void setReveal(bool reveal);
|
||||||
void setMix(bool mix);
|
|
||||||
void setShowControlCodes(bool showControlCodes);
|
void setShowControlCodes(bool showControlCodes);
|
||||||
|
void setRenderMode(TeletextPageRender::RenderMode renderMode);
|
||||||
void updateFlashTimer(int newFlashTimer);
|
void updateFlashTimer(int newFlashTimer);
|
||||||
void pauseFlash(bool pauseNow);
|
void pauseFlash(bool pauseNow);
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void updateCursor();
|
void updateCursor();
|
||||||
void updateSelection();
|
void updateSelection();
|
||||||
void setMix(bool mix);
|
void setRenderMode(TeletextPageRender::RenderMode renderMode);
|
||||||
void toggleGrid(bool gridOn);
|
void toggleGrid(bool gridOn);
|
||||||
void hideGUIElements(bool hidden);
|
void hideGUIElements(bool hidden);
|
||||||
void setFullScreenColour(const QColor &newColor);
|
void setFullScreenColour(const QColor &newColor);
|
||||||
@@ -189,11 +189,9 @@ void MainWindow::exportPNG()
|
|||||||
m_textWidget->pauseFlash(true);
|
m_textWidget->pauseFlash(true);
|
||||||
m_textScene->hideGUIElements(true);
|
m_textScene->hideGUIElements(true);
|
||||||
// Disable exporting in Mix mode as it corrupts the background
|
// Disable exporting in Mix mode as it corrupts the background
|
||||||
bool reMix = m_textWidget->pageRender()->mix();
|
bool reMix = m_textWidget->pageRender()->renderMode() == TeletextPageRender::RenderMix;
|
||||||
if (reMix) {
|
if (reMix)
|
||||||
m_textWidget->setMix(false);
|
m_textScene->setRenderMode(TeletextPageRender::RenderNormal);
|
||||||
m_textScene->setMix(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the image from the scene
|
// Extract the image from the scene
|
||||||
QImage interImage = QImage(m_textScene->sceneRect().size().toSize(), QImage::Format_RGB32);
|
QImage interImage = QImage(m_textScene->sceneRect().size().toSize(), QImage::Format_RGB32);
|
||||||
@@ -205,10 +203,8 @@ void MainWindow::exportPNG()
|
|||||||
|
|
||||||
// Now we've extracted the image we can put the GUI things back
|
// Now we've extracted the image we can put the GUI things back
|
||||||
m_textScene->hideGUIElements(false);
|
m_textScene->hideGUIElements(false);
|
||||||
if (reMix) {
|
if (reMix)
|
||||||
m_textWidget->setMix(true);
|
m_textScene->setRenderMode(TeletextPageRender::RenderMix);
|
||||||
m_textScene->setMix(true);
|
|
||||||
}
|
|
||||||
m_textWidget->pauseFlash(false);
|
m_textWidget->pauseFlash(false);
|
||||||
|
|
||||||
// Now scale the extracted image to the selected aspect ratio
|
// Now scale the extracted image to the selected aspect ratio
|
||||||
@@ -503,13 +499,6 @@ void MainWindow::createActions()
|
|||||||
revealAct->setStatusTip(tr("Toggle reveal"));
|
revealAct->setStatusTip(tr("Toggle reveal"));
|
||||||
connect(revealAct, &QAction::toggled, m_textWidget, &TeletextWidget::setReveal);
|
connect(revealAct, &QAction::toggled, m_textWidget, &TeletextWidget::setReveal);
|
||||||
|
|
||||||
QAction *mixAct = viewMenu->addAction(tr("&Mix"));
|
|
||||||
mixAct->setCheckable(true);
|
|
||||||
mixAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_M));
|
|
||||||
mixAct->setStatusTip(tr("Toggle mix"));
|
|
||||||
connect(mixAct, &QAction::toggled, m_textWidget, &TeletextWidget::setMix);
|
|
||||||
connect(mixAct, &QAction::toggled, m_textScene, &LevelOneScene::setMix);
|
|
||||||
|
|
||||||
QAction *gridAct = viewMenu->addAction(tr("&Grid"));
|
QAction *gridAct = viewMenu->addAction(tr("&Grid"));
|
||||||
gridAct->setCheckable(true);
|
gridAct->setCheckable(true);
|
||||||
gridAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
|
gridAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
|
||||||
@@ -524,6 +513,17 @@ void MainWindow::createActions()
|
|||||||
|
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
|
QMenu *renderModeSubMenu = viewMenu->addMenu(tr("Render mode"));
|
||||||
|
QAction *renderModeActs[4];
|
||||||
|
renderModeActs[0] = renderModeSubMenu->addAction(tr("Normal"));
|
||||||
|
renderModeActs[0]->setStatusTip(tr("Render page normally"));
|
||||||
|
renderModeActs[1] = renderModeSubMenu->addAction(tr("Mix"));
|
||||||
|
renderModeActs[1]->setStatusTip(tr("Render page in mix mode"));
|
||||||
|
renderModeActs[2] = renderModeSubMenu->addAction(tr("White on black"));
|
||||||
|
renderModeActs[2]->setStatusTip(tr("Render page with white foreground on black background"));
|
||||||
|
renderModeActs[3] = renderModeSubMenu->addAction(tr("Black on white"));
|
||||||
|
renderModeActs[3]->setStatusTip(tr("Render page with black foreground on white background"));
|
||||||
|
|
||||||
QMenu *borderSubMenu = viewMenu->addMenu(tr("Border"));
|
QMenu *borderSubMenu = viewMenu->addMenu(tr("Border"));
|
||||||
m_borderActs[0] = borderSubMenu->addAction(tr("None"));
|
m_borderActs[0] = borderSubMenu->addAction(tr("None"));
|
||||||
m_borderActs[0]->setStatusTip(tr("View with no border"));
|
m_borderActs[0]->setStatusTip(tr("View with no border"));
|
||||||
@@ -542,18 +542,26 @@ void MainWindow::createActions()
|
|||||||
m_aspectRatioActs[3] = aspectRatioSubMenu->addAction(tr("Pixel 1:2"));
|
m_aspectRatioActs[3] = aspectRatioSubMenu->addAction(tr("Pixel 1:2"));
|
||||||
m_aspectRatioActs[3]->setStatusTip(tr("View with 1:2 pixel mapping"));
|
m_aspectRatioActs[3]->setStatusTip(tr("View with 1:2 pixel mapping"));
|
||||||
|
|
||||||
|
QActionGroup *renderModeGroup = new QActionGroup(this);
|
||||||
QActionGroup *borderGroup = new QActionGroup(this);
|
QActionGroup *borderGroup = new QActionGroup(this);
|
||||||
QActionGroup *aspectRatioGroup = new QActionGroup(this);
|
QActionGroup *aspectRatioGroup = new QActionGroup(this);
|
||||||
for (int i=0; i<=3; i++) {
|
for (int i=0; i<=3; i++) {
|
||||||
|
renderModeActs[i]->setCheckable(true);
|
||||||
|
connect(renderModeActs[i], &QAction::triggered, [=]() { m_textScene->setRenderMode(static_cast<TeletextPageRender::RenderMode>(i)); });
|
||||||
|
renderModeGroup->addAction(renderModeActs[i]);
|
||||||
|
|
||||||
m_aspectRatioActs[i]->setCheckable(true);
|
m_aspectRatioActs[i]->setCheckable(true);
|
||||||
connect(m_aspectRatioActs[i], &QAction::triggered, [=]() { setAspectRatio(i); });
|
connect(m_aspectRatioActs[i], &QAction::triggered, [=]() { setAspectRatio(i); });
|
||||||
aspectRatioGroup->addAction(m_aspectRatioActs[i]);
|
aspectRatioGroup->addAction(m_aspectRatioActs[i]);
|
||||||
|
|
||||||
if (i == 3)
|
if (i == 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
m_borderActs[i]->setCheckable(true);
|
m_borderActs[i]->setCheckable(true);
|
||||||
connect(m_borderActs[i], &QAction::triggered, [=]() { setBorder(i); });
|
connect(m_borderActs[i], &QAction::triggered, [=]() { setBorder(i); });
|
||||||
borderGroup->addAction(m_borderActs[i]);
|
borderGroup->addAction(m_borderActs[i]);
|
||||||
}
|
}
|
||||||
|
renderModeActs[0]->setChecked(true);
|
||||||
|
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
@@ -602,15 +602,14 @@ void X26DockWidget::selectX26ListRow(int row)
|
|||||||
|
|
||||||
void X26DockWidget::loadX26List()
|
void X26DockWidget::loadX26List()
|
||||||
{
|
{
|
||||||
|
disableTripletWidgets();
|
||||||
m_x26Model->setX26ListLoaded(true);
|
m_x26Model->setX26ListLoaded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void X26DockWidget::unloadX26List()
|
void X26DockWidget::unloadX26List()
|
||||||
{
|
{
|
||||||
|
disableTripletWidgets();
|
||||||
m_x26Model->setX26ListLoaded(false);
|
m_x26Model->setX26ListLoaded(false);
|
||||||
m_rawTripletAddressSpinBox->setEnabled(false);
|
|
||||||
m_rawTripletDataSpinBox->setEnabled(false);
|
|
||||||
m_rawTripletModeSpinBox->setEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void X26DockWidget::rowSelected(const QModelIndex ¤t, const QModelIndex &previous)
|
void X26DockWidget::rowSelected(const QModelIndex ¤t, const QModelIndex &previous)
|
||||||
@@ -69,19 +69,27 @@ QVariant X26Model::data(const QModelIndex &index, int role) const
|
|||||||
if (role == Qt::ForegroundRole) {
|
if (role == Qt::ForegroundRole) {
|
||||||
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
||||||
return QColor(252, 252, 252);
|
return QColor(252, 252, 252);
|
||||||
else if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
||||||
|
return QColor(35, 38, 39);
|
||||||
|
if (index.column() <= 1 && triplet.activePosition1p5Differs())
|
||||||
return QColor(35, 38, 39);
|
return QColor(35, 38, 39);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::BackgroundRole) {
|
if (role == Qt::BackgroundRole) {
|
||||||
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
if (triplet.error() != X26Triplet::NoError && index.column() == m_tripletErrors[triplet.error()].columnHighlight)
|
||||||
return QColor(218, 68, 63);
|
return QColor(218, 68, 63);
|
||||||
else if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
if ((index.column() == 2 && triplet.reservedMode()) || (index.column() == 3 && triplet.reservedData()))
|
||||||
|
return QColor(246, 116, 0);
|
||||||
|
if (index.column() <= 1 && triplet.activePosition1p5Differs())
|
||||||
return QColor(246, 116, 0);
|
return QColor(246, 116, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::ToolTipRole && triplet.error() != X26Triplet::NoError)
|
if (role == Qt::ToolTipRole) {
|
||||||
return m_tripletErrors[triplet.error()].message;
|
if (triplet.error() != X26Triplet::NoError)
|
||||||
|
return m_tripletErrors[triplet.error()].message;
|
||||||
|
if (triplet.activePosition1p5Differs())
|
||||||
|
return "Active Position differs between Level 1.5 and higher levels";
|
||||||
|
}
|
||||||
|
|
||||||
if (role == Qt::DisplayRole || role == Qt::EditRole)
|
if (role == Qt::DisplayRole || role == Qt::EditRole)
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||