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 }