/*
* Copyright (C) 2020-2024 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 "x26dockwidget.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "render.h"
#include "x26menus.h"
CharacterListModel::CharacterListModel(QObject *parent): QAbstractListModel(parent)
{
m_characterSet = 0;
}
int CharacterListModel::rowCount(const QModelIndex & /*parent*/) const
{
return 96;
}
QVariant CharacterListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DisplayRole)
return QString("0x%1").arg(index.row()+0x20, 2, 16);
if (role == Qt::DecorationRole)
return m_fontBitmap.charBitmap(index.row()+32, m_characterSet);
return QVariant();
}
void CharacterListModel::setCharacterSet(int characterSet)
{
if (characterSet != m_characterSet) {
m_characterSet = characterSet;
emit dataChanged(createIndex(0, 0), createIndex(95, 0), QVector(Qt::DecorationRole));
}
}
X26DockWidget::X26DockWidget(TeletextWidget *parent): QDockWidget(parent)
{
QVBoxLayout *x26Layout = new QVBoxLayout;
QWidget *x26Widget = new QWidget;
m_parentMainWidget = parent;
this->setObjectName("x26DockWidget");
this->setWindowTitle("X/26 triplets");
// Table listing of local enhancements
m_x26View = new QTableView;
m_x26Model = new X26Model(m_parentMainWidget);
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;
// Checkbox to select user-friendly or raw-value widgets
QCheckBox *rawOrCookedCheckBox = new QCheckBox(tr("Raw values"));
tripletSelectLayout->addWidget(rawOrCookedCheckBox);
// "Cooked" or user-friendly triplet type, row and column selection
QHBoxLayout *cookedTripletLayout = new QHBoxLayout;
// Cooked row spinbox
QLabel *rowLabel = new QLabel(tr("Row"));
rowLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
cookedTripletLayout->addWidget(rowLabel);
m_cookedRowSpinBox = new QSpinBox;
m_cookedRowSpinBox->setMaximum(24); // FIXME maximum value reset when triplet is selected
m_cookedRowSpinBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
cookedTripletLayout->addWidget(m_cookedRowSpinBox);
connect(m_cookedRowSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &X26DockWidget::cookedRowSpinBoxChanged);
// Cooked column spinbox
QLabel *columnLabel = new QLabel(tr("Column"));
columnLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
cookedTripletLayout->addWidget(columnLabel);
// cookedTripletLayout->addWidget(new QLabel(tr("Column")));
m_cookedColumnSpinBox = new QSpinBox;
m_cookedColumnSpinBox->setMaximum(39); // FIXME maximum value reset when triplet is selected
m_cookedColumnSpinBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
cookedTripletLayout->addWidget(m_cookedColumnSpinBox);
connect(m_cookedColumnSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &X26DockWidget::cookedColumnSpinBoxChanged);
// Cooked triplet mode
QLabel *modeLabel = new QLabel(tr("Mode"));
modeLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
cookedTripletLayout->addWidget(modeLabel);
m_cookedModePushButton = new QPushButton;
cookedTripletLayout->addWidget(m_cookedModePushButton);
// Cooked triplet menu
// We connect the menus for both "Insert" buttons at the same time
m_cookedModeMenu = new TripletModeQMenu(this);
m_insertBeforeMenu = new TripletModeQMenu(this);
m_insertAfterMenu = new TripletModeQMenu(this);
for (int m=0; m<64; m++) {
connect(static_cast(m_cookedModeMenu)->action(m), &QAction::triggered, [=]() { cookedModeMenuSelected(m); });
connect(static_cast(m_insertBeforeMenu)->action(m), &QAction::triggered, [=]() { insertTriplet(m, false); });
connect(static_cast(m_insertAfterMenu)->action(m), &QAction::triggered, [=]() { insertTriplet(m, true); });
}
m_cookedModePushButton->setMenu(m_cookedModeMenu);
// Raw triplet values
QHBoxLayout *rawTripletLayout = new QHBoxLayout;
// Raw triplet address
rawTripletLayout->addWidget(new QLabel(tr("Address")));
m_rawTripletAddressSpinBox = new QSpinBox;
m_rawTripletAddressSpinBox->setMaximum(63);
rawTripletLayout->addWidget(m_rawTripletAddressSpinBox);
// Raw triplet mode
rawTripletLayout->addWidget(new QLabel(tr("Mode")));
m_rawTripletModeSpinBox = new QSpinBox;
m_rawTripletModeSpinBox->setMaximum(31);
rawTripletLayout->addWidget(m_rawTripletModeSpinBox);
// Raw triplet data
rawTripletLayout->addWidget(new QLabel(tr("Data")));
m_rawTripletDataSpinBox = new QSpinBox;
m_rawTripletDataSpinBox->setMaximum(127);
rawTripletLayout->addWidget(m_rawTripletDataSpinBox);
connect(m_rawTripletAddressSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &X26DockWidget::rawTripletAddressSpinBoxChanged);
connect(m_rawTripletModeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &X26DockWidget::rawTripletModeSpinBoxChanged);
connect(m_rawTripletDataSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &X26DockWidget::rawTripletDataSpinBoxChanged);
// Stack the raw and cooked layouts together
m_rawOrCookedStackedLayout = new QStackedLayout;
QWidget *cookedTripletWidget = new QWidget;
cookedTripletWidget->setLayout(cookedTripletLayout);
cookedTripletWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_rawOrCookedStackedLayout->addWidget(cookedTripletWidget);
QWidget *rawTripletWidget = new QWidget;
rawTripletWidget->setLayout(rawTripletLayout);
rawTripletWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_rawOrCookedStackedLayout->addWidget(rawTripletWidget);
// We could simply: tripletSelectLayout->addLayout(m_rawOrCookedStackedLayout);
// but we need to keep it to the smallest size, only possible by putting it inside a QWidget
QWidget *rawOrCookedWidget = new QWidget;
rawOrCookedWidget->setLayout(m_rawOrCookedStackedLayout);
rawOrCookedWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
tripletSelectLayout->addWidget(rawOrCookedWidget);
x26Layout->addLayout(tripletSelectLayout);
connect(rawOrCookedCheckBox, &QCheckBox::stateChanged,[=](const int value) { m_rawOrCookedStackedLayout->setCurrentIndex(value == 2); } );
// Widgets that alter the parameters of triplets which will all be stacked
// Index 0 is a blank label widget, we allocate it later when we start stacking
// Index 1 - Colour and "this row only"/"down to bottom" selection
QHBoxLayout *colourAndRowLayout = new QHBoxLayout;
m_colourComboBox = new QComboBox;
m_colourComboBox->setModel(m_parentMainWidget->document()->clutModel());
colourAndRowLayout->addWidget(m_colourComboBox);
connect(m_colourComboBox, QOverload::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
m_fullRowColourThisRowOnlyRadioButton = new QRadioButton(tr("This row only"));
m_fullRowColourDownToBottomRadioButton = new QRadioButton(tr("Down to bottom"));
QButtonGroup *rowsButtonGroup = new QButtonGroup;
rowsButtonGroup->addButton(m_fullRowColourThisRowOnlyRadioButton, 0);
rowsButtonGroup->addButton(m_fullRowColourDownToBottomRadioButton, 1);
colourAndRowLayout->addWidget(m_fullRowColourThisRowOnlyRadioButton);
colourAndRowLayout->addWidget(m_fullRowColourDownToBottomRadioButton);
connect(m_fullRowColourThisRowOnlyRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(0, Qt::UserRole+2); } );
connect(m_fullRowColourDownToBottomRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(1, Qt::UserRole+2); } );
// Index 2 - Character code
QHBoxLayout *characterCodeLayout = new QHBoxLayout;
m_characterCodeComboBox = new QComboBox;
m_characterCodeComboBox->setModel(&m_characterListModel);
characterCodeLayout->addWidget(m_characterCodeComboBox);
connect(m_characterCodeComboBox, QOverload::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value+32, Qt::UserRole+1); } );
// Index 3 - Flash rate and phase
QHBoxLayout *flashModeRateLayout = new QHBoxLayout;
m_flashModeComboBox = new QComboBox;
m_flashModeComboBox->addItem(tr("Steady"));
m_flashModeComboBox->addItem(tr("Normal"));
m_flashModeComboBox->addItem(tr("Invert"));
m_flashModeComboBox->addItem(tr("Adjacent CLUT"));
flashModeRateLayout->addWidget(m_flashModeComboBox);
connect(m_flashModeComboBox, QOverload::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
m_flashRateComboBox = new QComboBox;
m_flashRateComboBox->addItem(tr("Slow 1Hz"));
m_flashRateComboBox->addItem(tr("Fast 2Hz phase 1"));
m_flashRateComboBox->addItem(tr("Fast 2Hz phase 2"));
m_flashRateComboBox->addItem(tr("Fast 2Hz phase 3"));
m_flashRateComboBox->addItem(tr("Fast 2Hz inc/right"));
m_flashRateComboBox->addItem(tr("Fast 2Hz dec/left"));
flashModeRateLayout->addWidget(m_flashRateComboBox);
connect(m_flashRateComboBox, QOverload::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
// Index 4 - Display attributes
QHBoxLayout *displayAttributesLayout = new QHBoxLayout;
m_textSizeComboBox = new QComboBox;
m_textSizeComboBox->addItem(tr("Normal size"));
m_textSizeComboBox->addItem(tr("Double height"));
m_textSizeComboBox->addItem(tr("Double width"));
m_textSizeComboBox->addItem(tr("Double size"));
displayAttributesLayout->addWidget(m_textSizeComboBox);
connect(m_textSizeComboBox, QOverload::of(&QComboBox::activated), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
m_displayAttributeBoxingCheckBox = new QCheckBox(tr("Box/Win"));
m_displayAttributeConcealCheckBox = new QCheckBox(tr("Conceal"));
m_displayAttributeInvertCheckBox = new QCheckBox(tr("Invert"));
m_displayAttributeUnderlineCheckBox = new QCheckBox(tr("Und/Sep mosaics"));
displayAttributesLayout->addWidget(m_displayAttributeBoxingCheckBox);
displayAttributesLayout->addWidget(m_displayAttributeConcealCheckBox);
displayAttributesLayout->addWidget(m_displayAttributeInvertCheckBox);
displayAttributesLayout->addWidget(m_displayAttributeUnderlineCheckBox);
connect(m_displayAttributeBoxingCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+2); } );
connect(m_displayAttributeConcealCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+3); } );
connect(m_displayAttributeInvertCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+4); } );
connect(m_displayAttributeUnderlineCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+5); } );
// Index 5 - Invoke Object
QHBoxLayout *invokeObjectLayout = new QHBoxLayout;
// From local or (G)POP page
m_objectSourceComboBox = new QComboBox;
m_objectSourceComboBox->addItem("Local");
m_objectSourceComboBox->addItem("POP");
m_objectSourceComboBox->addItem("GPOP");
invokeObjectLayout->addWidget(m_objectSourceComboBox);
connect(m_objectSourceComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); updateAllCookedTripletWidgets(m_x26View->currentIndex()); } );
// Object required at which levels
m_objectRequiredAtLevelsComboBox = new QComboBox;
m_objectRequiredAtLevelsComboBox->addItem("L2.5 only");
m_objectRequiredAtLevelsComboBox->addItem("L3.5 only");
m_objectRequiredAtLevelsComboBox->addItem("L2.5 + 3.5");
invokeObjectLayout->addWidget(m_objectRequiredAtLevelsComboBox);
connect(m_objectRequiredAtLevelsComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
// Invoke Local Objects
QHBoxLayout *invokeLocalObjectLayout = new QHBoxLayout;
m_invokeLocalObjectDesignationCodeLabel = new QLabel(tr("Designation"));
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeLabel);
m_invokeLocalObjectDesignationCodeSpinBox = new QSpinBox;
m_invokeLocalObjectDesignationCodeSpinBox->setMaximum(15);
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectDesignationCodeSpinBox);
connect(m_invokeLocalObjectDesignationCodeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
m_invokeLocalObjectTripletNumberLabel = new QLabel(tr("Triplet"));
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberLabel);
m_invokeLocalObjectTripletNumberSpinBox = new QSpinBox;
m_invokeLocalObjectTripletNumberSpinBox->setMaximum(12);
invokeLocalObjectLayout->addWidget(m_invokeLocalObjectTripletNumberSpinBox);
connect(m_invokeLocalObjectTripletNumberSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+3); } );
// Invoke (G)POP Object
QHBoxLayout *invokePOPLayout = new QHBoxLayout;
// (G)POP subpage
invokePOPLayout->addWidget(new QLabel(tr("Subpage")));
m_invokePOPSubPageSpinBox = new QSpinBox;
m_invokePOPSubPageSpinBox->setMaximum(15);
m_invokePOPSubPageSpinBox->setWrapping(true);
invokePOPLayout->addWidget(m_invokePOPSubPageSpinBox);
connect(m_invokePOPSubPageSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
// (G)POP packet to locate the pointer
invokePOPLayout->addWidget(new QLabel(tr("Packet")));
m_invokePOPPacketNumberSpinBox = new QSpinBox;
m_invokePOPPacketNumberSpinBox->setRange(1, 4);
m_invokePOPPacketNumberSpinBox->setWrapping(true);
invokePOPLayout->addWidget(m_invokePOPPacketNumberSpinBox);
connect(m_invokePOPPacketNumberSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+3); } );
// (G)POP triplet with pointer
m_invokePOPTripletNumberComboBox = new QComboBox;
invokePOPLayout->addWidget(m_invokePOPTripletNumberComboBox);
connect(m_invokePOPTripletNumberComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+4); } );
// (G)POP which bits of triplet for the pointer
m_invokePOPPointerBitsComboBox = new QComboBox;
m_invokePOPPointerBitsComboBox->addItem("Bits 1-9");
m_invokePOPPointerBitsComboBox->addItem("Bits 10-18");
invokePOPLayout->addWidget(m_invokePOPPointerBitsComboBox);
connect(m_invokePOPPointerBitsComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+5); } );
// Stack the Local and (G)POP layouts together
m_invokeObjectSourceStackedLayout = new QStackedLayout;
QWidget *invokeLocalObjectWidget = new QWidget;
invokeLocalObjectWidget->setLayout(invokeLocalObjectLayout);
invokeLocalObjectWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_invokeObjectSourceStackedLayout->addWidget(invokeLocalObjectWidget);
QWidget *invokeGPOPWidget = new QWidget;
invokeGPOPWidget->setLayout(invokePOPLayout);
invokeGPOPWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_invokeObjectSourceStackedLayout->addWidget(invokeGPOPWidget);
// We could simply: invokeObjectLayout->addLayout(m_rawOrCookedStackedLayout);
// but we need to keep it to the smallest size, only possible by putting it inside a QWidget
QWidget *invokeObjectSourceWidget = new QWidget;
invokeObjectSourceWidget->setLayout(m_invokeObjectSourceStackedLayout);
invokeObjectSourceWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
invokeObjectLayout->addWidget(invokeObjectSourceWidget);
// Index 6 - DRCS mode
QHBoxLayout *DRCSModeLayout = new QHBoxLayout;
m_DRCSModeRequiredAtLevelsComboBox = new QComboBox;
m_DRCSModeRequiredAtLevelsComboBox->addItem("L2.5 only");
m_DRCSModeRequiredAtLevelsComboBox->addItem("L3.5 only");
m_DRCSModeRequiredAtLevelsComboBox->addItem("L2.5 + 3.5");
DRCSModeLayout->addWidget(m_DRCSModeRequiredAtLevelsComboBox);
connect(m_DRCSModeRequiredAtLevelsComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
m_DRCSModeGlobalRadioButton = new QRadioButton("Global");
m_DRCSModeNormalRadioButton = new QRadioButton("Normal");
QButtonGroup *DRCSModeButtonGroup = new QButtonGroup;
DRCSModeButtonGroup->addButton(m_DRCSModeGlobalRadioButton, 0);
DRCSModeButtonGroup->addButton(m_DRCSModeNormalRadioButton, 1);
DRCSModeLayout->addWidget(m_DRCSModeGlobalRadioButton);
DRCSModeLayout->addWidget(m_DRCSModeNormalRadioButton);
connect(m_DRCSModeGlobalRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(0, Qt::UserRole+3); } );
connect(m_DRCSModeNormalRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(1, Qt::UserRole+3); } );
DRCSModeLayout->addWidget(new QLabel(tr("Subpage")));
m_DRCSModeSubPageSpinBox = new QSpinBox;
m_DRCSModeSubPageSpinBox->setMaximum(15);
m_DRCSModeSubPageSpinBox->setWrapping(true);
DRCSModeLayout->addWidget(m_DRCSModeSubPageSpinBox);
connect(m_DRCSModeSubPageSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+4); } );
// Index 7 - DRCS character
QHBoxLayout *DRCSCharacterLayout = new QHBoxLayout;
m_DRCSCharacterGlobalRadioButton = new QRadioButton("Global");
m_DRCSCharacterNormalRadioButton = new QRadioButton("Normal");
QButtonGroup *DRCSCharacterButtonGroup = new QButtonGroup;
DRCSCharacterButtonGroup->addButton(m_DRCSCharacterGlobalRadioButton, 0);
DRCSCharacterButtonGroup->addButton(m_DRCSCharacterNormalRadioButton, 1);
DRCSCharacterLayout->addWidget(m_DRCSCharacterGlobalRadioButton);
DRCSCharacterLayout->addWidget(m_DRCSCharacterNormalRadioButton);
connect(m_DRCSCharacterGlobalRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(0, Qt::UserRole+1); } );
connect(m_DRCSCharacterNormalRadioButton, &QAbstractButton::clicked, this, [=] { updateModelFromCookedWidget(1, Qt::UserRole+1); } );
DRCSCharacterLayout->addWidget(new QLabel(tr("Character")));
m_DRCSCharacterCodeSpinBox = new QSpinBox;
m_DRCSCharacterCodeSpinBox->setMaximum(47);
m_DRCSCharacterCodeSpinBox->setWrapping(true);
DRCSCharacterLayout->addWidget(m_DRCSCharacterCodeSpinBox);
connect(m_DRCSCharacterCodeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+2); } );
// Index 8 - Font style
QHBoxLayout *fontStyleLayout = new QHBoxLayout;
m_fontStyleProportionalCheckBox = new QCheckBox(tr("Proportional"));
m_fontStyleBoldCheckBox = new QCheckBox(tr("Bold"));
m_fontStyleItalicCheckBox = new QCheckBox(tr("Italic"));
fontStyleLayout->addWidget(m_fontStyleProportionalCheckBox);
fontStyleLayout->addWidget(m_fontStyleBoldCheckBox);
fontStyleLayout->addWidget(m_fontStyleItalicCheckBox);
connect(m_fontStyleProportionalCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+1); } );
connect(m_fontStyleBoldCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+2);; } );
connect(m_fontStyleItalicCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget((value == 2), Qt::UserRole+3); } );
m_fontStyleRowsSpinBox = new QSpinBox;
m_fontStyleRowsSpinBox->setRange(0, 7);
fontStyleLayout->addWidget(m_fontStyleRowsSpinBox);
connect(m_fontStyleRowsSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+4); } );
// Index 9 - Reserved/PDC data
QHBoxLayout *reservedPDCLayout = new QHBoxLayout;
m_reservedPDCSpinBox = new QSpinBox;
m_reservedPDCSpinBox->setMaximum(127);
m_reservedPDCSpinBox->setDisplayIntegerBase(16);
m_reservedPDCSpinBox->setPrefix("0x");
m_reservedPDCSpinBox->setWrapping(true);
reservedPDCLayout->addWidget(m_reservedPDCSpinBox);
connect(m_reservedPDCSpinBox, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
// Index 10 - Terminator
QHBoxLayout *terminationMarkerLayout = new QHBoxLayout;
m_terminationMarkerPageTypeComboBox = new QComboBox;
m_terminationMarkerPageTypeComboBox->addItem("Interm (G)POP page");
m_terminationMarkerPageTypeComboBox->addItem("Last (G)POP page");
m_terminationMarkerPageTypeComboBox->addItem("Local Object defs");
m_terminationMarkerPageTypeComboBox->addItem("Local enhancements");
terminationMarkerLayout->addWidget(m_terminationMarkerPageTypeComboBox);
connect(m_terminationMarkerPageTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), [=](const int value) { updateModelFromCookedWidget(value, Qt::UserRole+1); } );
m_terminationMarkerMoreFollowsCheckBox = new QCheckBox(tr("Objects follow"));
terminationMarkerLayout->addWidget(m_terminationMarkerMoreFollowsCheckBox);
connect(m_terminationMarkerMoreFollowsCheckBox, &QCheckBox::stateChanged, this, [=](const int value) { updateModelFromCookedWidget(value != 2, Qt::UserRole+2); } );
// Stack all the triplet parameter layouts together
m_tripletParameterStackedLayout = new QStackedLayout;
// Index 0
// Blank label
QLabel *blankWidget = new QLabel(" ");
m_tripletParameterStackedLayout->addWidget(blankWidget);
// Index 1
colourAndRowLayout->setContentsMargins(0, 0, 0, 0);
QWidget *colourAndRowWidget = new QWidget;
colourAndRowWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
colourAndRowWidget->setLayout(colourAndRowLayout);
m_tripletParameterStackedLayout->addWidget(colourAndRowWidget);
// Index 2
characterCodeLayout->setContentsMargins(0, 0, 0, 0);
QWidget *characterCodeWidget = new QWidget;
characterCodeWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
characterCodeWidget->setLayout(characterCodeLayout);
m_tripletParameterStackedLayout->addWidget(characterCodeWidget);
// Index 3
flashModeRateLayout->setContentsMargins(0, 0, 0, 0);
QWidget *flashModeRateWidget = new QWidget;
flashModeRateWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
flashModeRateWidget->setLayout(flashModeRateLayout);
m_tripletParameterStackedLayout->addWidget(flashModeRateWidget);
// Index 4
displayAttributesLayout->setContentsMargins(0, 0, 0, 0);
QWidget *displayAttributesWidget = new QWidget;
displayAttributesWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
displayAttributesWidget->setLayout(displayAttributesLayout);
m_tripletParameterStackedLayout->addWidget(displayAttributesWidget);
// Index 5
invokeObjectLayout->setContentsMargins(0, 0, 0, 0);
QWidget *invokeObjectWidget = new QWidget;
invokeObjectWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
invokeObjectWidget->setLayout(invokeObjectLayout);
m_tripletParameterStackedLayout->addWidget(invokeObjectWidget);
// Index 6
DRCSModeLayout->setContentsMargins(0, 0, 0, 0);
QWidget *DRCSModeWidget = new QWidget;
DRCSModeWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
DRCSModeWidget->setLayout(DRCSModeLayout);
m_tripletParameterStackedLayout->addWidget(DRCSModeWidget);
// Index 7
DRCSCharacterLayout->setContentsMargins(0, 0, 0, 0);
QWidget *DRCSCharacterWidget = new QWidget;
DRCSCharacterWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
DRCSCharacterWidget->setLayout(DRCSCharacterLayout);
m_tripletParameterStackedLayout->addWidget(DRCSCharacterWidget);
// Index 8
fontStyleLayout->setContentsMargins(0, 0, 0, 0);
QWidget *fontStyleWidget = new QWidget;
fontStyleWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
fontStyleWidget->setLayout(fontStyleLayout);
m_tripletParameterStackedLayout->addWidget(fontStyleWidget);
// Index 9
reservedPDCLayout->setContentsMargins(0, 0, 0, 0);
QWidget *reservedPDCWidget = new QWidget;
reservedPDCWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
reservedPDCWidget->setLayout(reservedPDCLayout);
m_tripletParameterStackedLayout->addWidget(reservedPDCWidget);
// Index 10
terminationMarkerLayout->setContentsMargins(0, 0, 0, 0);
QWidget *terminationMarkerWidget = new QWidget;
terminationMarkerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
terminationMarkerWidget->setLayout(terminationMarkerLayout);
m_tripletParameterStackedLayout->addWidget(terminationMarkerWidget);
// We could simply: x26Layout->addLayout(m_tripletParameterStackedLayout);
// but we need to keep it to the smallest size, only possible by putting it inside a QWidget
QWidget *tripletParameterWidget = new QWidget;
tripletParameterWidget->setLayout(m_tripletParameterStackedLayout);
tripletParameterWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
x26Layout->addWidget(tripletParameterWidget);
// Insert and delete widgets
QHBoxLayout *insertDeleteLayout = new QHBoxLayout;
m_insertBeforePushButton = new QPushButton(tr("Insert before"));
insertDeleteLayout->addWidget(m_insertBeforePushButton);
m_insertBeforePushButton->setMenu(m_insertBeforeMenu);
m_insertAfterPushButton = new QPushButton(tr("Insert after"));
insertDeleteLayout->addWidget(m_insertAfterPushButton);
m_insertAfterPushButton->setMenu(m_insertAfterMenu);
m_insertCopyPushButton = new QPushButton(tr("Insert copy"));
insertDeleteLayout->addWidget(m_insertCopyPushButton);
m_deletePushButton = new QPushButton(tr("Delete"));
insertDeleteLayout->addWidget(m_deletePushButton);
connect(m_insertCopyPushButton, &QPushButton::clicked, this, &X26DockWidget::insertTripletCopy);
connect(m_deletePushButton, &QPushButton::clicked, this, &X26DockWidget::deleteTriplet);
x26Layout->addLayout(insertDeleteLayout);
disableTripletWidgets();
x26Widget->setLayout(x26Layout);
this->setWidget(x26Widget);
m_x26View->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_x26View, &QWidget::customContextMenuRequested, this, &X26DockWidget::customMenuRequested);
connect(m_x26View->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &X26DockWidget::rowSelected);
}
void X26DockWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Insert:
insertTripletCopy();
break;
case Qt::Key_Delete:
deleteTriplet();
break;
default:
QWidget::keyPressEvent(event);
}
}
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);
rowSelected(m_x26View->currentIndex(), QModelIndex());
}
void X26DockWidget::loadX26List()
{
m_x26Model->setX26ListLoaded(true);
}
void X26DockWidget::unloadX26List()
{
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)
{
Q_UNUSED(previous);
if (current.isValid()) {
updateAllRawTripletSpinBoxes(current);
updateAllCookedTripletWidgets(current);
} else
disableTripletWidgets();
}
void X26DockWidget::disableTripletWidgets()
{
m_rawTripletAddressSpinBox->setEnabled(false);
m_rawTripletDataSpinBox->setEnabled(false);
m_rawTripletModeSpinBox->setEnabled(false);
m_rawTripletAddressSpinBox->blockSignals(true);
m_rawTripletModeSpinBox->blockSignals(true);
m_rawTripletDataSpinBox->blockSignals(true);
m_rawTripletAddressSpinBox->setValue(0);
m_rawTripletModeSpinBox->setValue(0);
m_rawTripletDataSpinBox->setValue(0);
m_rawTripletAddressSpinBox->blockSignals(false);
m_rawTripletModeSpinBox->blockSignals(false);
m_rawTripletDataSpinBox->blockSignals(false);
m_cookedRowSpinBox->setEnabled(false);
m_cookedColumnSpinBox->setEnabled(false);
m_cookedRowSpinBox->blockSignals(true);
m_cookedColumnSpinBox->blockSignals(true);
m_cookedRowSpinBox->setValue(1);
m_cookedColumnSpinBox->setValue(0);
m_cookedRowSpinBox->blockSignals(false);
m_cookedColumnSpinBox->blockSignals(false);
m_cookedModePushButton->setEnabled(false);
m_cookedModePushButton->setText(QString());
m_tripletParameterStackedLayout->setCurrentIndex(0);
}
void X26DockWidget::updateAllCookedTripletWidgets(const QModelIndex &index)
{
const int modeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
m_cookedModePushButton->setEnabled(true);
m_cookedModePushButton->setText(m_modeTripletNames.modeName(modeExt));
switch (modeExt) {
case 0x04: // Set active position
case 0x10: // Origin modifier
m_tripletParameterStackedLayout->setCurrentIndex(0);
break;
case 0x01: // Full row colour
case 0x07: // Address row 0
m_fullRowColourThisRowOnlyRadioButton->blockSignals(true);
m_fullRowColourDownToBottomRadioButton->blockSignals(true);
m_fullRowColourThisRowOnlyRadioButton->setChecked(!index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toBool());
m_fullRowColourDownToBottomRadioButton->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toBool());
m_fullRowColourThisRowOnlyRadioButton->blockSignals(false);
m_fullRowColourDownToBottomRadioButton->blockSignals(false);
// fall-through
case 0x00: // Full screen colour
case 0x20: // Foreground colour
case 0x23: // Background colour
m_fullRowColourThisRowOnlyRadioButton->setVisible((modeExt == 0x01) || (modeExt == 0x07));
m_fullRowColourDownToBottomRadioButton->setVisible((modeExt == 0x01) || (modeExt == 0x07));
m_colourComboBox->blockSignals(true);
m_colourComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_colourComboBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(1);
break;
case 0x21: // G1 character
case 0x22: // G3 character at Level 1.5
case 0x29: // G0 character
case 0x2b: // G3 character at Level 2.5
case 0x2f: // G2 character
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f: // G0 character with diacritical
m_characterCodeComboBox->blockSignals(true);
if (modeExt == 0x21)
m_characterListModel.setCharacterSet(24);
else if (modeExt == 0x22 || modeExt == 0x2b)
m_characterListModel.setCharacterSet(26);
else
m_characterListModel.setCharacterSet(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_characterCodeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt()-32);
m_characterCodeComboBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(2);
break;
// TODO case 0x28: Modified G0 and G2 designation
case 0x27: // Flash functions
m_flashModeComboBox->blockSignals(true);
m_flashRateComboBox->blockSignals(true);
m_flashModeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_flashRateComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_flashModeComboBox->blockSignals(false);
m_flashRateComboBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(3);
break;
case 0x2c: // Display attributes
m_textSizeComboBox->blockSignals(true);
m_displayAttributeBoxingCheckBox->blockSignals(true);
m_displayAttributeConcealCheckBox->blockSignals(true);
m_displayAttributeInvertCheckBox->blockSignals(true);
m_displayAttributeUnderlineCheckBox->blockSignals(true);
m_textSizeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_displayAttributeBoxingCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toBool());
m_displayAttributeConcealCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toBool());
m_displayAttributeInvertCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+4).toBool());
m_displayAttributeUnderlineCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+5).toBool());
m_textSizeComboBox->blockSignals(false);
m_displayAttributeBoxingCheckBox->blockSignals(false);
m_displayAttributeConcealCheckBox->blockSignals(false);
m_displayAttributeInvertCheckBox->blockSignals(false);
m_displayAttributeUnderlineCheckBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(4);
break;
case 0x11: // Invoke Active Object
case 0x12: // Invoke Adaptive Object
case 0x13: // Invoke Passive Object
case 0x15: // Define Active Object
case 0x16: // Define Adaptive Object
case 0x17: // Define Passive Object
if (index.model()->data(index.model()->index(index.row(), 1), Qt::UserRole).toInt() & 0x04) {
// Define object
m_objectSourceComboBox->setVisible(false);
m_objectRequiredAtLevelsComboBox->setVisible(true);
m_objectRequiredAtLevelsComboBox->blockSignals(true);
m_objectRequiredAtLevelsComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_objectRequiredAtLevelsComboBox->blockSignals(false);
} else {
// Invoke object
m_objectRequiredAtLevelsComboBox->setVisible(false);
m_objectSourceComboBox->setVisible(true);
}
m_objectSourceComboBox->blockSignals(true);
// BUG we're only dealing with Local Object Definitions at the moment!
if (index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt() == 0 || (index.model()->data(index.model()->index(index.row(), 1), Qt::UserRole).toInt() & 0x04)) {
// if (triplet.objectSource() == X26Triplet::LocalObjectSource) {
const bool tripletLocationWidgetsVisible = (modeExt & 0x04) != 0x04;
m_invokeLocalObjectDesignationCodeLabel->setVisible(tripletLocationWidgetsVisible);
m_invokeLocalObjectDesignationCodeSpinBox->setVisible(tripletLocationWidgetsVisible);
m_invokeLocalObjectTripletNumberLabel->setVisible(tripletLocationWidgetsVisible);
m_invokeLocalObjectTripletNumberSpinBox->setVisible(tripletLocationWidgetsVisible);
m_objectSourceComboBox->setCurrentIndex(0);
m_invokeObjectSourceStackedLayout->setCurrentIndex(0);
if (tripletLocationWidgetsVisible) {
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(true);
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(true);
m_invokeLocalObjectDesignationCodeSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_invokeLocalObjectTripletNumberSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toInt());
m_invokeLocalObjectDesignationCodeSpinBox->blockSignals(false);
m_invokeLocalObjectTripletNumberSpinBox->blockSignals(false);
}
} else { // if (triplet.objectSource() != X26Triplet::IllegalObjectSource) {
m_objectSourceComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_invokeObjectSourceStackedLayout->setCurrentIndex(1);
m_invokePOPSubPageSpinBox->blockSignals(true);
m_invokePOPPacketNumberSpinBox->blockSignals(true);
m_invokePOPTripletNumberComboBox->blockSignals(true);
m_invokePOPPointerBitsComboBox->blockSignals(true);
m_invokePOPSubPageSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_invokePOPPacketNumberSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toInt());
while (m_invokePOPTripletNumberComboBox->count() > 0)
m_invokePOPTripletNumberComboBox->removeItem(0);
for (int i=0; i<4; i++)
m_invokePOPTripletNumberComboBox->addItem(QString("Triplet %1").arg(i*3+(modeExt & 0x03)));
m_invokePOPTripletNumberComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+4).toInt());
m_invokePOPPointerBitsComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+5).toInt());
m_invokePOPSubPageSpinBox->blockSignals(false);
m_invokePOPPacketNumberSpinBox->blockSignals(false);
m_invokePOPTripletNumberComboBox->blockSignals(false);
m_invokePOPPointerBitsComboBox->blockSignals(false);
}
m_objectSourceComboBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(5);
break;
case 0x18: // DRCS mode
m_DRCSModeRequiredAtLevelsComboBox->blockSignals(true);
m_DRCSModeGlobalRadioButton->blockSignals(true);
m_DRCSModeNormalRadioButton->blockSignals(true);
m_DRCSModeSubPageSpinBox->blockSignals(true);
m_DRCSModeRequiredAtLevelsComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_DRCSModeGlobalRadioButton->setChecked(!index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toBool());
m_DRCSModeNormalRadioButton->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toBool());
m_DRCSModeSubPageSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+4).toInt());
m_DRCSModeRequiredAtLevelsComboBox->blockSignals(false);
m_DRCSModeGlobalRadioButton->blockSignals(false);
m_DRCSModeNormalRadioButton->blockSignals(false);
m_DRCSModeSubPageSpinBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(6);
break;
case 0x2d: // DRCS character
m_DRCSCharacterGlobalRadioButton->blockSignals(true);
m_DRCSCharacterNormalRadioButton->blockSignals(true);
m_DRCSCharacterCodeSpinBox->blockSignals(true);
m_DRCSCharacterGlobalRadioButton->setChecked(!index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toBool());
m_DRCSCharacterNormalRadioButton->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toBool());
m_DRCSCharacterCodeSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toInt());
m_DRCSCharacterGlobalRadioButton->blockSignals(false);
m_DRCSCharacterNormalRadioButton->blockSignals(false);
m_DRCSCharacterCodeSpinBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(7);
break;
case 0x2e: // Font style
m_fontStyleProportionalCheckBox->blockSignals(true);
m_fontStyleBoldCheckBox->blockSignals(true);
m_fontStyleItalicCheckBox->blockSignals(true);
m_fontStyleRowsSpinBox->blockSignals(true);
m_fontStyleProportionalCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toBool());
m_fontStyleBoldCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toBool());
m_fontStyleItalicCheckBox->setChecked(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+3).toBool());
m_fontStyleRowsSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+4).toInt());
m_fontStyleProportionalCheckBox->blockSignals(false);
m_fontStyleBoldCheckBox->blockSignals(false);
m_fontStyleItalicCheckBox->blockSignals(false);
m_fontStyleRowsSpinBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(8);
break;
case 0x1f: // Termination marker
m_terminationMarkerPageTypeComboBox->blockSignals(true);
m_terminationMarkerMoreFollowsCheckBox->blockSignals(true);
m_terminationMarkerPageTypeComboBox->setCurrentIndex(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_terminationMarkerMoreFollowsCheckBox->setChecked(!index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+2).toBool());
m_terminationMarkerPageTypeComboBox->blockSignals(false);
m_terminationMarkerMoreFollowsCheckBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(10);
break;
default: // PDC and reserved triplet
m_reservedPDCSpinBox->blockSignals(true);
m_reservedPDCSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole+1).toInt());
m_reservedPDCSpinBox->blockSignals(false);
m_tripletParameterStackedLayout->setCurrentIndex(9);
break;
}
// Now deal with cooked row and column spinboxes
m_cookedRowSpinBox->blockSignals(true);
m_cookedColumnSpinBox->blockSignals(true);
const QVariant rowVariant = index.model()->data(index.model()->index(index.row(), 0), Qt::EditRole);
if (rowVariant.isNull()) {
m_cookedRowSpinBox->setEnabled(false);
m_cookedRowSpinBox->setValue(0);
m_cookedRowSpinBox->setPrefix("");
} else {
m_cookedRowSpinBox->setEnabled(true);
if (modeExt == 0x10) {
m_cookedRowSpinBox->setRange(0, 23);
m_cookedRowSpinBox->setPrefix("+");
} else {
m_cookedRowSpinBox->setRange(1, 24);
m_cookedRowSpinBox->setPrefix("");
}
m_cookedRowSpinBox->setValue(rowVariant.toInt());
}
const QVariant columnVariant = index.model()->data(index.model()->index(index.row(), 1), Qt::EditRole);
if (columnVariant.isNull()) {
m_cookedColumnSpinBox->setEnabled(false);
m_cookedColumnSpinBox->setValue(0);
m_cookedColumnSpinBox->setPrefix("");
} else {
m_cookedColumnSpinBox->setEnabled(true);
if (modeExt == 0x10) {
m_cookedColumnSpinBox->setMaximum(71);
m_cookedColumnSpinBox->setPrefix("+");
} else {
m_cookedColumnSpinBox->setMaximum(39);
m_cookedColumnSpinBox->setPrefix("");
}
m_cookedColumnSpinBox->setValue(columnVariant.toInt());
}
m_cookedRowSpinBox->blockSignals(false);
m_cookedColumnSpinBox->blockSignals(false);
}
void X26DockWidget::updateAllRawTripletSpinBoxes(const QModelIndex &index)
{
m_rawTripletAddressSpinBox->setEnabled(true);
m_rawTripletDataSpinBox->setEnabled(true);
m_rawTripletModeSpinBox->setEnabled(true);
m_rawTripletAddressSpinBox->blockSignals(true);
m_rawTripletModeSpinBox->blockSignals(true);
m_rawTripletDataSpinBox->blockSignals(true);
m_rawTripletAddressSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole).toInt());
m_rawTripletModeSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 1), Qt::UserRole).toInt());
m_rawTripletDataSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 2), Qt::UserRole).toInt());
m_rawTripletAddressSpinBox->blockSignals(false);
m_rawTripletModeSpinBox->blockSignals(false);
m_rawTripletDataSpinBox->blockSignals(false);
}
void X26DockWidget::updateRawTripletDataSpinBox(const QModelIndex &index)
{
m_rawTripletDataSpinBox->blockSignals(true);
m_rawTripletDataSpinBox->setValue(index.model()->data(index.model()->index(index.row(), 2), Qt::UserRole).toInt());
m_rawTripletDataSpinBox->blockSignals(false);
}
void X26DockWidget::rawTripletAddressSpinBoxChanged(int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 0), value, Qt::UserRole);
updateAllCookedTripletWidgets(m_x26View->currentIndex());
}
void X26DockWidget::rawTripletModeSpinBoxChanged(int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 1), value, Qt::UserRole);
updateAllCookedTripletWidgets(m_x26View->currentIndex());
}
void X26DockWidget::rawTripletDataSpinBoxChanged(int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 2), value, Qt::UserRole);
updateAllCookedTripletWidgets(m_x26View->currentIndex());
}
void X26DockWidget::cookedRowSpinBoxChanged(const int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 0), value, Qt::EditRole);
updateAllRawTripletSpinBoxes(m_x26View->currentIndex());
}
void X26DockWidget::cookedColumnSpinBoxChanged(const int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 1), value, Qt::EditRole);
updateAllRawTripletSpinBoxes(m_x26View->currentIndex());
}
void X26DockWidget::cookedModeMenuSelected(const int value)
{
if (!m_x26View->currentIndex().isValid())
return;
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 2), value, Qt::EditRole);
updateAllRawTripletSpinBoxes(m_x26View->currentIndex());
updateAllCookedTripletWidgets(m_x26View->currentIndex());
}
void X26DockWidget::updateModelFromCookedWidget(const int value, const int role)
{
m_x26Model->setData(m_x26Model->index(m_x26View->currentIndex().row(), 0), value, role);
updateAllRawTripletSpinBoxes(m_x26View->currentIndex());
}
void X26DockWidget::insertTriplet(int modeExt, bool after)
{
QModelIndex index = m_x26View->currentIndex();
if (index.isValid())
insertTriplet(modeExt, index.row()+after);
else
insertTriplet(modeExt);
}
void X26DockWidget::insertTriplet(int modeExt, int row)
{
X26Triplet newTriplet(modeExt < 0x20 ? 41 : 0, modeExt & 0x1f, 0);
if (row != -1) {
QModelIndex index = m_x26View->currentIndex();
if (index.isValid()) {
// If we're inserting a column triplet next to another column triplet,
// duplicate the column number
// Avoid the PDC and reserved mode triplets
if (modeExt >= 0x20 && modeExt != 0x24 && modeExt != 0x25 && modeExt != 0x26 && modeExt != 0x2a) {
const int existingTripletModeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
if (existingTripletModeExt >= 0x20 && existingTripletModeExt != 0x24 && existingTripletModeExt != 0x25 && existingTripletModeExt != 0x26 && existingTripletModeExt != 0x2a)
newTriplet.setAddress(index.model()->data(index.model()->index(index.row(), 0), Qt::UserRole).toInt());
}
// If we're inserting a Set Active Position or Full Row Colour triplet,
// look for a previous row setting triplet and set this one to the row after
if (modeExt == 0x04 || modeExt == 0x01) {
for (int i=row-1; i>=0; i--) {
const int scanTripletModeExt = index.model()->data(index.model()->index(i, 2), Qt::EditRole).toInt();
if (scanTripletModeExt == 0x04 || scanTripletModeExt == 0x01) {
const int scanActivePositionRow = index.model()->data(index.model()->index(i, 0), Qt::EditRole).toInt()+1;
if (scanActivePositionRow < 25)
newTriplet.setAddressRow(scanActivePositionRow);
else
newTriplet.setAddressRow(24);
break;
}
}
}
}
} else
row = 0;
// For character triplets, ensure Data is not reserved
if (modeExt == 0x21 || modeExt == 0x22 || modeExt == 0x29 || modeExt == 0x2b || modeExt >= 0x2f)
newTriplet.setData(0x20);
// For Address Row 0, set Address
if (modeExt == 0x07)
newTriplet.setAddress(63);
// For Termination Marker, set Address and Mode
if (modeExt == 0x1f) {
newTriplet.setAddress(63);
newTriplet.setData(7);
}
m_x26Model->insertRows(row, 1, QModelIndex(), newTriplet);
}
void X26DockWidget::insertTripletCopy()
{
QModelIndex index = m_x26View->currentIndex();
if (index.isValid())
m_x26Model->insertRow(index.row(), QModelIndex());
else
// No existing triplet to copy, so insert a Termination Marker
m_x26Model->insertRows(0, 1, QModelIndex(), X26Triplet(63, 31, 7));
}
void X26DockWidget::deleteTriplet()
{
QModelIndex index = m_x26View->currentIndex();
if (index.isValid())
m_x26Model->removeRow(index.row(), index.parent());
}
void X26DockWidget::customMenuRequested(QPoint pos)
{
QMenu *customMenu = nullptr;
QModelIndex index = m_x26View->indexAt(pos);
if (index.isValid()) {
const int modeExt = index.model()->data(index.model()->index(index.row(), 2), Qt::EditRole).toInt();
switch (modeExt) {
case 0x01: // Full Row colour
case 0x07: // Address row 0
customMenu = new TripletCLUTQMenu(true, this);
connect(static_cast(customMenu)->action(32), &QAction::triggered, [=]() { updateModelFromCookedWidget(0, Qt::UserRole+2); updateAllCookedTripletWidgets(index); });
connect(static_cast(customMenu)->action(33), &QAction::triggered, [=]() { updateModelFromCookedWidget(1, Qt::UserRole+2); updateAllCookedTripletWidgets(index); });
// fall-through
case 0x00: // Full Screen colour
case 0x20: // Foreground colour
case 0x23: // Background colour
if (!customMenu)
customMenu = new TripletCLUTQMenu(false, this);
for (int m=0; m<32; m++) {
static_cast(customMenu)->setColour(m, m_parentMainWidget->document()->currentSubPage()->CLUTtoQColor(m));
connect(static_cast(customMenu)->action(m), &QAction::triggered, [=]() { updateModelFromCookedWidget(m, Qt::UserRole+1); updateAllCookedTripletWidgets(index); });
}
break;
case 0x21: // G1 mosaic character
case 0x22: // G3 mosaic character at level 1.5
case 0x2b: // G3 mosaic character at level >=2.5
case 0x29: // G0 character
case 0x2f: // G2 character
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f: // G0 character with diacritical
customMenu = new TripletCharacterQMenu(m_x26Model->data(index.model()->index(index.row(), 2), Qt::UserRole+2).toInt(), this);
for (int m=0; m<96; m++)
connect(static_cast(customMenu)->action(m), &QAction::triggered, [=]() { updateModelFromCookedWidget(m+32, Qt::UserRole+1); updateAllCookedTripletWidgets(index); });
break;
}
if (customMenu)
customMenu->addSeparator();
else
customMenu = new QMenu(this);
TripletModeQMenu *modeChangeMenu = new TripletModeQMenu(this);
modeChangeMenu->setTitle(tr("Change mode"));
customMenu->addMenu(modeChangeMenu);
customMenu->addSeparator();
TripletModeQMenu *insertBeforeQMenu = new TripletModeQMenu(this);
insertBeforeQMenu->setTitle(tr("Insert before"));
customMenu->addMenu(insertBeforeQMenu);
TripletModeQMenu *insertAfterQMenu = new TripletModeQMenu(this);
insertAfterQMenu->setTitle(tr("Insert after"));
customMenu->addMenu(insertAfterQMenu);
for (int m=0; m<64; m++) {
connect(static_cast(modeChangeMenu)->action(m), &QAction::triggered, [=]() { cookedModeMenuSelected(m); });
connect(static_cast(insertBeforeQMenu)->action(m), &QAction::triggered, [=]() { insertTriplet(m, false); });
connect(static_cast(insertAfterQMenu)->action(m), &QAction::triggered, [=]() { insertTriplet(m, true); });
}
QAction *insertCopyAct = new QAction(tr("Insert copy"), this);
customMenu->addAction(insertCopyAct);
connect(insertCopyAct, &QAction::triggered, this, &X26DockWidget::insertTripletCopy);
QAction *deleteAct = new QAction("Delete triplet", this);
customMenu->addAction(deleteAct);
connect(deleteAct, &QAction::triggered, this, &X26DockWidget::deleteTriplet);
} else {
customMenu = new QMenu(this);
TripletModeQMenu *appendModeMenu = new TripletModeQMenu(this);
appendModeMenu->setTitle(tr("Append"));
customMenu->addMenu(appendModeMenu);
for (int m=0; m<64; m++)
connect(static_cast(appendModeMenu)->action(m), &QAction::triggered, [=]() { insertTriplet(m, m_x26Model->rowCount()); });
}
customMenu->popup(m_x26View->viewport()->mapToGlobal(pos));
}