File indexing completed on 2025-10-26 03:35:36

0001 /*
0002     File                 : ROOTOptionsWidget.cpp
0003     Project              : LabPlot
0004     Description          : widget providing options for the import of ROOT data
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2018 Christoph Roick <chrisito@gmx.de>
0007     SPDX-FileCopyrightText: 2022 Stefan Gerlach <stefan.gerlach@uni.kn>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "ROOTOptionsWidget.h"
0013 #include "ImportFileWidget.h"
0014 
0015 #include "backend/datasources/filters/ROOTFilter.h"
0016 #include "backend/lib/macros.h"
0017 
0018 ROOTOptionsWidget::ROOTOptionsWidget(QWidget* parent, ImportFileWidget* fileWidget)
0019     : QWidget(parent)
0020     , m_fileWidget(fileWidget) {
0021     ui.setupUi(parent);
0022     histItem = new QTreeWidgetItem(ui.twContent, QStringList(i18n("Histograms")));
0023     histItem->setFlags(Qt::ItemIsEnabled);
0024     treeItem = new QTreeWidgetItem(ui.twContent, QStringList(i18n("Trees and Tuples")));
0025     treeItem->setFlags(Qt::ItemIsEnabled);
0026 
0027     connect(ui.twContent, &QTreeWidget::itemSelectionChanged, this, &ROOTOptionsWidget::rootObjectSelectionChanged);
0028     connect(ui.bRefreshPreview, &QPushButton::clicked, fileWidget, &ImportFileWidget::refreshPreview);
0029 }
0030 
0031 void ROOTOptionsWidget::clear() {
0032     qDeleteAll(histItem->takeChildren());
0033     qDeleteAll(treeItem->takeChildren());
0034     ui.twPreview->clearContents();
0035 }
0036 
0037 void fillTree(QTreeWidgetItem* node, const ROOTFilter::Directory& dir) {
0038     node->setFlags(Qt::ItemIsEnabled);
0039     for (const auto& child : dir.children)
0040         fillTree(new QTreeWidgetItem(node, QStringList(child.name)), child);
0041     for (const auto& content : dir.content)
0042         (new QTreeWidgetItem(node, QStringList(content.first)))->setData(0, Qt::UserRole, content.second);
0043 }
0044 
0045 QMultiHash<QStringList, QVector<QStringList>>
0046 findLeaves(QTreeWidgetItem* node, ROOTFilter* filter, const QString& fileName, const QStringList& path = QStringList{}) {
0047     QMultiHash<QStringList, QVector<QStringList>> leaves;
0048     if (node->childCount() > 0) {
0049         for (int i = 0; i < node->childCount(); ++i)
0050             leaves.unite(findLeaves(node->child(i), filter, fileName, path + QStringList(node->child(i)->text(0))));
0051     } else {
0052         leaves.insert(path, filter->listLeaves(fileName, node->data(0, Qt::UserRole).toLongLong()));
0053     }
0054     return leaves;
0055 }
0056 
0057 void ROOTOptionsWidget::updateContent(ROOTFilter* filter, const QString& fileName) {
0058     DEBUG(Q_FUNC_INFO);
0059 
0060     qDeleteAll(histItem->takeChildren());
0061     qDeleteAll(treeItem->takeChildren());
0062     fillTree(histItem, filter->listHistograms(fileName));
0063     fillTree(treeItem, filter->listTrees(fileName));
0064     leaves = findLeaves(treeItem, filter, fileName);
0065 }
0066 
0067 void ROOTOptionsWidget::rootObjectSelectionChanged() {
0068     DEBUG("rootObjectSelectionChanged()");
0069     auto items = ui.twContent->selectedItems();
0070     QDEBUG("SELECTED ITEMS =" << items);
0071 
0072     ui.twColumns->clear();
0073 
0074     if (items.isEmpty()) {
0075         ui.twColumns->setHeaderHidden(true);
0076         return;
0077     }
0078 
0079     QTreeWidgetItem* p = items.first();
0080     QStringList path;
0081     while (p && p != histItem && p != treeItem) {
0082         path.prepend(p->text(0));
0083         p = p->parent();
0084     }
0085 
0086     if (p == histItem) {
0087         ui.twColumns->setColumnCount(1);
0088         ui.twColumns->setHeaderHidden(false);
0089         ui.twColumns->setHeaderLabels(QStringList(i18n("Histogram Data")));
0090 
0091         auto center = new QTreeWidgetItem(ui.twColumns, QStringList(i18n("Bin Center")));
0092         center->setData(0, Qt::UserRole, QStringList(QStringLiteral("center")));
0093         center->setSelected(true);
0094         center->setFirstColumnSpanned(true);
0095         auto low = new QTreeWidgetItem(ui.twColumns, QStringList(i18n("Low Edge")));
0096         low->setData(0, Qt::UserRole, QStringList(QStringLiteral("low")));
0097         low->setFirstColumnSpanned(true);
0098         auto content = new QTreeWidgetItem(ui.twColumns, QStringList(i18n("Content")));
0099         content->setData(0, Qt::UserRole, QStringList(QStringLiteral("content")));
0100         content->setSelected(true);
0101         content->setFirstColumnSpanned(true);
0102         auto error = new QTreeWidgetItem(ui.twColumns, QStringList(i18n("Error")));
0103         error->setData(0, Qt::UserRole, QStringList(QStringLiteral("error")));
0104         error->setSelected(true);
0105         error->setFirstColumnSpanned(true);
0106 
0107         if (!histselected) {
0108             histselected = true;
0109             ui.sbFirst->setMaximum(0);
0110             ui.sbLast->setMaximum(0);
0111         }
0112     } else if (p == treeItem) {
0113         ui.twColumns->setColumnCount(2);
0114         ui.twColumns->setHeaderHidden(false);
0115         ui.twColumns->setHeaderLabels(QStringList({i18n("Branch/Leaf"), i18n("Array Size")}));
0116 
0117         auto it = leaves.find(path);
0118         if (it != leaves.end()) {
0119             for (const auto& l : it.value()) {
0120                 auto leaf = new QTreeWidgetItem(ui.twColumns, l);
0121                 bool ok = false;
0122                 if (l.count() > 1) {
0123                     QString index(l.back());
0124                     if (index.at(0) == QLatin1Char('[') && index.at(index.size() - 1) == QLatin1Char(']')) {
0125                         size_t elements = index.mid(1, index.length() - 2).toUInt(&ok);
0126                         if (ok) {
0127                             leaf->setFlags(Qt::ItemIsEnabled);
0128                             QStringList elname({l.at(l.count() - 2), QString()});
0129                             QStringList eldata(elname);
0130                             if (l.count() > 2)
0131                                 eldata.prepend(l.front());
0132                             for (size_t i = 0; i < elements; ++i) {
0133                                 eldata.last() = elname.last() = QStringLiteral("[%1]").arg(i);
0134                                 auto el = new QTreeWidgetItem(leaf, elname);
0135                                 el->setData(0, Qt::UserRole, eldata);
0136                             }
0137                         }
0138                     }
0139                 } else
0140                     leaf->setFirstColumnSpanned(true);
0141 
0142                 if (!ok)
0143                     leaf->setData(0, Qt::UserRole, l);
0144             }
0145 
0146             ui.twColumns->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0147         }
0148 
0149         if (histselected) {
0150             histselected = false;
0151             ui.sbFirst->setMaximum(0);
0152             ui.sbLast->setMaximum(0);
0153         }
0154     }
0155 
0156     m_fileWidget->refreshPreview();
0157 }
0158 
0159 const QStringList ROOTOptionsWidget::selectedNames() const {
0160     QStringList names;
0161 
0162     for (QTreeWidgetItem* item : ui.twContent->selectedItems()) {
0163         QString path;
0164         while (item && item != histItem && item != treeItem) {
0165             path.prepend(QLatin1Char('/') + item->text(0));
0166             item = item->parent();
0167         }
0168         path[0] = QLatin1Char(':');
0169         if (item == histItem)
0170             names << QStringLiteral("Hist") + path;
0171         else if (item == treeItem)
0172             names << QStringLiteral("Tree") + path;
0173     }
0174 
0175     return names;
0176 }
0177 
0178 QVector<QStringList> ROOTOptionsWidget::columns() const {
0179     QVector<QStringList> cols;
0180 
0181     // ui.twColumns->selectedItems() returns the items in the order of selection.
0182     // Iterate through the tree to retain the displayed order.
0183     for (int t = 0; t < ui.twColumns->topLevelItemCount(); ++t) {
0184         auto titem = ui.twColumns->topLevelItem(t);
0185         if (titem->isSelected())
0186             cols << titem->data(0, Qt::UserRole).toStringList();
0187         for (int c = 0; c < titem->childCount(); ++c) {
0188             auto citem = titem->child(c);
0189             if (citem->isSelected())
0190                 cols << citem->data(0, Qt::UserRole).toStringList();
0191         }
0192     }
0193 
0194     return cols;
0195 }
0196 
0197 void ROOTOptionsWidget::setNRows(int nrows) {
0198     // try to retain the range settings:
0199     // - if nrows was not 0, keep start row,
0200     //   else set it to one after underflow
0201     // - if nrows didn't change, keep end row,
0202     //   else set it to one before overflow
0203     const int max = std::max(nrows - 1, 0);
0204     int firstval = ui.sbFirst->value();
0205     if (ui.sbFirst->maximum() == 0)
0206         firstval = std::min(nrows - 1, histselected ? 1 : 0);
0207     ui.sbFirst->setMaximum(max);
0208     ui.sbFirst->setValue(firstval);
0209 
0210     int lastval = max == ui.sbLast->maximum() ? ui.sbLast->value() : std::max(max - (histselected ? 1 : 0), 0);
0211     ui.sbLast->setMaximum(max);
0212     ui.sbLast->setValue(lastval);
0213 }