- Add ListPreviewWidget for displaying parsed list data with table view - Add TextViewerWidget for text file preview with syntax highlighting - Add TreeBuilder class to organize parsed data into tree structure - Enhance HexView with selection support, copy functionality, keyboard navigation - Enhance ImagePreviewWidget with additional format support and metadata display - Minor audio preview widget adjustments Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
272 lines
8.7 KiB
C++
272 lines
8.7 KiB
C++
#include "listpreviewwidget.h"
|
|
#include <QFileInfo>
|
|
|
|
ListPreviewWidget::ListPreviewWidget(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
setupUi();
|
|
|
|
// Connect to theme changes
|
|
connect(&Settings::instance(), &Settings::themeChanged,
|
|
this, &ListPreviewWidget::applyTheme);
|
|
applyTheme(Settings::instance().theme());
|
|
}
|
|
|
|
void ListPreviewWidget::setupUi()
|
|
{
|
|
auto *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
mainLayout->setSpacing(0);
|
|
|
|
// Info label at top
|
|
mInfoLabel = new QLabel(this);
|
|
mInfoLabel->setContentsMargins(8, 4, 8, 4);
|
|
mainLayout->addWidget(mInfoLabel);
|
|
|
|
// Splitter for table and metadata
|
|
mSplitter = new QSplitter(Qt::Horizontal, this);
|
|
mainLayout->addWidget(mSplitter, 1);
|
|
|
|
// Table widget for list items
|
|
mTableWidget = new QTableWidget(this);
|
|
mTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
mTableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
mTableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
mTableWidget->horizontalHeader()->setStretchLastSection(true);
|
|
mTableWidget->verticalHeader()->setVisible(false);
|
|
mTableWidget->setAlternatingRowColors(true);
|
|
mSplitter->addWidget(mTableWidget);
|
|
|
|
// Metadata tree on the right
|
|
mMetadataTree = new QTreeWidget(this);
|
|
mMetadataTree->setHeaderLabels({"Property", "Value"});
|
|
mMetadataTree->setColumnCount(2);
|
|
mMetadataTree->header()->setStretchLastSection(true);
|
|
mMetadataTree->setMinimumWidth(200);
|
|
mSplitter->addWidget(mMetadataTree);
|
|
|
|
// Set initial splitter sizes (70% table, 30% metadata)
|
|
mSplitter->setSizes({700, 300});
|
|
|
|
// Connect selection change
|
|
connect(mTableWidget, &QTableWidget::itemSelectionChanged,
|
|
this, &ListPreviewWidget::onItemSelectionChanged);
|
|
}
|
|
|
|
void ListPreviewWidget::setListData(const QVariantList &items, const QString &listName)
|
|
{
|
|
mItems = items;
|
|
mListName = listName;
|
|
|
|
// Update info label
|
|
mInfoLabel->setText(QString("%1 | %2 items")
|
|
.arg(listName.isEmpty() ? "List" : listName)
|
|
.arg(items.size()));
|
|
|
|
populateTable(items);
|
|
}
|
|
|
|
void ListPreviewWidget::populateTable(const QVariantList &items)
|
|
{
|
|
mTableWidget->clear();
|
|
mTableWidget->setRowCount(0);
|
|
|
|
if (items.isEmpty()) {
|
|
mTableWidget->setColumnCount(1);
|
|
mTableWidget->setHorizontalHeaderLabels({"(empty)"});
|
|
return;
|
|
}
|
|
|
|
// Collect all unique keys from all items to determine columns
|
|
QStringList columns;
|
|
for (const QVariant &item : items) {
|
|
if (item.typeId() == QMetaType::QVariantMap) {
|
|
const QVariantMap map = item.toMap();
|
|
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
|
|
const QString &key = it.key();
|
|
// Skip internal fields and binary data
|
|
if (key.startsWith('_') && key != "_name")
|
|
continue;
|
|
if (it.value().typeId() == QMetaType::QByteArray)
|
|
continue;
|
|
if (!columns.contains(key))
|
|
columns.append(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If items are simple values (not maps), use a single column
|
|
if (columns.isEmpty()) {
|
|
columns.append("Value");
|
|
}
|
|
|
|
// Setup columns
|
|
mTableWidget->setColumnCount(columns.size());
|
|
mTableWidget->setHorizontalHeaderLabels(columns);
|
|
|
|
// Add rows
|
|
mTableWidget->setRowCount(items.size());
|
|
for (int row = 0; row < items.size(); ++row) {
|
|
const QVariant &item = items[row];
|
|
|
|
if (item.typeId() == QMetaType::QVariantMap) {
|
|
const QVariantMap map = item.toMap();
|
|
for (int col = 0; col < columns.size(); ++col) {
|
|
const QString &key = columns[col];
|
|
QVariant val = map.value(key);
|
|
QString displayText;
|
|
|
|
if (val.typeId() == QMetaType::QVariantMap) {
|
|
displayText = QString("{%1 fields}").arg(val.toMap().size());
|
|
} else if (val.typeId() == QMetaType::QVariantList) {
|
|
displayText = QString("[%1 items]").arg(val.toList().size());
|
|
} else if (val.typeId() == QMetaType::QByteArray) {
|
|
displayText = QString("<%1 bytes>").arg(val.toByteArray().size());
|
|
} else {
|
|
displayText = val.toString();
|
|
}
|
|
|
|
auto *tableItem = new QTableWidgetItem(displayText);
|
|
mTableWidget->setItem(row, col, tableItem);
|
|
}
|
|
} else {
|
|
// Simple value
|
|
auto *tableItem = new QTableWidgetItem(item.toString());
|
|
mTableWidget->setItem(row, 0, tableItem);
|
|
}
|
|
}
|
|
|
|
// Resize columns to content
|
|
mTableWidget->resizeColumnsToContents();
|
|
|
|
// Clear metadata tree initially
|
|
mMetadataTree->clear();
|
|
}
|
|
|
|
void ListPreviewWidget::setMetadata(const QVariantMap &metadata)
|
|
{
|
|
// This can be used to set overall list metadata
|
|
updateMetadataTree(metadata);
|
|
}
|
|
|
|
void ListPreviewWidget::clear()
|
|
{
|
|
mItems.clear();
|
|
mListName.clear();
|
|
mTableWidget->clear();
|
|
mTableWidget->setRowCount(0);
|
|
mTableWidget->setColumnCount(0);
|
|
mMetadataTree->clear();
|
|
mInfoLabel->setText("");
|
|
}
|
|
|
|
void ListPreviewWidget::onItemSelectionChanged()
|
|
{
|
|
int row = mTableWidget->currentRow();
|
|
if (row >= 0 && row < mItems.size()) {
|
|
const QVariant &item = mItems[row];
|
|
if (item.typeId() == QMetaType::QVariantMap) {
|
|
updateMetadataTree(item.toMap());
|
|
} else {
|
|
QVariantMap simple;
|
|
simple["value"] = item;
|
|
simple["index"] = row;
|
|
updateMetadataTree(simple);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ListPreviewWidget::updateMetadataTree(const QVariantMap &item)
|
|
{
|
|
mMetadataTree->clear();
|
|
|
|
for (auto it = item.constBegin(); it != item.constEnd(); ++it) {
|
|
const QString &key = it.key();
|
|
const QVariant &val = it.value();
|
|
|
|
auto *treeItem = new QTreeWidgetItem(mMetadataTree);
|
|
treeItem->setText(0, key);
|
|
|
|
if (val.typeId() == QMetaType::QVariantMap) {
|
|
treeItem->setText(1, QString("{%1 fields}").arg(val.toMap().size()));
|
|
// Add nested items
|
|
const QVariantMap nested = val.toMap();
|
|
for (auto nit = nested.constBegin(); nit != nested.constEnd(); ++nit) {
|
|
auto *child = new QTreeWidgetItem(treeItem);
|
|
child->setText(0, nit.key());
|
|
if (nit.value().typeId() == QMetaType::QByteArray) {
|
|
child->setText(1, QString("<%1 bytes>").arg(nit.value().toByteArray().size()));
|
|
} else {
|
|
child->setText(1, nit.value().toString());
|
|
}
|
|
}
|
|
} else if (val.typeId() == QMetaType::QVariantList) {
|
|
treeItem->setText(1, QString("[%1 items]").arg(val.toList().size()));
|
|
} else if (val.typeId() == QMetaType::QByteArray) {
|
|
treeItem->setText(1, QString("<%1 bytes>").arg(val.toByteArray().size()));
|
|
} else {
|
|
treeItem->setText(1, val.toString());
|
|
}
|
|
}
|
|
|
|
mMetadataTree->expandAll();
|
|
mMetadataTree->resizeColumnToContents(0);
|
|
}
|
|
|
|
void ListPreviewWidget::applyTheme(const Theme &theme)
|
|
{
|
|
// Style info label
|
|
mInfoLabel->setStyleSheet(QString(
|
|
"QLabel {"
|
|
" background-color: %1;"
|
|
" color: %2;"
|
|
" border-bottom: 1px solid %3;"
|
|
"}"
|
|
).arg(theme.panelColor, theme.textColorMuted, theme.borderColor));
|
|
|
|
// Style table widget
|
|
mTableWidget->setStyleSheet(QString(
|
|
"QTableWidget {"
|
|
" background-color: %1;"
|
|
" color: %2;"
|
|
" border: 1px solid %3;"
|
|
" gridline-color: %3;"
|
|
"}"
|
|
"QTableWidget::item {"
|
|
" padding: 4px;"
|
|
"}"
|
|
"QTableWidget::item:selected {"
|
|
" background-color: %4;"
|
|
"}"
|
|
"QTableWidget::item:alternate {"
|
|
" background-color: %5;"
|
|
"}"
|
|
"QHeaderView::section {"
|
|
" background-color: %1;"
|
|
" color: %2;"
|
|
" border: 1px solid %3;"
|
|
" padding: 4px;"
|
|
" font-weight: bold;"
|
|
"}"
|
|
).arg(theme.panelColor, theme.textColor, theme.borderColor,
|
|
theme.accentColor, theme.backgroundColor));
|
|
|
|
// Style metadata tree
|
|
mMetadataTree->setStyleSheet(QString(
|
|
"QTreeWidget {"
|
|
" background-color: %1;"
|
|
" color: %2;"
|
|
" border: 1px solid %3;"
|
|
"}"
|
|
"QTreeWidget::item:selected {"
|
|
" background-color: %4;"
|
|
"}"
|
|
"QHeaderView::section {"
|
|
" background-color: %1;"
|
|
" color: %2;"
|
|
" border: 1px solid %3;"
|
|
" padding: 4px;"
|
|
"}"
|
|
).arg(theme.panelColor, theme.textColor, theme.borderColor, theme.accentColor));
|
|
}
|