File indexing completed on 2024-05-12 05:17:28
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "uic9183widget.h" 0008 #include "ui_uic9183widget.h" 0009 #include "uic9183ticketlayoutmodel.h" 0010 #include "standarditemmodelhelper.h" 0011 0012 #include <KItinerary/Uic9183Block> 0013 #include <KItinerary/Uic9183Header> 0014 #include <KItinerary/Vendor0080Block> 0015 #include <KItinerary/Vendor0080VUBlockData> 0016 #include <KItinerary/Vendor1154Block> 0017 0018 #include <KLocalizedString> 0019 0020 #include <QClipboard> 0021 #include <QDebug> 0022 #include <QMenu> 0023 #include <QMimeData> 0024 #include <QGuiApplication> 0025 #include <QStandardItemModel> 0026 0027 using namespace KItinerary; 0028 0029 Uic9183Widget::Uic9183Widget(QWidget *parent) 0030 : QWidget(parent) 0031 , ui(new Ui::Uic9183Widget) 0032 , m_uic9183BlockModel(new QStandardItemModel(this)) 0033 , m_ticketLayoutModel(new Uic9183TicketLayoutModel(this)) 0034 , m_layoutFieldsModel(new QStandardItemModel(this)) 0035 , m_vendor0080BLModel(new QStandardItemModel(this)) 0036 , m_vendor0080BLOrderModel(new QStandardItemModel(this)) 0037 , m_genericBlockModel(new QStandardItemModel(this)) 0038 { 0039 ui->setupUi(this); 0040 ui->splitter->setStretchFactor(0, 1); 0041 ui->splitter->setStretchFactor(1, 2); 0042 0043 m_uic9183BlockModel->setHorizontalHeaderLabels({i18n("Block"), i18n("Version"), i18n("Size"), i18n("Content")}); 0044 ui->blockView->setModel(m_uic9183BlockModel); 0045 ui->blockView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0046 connect(ui->blockView, &QTreeView::customContextMenuRequested, this, [this](QPoint pos) { 0047 auto idx = ui->blockView->currentIndex(); 0048 if (!idx.isValid()) 0049 return; 0050 idx = idx.sibling(idx.row(), 3); 0051 0052 QMenu menu; 0053 const auto copyContent = menu.addAction(i18n("Copy Content")); 0054 auto action = menu.exec(ui->blockView->viewport()->mapToGlobal(pos)); 0055 if (action == copyContent) { 0056 auto md = new QMimeData; 0057 md->setData(QStringLiteral("application/octet-stream"), idx.data(Qt::UserRole).toByteArray()); 0058 QGuiApplication::clipboard()->setMimeData(md); 0059 } 0060 }); 0061 0062 connect(ui->blockView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &Uic9183Widget::blockSelectionChanged); 0063 0064 ui->ticketLayoutTemplate->addItem(i18n("<no template>"), -1); 0065 const auto layoutTemplates = m_ticketLayoutModel->supportedTemplates(); 0066 int i = 0; 0067 for (const auto &tpl : layoutTemplates) { 0068 ui->ticketLayoutTemplate->addItem(tpl, i++); 0069 } 0070 ui->ticketLayoutView->setModel(m_ticketLayoutModel); 0071 connect(ui->ticketLayoutTemplate, qOverload<int>(&QComboBox::currentIndexChanged), this, [this]() { 0072 m_ticketLayoutModel->setLayoutTemplate(ui->ticketLayoutTemplate->currentData().toInt()); 0073 }); 0074 QFontMetrics fm(font()); 0075 const auto cellWidth = fm.boundingRect(QStringLiteral("m")).width() + 6; 0076 ui->ticketLayoutView->horizontalHeader()->setMinimumSectionSize(cellWidth); 0077 ui->ticketLayoutView->horizontalHeader()->setDefaultSectionSize(cellWidth); 0078 ui->ticketLayoutView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); 0079 ui->ticketLayoutView->verticalHeader()->setMinimumSectionSize(fm.height()); 0080 ui->ticketLayoutView->verticalHeader()->setMinimumSectionSize(fm.height()); 0081 ui->ticketLayoutView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); 0082 connect(ui->ticketLayoutView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() { 0083 const auto sel = ui->ticketLayoutView->selectionModel()->selection(); 0084 if (sel.isEmpty()) { 0085 ui->ticketLayoutSelection->clear(); 0086 } else { 0087 const auto range = sel.at(0); 0088 ui->ticketLayoutSelection->setText(i18n("Row: %1 Column: %2 Width: %3 Height: %4", range.top(), range.left(), range.right() - range.left() + 1, range.bottom() - range.top() + 1)); 0089 } 0090 }); 0091 0092 m_layoutFieldsModel->setHorizontalHeaderLabels({i18n("Key"), i18n("Value")}); 0093 ui->layoutRawView->setModel(m_layoutFieldsModel); 0094 ui->layoutRawView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0095 0096 m_vendor0080BLModel->setHorizontalHeaderLabels({i18n("Name"), i18n("Size"), i18n("Content")}); 0097 ui->vendor0080BLView->setModel(m_vendor0080BLModel); 0098 ui->vendor0080BLView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0099 m_vendor0080BLOrderModel->setHorizontalHeaderLabels({i18n("Field"), i18n("Value")}); 0100 ui->vendor0080BLOrderView->setModel(m_vendor0080BLOrderModel); 0101 ui->vendor0080BLOrderView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0102 0103 m_genericBlockModel->setHorizontalHeaderLabels({i18n("Field"), i18n("Value")}); 0104 ui->genericBlockView->setModel(m_genericBlockModel); 0105 ui->genericBlockView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0106 } 0107 0108 Uic9183Widget::~Uic9183Widget() = default; 0109 0110 void Uic9183Widget::clear() 0111 { 0112 StandardItemModelHelper::clearContent(m_uic9183BlockModel); 0113 StandardItemModelHelper::clearContent(m_layoutFieldsModel); 0114 StandardItemModelHelper::clearContent(m_vendor0080BLModel); 0115 StandardItemModelHelper::clearContent(m_vendor0080BLOrderModel); 0116 StandardItemModelHelper::clearContent(m_genericBlockModel); 0117 } 0118 0119 void Uic9183Widget::setContent(const KItinerary::Uic9183Parser &p) 0120 { 0121 clear(); 0122 m_ticketLayoutModel->setLayout(p.ticketLayout()); 0123 auto idx = ui->ticketLayoutTemplate->findText(p.ticketLayout().type()); 0124 ui->ticketLayoutTemplate->setCurrentIndex(std::max(idx, 0)); 0125 int i = 0; 0126 for (auto field = p.ticketLayout().firstField(); !field.isNull(); field = field.next()) { 0127 auto item = StandardItemModelHelper::addEntry(i18n("Field %1", ++i), {}, m_layoutFieldsModel->invisibleRootItem()); 0128 StandardItemModelHelper::fillFromGadget(field, item); 0129 } 0130 ui->layoutRawView->expandAll(); 0131 0132 auto block = p.firstBlock(); 0133 while (!block.isNull()) { 0134 auto nameItem = new QStandardItem(QString::fromUtf8(block.name(), 6)); 0135 auto versionItem = new QStandardItem(QString::number(block.version())); 0136 auto sizeItem = new QStandardItem(QString::number(block.contentSize())); 0137 auto contentItem = new QStandardItem; 0138 contentItem->setData(QByteArray(block.content(), block.contentSize()), Qt::UserRole); 0139 contentItem->setData(QString::fromUtf8(block.content(), block.contentSize()), Qt::DisplayRole); 0140 m_uic9183BlockModel->appendRow({nameItem, versionItem, sizeItem, contentItem}); 0141 block = block.nextBlock(); 0142 } 0143 0144 const auto vendor0080BL = p.findBlock<Vendor0080BLBlock>(); 0145 if (vendor0080BL.isValid()) { 0146 auto sblock = vendor0080BL.firstBlock(); 0147 while (!sblock.isNull()) { 0148 auto nameItem = new QStandardItem(QString::fromUtf8(sblock.id(), 3)); 0149 auto sizeItem = new QStandardItem(QString::number(sblock.contentSize())); 0150 auto contentItem = new QStandardItem(sblock.toString()); 0151 m_vendor0080BLModel->appendRow({nameItem, sizeItem, contentItem}); 0152 sblock = sblock.nextBlock(); 0153 } 0154 for (int i = 0; i < vendor0080BL.orderBlockCount(); ++i) { 0155 auto item = StandardItemModelHelper::addEntry(i18n("Order %1", i + 1), {}, m_vendor0080BLOrderModel->invisibleRootItem()); 0156 StandardItemModelHelper::fillFromGadget(vendor0080BL.orderBlock(i), item); 0157 } 0158 ui->vendor0080BLOrderView->expandAll(); 0159 } 0160 0161 m_uic9183 = p; 0162 ui->blockView->selectionModel()->clear(); 0163 blockSelectionChanged(); 0164 } 0165 0166 void Uic9183Widget::blockSelectionChanged() 0167 { 0168 const auto sel = ui->blockView->selectionModel()->selectedRows(); 0169 if (sel.isEmpty()) { 0170 if (m_uic9183.isValid()) { 0171 StandardItemModelHelper::clearContent(m_genericBlockModel); 0172 const auto header = m_uic9183.header(); 0173 StandardItemModelHelper::fillFromGadget(header, m_genericBlockModel->invisibleRootItem()); 0174 ui->detailsStack->setCurrentWidget(ui->genericPage); 0175 } else { 0176 ui->detailsStack->setCurrentWidget(ui->genericPage); 0177 } 0178 return; 0179 } 0180 const auto blockName = sel.at(0).data(Qt::DisplayRole).toString(); 0181 if (blockName == QLatin1String(Uic9183TicketLayout::RecordId)) { 0182 ui->detailsStack->setCurrentWidget(ui->layoutPage); 0183 } else if (blockName == QLatin1String(Vendor0080BLBlock::RecordId)) { 0184 ui->detailsStack->setCurrentWidget(ui->vendor0080BLPage); 0185 } else if (blockName == QLatin1String(Vendor0080VUBlock::RecordId)) { 0186 const auto block = m_uic9183.findBlock<Vendor0080VUBlock>(); 0187 StandardItemModelHelper::clearContent(m_genericBlockModel); 0188 StandardItemModelHelper::fillFromGadget(block.commonData(), m_genericBlockModel->invisibleRootItem()); 0189 for (int i = 0; i < (int)block.commonData()->numberOfTickets; ++i) { 0190 const auto ticket = block.ticketData(i); 0191 auto item = StandardItemModelHelper::addEntry(i18n("Ticket %1", i + 1), {}, m_genericBlockModel->invisibleRootItem()); 0192 StandardItemModelHelper::fillFromGadget(ticket, item); 0193 StandardItemModelHelper::fillFromGadget(ticket->validityArea, item); 0194 StandardItemModelHelper::addEntry(i18n("Payload"), StandardItemModelHelper::dataToHex((const uint8_t*)&ticket->validityArea, ticket->validityAreaDataSize, sizeof(VdvTicketValidityAreaData)), item); 0195 } 0196 ui->genericBlockView->expandAll(); 0197 ui->detailsStack->setCurrentWidget(ui->genericPage); 0198 } else if (blockName == QLatin1String(Vendor1154UTBlock::RecordId)) { 0199 Vendor1154UTBlock block = m_uic9183.findBlock<Vendor1154UTBlock>(); 0200 StandardItemModelHelper::clearContent(m_genericBlockModel); 0201 for (auto b = block.firstBlock(); !b.isNull(); b = b.nextBlock()) { 0202 StandardItemModelHelper::addEntry(QString::fromUtf8(b.id(), 2), b.toString(), m_genericBlockModel->invisibleRootItem()); 0203 } 0204 ui->detailsStack->setCurrentWidget(ui->genericPage); 0205 } else { 0206 const auto block = m_uic9183.block(blockName); 0207 StandardItemModelHelper::clearContent(m_genericBlockModel); 0208 StandardItemModelHelper::fillFromGadget(block, m_genericBlockModel->invisibleRootItem()); 0209 if (m_genericBlockModel->rowCount() > 0) { 0210 ui->detailsStack->setCurrentWidget(ui->genericPage); 0211 } else { 0212 ui->detailsStack->setCurrentWidget(ui->noDetailsPage); 0213 } 0214 } 0215 ui->genericBlockView->expandAll(); 0216 }