| 538 | } |
| 539 | |
| 540 | void CurveListPanel::addCustomCurve(const QString& catalog_key, const QString& display_name) { |
| 541 | if (custom_view_ == nullptr) { |
| 542 | return; |
| 543 | } |
| 544 | // Idempotent BY NAME: a plugin toolbox re-announces ALL its transforms on every |
| 545 | // data change. A plain Create re-announces the same key (skip it). A Modify mints |
| 546 | // a NEW output topic id → new key for the SAME name; drop the stale entry first so |
| 547 | // we replace it in place instead of appending a duplicate row. |
| 548 | const auto existing = custom_name_to_key_.constFind(display_name); |
| 549 | if (existing != custom_name_to_key_.constEnd()) { |
| 550 | if (existing.value() == catalog_key) { |
| 551 | return; // same series, same key — nothing to do |
| 552 | } |
| 553 | removeCustomCurve(existing.value()); // stale key from a prior version — remove it |
| 554 | } |
| 555 | if (custom_keys_.contains(catalog_key)) { |
| 556 | return; |
| 557 | } |
| 558 | custom_keys_.insert(catalog_key); |
| 559 | custom_name_to_key_.insert(display_name, catalog_key); |
| 560 | CurveTreeView::CurvePath path; |
| 561 | path.key = catalog_key; |
| 562 | path.dataset = display_name; |
| 563 | path.topic = QString{}; |
| 564 | path.field = QString{}; |
| 565 | custom_view_->addCatalogItem(path); |
| 566 | rebuildTree(tree_view_, catalog_, custom_keys_); |
| 567 | } |
| 568 | |
| 569 | void CurveListPanel::removeCustomCurve(const QString& catalog_key) { |
| 570 | if (custom_view_ == nullptr) { |
no test coverage detected