VisualizationTabWidget.cpp
415 lines
| 13.0 KiB
| text/x-c
|
CppLexer
r95 | #include "Visualization/VisualizationTabWidget.h" | |||
Alexandre Leroux
|
r207 | #include "Visualization/IVisualizationWidgetVisitor.h" | ||
r58 | #include "ui_VisualizationTabWidget.h" | |||
r839 | #include "Visualization/VisualizationGraphWidget.h" | |||
r844 | #include "Visualization/VisualizationZoneWidget.h" | |||
r839 | ||||
Thibaud Rabillard
|
r930 | #include "Visualization/MacScrollBarStyle.h" | ||
r1287 | #include "DataSource/DataSourceController.h" | |||
r1348 | #include "Variable/VariableController2.h" | |||
r841 | ||||
r848 | #include "Common/MimeTypesDef.h" | |||
r1075 | #include "DragAndDrop/DragDropGuiController.h" | |||
r844 | #include "SqpApplication.h" | |||
r118 | ||||
Alexandre Leroux
|
r219 | Q_LOGGING_CATEGORY(LOG_VisualizationTabWidget, "VisualizationTabWidget") | ||
r1420 | namespace | |||
{ | ||||
Alexandre Leroux
|
r201 | |||
Alexandre Leroux
|
r738 | /** | ||
* Applies a function to all zones of the tab represented by its layout | ||||
* @param layout the layout that contains zones | ||||
* @param fun the function to apply to each zone | ||||
*/ | ||||
template <typename Fun> | ||||
r1420 | void processZones(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 visualizationZoneWidget | ||
r1420 | = qobject_cast<VisualizationZoneWidget*>(item->widget())) | |||
{ | ||||
Alexandre Leroux
|
r738 | fun(*visualizationZoneWidget); | ||
} | ||||
} | ||||
} | ||||
} | ||||
r1137 | /// Generates a default name for a new zone, according to the number of zones already displayed in | |||
/// the tab | ||||
r1420 | QString defaultZoneName(QLayout& layout) | |||
r1137 | { | |||
QSet<QString> existingNames; | ||||
r1420 | processZones( | |||
layout, [&existingNames](auto& zoneWidget) { existingNames.insert(zoneWidget.name()); }); | ||||
r1137 | ||||
int zoneNum = 1; | ||||
QString name; | ||||
r1420 | do | |||
{ | ||||
r1137 | name = QObject::tr("Zone ").append(QString::number(zoneNum)); | |||
++zoneNum; | ||||
} while (existingNames.contains(name)); | ||||
return name; | ||||
} | ||||
Alexandre Leroux
|
r201 | } // namespace | ||
r1420 | struct VisualizationTabWidget::VisualizationTabWidgetPrivate | |||
{ | ||||
explicit VisualizationTabWidgetPrivate(const QString& name) : m_Name { name } {} | ||||
r118 | ||||
Alexandre Leroux
|
r198 | QString m_Name; | ||
r850 | ||||
Thibaud Rabillard
|
r930 | #ifdef Q_OS_MAC | ||
std::unique_ptr<MacScrollBarStyle> m_MacScrollBarStyle = std::make_unique<MacScrollBarStyle>(); | ||||
#endif | ||||
r1420 | void dropGraph(int index, VisualizationTabWidget* tabWidget); | |||
void dropZone(int index, VisualizationTabWidget* tabWidget); | ||||
void dropVariables(const std::vector<std::shared_ptr<Variable2>>& variables, int index, | ||||
VisualizationTabWidget* tabWidget); | ||||
void dropProducts( | ||||
const QVariantList& productsMetaData, int index, VisualizationTabWidget* tabWidget); | ||||
Alexandre Leroux
|
r198 | }; | ||
r1420 | VisualizationTabWidget::VisualizationTabWidget(const QString& name, QWidget* parent) | |||
: QWidget { parent } | ||||
, ui { new Ui::VisualizationTabWidget } | ||||
, impl { spimpl::make_unique_impl<VisualizationTabWidgetPrivate>(name) } | ||||
r58 | { | |||
ui->setupUi(this); | ||||
Alexandre Leroux
|
r267 | |||
Thibaud Rabillard
|
r930 | #ifdef Q_OS_MAC | ||
impl->m_MacScrollBarStyle->selfInstallOn(ui->scrollArea, true); | ||||
#endif | ||||
r1075 | ui->dragDropContainer->setPlaceHolderType(DragDropGuiController::PlaceHolderType::Zone, "Zone"); | |||
r937 | ui->dragDropContainer->layout()->setContentsMargins(0, 0, 0, 12); | |||
ui->dragDropContainer->layout()->setSpacing(0); | ||||
r1420 | ui->dragDropContainer->setMimeType( | |||
MIME_TYPE_GRAPH, VisualizationDragDropContainer::DropBehavior::Inserted); | ||||
ui->dragDropContainer->setMimeType( | ||||
MIME_TYPE_ZONE, VisualizationDragDropContainer::DropBehavior::Inserted); | ||||
ui->dragDropContainer->setMimeType( | ||||
MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); | ||||
ui->dragDropContainer->setMimeType( | ||||
MIME_TYPE_PRODUCT_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); | ||||
r879 | ||||
r850 | ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { | |||
r1420 | return sqpApp->dragDropGuiController().checkMimeDataForVisualization( | |||
mimeData, ui->dragDropContainer); | ||||
r850 | }); | |||
r879 | ||||
connect(ui->dragDropContainer, &VisualizationDragDropContainer::dropOccuredInContainer, this, | ||||
r1420 | &VisualizationTabWidget::dropMimeData); | |||
r879 | ||||
r1075 | sqpApp->dragDropGuiController().addDragDropScrollArea(ui->scrollArea); | |||
r839 | ||||
Alexandre Leroux
|
r267 | // Widget is deleted when closed | ||
setAttribute(Qt::WA_DeleteOnClose); | ||||
r58 | } | |||
VisualizationTabWidget::~VisualizationTabWidget() | ||||
{ | ||||
r1075 | sqpApp->dragDropGuiController().removeDragDropScrollArea(ui->scrollArea); | |||
r58 | delete ui; | |||
} | ||||
r118 | ||||
r1420 | void VisualizationTabWidget::addZone(VisualizationZoneWidget* zoneWidget) | |||
r118 | { | |||
r839 | ui->dragDropContainer->addDragWidget(zoneWidget); | |||
} | ||||
r1420 | void VisualizationTabWidget::insertZone(int index, VisualizationZoneWidget* zoneWidget) | |||
r839 | { | |||
r844 | ui->dragDropContainer->insertDragWidget(index, zoneWidget); | |||
r118 | } | |||
r1136 | QStringList VisualizationTabWidget::availableZoneWidgets() const | |||
{ | ||||
QStringList zones; | ||||
r1420 | processZones( | |||
tabLayout(), [&zones](VisualizationZoneWidget& zoneWidget) { zones << zoneWidget.name(); }); | ||||
r1136 | ||||
return zones; | ||||
} | ||||
r1420 | VisualizationZoneWidget* VisualizationTabWidget::getZoneWithName(const QString& zoneName) | |||
r1138 | { | |||
r1420 | VisualizationZoneWidget* result = nullptr; | |||
processZones(tabLayout(), [&zoneName, &result](VisualizationZoneWidget& zoneWidget) { | ||||
if (!result && zoneWidget.name() == zoneName) | ||||
{ | ||||
r1138 | result = &zoneWidget; | |||
} | ||||
}); | ||||
return result; | ||||
} | ||||
r1420 | VisualizationZoneWidget* VisualizationTabWidget::createZone(std::shared_ptr<Variable2> variable) | |||
r118 | { | |||
r1420 | return createZone({ variable }, -1); | |||
r839 | } | |||
r1420 | VisualizationZoneWidget* VisualizationTabWidget::createZone( | |||
const std::vector<std::shared_ptr<Variable2>>& variables, int index) | ||||
r839 | { | |||
r841 | auto zoneWidget = createEmptyZone(index); | |||
r118 | ||||
Alexandre Leroux
|
r201 | // Creates a new graph into the zone | ||
r839 | zoneWidget->createGraph(variables, index); | |||
Alexandre Leroux
|
r201 | |||
r118 | return zoneWidget; | |||
} | ||||
r1420 | VisualizationZoneWidget* VisualizationTabWidget::createEmptyZone(int index) | |||
r841 | { | |||
r844 | auto zoneWidget | |||
r1420 | = new VisualizationZoneWidget { defaultZoneName(*ui->dragDropContainer->layout()), this }; | |||
r841 | this->insertZone(index, zoneWidget); | |||
return zoneWidget; | ||||
} | ||||
r1420 | void VisualizationTabWidget::accept(IVisualizationWidgetVisitor* visitor) | |||
r118 | { | |||
r1420 | if (visitor) | |||
{ | ||||
Alexandre Leroux
|
r208 | visitor->visitEnter(this); | ||
Alexandre Leroux
|
r738 | // Apply visitor to zone children: widgets different from zones are not visited (no action) | ||
r1420 | processZones(tabLayout(), | |||
[visitor](VisualizationZoneWidget& zoneWidget) { zoneWidget.accept(visitor); }); | ||||
Alexandre Leroux
|
r208 | |||
visitor->visitLeave(this); | ||||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r219 | qCCritical(LOG_VisualizationTabWidget()) << tr("Can't visit widget : the visitor is null"); | ||
} | ||||
r118 | } | |||
r1420 | bool VisualizationTabWidget::canDrop(Variable2& variable) const | |||
Alexandre Leroux
|
r209 | { | ||
// A tab can always accomodate a variable | ||||
Q_UNUSED(variable); | ||||
return true; | ||||
} | ||||
r1420 | bool VisualizationTabWidget::contains(Variable2& variable) const | |||
Alexandre Leroux
|
r327 | { | ||
Q_UNUSED(variable); | ||||
return false; | ||||
} | ||||
r119 | QString VisualizationTabWidget::name() const | |||
r118 | { | |||
Alexandre Leroux
|
r198 | return impl->m_Name; | ||
r118 | } | |||
Alexandre Leroux
|
r307 | |||
r1420 | void VisualizationTabWidget::closeEvent(QCloseEvent* event) | |||
Alexandre Leroux
|
r738 | { | ||
// Closes zones in the tab | ||||
r1420 | processZones(tabLayout(), [](VisualizationZoneWidget& zoneWidget) { zoneWidget.close(); }); | |||
Alexandre Leroux
|
r738 | |||
QWidget::closeEvent(event); | ||||
} | ||||
r1420 | QLayout& VisualizationTabWidget::tabLayout() const noexcept | |||
Alexandre Leroux
|
r307 | { | ||
r839 | return *ui->dragDropContainer->layout(); | |||
} | ||||
r1420 | void VisualizationTabWidget::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_ZONE)) | |||
{ | ||||
r850 | impl->dropZone(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 productsData = sqpApp->dataSourceController().productsDataForMimeData( | |||
mimeData->data(MIME_TYPE_PRODUCT_LIST)); | ||||
impl->dropProducts(productsData, index, this); | ||||
} | ||||
r1420 | else | |||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropMimeData, unknown MIME data received."); | ||||
} | ||||
} | ||||
void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropGraph( | ||||
r1420 | int index, VisualizationTabWidget* tabWidget) | |||
r850 | { | |||
r1420 | auto& helper = sqpApp->dragDropGuiController(); | |||
r850 | ||||
r1420 | auto graphWidget = qobject_cast<VisualizationGraphWidget*>(helper.getCurrentDragWidget()); | |||
if (!graphWidget) | ||||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::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("VisualizationTabWidget::dropGraph, drop aborted, the parent container of " | ||||
"the dropped graph is not found."); | ||||
Q_ASSERT(false); | ||||
return; | ||||
} | ||||
auto nbGraph = parentDragDropContainer->countDragWidget(); | ||||
r841 | ||||
r1420 | const auto& variables = graphWidget->variables(); | |||
r850 | ||||
r1420 | if (!variables.empty()) | |||
{ | ||||
r850 | // 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); | ||||
//} | ||||
r1420 | if (nbGraph == 1) | |||
{ | ||||
r850 | // This is the only graph in the previous zone, close the zone | |||
Thibaud Rabillard
|
r911 | helper.delayedCloseWidget(graphWidget->parentZoneWidget()); | ||
r839 | } | |||
r1420 | else | |||
{ | ||||
r850 | // Close the graph | |||
Thibaud Rabillard
|
r911 | helper.delayedCloseWidget(graphWidget); | ||
r850 | } | |||
r839 | ||||
r1048 | auto zoneWidget = tabWidget->createZone(variables, index); | |||
auto firstGraph = zoneWidget->firstGraph(); | ||||
r1420 | if (firstGraph) | |||
{ | ||||
r1048 | firstGraph->addSelectionZones(graphWidget->selectionZoneRanges()); | |||
} | ||||
r1420 | else | |||
{ | ||||
r1048 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropGraph, no graph added in the widget."); | ||||
Q_ASSERT(false); | ||||
} | ||||
r850 | } | |||
r1420 | else | |||
{ | ||||
r850 | // The graph is empty, create an empty zone and move the graph inside | |||
r841 | ||||
r850 | auto parentZoneWidget = graphWidget->parentZoneWidget(); | |||
r841 | ||||
r850 | parentDragDropContainer->layout()->removeWidget(graphWidget); | |||
r841 | ||||
r850 | auto zoneWidget = tabWidget->createEmptyZone(index); | |||
zoneWidget->addGraph(graphWidget); | ||||
// Close the old zone if it was the only graph inside | ||||
r1420 | if (nbGraph == 1) | |||
{ | ||||
Thibaud Rabillard
|
r911 | helper.delayedCloseWidget(parentZoneWidget); | ||
r841 | } | |||
r839 | } | |||
r850 | } | |||
r839 | ||||
r850 | void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropZone( | |||
r1420 | int index, VisualizationTabWidget* tabWidget) | |||
r850 | { | |||
r1420 | auto& helper = sqpApp->dragDropGuiController(); | |||
r850 | ||||
r1420 | auto zoneWidget = qobject_cast<VisualizationZoneWidget*>(helper.getCurrentDragWidget()); | |||
if (!zoneWidget) | ||||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropZone, drop aborted, the dropped zone is not " | ||||
"found or invalid."); | ||||
Q_ASSERT(false); | ||||
return; | ||||
} | ||||
auto parentDragDropContainer | ||||
r1420 | = qobject_cast<VisualizationDragDropContainer*>(zoneWidget->parentWidget()); | |||
if (!parentDragDropContainer) | ||||
{ | ||||
r850 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropZone, drop aborted, the parent container of " | ||||
"the dropped zone is not found."); | ||||
Q_ASSERT(false); | ||||
return; | ||||
r839 | } | |||
r850 | ||||
// Simple move of the zone, no variable operation associated | ||||
parentDragDropContainer->layout()->removeWidget(zoneWidget); | ||||
tabWidget->ui->dragDropContainer->insertDragWidget(index, zoneWidget); | ||||
} | ||||
void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables( | ||||
r1420 | const std::vector<std::shared_ptr<Variable2>>& variables, int index, | |||
VisualizationTabWidget* tabWidget) | ||||
r850 | { | |||
r868 | // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and | |||
// compatible variable here | ||||
r1420 | if (variables.size() > 1) | |||
{ | ||||
r868 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropVariables, dropping multiple variables, operation " | ||||
"aborted."); | ||||
return; | ||||
r870 | } | |||
r868 | ||||
r850 | tabWidget->createZone(variables, index); | |||
Alexandre Leroux
|
r307 | } | ||
r1287 | ||||
void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropProducts( | ||||
r1420 | const QVariantList& productsMetaData, int index, VisualizationTabWidget* tabWidget) | |||
r1287 | { | |||
// Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and | ||||
// compatible variable here | ||||
r1420 | if (productsMetaData.count() != 1) | |||
{ | ||||
r1287 | qCWarning(LOG_VisualizationZoneWidget()) | |||
<< tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " | ||||
"aborted."); | ||||
return; | ||||
} | ||||
r1420 | auto context = new QObject { tabWidget }; | |||
r1348 | connect(&sqpApp->variableController(), &VariableController2::variableAdded, context, | |||
r1420 | [this, index, tabWidget, context](auto variable) { | |||
tabWidget->createZone({ variable }, index); | ||||
delete context; // removes the connection | ||||
}, | ||||
Qt::QueuedConnection); | ||||
r1288 | ||||
r1287 | auto productData = productsMetaData.first().toHash(); | |||
QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", | ||||
r1420 | Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); | |||
r1287 | } | |||