| 434 | } |
| 435 | |
| 436 | void CurveListPanel::onTreeContextMenu(const QPoint& pos) { |
| 437 | if (catalog_ == nullptr) { |
| 438 | return; |
| 439 | } |
| 440 | // The menu is offered only on a dataset node (top-level group with topic children). |
| 441 | const auto is_dataset_node = [](QTreeWidgetItem* node) { |
| 442 | return node != nullptr && node->parent() == nullptr && node->childCount() > 0; |
| 443 | }; |
| 444 | QTreeWidgetItem* clicked = tree_view_->itemAt(pos); |
| 445 | if (!is_dataset_node(clicked)) { |
| 446 | return; // topic/curve nodes get no menu |
| 447 | } |
| 448 | |
| 449 | QHash<QString, DatasetId> id_by_name; |
| 450 | for (const auto& [id, name] : catalog_->datasets()) { |
| 451 | id_by_name.insert(name, id); |
| 452 | } |
| 453 | QList<DatasetId> dataset_ids; |
| 454 | const auto add_dataset = [&](QTreeWidgetItem* node) { |
| 455 | if (!is_dataset_node(node)) { |
| 456 | return; |
| 457 | } |
| 458 | const auto found = id_by_name.constFind(node->text(0)); |
| 459 | if (found != id_by_name.constEnd() && !dataset_ids.contains(found.value())) { |
| 460 | dataset_ids.push_back(found.value()); |
| 461 | } |
| 462 | }; |
| 463 | // Always include the right-clicked dataset; if it is part of a multi-selection, |
| 464 | // include every other selected dataset too. Right-clicking a dataset outside the |
| 465 | // selection targets just that one (standard list behavior). Does not rely on the |
| 466 | // clicked row being selected, so the menu works even on a fresh right-click. |
| 467 | add_dataset(clicked); |
| 468 | if (clicked->isSelected()) { |
| 469 | for (QTreeWidgetItem* item : tree_view_->selectedItems()) { |
| 470 | add_dataset(item); |
| 471 | } |
| 472 | } |
| 473 | if (dataset_ids.isEmpty()) { |
| 474 | return; // clicked name no longer resolves to a catalog dataset |
| 475 | } |
| 476 | |
| 477 | // Build the menu from flat QPushButtons wrapped in QWidgetActions — the same |
| 478 | // pattern as the "Remove all Datasets" item in the datasets popup — so each item |
| 479 | // carries a themed leading icon and the destructive ones paint in the shared |
| 480 | // ${purple} via the central `QMenu#PJMenu QPushButton[destructive="true"]` rule. |
| 481 | QMenu menu(this); |
| 482 | menu.setObjectName(QStringLiteral("PJMenu")); |
| 483 | const QString theme = currentTheme(); |
| 484 | const auto add_item = [&](const QString& icon, const QString& text, bool destructive, bool enabled) { |
| 485 | auto* button = new QPushButton(loadSvg(icon, theme), text, &menu); |
| 486 | button->setFlat(true); |
| 487 | button->setEnabled(enabled); |
| 488 | if (destructive) { |
| 489 | button->setProperty("destructive", true); |
| 490 | } |
| 491 | auto* action = new QWidgetAction(&menu); |
| 492 | action->setDefaultWidget(button); |
| 493 | menu.addAction(action); |
nothing calls this directly
no test coverage detected