VisualizationZoneWidget.cpp
695 lines
| 25.4 KiB
| text/x-c
|
CppLexer
r95 | #include "Visualization/VisualizationZoneWidget.h" | |||
r444 | ||||
Alexandre Leroux
|
r207 | #include "Visualization/IVisualizationWidgetVisitor.h" | ||
Alexandre Leroux
|
r730 | #include "Visualization/QCustomPlotSynchronizer.h" | ||
r444 | #include "Visualization/VisualizationGraphWidget.h" | |||
r850 | #include "Visualization/VisualizationWidget.h" | |||
r58 | #include "ui_VisualizationZoneWidget.h" | |||
r848 | #include "Common/MimeTypesDef.h" | |||
r851 | #include "Common/VisualizationDef.h" | |||
r1347 | #include <Data/DateTimeRange.h> | |||
r1348 | #include <Data/DateTimeRangeHelper.h> | |||
r1287 | #include <DataSource/DataSourceController.h> | |||
r878 | #include <Time/TimeController.h> | |||
r1420 | #include <Variable/Variable2.h> | |||
r1348 | #include <Variable/VariableController2.h> | |||
r118 | ||||
r850 | #include <Visualization/operations/FindVariableOperation.h> | |||
r1075 | #include <DragAndDrop/DragDropGuiController.h> | |||
r539 | #include <QUuid> | |||
Alexandre Leroux
|
r265 | #include <SqpApplication.h> | ||
r621 | #include <cmath> | |||
Alexandre Leroux
|
r265 | |||
r839 | #include <QLayout> | |||
r1347 | #include <QStyle> | |||
r839 | ||||
Alexandre Leroux
|
r219 | Q_LOGGING_CATEGORY(LOG_VisualizationZoneWidget, "VisualizationZoneWidget") | ||
r1420 | namespace | |||
{ | ||||
Alexandre Leroux
|
r200 | |||
Alexandre Leroux
|
r738 | /** | ||
* Applies a function to all graphs of the zone represented by its layout | ||||
* @param layout the layout that contains graphs | ||||
* @param fun the function to apply to each graph | ||||
*/ | ||||
template <typename Fun> | ||||
r1420 | void processGraphs(QLayout& layout, Fun fun) | |||
Alexandre Leroux
|
r738 | { | ||
r1420 | for (auto i = 0; i < layout.count(); ++i) | |||
{ | ||||
if (auto item = layout.itemAt(i)) | ||||
{ | ||||
Alexandre Leroux
|
r738 | if (auto visualizationGraphWidget | ||
r1420 | = qobject_cast<VisualizationGraphWidget*>(item->widget())) | |||
{ | ||||
Alexandre Leroux
|
r738 | fun(*visualizationGraphWidget); | ||
} | ||||
} | ||||
} | ||||
} | ||||
r1137 | /// Generates a default name for a new graph, according to the number of graphs already displayed in | |||
/// the zone | ||||
r1420 | QString defaultGraphName(QLayout& layout) | |||
r1137 | { | |||
QSet<QString> existingNames; | ||||
processGraphs( | ||||
r1420 | layout, [&existingNames](auto& graphWidget) { existingNames.insert(graphWidget.name()); }); | |||
r1137 | ||||
int zoneNum = 1; | ||||
QString name; | ||||
r1420 | do | |||
{ | ||||
r1137 | name = QObject::tr("Graph ").append(QString::number(zoneNum)); | |||
++zoneNum; | ||||
} while (existingNames.contains(name)); | ||||
return name; | ||||
} | ||||
Alexandre Leroux
|
r200 | } // namespace | ||
r1420 | struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate | |||
{ | ||||
r539 | ||||
Alexandre Leroux
|
r730 | explicit VisualizationZoneWidgetPrivate() | ||
r1420 | : m_SynchronisationGroupId { QUuid::createUuid() } | |||
, m_Synchronizer { std::make_unique<QCustomPlotSynchronizer>() } | ||||
Alexandre Leroux
|
r730 | { | ||
} | ||||
r539 | QUuid m_SynchronisationGroupId; | |||
Alexandre Leroux
|
r730 | std::unique_ptr<IGraphSynchronizer> m_Synchronizer; | ||
r850 | ||||
r1420 | void dropGraph(int index, VisualizationZoneWidget* zoneWidget); | |||
void dropVariables(const std::vector<std::shared_ptr<Variable2>>& variables, int index, | ||||
VisualizationZoneWidget* zoneWidget); | ||||
void dropProducts( | ||||
const QVariantList& productsData, int index, VisualizationZoneWidget* zoneWidget); | ||||
r539 | }; | |||
r1420 | VisualizationZoneWidget::VisualizationZoneWidget(const QString& name, QWidget* parent) | |||
: VisualizationDragWidget { parent } | ||||
, ui { new Ui::VisualizationZoneWidget } | ||||
, impl { spimpl::make_unique_impl<VisualizationZoneWidgetPrivate>() } | ||||
r58 | { | |||
ui->setupUi(this); | ||||
Alexandre Leroux
|
r197 | |||
ui->zoneNameLabel->setText(name); | ||||
Alexandre Leroux
|
r265 | |||
r1075 | ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Graph); | |||
r1420 | ui->dragDropContainer->setMimeType( | |||
MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted); | ||||
r936 | ui->dragDropContainer->setMimeType( | |||
r879 | MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged); | |||
r1287 | ui->dragDropContainer->setMimeType( | |||
MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::InsertedAndMerged); | ||||
r1420 | ui->dragDropContainer->setMimeType( | |||
MIME_TYPE_TIME_RANGE, VisualizationDragDropContainer::DropBehavior::Merged); | ||||
ui->dragDropContainer->setMimeType( | ||||
MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Forbidden); | ||||
ui->dragDropContainer->setMimeType( | ||||
MIME_TYPE_SELECTION_ZONE, VisualizationDragDropContainer::DropBehavior::Forbidden); | ||||
r850 | ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { | |||
r1420 | return sqpApp->dragDropGuiController().checkMimeDataForVisualization( | |||
mimeData, ui->dragDropContainer); | ||||
r850 | }); | |||
r875 | ||||
Alexandre Leroux
|
r1023 | auto acceptDragWidgetFun = [](auto dragWidget, auto mimeData) { | ||
r1420 | if (!mimeData) | |||
{ | ||||
Alexandre Leroux
|
r1023 | return false; | ||
} | ||||
r1420 | if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |||
{ | ||||
r1348 | auto variables = sqpApp->variableController().variables( | |||
r1420 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |||
Alexandre Leroux
|
r1023 | |||
r1420 | if (variables.size() != 1) | |||
{ | ||||
Alexandre Leroux
|
r1023 | return false; | ||
} | ||||
r1348 | auto variable = variables.front(); | |||
Alexandre Leroux
|
r1023 | |||
r1420 | if (auto graphWidget = dynamic_cast<const VisualizationGraphWidget*>(dragWidget)) | |||
{ | ||||
Alexandre Leroux
|
r1023 | return graphWidget->canDrop(*variable); | ||
} | ||||
} | ||||
return true; | ||||
}; | ||||
ui->dragDropContainer->setAcceptDragWidgetFunction(acceptDragWidgetFun); | ||||
r875 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this, | |||
r1420 | &VisualizationZoneWidget::dropMimeData); | |||
r875 | connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredOnWidget, this, | |||
r1420 | &VisualizationZoneWidget::dropMimeDataOnGraph); | |||
r839 | ||||
Alexandre Leroux
|
r265 | // 'Close' options : widget is deleted when closed | ||
setAttribute(Qt::WA_DeleteOnClose); | ||||
connect(ui->closeButton, &QToolButton::clicked, this, &VisualizationZoneWidget::close); | ||||
ui->closeButton->setIcon(sqpApp->style()->standardIcon(QStyle::SP_TitleBarCloseButton)); | ||||
r539 | ||||
// Synchronisation id | ||||
r1420 | // QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronizationGroupId", | |||
// Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | ||||
r58 | } | |||
VisualizationZoneWidget::~VisualizationZoneWidget() | ||||
{ | ||||
delete ui; | ||||
} | ||||
r118 | ||||
r1420 | void VisualizationZoneWidget::setZoneRange(const DateTimeRange& range) | |||
r1138 | { | |||
r1420 | if (auto graph = firstGraph()) | |||
{ | ||||
r1138 | graph->setGraphRange(range); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1138 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("setZoneRange:Cannot set the range of an empty zone."); | ||||
} | ||||
} | ||||
r1420 | void VisualizationZoneWidget::addGraph(VisualizationGraphWidget* graphWidget) | |||
r118 | { | |||
Alexandre Leroux
|
r730 | // Synchronize new graph with others in the zone | ||
r1420 | // impl->m_Synchronizer->addGraph(*graphWidget); | |||
r1373 | ||||
r1420 | // ui->dragDropContainer->addDragWidget(graphWidget); | |||
insertGraph(0, graphWidget); | ||||
r839 | } | |||
r1420 | void VisualizationZoneWidget::insertGraph(int index, VisualizationGraphWidget* graphWidget) | |||
r839 | { | |||
r1373 | DEPRECATE( | |||
r1420 | auto layout = ui->dragDropContainer->layout(); for (int i = 0; i < layout->count(); i++) { | |||
auto graph = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(i)->widget()); | ||||
connect(graphWidget, &VisualizationGraphWidget::setrange_sig, graph, | ||||
&VisualizationGraphWidget::setGraphRange); | ||||
connect(graph, &VisualizationGraphWidget::setrange_sig, graphWidget, | ||||
&VisualizationGraphWidget::setGraphRange); | ||||
} if (auto graph = firstGraph()) { graphWidget->setGraphRange(graph->graphRange(), true); }) | ||||
r1373 | ||||
r839 | // Synchronize new graph with others in the zone | |||
impl->m_Synchronizer->addGraph(*graphWidget); | ||||
ui->dragDropContainer->insertDragWidget(index, graphWidget); | ||||
r118 | } | |||
r1420 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph(std::shared_ptr<Variable2> variable) | |||
r839 | { | |||
return createGraph(variable, -1); | ||||
} | ||||
r1420 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph( | |||
std::shared_ptr<Variable2> variable, int index) | ||||
r118 | { | |||
r844 | auto graphWidget | |||
r1420 | = new VisualizationGraphWidget { defaultGraphName(*ui->dragDropContainer->layout()), this }; | |||
Alexandre Leroux
|
r307 | |||
r444 | ||||
Alexandre Leroux
|
r307 | // Set graph properties | ||
graphWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); | ||||
graphWidget->setMinimumHeight(GRAPH_MINIMUM_HEIGHT); | ||||
Alexandre Leroux
|
r200 | |||
r444 | // Lambda to synchronize zone widget | |||
r1420 | // auto synchronizeZoneWidget = [this, graphWidget](const DateTimeRange &graphRange, | |||
// const DateTimeRange &oldGraphRange) { | ||||
// auto zoomType = DateTimeRangeHelper::getTransformationType(oldGraphRange, graphRange); | ||||
// auto frameLayout = ui->dragDropContainer->layout(); | ||||
// for (auto i = 0; i < frameLayout->count(); ++i) { | ||||
// auto graphChild | ||||
// = dynamic_cast<VisualizationGraphWidget *>(frameLayout->itemAt(i)->widget()); | ||||
// if (graphChild && (graphChild != graphWidget)) { | ||||
// auto graphChildRange = graphChild->graphRange(); | ||||
// switch (zoomType) { | ||||
// case TransformationType::ZoomIn: { | ||||
// auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | ||||
// auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | ||||
// graphChildRange.m_TStart += deltaLeft; | ||||
// graphChildRange.m_TEnd -= deltaRight; | ||||
// break; | ||||
// } | ||||
// case TransformationType::ZoomOut: { | ||||
// auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | ||||
// auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | ||||
// graphChildRange.m_TStart -= deltaLeft; | ||||
// graphChildRange.m_TEnd += deltaRight; | ||||
// break; | ||||
// } | ||||
// case TransformationType::PanRight: { | ||||
// auto deltaLeft = graphRange.m_TStart - oldGraphRange.m_TStart; | ||||
// auto deltaRight = graphRange.m_TEnd - oldGraphRange.m_TEnd; | ||||
// graphChildRange.m_TStart += deltaLeft; | ||||
// graphChildRange.m_TEnd += deltaRight; | ||||
// break; | ||||
// } | ||||
// case TransformationType::PanLeft: { | ||||
// auto deltaLeft = oldGraphRange.m_TStart - graphRange.m_TStart; | ||||
// auto deltaRight = oldGraphRange.m_TEnd - graphRange.m_TEnd; | ||||
// graphChildRange.m_TStart -= deltaLeft; | ||||
// graphChildRange.m_TEnd -= deltaRight; | ||||
// break; | ||||
// } | ||||
// case TransformationType::Unknown: { | ||||
// break; | ||||
// } | ||||
// default: | ||||
// qCCritical(LOG_VisualizationZoneWidget()) | ||||
// << tr("Impossible to synchronize: zoom type not take into | ||||
// account"); | ||||
// // No action | ||||
// break; | ||||
// } | ||||
// graphChild->setFlags(GraphFlag::DisableAll); | ||||
// graphChild->setGraphRange(graphChildRange, true); | ||||
// graphChild->setFlags(GraphFlag::EnableAll); | ||||
// } | ||||
// } | ||||
// }; | ||||
r444 | ||||
// connection for synchronization | ||||
r1420 | // connect(graphWidget, &VisualizationGraphWidget::synchronize, synchronizeZoneWidget); | |||
r540 | connect(graphWidget, &VisualizationGraphWidget::variableAdded, this, | |||
r1420 | &VisualizationZoneWidget::onVariableAdded); | |||
Alexandre Leroux
|
r737 | connect(graphWidget, &VisualizationGraphWidget::variableAboutToBeRemoved, this, | ||
r1420 | &VisualizationZoneWidget::onVariableAboutToBeRemoved); | |||
r540 | ||||
r1420 | auto range = DateTimeRange {}; | |||
if (auto firstGraph = this->firstGraph()) | ||||
{ | ||||
r548 | // Case of a new graph in a existant zone | |||
r878 | range = firstGraph->graphRange(); | |||
r548 | } | |||
r1420 | else | |||
{ | ||||
r548 | // Case of a new graph as the first of the zone | |||
range = variable->range(); | ||||
} | ||||
r839 | this->insertGraph(index, graphWidget); | |||
r540 | ||||
r548 | graphWidget->addVariable(variable, range); | |||
Alexandre Leroux
|
r900 | graphWidget->setYRange(variable); | ||
r444 | ||||
r118 | return graphWidget; | |||
} | ||||
r1420 | VisualizationGraphWidget* VisualizationZoneWidget::createGraph( | |||
const std::vector<std::shared_ptr<Variable2>> variables, int index) | ||||
r839 | { | |||
r1420 | if (variables.empty()) | |||
{ | ||||
r839 | return nullptr; | |||
r844 | } | |||
r839 | ||||
r1348 | auto graphWidget = createGraph(variables.front(), index); | |||
r1420 | for (auto variableIt = variables.cbegin() + 1; variableIt != variables.cend(); ++variableIt) | |||
{ | ||||
r839 | graphWidget->addVariable(*variableIt, graphWidget->graphRange()); | |||
} | ||||
return graphWidget; | ||||
} | ||||
r1420 | VisualizationGraphWidget* VisualizationZoneWidget::firstGraph() const | |||
r1048 | { | |||
r1420 | VisualizationGraphWidget* firstGraph = nullptr; | |||
r1048 | auto layout = ui->dragDropContainer->layout(); | |||
r1420 | if (layout->count() > 0) | |||
{ | ||||
r1048 | if (auto visualizationGraphWidget | |||
r1420 | = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(0)->widget())) | |||
{ | ||||
r1048 | firstGraph = visualizationGraphWidget; | |||
} | ||||
} | ||||
return firstGraph; | ||||
} | ||||
r1307 | void VisualizationZoneWidget::closeAllGraphs() | |||
{ | ||||
processGraphs(*ui->dragDropContainer->layout(), | ||||
r1420 | [](VisualizationGraphWidget& graphWidget) { graphWidget.close(); }); | |||
r1307 | } | |||
r1420 | void VisualizationZoneWidget::accept(IVisualizationWidgetVisitor* visitor) | |||
r118 | { | |||
r1420 | if (visitor) | |||
{ | ||||
Alexandre Leroux
|
r208 | visitor->visitEnter(this); | ||
Alexandre Leroux
|
r738 | // Apply visitor to graph children: widgets different from graphs are not visited (no | ||
// action) | ||||
r1420 | processGraphs(*ui->dragDropContainer->layout(), | |||
[visitor](VisualizationGraphWidget& graphWidget) { graphWidget.accept(visitor); }); | ||||
Alexandre Leroux
|
r208 | |||
visitor->visitLeave(this); | ||||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r219 | qCCritical(LOG_VisualizationZoneWidget()) << tr("Can't visit widget : the visitor is null"); | ||
} | ||||
r118 | } | |||
r1420 | bool VisualizationZoneWidget::canDrop(Variable2& variable) const | |||
Alexandre Leroux
|
r209 | { | ||
// A tab can always accomodate a variable | ||||
Q_UNUSED(variable); | ||||
return true; | ||||
} | ||||
r1420 | bool VisualizationZoneWidget::contains(Variable2& variable) const | |||
Alexandre Leroux
|
r327 | { | ||
Q_UNUSED(variable); | ||||
return false; | ||||
} | ||||
r119 | QString VisualizationZoneWidget::name() const | |||
r118 | { | |||
Alexandre Leroux
|
r197 | return ui->zoneNameLabel->text(); | ||
r118 | } | |||
r540 | ||||
r1420 | QMimeData* VisualizationZoneWidget::mimeData(const QPoint& position) const | |||
r839 | { | |||
r1047 | Q_UNUSED(position); | |||
r846 | auto mimeData = new QMimeData; | |||
r1420 | mimeData->setData(MIME_TYPE_ZONE, QByteArray {}); | |||
r839 | ||||
r1420 | if (auto firstGraph = this->firstGraph()) | |||
{ | ||||
r936 | auto timeRangeData = TimeController::mimeDataForTimeRange(firstGraph->graphRange()); | |||
mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData); | ||||
} | ||||
r839 | return mimeData; | |||
} | ||||
bool VisualizationZoneWidget::isDragAllowed() const | ||||
{ | ||||
return true; | ||||
} | ||||
r1420 | void VisualizationZoneWidget::notifyMouseMoveInGraph(const QPointF& graphPosition, | |||
const QPointF& plotPosition, VisualizationGraphWidget* graphWidget) | ||||
r960 | { | |||
r1420 | processGraphs(*ui->dragDropContainer->layout(), | |||
[&graphPosition, &plotPosition, &graphWidget](VisualizationGraphWidget& processedGraph) { | ||||
switch (sqpApp->plotsCursorMode()) | ||||
{ | ||||
case SqpApplication::PlotsCursorMode::Vertical: | ||||
r960 | processedGraph.removeHorizontalCursor(); | |||
processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x()); | ||||
r1420 | break; | |||
case SqpApplication::PlotsCursorMode::Temporal: | ||||
processedGraph.addVerticalCursor(plotPosition.x()); | ||||
r960 | processedGraph.removeHorizontalCursor(); | |||
r1420 | break; | |||
case SqpApplication::PlotsCursorMode::Horizontal: | ||||
r960 | processedGraph.removeVerticalCursor(); | |||
r1420 | if (&processedGraph == graphWidget) | |||
{ | ||||
processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | ||||
} | ||||
else | ||||
{ | ||||
processedGraph.removeHorizontalCursor(); | ||||
} | ||||
break; | ||||
case SqpApplication::PlotsCursorMode::Cross: | ||||
if (&processedGraph == graphWidget) | ||||
{ | ||||
processedGraph.addVerticalCursorAtViewportPosition(graphPosition.x()); | ||||
processedGraph.addHorizontalCursorAtViewportPosition(graphPosition.y()); | ||||
} | ||||
else | ||||
{ | ||||
processedGraph.removeHorizontalCursor(); | ||||
processedGraph.removeVerticalCursor(); | ||||
} | ||||
break; | ||||
case SqpApplication::PlotsCursorMode::NoCursor: | ||||
processedGraph.removeHorizontalCursor(); | ||||
processedGraph.removeVerticalCursor(); | ||||
break; | ||||
} | ||||
}); | ||||
r960 | } | |||
r1420 | void VisualizationZoneWidget::notifyMouseLeaveGraph(VisualizationGraphWidget* graphWidget) | |||
r960 | { | |||
r1420 | processGraphs(*ui->dragDropContainer->layout(), [](VisualizationGraphWidget& processedGraph) { | |||
r960 | processedGraph.removeHorizontalCursor(); | |||
processedGraph.removeVerticalCursor(); | ||||
}); | ||||
} | ||||
r1420 | void VisualizationZoneWidget::closeEvent(QCloseEvent* event) | |||
Alexandre Leroux
|
r738 | { | ||
// Closes graphs in the zone | ||||
r839 | processGraphs(*ui->dragDropContainer->layout(), | |||
r1420 | [](VisualizationGraphWidget& graphWidget) { graphWidget.close(); }); | |||
Alexandre Leroux
|
r738 | |||
Alexandre Leroux
|
r739 | // Delete synchronization group from variable controller | ||
QMetaObject::invokeMethod(&sqpApp->variableController(), "onRemoveSynchronizationGroupId", | ||||
r1420 | Qt::QueuedConnection, Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |||
Alexandre Leroux
|
r739 | |||
Alexandre Leroux
|
r738 | QWidget::closeEvent(event); | ||
} | ||||
r1420 | void VisualizationZoneWidget::onVariableAdded(std::shared_ptr<Variable2> variable) | |||
r540 | { | |||
QMetaObject::invokeMethod(&sqpApp->variableController(), "onAddSynchronized", | ||||
r1420 | Qt::QueuedConnection, Q_ARG(std::shared_ptr<Variable2>, variable), | |||
Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | ||||
r540 | } | |||
Alexandre Leroux
|
r737 | |||
r1420 | void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptr<Variable2> variable) | |||
Alexandre Leroux
|
r737 | { | ||
QMetaObject::invokeMethod(&sqpApp->variableController(), "desynchronize", Qt::QueuedConnection, | ||||
r1420 | Q_ARG(std::shared_ptr<Variable2>, variable), Q_ARG(QUuid, impl->m_SynchronisationGroupId)); | |||
Alexandre Leroux
|
r737 | } | ||
r839 | ||||
r1420 | void VisualizationZoneWidget::dropMimeData(int index, const QMimeData* mimeData) | |||
r839 | { | |||
r1420 | if (mimeData->hasFormat(MIME_TYPE_GRAPH)) | |||
{ | ||||
r850 | impl->dropGraph(index, this); | |||
} | ||||
r1420 | else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |||
{ | ||||
r1348 | auto variables = sqpApp->variableController().variables( | |||
r1420 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |||
r850 | impl->dropVariables(variables, index, this); | |||
} | ||||
r1420 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |||
{ | ||||
r1287 | auto products = sqpApp->dataSourceController().productsDataForMimeData( | |||
mimeData->data(MIME_TYPE_PRODUCT_LIST)); | ||||
impl->dropProducts(products, index, this); | ||||
} | ||||
r1420 | else | |||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received."); | ||||
} | ||||
} | ||||
r1420 | void VisualizationZoneWidget::dropMimeDataOnGraph( | |||
VisualizationDragWidget* dragWidget, const QMimeData* mimeData) | ||||
r875 | { | |||
r1420 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(dragWidget); | |||
if (!graphWidget) | ||||
{ | ||||
r875 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationZoneWidget::dropMimeDataOnGraph, dropping in an unknown widget, " | ||||
"drop aborted"); | ||||
Q_ASSERT(false); | ||||
return; | ||||
} | ||||
r1420 | if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) | |||
{ | ||||
r1348 | auto variables = sqpApp->variableController().variables( | |||
r1420 | Variable2::IDs(mimeData->data(MIME_TYPE_VARIABLE_LIST))); | |||
for (const auto& var : variables) | ||||
{ | ||||
r875 | graphWidget->addVariable(var, graphWidget->graphRange()); | |||
} | ||||
} | ||||
r1420 | else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) | |||
{ | ||||
r1287 | auto products = sqpApp->dataSourceController().productsDataForMimeData( | |||
mimeData->data(MIME_TYPE_PRODUCT_LIST)); | ||||
r1420 | auto context = new QObject { this }; | |||
r1378 | auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE)); | |||
// BTW this is really dangerous, this assumes the next created variable will be this one... | ||||
r1348 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, | |||
r1420 | [this, graphWidget, context, range](auto variable) { | |||
if (sqpApp->variableController().isReady(variable)) | ||||
{ | ||||
graphWidget->addVariable(variable, range); | ||||
delete context; | ||||
} | ||||
else | ||||
{ | ||||
// -> this is pure insanity! this is a workaround to make a bad design work | ||||
QObject::connect(variable.get(), &Variable2::updated, context, | ||||
[graphWidget, context, range, variable]() { | ||||
graphWidget->addVariable(variable, range); | ||||
delete context; | ||||
}); | ||||
} | ||||
}, | ||||
Qt::QueuedConnection); | ||||
r1288 | ||||
r1287 | auto productData = products.first().toHash(); | |||
QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", | ||||
r1420 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |||
r1287 | } | |||
r1420 | else if (mimeData->hasFormat(MIME_TYPE_TIME_RANGE)) | |||
{ | ||||
r879 | auto range = TimeController::timeRangeForMimeData(mimeData->data(MIME_TYPE_TIME_RANGE)); | |||
r1377 | graphWidget->setGraphRange(range, true, true); | |||
r879 | } | |||
r1420 | else | |||
{ | ||||
r875 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationZoneWidget::dropMimeDataOnGraph, unknown MIME data received."); | ||||
} | ||||
} | ||||
r850 | void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph( | |||
r1420 | int index, VisualizationZoneWidget* zoneWidget) | |||
r850 | { | |||
r1420 | auto& helper = sqpApp->dragDropGuiController(); | |||
r850 | ||||
r1420 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(helper.getCurrentDragWidget()); | |||
if (!graphWidget) | ||||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not " | ||||
"found or invalid."); | ||||
Q_ASSERT(false); | ||||
return; | ||||
} | ||||
auto parentDragDropContainer | ||||
r1420 | = qobject_cast<VisualizationDragDropContainer*>(graphWidget->parentWidget()); | |||
if (!parentDragDropContainer) | ||||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of " | ||||
"the dropped graph is not found."); | ||||
Q_ASSERT(false); | ||||
return; | ||||
} | ||||
r1420 | const auto& variables = graphWidget->variables(); | |||
r841 | ||||
r1420 | if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.empty()) | |||
{ | ||||
r850 | // The drop didn't occur in the same zone | |||
// Abort the requests for the variables (if any) | ||||
// Commented, because it's not sure if it's needed or not | ||||
// for (const auto& var : variables) | ||||
//{ | ||||
// sqpApp->variableController().onAbortProgressRequested(var); | ||||
//} | ||||
auto previousParentZoneWidget = graphWidget->parentZoneWidget(); | ||||
auto nbGraph = parentDragDropContainer->countDragWidget(); | ||||
r1420 | if (nbGraph == 1) | |||
{ | ||||
r850 | // This is the only graph in the previous zone, close the zone | |||
Thibaud Rabillard
|
r911 | helper.delayedCloseWidget(previousParentZoneWidget); | ||
r841 | } | |||
r1420 | else | |||
{ | ||||
r850 | // Close the graph | |||
Thibaud Rabillard
|
r911 | helper.delayedCloseWidget(graphWidget); | ||
r850 | } | |||
// Creates the new graph in the zone | ||||
r1048 | auto newGraphWidget = zoneWidget->createGraph(variables, index); | |||
newGraphWidget->addSelectionZones(graphWidget->selectionZoneRanges()); | ||||
r850 | } | |||
r1420 | else | |||
{ | ||||
r850 | // The drop occurred in the same zone or the graph is empty | |||
// Simple move of the graph, no variable operation associated | ||||
parentDragDropContainer->layout()->removeWidget(graphWidget); | ||||
r1420 | if (variables.empty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) | |||
{ | ||||
r850 | // The graph is empty and dropped in a different zone. | |||
// Take the range of the first graph in the zone (if existing). | ||||
auto layout = zoneWidget->ui->dragDropContainer->layout(); | ||||
r1420 | if (layout->count() > 0) | |||
{ | ||||
r850 | if (auto visualizationGraphWidget | |||
r1420 | = qobject_cast<VisualizationGraphWidget*>(layout->itemAt(0)->widget())) | |||
{ | ||||
r850 | graphWidget->setGraphRange(visualizationGraphWidget->graphRange()); | |||
r841 | } | |||
} | ||||
r850 | } | |||
zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget); | ||||
} | ||||
} | ||||
void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables( | ||||
r1420 | const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |||
VisualizationZoneWidget* zoneWidget) | ||||
r850 | { | |||
r868 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and | |||
// compatible variable here | ||||
r1420 | if (variables.size() > 1) | |||
{ | ||||
r850 | return; | |||
} | ||||
r870 | zoneWidget->createGraph(variables, index); | |||
r839 | } | |||
r1287 | ||||
void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropProducts( | ||||
r1420 | const QVariantList& productsData, int index, VisualizationZoneWidget* zoneWidget) | |||
r1287 | { | |||
// Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and | ||||
// compatible variable here | ||||
r1420 | if (productsData.count() != 1) | |||
{ | ||||
r1287 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " | ||||
"aborted."); | ||||
return; | ||||
} | ||||
r1420 | auto context = new QObject { zoneWidget }; | |||
r1348 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, | |||
r1420 | [this, index, zoneWidget, context](auto variable) { | |||
zoneWidget->createGraph(variable, index); | ||||
delete context; // removes the connection | ||||
}, | ||||
Qt::QueuedConnection); | ||||
r1288 | ||||
r1287 | auto productData = productsData.first().toHash(); | |||
QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", | ||||
r1420 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |||
r1287 | } | |||