diff --git a/core/src/Variable/VariableModel.cpp b/core/src/Variable/VariableModel.cpp index 6e13efd..ccc9933 100644 --- a/core/src/Variable/VariableModel.cpp +++ b/core/src/Variable/VariableModel.cpp @@ -287,9 +287,11 @@ QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const QList > variableList; for (const auto &index : indexes) { - auto variable = impl->m_Variables.at(index.row()); - if (variable.get() && index.isValid()) { - variableList << variable; + if (index.column() == 0) { // only the first column + auto variable = impl->m_Variables.at(index.row()); + if (variable.get() && index.isValid()) { + variableList << variable; + } } } diff --git a/gui/include/DragDropHelper.h b/gui/include/DragDropHelper.h index 95a9c45..871b84d 100644 --- a/gui/include/DragDropHelper.h +++ b/gui/include/DragDropHelper.h @@ -2,39 +2,23 @@ #define SCIQLOP_DRAGDROPHELPER_H #include +#include #include class QVBoxLayout; class QScrollArea; class VisualizationDragWidget; +class VisualizationDragDropContainer; class QMimeData; -/** - * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. - * @note A QScrollArea inside an other QScrollArea is not fully supported. - */ -class DragDropScroller : public QObject { - Q_OBJECT - -public: - DragDropScroller(QObject *parent = nullptr); - - void addScrollArea(QScrollArea *scrollArea); - void removeScrollArea(QScrollArea *scrollArea); - -protected: - bool eventFilter(QObject *obj, QEvent *event); - -private: - class DragDropScrollerPrivate; - spimpl::unique_impl_ptr impl; - -private slots: - void onTimer(); -}; +Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropHelper) /** * @brief Helper class for drag&drop operations. + * @note The helper is accessible from the sqpApp singleton and has the same life as the whole + * application (like a controller). But contrary to a controller, it doesn't live in a thread and + * can interect with the gui. + * @see SqpApplication */ class DragDropHelper { public: @@ -44,7 +28,15 @@ public: DragDropHelper(); virtual ~DragDropHelper(); + /// Resets some internal variables. Must be called before any new drag&drop operation. + void resetDragAndDrop(); + + /// Sets the visualization widget currently being drag on the visualization. void setCurrentDragWidget(VisualizationDragWidget *dragWidget); + + /// Returns the visualization widget currently being drag on the visualization. + /// Can be null if a new visualization widget is intended to be created by the drag&drop + /// operation. VisualizationDragWidget *getCurrentDragWidget() const; QWidget &placeHolder() const; @@ -52,6 +44,10 @@ public: void removePlaceHolder(); bool isPlaceHolderSet() const; + /// Checks if the specified mime data is valid for a drop in the visualization + bool checkMimeDataForVisualization(const QMimeData *mimeData, + VisualizationDragDropContainer *dropContainer); + void addDragDropScrollArea(QScrollArea *scrollArea); void removeDragDropScrollArea(QScrollArea *scrollArea); @@ -62,4 +58,28 @@ private: spimpl::unique_impl_ptr impl; }; +/** + * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. + * @note A QScrollArea inside an other QScrollArea is not fully supported. + */ +class DragDropScroller : public QObject { + Q_OBJECT + +public: + DragDropScroller(QObject *parent = nullptr); + + void addScrollArea(QScrollArea *scrollArea); + void removeScrollArea(QScrollArea *scrollArea); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + class DragDropScrollerPrivate; + spimpl::unique_impl_ptr impl; + +private slots: + void onTimer(); +}; + #endif // SCIQLOP_DRAGDROPHELPER_H diff --git a/gui/include/SqpApplication.h b/gui/include/SqpApplication.h index 1027e29..c03124a 100644 --- a/gui/include/SqpApplication.h +++ b/gui/include/SqpApplication.h @@ -45,7 +45,8 @@ public: VariableController &variableController() noexcept; VisualizationController &visualizationController() noexcept; - /// Accessors for the differents sciqlop helpers + /// Accessors for the differents sciqlop helpers, these helpers classes are like controllers but + /// doesn't live in a thread and access gui DragDropHelper &dragDropHelper() noexcept; private: diff --git a/gui/include/Visualization/VisualizationDragDropContainer.h b/gui/include/Visualization/VisualizationDragDropContainer.h index d8460de..6abfbc3 100644 --- a/gui/include/Visualization/VisualizationDragDropContainer.h +++ b/gui/include/Visualization/VisualizationDragDropContainer.h @@ -2,10 +2,15 @@ #define SCIQLOP_VISUALIZATIONDRAGDROPCONTAINER_H #include +#include #include #include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer) + class VisualizationDragWidget; class VisualizationDragDropContainer : public QWidget { @@ -15,6 +20,8 @@ signals: void dropOccured(int dropIndex, const QMimeData *mimeData); public: + using AcceptMimeDataFunction = std::function; + VisualizationDragDropContainer(QWidget *parent = nullptr); void addDragWidget(VisualizationDragWidget *dragWidget); @@ -25,6 +32,8 @@ public: int countDragWidget() const; + void setAcceptMimeDataFunction(AcceptMimeDataFunction fun); + protected: void dragEnterEvent(QDragEnterEvent *event); void dragLeaveEvent(QDragLeaveEvent *event); diff --git a/gui/src/DragDropHelper.cpp b/gui/src/DragDropHelper.cpp index 083197e..ec6cbe4 100644 --- a/gui/src/DragDropHelper.cpp +++ b/gui/src/DragDropHelper.cpp @@ -1,6 +1,14 @@ #include "DragDropHelper.h" #include "SqpApplication.h" +#include "Visualization/VisualizationDragDropContainer.h" #include "Visualization/VisualizationDragWidget.h" +#include "Visualization/VisualizationWidget.h" +#include "Visualization/operations/FindVariableOperation.h" + +#include "Variable/VariableController.h" + +#include "Common/MimeTypesDef.h" +#include "Common/VisualizationDef.h" #include #include @@ -13,6 +21,8 @@ const int SCROLL_SPEED = 5; const int SCROLL_ZONE_SIZE = 50; +Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper") + struct DragDropScroller::DragDropScrollerPrivate { QList m_ScrollAreas; @@ -156,7 +166,11 @@ struct DragDropHelper::DragDropHelperPrivate { m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy()); } else { - m_PlaceHolder->setMinimumSize(200, 200); + // Configuration of the placeHolder when there is no dragWidget + // (for instance with a drag from a variable) + + m_PlaceHolder->setMinimumSize(400, 300); + m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } } }; @@ -171,6 +185,11 @@ DragDropHelper::~DragDropHelper() QFile::remove(impl->m_ImageTempUrl); } +void DragDropHelper::resetDragAndDrop() +{ + setCurrentDragWidget(nullptr); +} + void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget) { impl->m_CurrentDragWidget = dragWidget; @@ -225,3 +244,46 @@ QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const image.save(impl->m_ImageTempUrl); return QUrl::fromLocalFile(impl->m_ImageTempUrl); } + +bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData, + VisualizationDragDropContainer *dropContainer) +{ + auto result = true; + + if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { + auto variables = sqpApp->variableController().variablesForMimeData( + mimeData->data(MIME_TYPE_VARIABLE_LIST)); + + if (variables.count() == 1) { + // Check that the viariable is not already in a graph + + // Search for the top level VisualizationWidget + auto parent = dropContainer->parentWidget(); + while (parent && qobject_cast(parent) == nullptr) { + parent = parent->parentWidget(); + } + + if (parent) { + auto visualizationWidget = static_cast(parent); + + FindVariableOperation findVariableOperation{variables.first()}; + visualizationWidget->accept(&findVariableOperation); + auto variableContainers = findVariableOperation.result(); + if (!variableContainers.empty()) { + result = false; + } + } + else { + qCWarning(LOG_DragDropHelper()) << QObject::tr( + "DragDropHelper::checkMimeDataForVisualization, the parent " + "VisualizationWidget cannot be found."); + result = false; + } + } + else { + result = false; + } + } + + return result; +} diff --git a/gui/src/Variable/VariableInspectorWidget.cpp b/gui/src/Variable/VariableInspectorWidget.cpp index 038bcdb..138fe12 100644 --- a/gui/src/Variable/VariableInspectorWidget.cpp +++ b/gui/src/Variable/VariableInspectorWidget.cpp @@ -12,6 +12,7 @@ #include #include +#include #include Q_LOGGING_CATEGORY(LOG_VariableInspectorWidget, "VariableInspectorWidget") @@ -151,6 +152,11 @@ VariableInspectorWidget::VariableInspectorWidget(QWidget *parent) ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->tableView, &QTableView::customContextMenuRequested, this, &VariableInspectorWidget::onTableMenuRequested); + + // Resets the drag&drop operation on a left-click (the drag&drop is also started by a left + // click). + connect(ui->tableView, &QTableView::clicked, + [](const auto &modelIndex) { sqpApp->dragDropHelper().resetDragAndDrop(); }); } VariableInspectorWidget::~VariableInspectorWidget() diff --git a/gui/src/Visualization/VisualizationDragDropContainer.cpp b/gui/src/Visualization/VisualizationDragDropContainer.cpp index e128e8d..3ee37ff 100644 --- a/gui/src/Visualization/VisualizationDragDropContainer.cpp +++ b/gui/src/Visualization/VisualizationDragDropContainer.cpp @@ -10,11 +10,15 @@ #include #include +Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer") + struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate { QVBoxLayout *m_Layout; QStringList m_AcceptedMimeTypes; QStringList m_MergeAllowedMimeTypes; + VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun + = [](auto mimeData) { return true; }; explicit VisualizationDragDropContainerPrivate(QWidget *widget) { @@ -25,7 +29,7 @@ struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate { bool acceptMimeData(const QMimeData *data) const { for (const auto &type : m_AcceptedMimeTypes) { - if (data->hasFormat(type)) { + if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) { return true; } } @@ -121,10 +125,17 @@ int VisualizationDragDropContainer::countDragWidget() const return nbGraph; } +void VisualizationDragDropContainer::setAcceptMimeDataFunction( + VisualizationDragDropContainer::AcceptMimeDataFunction fun) +{ + impl->m_AcceptMimeDataFun = fun; +} + void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget, const QPoint &dragPosition) { auto &helper = sqpApp->dragDropHelper(); + helper.resetDragAndDrop(); // Note: The management of the drag object is done by Qt auto drag = new QDrag{dragWidget}; @@ -149,10 +160,15 @@ void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidg helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex); dragWidget->setVisible(false); } - } - // Note: The exec() is blocking on windows but not on linux and macOS - drag->exec(Qt::MoveAction | Qt::CopyAction); + // Note: The exec() is blocking on windows but not on linux and macOS + drag->exec(Qt::MoveAction | Qt::CopyAction); + } + else { + qCWarning(LOG_VisualizationDragDropContainer()) + << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified " + "VisualizationDragWidget is not found in this container."); + } } void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) @@ -164,20 +180,30 @@ void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) if (!impl->hasPlaceHolder()) { auto dragWidget = helper.getCurrentDragWidget(); - auto parentWidget - = qobject_cast(dragWidget->parentWidget()); - if (parentWidget) { - dragWidget->setVisible(false); + + if (dragWidget) { + // If the drag&drop is internal to the visualization, entering the container hide + // the dragWidget which was hidden by the dragLeaveEvent + auto parentWidget + = qobject_cast(dragWidget->parentWidget()); + if (parentWidget) { + dragWidget->setVisible(false); + } } auto dragWidgetHovered = impl->getChildDragWidgetAt(this, event->pos()); if (dragWidgetHovered) { auto hoveredWidgetIndex = impl->m_Layout->indexOf(dragWidgetHovered); - auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget()); - if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) { - hoveredWidgetIndex - += 1; // Correction of the index if the drop occurs in the same container + + if (dragWidget) { + auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget()); + if (dragWidgetIndex >= 0 && dragWidgetIndex <= hoveredWidgetIndex) { + // Correction of the index if the drop occurs in the same container + // and if the drag is started from the visualization (in that case, the + // dragWidget is hidden) + hoveredWidgetIndex += 1; + } } helper.insertPlaceHolder(impl->m_Layout, hoveredWidgetIndex); @@ -186,6 +212,9 @@ void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event) helper.insertPlaceHolder(impl->m_Layout, 0); } } + else { + // do nothing + } } else { event->ignore(); @@ -203,19 +232,23 @@ void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event) if (!impl->cursorIsInContainer(this)) { helper.removePlaceHolder(); - bool isInternal = true; - if (isInternal) { - // Only if the drag is started from the visualization - // Show the drag widget at its original place + auto dragWidget = helper.getCurrentDragWidget(); + if (dragWidget) { + // dragWidget has a value only if the drag is started from the visualization + // In that case, shows the drag widget at its original place // So the drag widget doesn't stay hidden if the drop occurs outside the visualization // drop zone (It is not possible to catch a drop event outside of the application) - auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); if (dragWidget) { dragWidget->setVisible(true); } } } + else { + // Leave event probably received for a child widget. + // Do nothing. + // Note: The DragLeave event, doesn't have any mean to determine who sent it. + } QWidget::dragLeaveEvent(event); } @@ -244,10 +277,15 @@ void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event) dropIndex += 1; } - auto dragWidgetIndex = impl->m_Layout->indexOf(helper.getCurrentDragWidget()); - if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) { - dropIndex += 1; // Correction of the index if the drop occurs in the same - // container + if (helper.getCurrentDragWidget()) { + auto dragWidgetIndex + = impl->m_Layout->indexOf(helper.getCurrentDragWidget()); + if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) { + // Correction of the index if the drop occurs in the same container + // and if the drag is started from the visualization (in that case, the + // dragWidget is hidden) + dropIndex += 1; + } } if (dropIndex != placeHolderIndex) { @@ -261,6 +299,15 @@ void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event) } } } + else { + qCWarning(LOG_VisualizationDragDropContainer()) + << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the " + "container"); + } + } + else { + // No hovered drag widget, the mouse is probably hover the placeHolder + // Do nothing } } else { @@ -274,18 +321,22 @@ void VisualizationDragDropContainer::dropEvent(QDropEvent *event) { if (impl->acceptMimeData(event->mimeData())) { auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget(); - if (impl->hasPlaceHolder() && dragWidget) { + if (impl->hasPlaceHolder()) { auto &helper = sqpApp->dragDropHelper(); auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder()); - auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget); - if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) { - droppedIndex - -= 1; // Correction of the index if the drop occurs in the same container - } + if (dragWidget) { + auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget); + if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) { + // Correction of the index if the drop occurs in the same container + // and if the drag is started from the visualization (in that case, the + // dragWidget is hidden) + droppedIndex -= 1; + } - dragWidget->setVisible(true); + dragWidget->setVisible(true); + } event->acceptProposedAction(); @@ -293,6 +344,12 @@ void VisualizationDragDropContainer::dropEvent(QDropEvent *event) emit dropOccured(droppedIndex, event->mimeData()); } + else { + qCWarning(LOG_VisualizationDragDropContainer()) + << tr("VisualizationDragDropContainer::dropEvent, couldn't drop because the " + "placeHolder is not found."); + Q_ASSERT(false); + } } else { event->ignore(); diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index e74aabd..d6b63d6 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -54,6 +54,11 @@ struct VisualizationTabWidget::VisualizationTabWidgetPrivate { explicit VisualizationTabWidgetPrivate(const QString &name) : m_Name{name} {} QString m_Name; + + void dropGraph(int index, VisualizationTabWidget *tabWidget); + void dropZone(int index, VisualizationTabWidget *tabWidget); + void dropVariables(const QList > &variables, int index, + VisualizationTabWidget *tabWidget); }; VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent) @@ -63,9 +68,14 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par { ui->setupUi(this); - ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_ZONE}); + ui->dragDropContainer->setAcceptedMimeTypes( + {MIME_TYPE_GRAPH, MIME_TYPE_ZONE, MIME_TYPE_VARIABLE_LIST}); connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationTabWidget::dropMimeData); + ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { + return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData, + ui->dragDropContainer); + }); sqpApp->dragDropHelper().addDragDropScrollArea(ui->scrollArea); // Widget is deleted when closed @@ -163,58 +173,122 @@ QLayout &VisualizationTabWidget::tabLayout() const noexcept void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData) { - auto &helper = sqpApp->dragDropHelper(); if (mimeData->hasFormat(MIME_TYPE_GRAPH)) { - auto graphWidget = static_cast(helper.getCurrentDragWidget()); - auto parentDragDropContainer - = qobject_cast(graphWidget->parentWidget()); - Q_ASSERT(parentDragDropContainer); - - auto nbGraph = parentDragDropContainer->countDragWidget(); - - const auto &variables = graphWidget->variables(); - - if (!variables.isEmpty()) { - // 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); - //} - - if (nbGraph == 1) { - // This is the only graph in the previous zone, close the zone - graphWidget->parentZoneWidget()->close(); - } - else { - // Close the graph - graphWidget->close(); - } + impl->dropGraph(index, this); + } + else if (mimeData->hasFormat(MIME_TYPE_ZONE)) { + impl->dropZone(index, this); + } + else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { + auto variables = sqpApp->variableController().variablesForMimeData( + mimeData->data(MIME_TYPE_VARIABLE_LIST)); + impl->dropVariables(variables, index, this); + } + else { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received."); + } +} + +void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph( + int index, VisualizationTabWidget *tabWidget) +{ + auto &helper = sqpApp->dragDropHelper(); + + auto graphWidget = qobject_cast(helper.getCurrentDragWidget()); + if (!graphWidget) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropGraph, drop aborted, the dropped graph is not " + "found or invalid."); + Q_ASSERT(false); + return; + } + + auto parentDragDropContainer + = qobject_cast(graphWidget->parentWidget()); + if (!parentDragDropContainer) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropGraph, drop aborted, the parent container of " + "the dropped graph is not found."); + Q_ASSERT(false); + return; + } + + auto nbGraph = parentDragDropContainer->countDragWidget(); - createZone(variables, index); + const auto &variables = graphWidget->variables(); + + if (!variables.isEmpty()) { + // 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); + //} + + if (nbGraph == 1) { + // This is the only graph in the previous zone, close the zone + graphWidget->parentZoneWidget()->close(); } else { - // The graph is empty, create an empty zone and move the graph inside + // Close the graph + graphWidget->close(); + } - auto parentZoneWidget = graphWidget->parentZoneWidget(); + tabWidget->createZone(variables, index); + } + else { + // The graph is empty, create an empty zone and move the graph inside - parentDragDropContainer->layout()->removeWidget(graphWidget); + auto parentZoneWidget = graphWidget->parentZoneWidget(); - auto zoneWidget = createEmptyZone(index); - zoneWidget->addGraph(graphWidget); + parentDragDropContainer->layout()->removeWidget(graphWidget); - // Close the old zone if it was the only graph inside - if (nbGraph == 1) { - parentZoneWidget->close(); - } + auto zoneWidget = tabWidget->createEmptyZone(index); + zoneWidget->addGraph(graphWidget); + + // Close the old zone if it was the only graph inside + if (nbGraph == 1) { + parentZoneWidget->close(); } } - else if (mimeData->hasFormat(MIME_TYPE_ZONE)) { - // Simple move of the zone, no variable operation associated - auto zoneWidget = static_cast(helper.getCurrentDragWidget()); - auto parentDragDropContainer = zoneWidget->parentWidget(); - parentDragDropContainer->layout()->removeWidget(zoneWidget); +} - ui->dragDropContainer->insertDragWidget(index, zoneWidget); +void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone( + int index, VisualizationTabWidget *tabWidget) +{ + auto &helper = sqpApp->dragDropHelper(); + + auto zoneWidget = qobject_cast(helper.getCurrentDragWidget()); + if (!zoneWidget) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not " + "found or invalid."); + Q_ASSERT(false); + return; + } + + auto parentDragDropContainer + = qobject_cast(zoneWidget->parentWidget()); + if (!parentDragDropContainer) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of " + "the dropped zone is not found."); + Q_ASSERT(false); + return; } + + // Simple move of the zone, no variable operation associated + parentDragDropContainer->layout()->removeWidget(zoneWidget); + tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget); +} + +void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables( + const QList > &variables, int index, + VisualizationTabWidget *tabWidget) +{ + // Note: we are sure that there is a single and compatible variable here + // because the AcceptMimeDataFunction, set on the drop container, makes the check before the + // drop can occur. + tabWidget->createZone(variables, index); } diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index 8b2b2eb..6c1a28e 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -3,6 +3,7 @@ #include "Visualization/IVisualizationWidgetVisitor.h" #include "Visualization/QCustomPlotSynchronizer.h" #include "Visualization/VisualizationGraphWidget.h" +#include "Visualization/VisualizationWidget.h" #include "ui_VisualizationZoneWidget.h" #include "Common/MimeTypesDef.h" @@ -10,6 +11,8 @@ #include #include +#include + #include #include #include @@ -67,6 +70,10 @@ struct VisualizationZoneWidget::VisualizationZoneWidgetPrivate { } QUuid m_SynchronisationGroupId; std::unique_ptr m_Synchronizer; + + void dropGraph(int index, VisualizationZoneWidget *zoneWidget); + void dropVariables(const QList > &variables, int index, + VisualizationZoneWidget *zoneWidget); }; VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *parent) @@ -78,7 +85,11 @@ VisualizationZoneWidget::VisualizationZoneWidget(const QString &name, QWidget *p ui->zoneNameLabel->setText(name); - ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH}); + ui->dragDropContainer->setAcceptedMimeTypes({MIME_TYPE_GRAPH, MIME_TYPE_VARIABLE_LIST}); + ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { + return sqpApp->dragDropHelper().checkMimeDataForVisualization(mimeData, + ui->dragDropContainer); + }); connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccured, this, &VisualizationZoneWidget::dropMimeData); @@ -355,57 +366,119 @@ void VisualizationZoneWidget::onVariableAboutToBeRemoved(std::shared_ptrdragDropHelper(); if (mimeData->hasFormat(MIME_TYPE_GRAPH)) { - auto graphWidget = static_cast(helper.getCurrentDragWidget()); - auto parentDragDropContainer - = qobject_cast(graphWidget->parentWidget()); - Q_ASSERT(parentDragDropContainer); - - const auto &variables = graphWidget->variables(); - - if (parentDragDropContainer != ui->dragDropContainer && !variables.isEmpty()) { - // 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(); - if (nbGraph == 1) { - // This is the only graph in the previous zone, close the zone - previousParentZoneWidget->close(); - } - else { - // Close the graph - graphWidget->close(); - } + impl->dropGraph(index, this); + } + else if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { + auto variables = sqpApp->variableController().variablesForMimeData( + mimeData->data(MIME_TYPE_VARIABLE_LIST)); + impl->dropVariables(variables, index, this); + } + else { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationZoneWidget::dropMimeData, unknown MIME data received."); + } +} + +void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropGraph( + int index, VisualizationZoneWidget *zoneWidget) +{ + auto &helper = sqpApp->dragDropHelper(); + + auto graphWidget = qobject_cast(helper.getCurrentDragWidget()); + if (!graphWidget) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationZoneWidget::dropGraph, drop aborted, the dropped graph is not " + "found or invalid."); + Q_ASSERT(false); + return; + } + + auto parentDragDropContainer + = qobject_cast(graphWidget->parentWidget()); + if (!parentDragDropContainer) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationZoneWidget::dropGraph, drop aborted, the parent container of " + "the dropped graph is not found."); + Q_ASSERT(false); + return; + } + + const auto &variables = graphWidget->variables(); - // Creates the new graph in the zone - createGraph(variables, index); + if (parentDragDropContainer != zoneWidget->ui->dragDropContainer && !variables.isEmpty()) { + // 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(); + if (nbGraph == 1) { + // This is the only graph in the previous zone, close the zone + previousParentZoneWidget->close(); } else { - // 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); - - if (variables.isEmpty() && parentDragDropContainer != ui->dragDropContainer) { - // The graph is empty and dropped in a different zone. - // Take the range of the first graph in the zone (if existing). - auto layout = ui->dragDropContainer->layout(); - if (layout->count() > 0) { - if (auto visualizationGraphWidget - = qobject_cast(layout->itemAt(0)->widget())) { - graphWidget->setGraphRange(visualizationGraphWidget->graphRange()); - } + // Close the graph + graphWidget->close(); + } + + // Creates the new graph in the zone + zoneWidget->createGraph(variables, index); + } + else { + // 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); + + if (variables.isEmpty() && parentDragDropContainer != zoneWidget->ui->dragDropContainer) { + // 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(); + if (layout->count() > 0) { + if (auto visualizationGraphWidget + = qobject_cast(layout->itemAt(0)->widget())) { + graphWidget->setGraphRange(visualizationGraphWidget->graphRange()); } } + } + + zoneWidget->ui->dragDropContainer->insertDragWidget(index, graphWidget); + } +} + +void VisualizationZoneWidget::VisualizationZoneWidgetPrivate::dropVariables( + const QList > &variables, int index, + VisualizationZoneWidget *zoneWidget) +{ + // Search for the top level VisualizationWidget + auto parent = zoneWidget->parentWidget(); + while (parent && qobject_cast(parent) == nullptr) { + parent = parent->parentWidget(); + } + + if (!parent) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationZoneWidget::dropVariables, drop aborted, the parent " + "VisualizationWidget cannot be found."); + Q_ASSERT(false); + return; + } + + auto visualizationWidget = static_cast(parent); - ui->dragDropContainer->insertDragWidget(index, graphWidget); + // Search for the first variable which can be dropped + for (auto variable : variables) { + FindVariableOperation findVariableOperation{variable}; + visualizationWidget->accept(&findVariableOperation); + auto variableContainers = findVariableOperation.result(); + if (variableContainers.empty()) { + zoneWidget->createGraph(variable, index); + break; } } }