| 857 | } |
| 858 | |
| 859 | bool PlotWidget::revalidate() { |
| 860 | if (catalog_ == nullptr) { |
| 861 | return false; |
| 862 | } |
| 863 | // A curve is stale once its source key is gone from the catalog. Collect first, |
| 864 | // then remove: removeCurve() mutates curve_list, so removing while iterating |
| 865 | // would invalidate the iterator. |
| 866 | QStringList to_remove; |
| 867 | for (const CurveInfo& info : curveList()) { |
| 868 | if (info.curve == nullptr) { |
| 869 | continue; |
| 870 | } |
| 871 | if (const auto* adapter = dynamic_cast<const DatastoreCurveAdapter*>(info.curve->data())) { |
| 872 | if (!catalog_->curveDescriptor(adapter->source().name).has_value()) { |
| 873 | to_remove.push_back(info.source_name); |
| 874 | } |
| 875 | } else if (const auto* xy_series = dynamic_cast<const PointSeriesXY*>(info.curve->data())) { |
| 876 | if (!catalog_->curveDescriptor(xy_series->xSource().name).has_value() || |
| 877 | !catalog_->curveDescriptor(xy_series->ySource().name).has_value()) { |
| 878 | to_remove.push_back(info.source_name); |
| 879 | } |
| 880 | } |
| 881 | } |
| 882 | if (to_remove.isEmpty()) { |
| 883 | return false; |
| 884 | } |
| 885 | for (const QString& source_name : to_remove) { |
| 886 | removeCurve(source_name); |
| 887 | } |
| 888 | // If revalidation drained every curve, mirror removeAllCurves()'s reset out of |
| 889 | // XY mode: onDragEnterEvent() only accepts an add_curve drop when !isXYPlot(), |
| 890 | // so a stuck-XY empty plot would reject every new curve dropped onto it. |
| 891 | if (curveList().empty()) { |
| 892 | setModeXY(false); |
| 893 | if (tracker_ != nullptr) { |
| 894 | tracker_->setEnabled(tracker_enabled_); |
| 895 | tracker_->redraw(); |
| 896 | } |
| 897 | } |
| 898 | updateMaximumZoomArea(); |
| 899 | replot(); |
| 900 | return true; |
| 901 | } |
| 902 | |
| 903 | bool PlotWidget::eventFilter(QObject* obj, QEvent* event) { |
| 904 | if (PlotWidgetBase::eventFilter(obj, event)) { |
no test coverage detected