DragDropHelper.cpp
353 lines
| 11.9 KiB
| text/x-c
|
CppLexer
r840 | #include "DragDropHelper.h" | |||
#include "SqpApplication.h" | ||||
r852 | #include "Visualization/VisualizationDragDropContainer.h" | |||
r847 | #include "Visualization/VisualizationDragWidget.h" | |||
r852 | #include "Visualization/VisualizationWidget.h" | |||
#include "Visualization/operations/FindVariableOperation.h" | ||||
r882 | #include "Variable/Variable.h" | |||
r852 | #include "Variable/VariableController.h" | |||
#include "Common/MimeTypesDef.h" | ||||
#include "Common/VisualizationDef.h" | ||||
r840 | ||||
r847 | #include <QDir> | |||
r840 | #include <QDragEnterEvent> | |||
r847 | #include <QDragMoveEvent> | |||
r840 | #include <QScrollArea> | |||
r847 | #include <QScrollBar> | |||
r840 | #include <QTimer> | |||
r847 | #include <QVBoxLayout> | |||
r840 | ||||
r843 | const int SCROLL_SPEED = 5; | |||
const int SCROLL_ZONE_SIZE = 50; | ||||
r852 | Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper") | |||
r843 | struct DragDropScroller::DragDropScrollerPrivate { | |||
r847 | QList<QScrollArea *> m_ScrollAreas; | |||
QScrollArea *m_CurrentScrollArea = nullptr; | ||||
std::unique_ptr<QTimer> m_Timer = nullptr; | ||||
r843 | ||||
r847 | enum class ScrollDirection { up, down, unknown }; | |||
ScrollDirection m_Direction = ScrollDirection::unknown; | ||||
r843 | ||||
r847 | explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()} | |||
r843 | { | |||
r847 | m_Timer->setInterval(0); | |||
r843 | } | |||
}; | ||||
r847 | DragDropScroller::DragDropScroller(QObject *parent) | |||
: QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()} | ||||
r843 | { | |||
r847 | connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer); | |||
r843 | } | |||
r847 | void DragDropScroller::addScrollArea(QScrollArea *scrollArea) | |||
r843 | { | |||
r847 | impl->m_ScrollAreas << scrollArea; | |||
r843 | scrollArea->viewport()->setAcceptDrops(true); | |||
} | ||||
r847 | void DragDropScroller::removeScrollArea(QScrollArea *scrollArea) | |||
r843 | { | |||
r847 | impl->m_ScrollAreas.removeAll(scrollArea); | |||
r843 | scrollArea->viewport()->setAcceptDrops(false); | |||
} | ||||
bool DragDropScroller::eventFilter(QObject *obj, QEvent *event) | ||||
{ | ||||
r847 | if (event->type() == QEvent::DragMove) { | |||
auto w = static_cast<QWidget *>(obj); | ||||
r843 | ||||
r847 | if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) { | |||
auto moveEvent = static_cast<QDragMoveEvent *>(event); | ||||
r843 | ||||
auto pos = moveEvent->pos(); | ||||
r847 | if (impl->m_CurrentScrollArea->viewport() != w) { | |||
r843 | auto globalPos = w->mapToGlobal(moveEvent->pos()); | |||
r847 | pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos); | |||
r843 | } | |||
r847 | auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height() | |||
- SCROLL_ZONE_SIZE; | ||||
r843 | auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE; | |||
r847 | if (!isInTopZone && !isInBottomZone) { | |||
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown; | ||||
impl->m_Timer->stop(); | ||||
r843 | } | |||
r847 | else if (!impl->m_Timer->isActive()) { | |||
impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up | ||||
: DragDropScrollerPrivate::ScrollDirection::down; | ||||
impl->m_Timer->start(); | ||||
r843 | } | |||
} | ||||
} | ||||
r847 | else if (event->type() == QEvent::DragEnter) { | |||
auto w = static_cast<QWidget *>(obj); | ||||
r843 | ||||
r847 | for (auto scrollArea : impl->m_ScrollAreas) { | |||
if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) { | ||||
auto enterEvent = static_cast<QDragEnterEvent *>(event); | ||||
r843 | enterEvent->acceptProposedAction(); | |||
enterEvent->setDropAction(Qt::IgnoreAction); | ||||
r847 | impl->m_CurrentScrollArea = scrollArea; | |||
r843 | break; | |||
} | ||||
} | ||||
} | ||||
r847 | else if (event->type() == QEvent::DragLeave) { | |||
if (impl->m_CurrentScrollArea) { | ||||
if (!QRect(QPoint(), impl->m_CurrentScrollArea->size()) | ||||
.contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) { | ||||
impl->m_CurrentScrollArea = nullptr; | ||||
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown; | ||||
impl->m_Timer->stop(); | ||||
r843 | } | |||
} | ||||
} | ||||
r847 | else if (event->type() == QEvent::Drop) { | |||
if (impl->m_CurrentScrollArea) { | ||||
impl->m_CurrentScrollArea = nullptr; | ||||
impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown; | ||||
impl->m_Timer->stop(); | ||||
r843 | } | |||
} | ||||
return false; | ||||
} | ||||
void DragDropScroller::onTimer() | ||||
{ | ||||
r847 | if (impl->m_CurrentScrollArea) { | |||
r843 | auto mvt = 0; | |||
r847 | switch (impl->m_Direction) { | |||
case DragDropScrollerPrivate::ScrollDirection::up: | ||||
mvt = SCROLL_SPEED; | ||||
break; | ||||
case DragDropScrollerPrivate::ScrollDirection::down: | ||||
mvt = -SCROLL_SPEED; | ||||
break; | ||||
default: | ||||
break; | ||||
r843 | } | |||
r847 | impl->m_CurrentScrollArea->verticalScrollBar()->setValue( | |||
impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt); | ||||
r843 | } | |||
} | ||||
r840 | ||||
struct DragDropHelper::DragDropHelperPrivate { | ||||
r847 | VisualizationDragWidget *m_CurrentDragWidget = nullptr; | |||
std::unique_ptr<QWidget> m_PlaceHolder = nullptr; | ||||
std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr; | ||||
QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using | ||||
// QTemporaryFile to have a name which is not generated. | ||||
r840 | ||||
r879 | VisualizationDragWidget *m_HighlightedDragWidget = nullptr; | |||
r880 | QMetaObject::Connection m_DragWidgetDestroyedConnection; | |||
QMetaObject::Connection m_HighlightedWidgetDestroyedConnection; | ||||
r840 | explicit DragDropHelperPrivate() | |||
r847 | : m_PlaceHolder{std::make_unique<QWidget>()}, | |||
m_DragDropScroller{std::make_unique<DragDropScroller>()} | ||||
r840 | { | |||
r847 | m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4"); | |||
sqpApp->installEventFilter(m_DragDropScroller.get()); | ||||
r840 | ||||
r882 | m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png"); | |||
r840 | } | |||
void preparePlaceHolder() const | ||||
{ | ||||
r847 | if (m_CurrentDragWidget) { | |||
m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size()); | ||||
m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy()); | ||||
r840 | } | |||
r847 | else { | |||
r852 | // Configuration of the placeHolder when there is no dragWidget | |||
// (for instance with a drag from a variable) | ||||
r853 | m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT); | |||
r852 | m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | |||
r840 | } | |||
} | ||||
}; | ||||
r848 | DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()} | |||
{ | ||||
} | ||||
r840 | ||||
DragDropHelper::~DragDropHelper() | ||||
{ | ||||
r847 | QFile::remove(impl->m_ImageTempUrl); | |||
r840 | } | |||
r852 | void DragDropHelper::resetDragAndDrop() | |||
{ | ||||
setCurrentDragWidget(nullptr); | ||||
r879 | impl->m_HighlightedDragWidget = nullptr; | |||
r852 | } | |||
r840 | void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget) | |||
{ | ||||
r880 | if (impl->m_CurrentDragWidget) { | |||
QObject::disconnect(impl->m_DragWidgetDestroyedConnection); | ||||
} | ||||
if (dragWidget) { | ||||
// ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed | ||||
impl->m_DragWidgetDestroyedConnection | ||||
= QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, | ||||
[this]() { impl->m_CurrentDragWidget = nullptr; }); | ||||
} | ||||
r847 | impl->m_CurrentDragWidget = dragWidget; | |||
r840 | } | |||
VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const | ||||
{ | ||||
r847 | return impl->m_CurrentDragWidget; | |||
r840 | } | |||
r847 | QWidget &DragDropHelper::placeHolder() const | |||
r840 | { | |||
r847 | return *impl->m_PlaceHolder; | |||
r840 | } | |||
void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index) | ||||
{ | ||||
removePlaceHolder(); | ||||
impl->preparePlaceHolder(); | ||||
r847 | layout->insertWidget(index, impl->m_PlaceHolder.get()); | |||
impl->m_PlaceHolder->show(); | ||||
r840 | } | |||
void DragDropHelper::removePlaceHolder() | ||||
{ | ||||
r847 | auto parentWidget = impl->m_PlaceHolder->parentWidget(); | |||
if (parentWidget) { | ||||
parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get()); | ||||
impl->m_PlaceHolder->setParent(nullptr); | ||||
impl->m_PlaceHolder->hide(); | ||||
r840 | } | |||
} | ||||
bool DragDropHelper::isPlaceHolderSet() const | ||||
{ | ||||
r847 | return impl->m_PlaceHolder->parentWidget(); | |||
r840 | } | |||
r843 | void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea) | |||
{ | ||||
r847 | impl->m_DragDropScroller->addScrollArea(scrollArea); | |||
r843 | } | |||
void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea) | ||||
{ | ||||
r847 | impl->m_DragDropScroller->removeScrollArea(scrollArea); | |||
r843 | } | |||
r847 | QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const | |||
r840 | { | |||
r847 | image.save(impl->m_ImageTempUrl); | |||
return QUrl::fromLocalFile(impl->m_ImageTempUrl); | ||||
r840 | } | |||
r852 | ||||
r879 | void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget) | |||
{ | ||||
if (impl->m_HighlightedDragWidget) { | ||||
impl->m_HighlightedDragWidget->highlightForMerge(false); | ||||
r880 | QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection); | |||
r879 | } | |||
if (dragWidget) { | ||||
r880 | dragWidget->highlightForMerge(true); | |||
// ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed | ||||
impl->m_DragWidgetDestroyedConnection | ||||
= QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, | ||||
[this]() { impl->m_HighlightedDragWidget = nullptr; }); | ||||
r879 | } | |||
r880 | ||||
impl->m_HighlightedDragWidget = dragWidget; | ||||
r879 | } | |||
r881 | VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const | |||
{ | ||||
return impl->m_HighlightedDragWidget; | ||||
} | ||||
r852 | bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData, | |||
VisualizationDragDropContainer *dropContainer) | ||||
{ | ||||
r876 | if (!mimeData || !dropContainer) { | |||
qCWarning(LOG_DragDropHelper()) << QObject::tr( | ||||
"DragDropHelper::checkMimeDataForVisualization, invalid input parameters."); | ||||
Q_ASSERT(false); | ||||
r882 | return false; | |||
r876 | } | |||
r882 | auto result = false; | |||
r852 | ||||
if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { | ||||
auto variables = sqpApp->variableController().variablesForMimeData( | ||||
mimeData->data(MIME_TYPE_VARIABLE_LIST)); | ||||
if (variables.count() == 1) { | ||||
r882 | auto variable = variables.first(); | |||
if (variable->dataSeries() != nullptr) { | ||||
// Check that the variable is not already in a graph | ||||
r852 | ||||
r882 | auto parent = dropContainer->parentWidget(); | |||
while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) { | ||||
parent = parent->parentWidget(); // Search for the top level VisualizationWidget | ||||
} | ||||
r852 | ||||
r882 | if (parent) { | |||
auto visualizationWidget = static_cast<VisualizationWidget *>(parent); | ||||
FindVariableOperation findVariableOperation{variable}; | ||||
visualizationWidget->accept(&findVariableOperation); | ||||
auto variableContainers = findVariableOperation.result(); | ||||
if (variableContainers.empty()) { | ||||
result = true; | ||||
} | ||||
else { | ||||
// result = false: the variable already exist in the visualisation | ||||
} | ||||
} | ||||
else { | ||||
qCWarning(LOG_DragDropHelper()) << QObject::tr( | ||||
"DragDropHelper::checkMimeDataForVisualization, the parent " | ||||
"VisualizationWidget cannot be found. Cannot check if the variable is " | ||||
"already used or not."); | ||||
r852 | } | |||
} | ||||
else { | ||||
r882 | // result = false: the variable is not fully loaded | |||
r852 | } | |||
} | ||||
else { | ||||
r882 | // result = false: cannot drop multiple variables in the visualisation | |||
r852 | } | |||
} | ||||
r882 | else { | |||
// Other MIME data | ||||
// no special rules, accepted by default | ||||
result = true; | ||||
} | ||||
r852 | ||||
return result; | ||||
} | ||||