##// END OF EJS Templates
Improves reliability
trabillard -
r880:7c654133459a
parent child
Show More
@@ -1,192 +1,192
1 #include "DataSource/DataSourceController.h"
1 #include "DataSource/DataSourceController.h"
2 #include "DataSource/DataSourceItem.h"
2 #include "DataSource/DataSourceItem.h"
3
3
4 #include <Data/IDataProvider.h>
4 #include <Data/IDataProvider.h>
5
5
6 #include <QMutex>
6 #include <QMutex>
7 #include <QThread>
7 #include <QThread>
8
8
9 #include <QDataStream>
9 #include <QDataStream>
10 #include <QDir>
10 #include <QDir>
11 #include <QStandardPaths>
11 #include <QStandardPaths>
12
12
13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
13 Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
14
14
15 namespace {
15 namespace {
16
16
17 /**
17 /**
18 * Builds the metadata of the variable that will be generated from the loading of an item
18 * Builds the metadata of the variable that will be generated from the loading of an item
19 * @param dataSourceItem the data source item from which to generate the metadata
19 * @param dataSourceItem the data source item from which to generate the metadata
20 * @return the metadata of the variable
20 * @return the metadata of the variable
21 */
21 */
22 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
22 QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
23 {
23 {
24 // Variable metadata contains...
24 // Variable metadata contains...
25
25
26 // ... all metadata of the item
26 // ... all metadata of the item
27 auto result = dataSourceItem.data();
27 auto result = dataSourceItem.data();
28
28
29 // ... and the name of the plugin, recovered from root item
29 // ... and the name of the plugin, recovered from root item
30 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
30 result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
31
31
32 return result;
32 return result;
33 }
33 }
34
34
35 } // namespace
35 } // namespace
36
36
37 class DataSourceController::DataSourceControllerPrivate {
37 class DataSourceController::DataSourceControllerPrivate {
38 public:
38 public:
39 QMutex m_WorkingMutex;
39 QMutex m_WorkingMutex;
40 /// Data sources registered
40 /// Data sources registered
41 QHash<QUuid, QString> m_DataSources;
41 QHash<QUuid, QString> m_DataSources;
42 /// Data sources structures
42 /// Data sources structures
43 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
43 std::map<QUuid, std::unique_ptr<DataSourceItem> > m_DataSourceItems;
44 /// Data providers registered
44 /// Data providers registered
45 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
45 /// @remarks Data providers are stored as shared_ptr as they can be sent to a variable and
46 /// continue to live without necessarily the data source controller
46 /// continue to live without necessarily the data source controller
47 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
47 std::map<QUuid, std::shared_ptr<IDataProvider> > m_DataProviders;
48
48
49 // Search for the first datasource item matching the specified data
49 // Search for the first datasource item matching the specified data
50 DataSourceItem *findDataSourceItem(const QVariantHash &data)
50 DataSourceItem *findDataSourceItem(const QVariantHash &data)
51 {
51 {
52 DataSourceItem *sourceItem = nullptr;
52 DataSourceItem *sourceItem = nullptr;
53 for (const auto &item : m_DataSourceItems) {
53 for (const auto &item : m_DataSourceItems) {
54 sourceItem = item.second->findItem(data, true);
54 sourceItem = item.second->findItem(data, true);
55 if (sourceItem) {
55 if (sourceItem) {
56 break;
56 break;
57 }
57 }
58 }
58 }
59
59
60 return sourceItem;
60 return sourceItem;
61 }
61 }
62 };
62 };
63
63
64 DataSourceController::DataSourceController(QObject *parent)
64 DataSourceController::DataSourceController(QObject *parent)
65 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
65 : impl{spimpl::make_unique_impl<DataSourceControllerPrivate>()}
66 {
66 {
67 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
67 qCDebug(LOG_DataSourceController()) << tr("DataSourceController construction")
68 << QThread::currentThread();
68 << QThread::currentThread();
69 }
69 }
70
70
71 DataSourceController::~DataSourceController()
71 DataSourceController::~DataSourceController()
72 {
72 {
73 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
73 qCDebug(LOG_DataSourceController()) << tr("DataSourceController destruction")
74 << QThread::currentThread();
74 << QThread::currentThread();
75 this->waitForFinish();
75 this->waitForFinish();
76 }
76 }
77
77
78 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
78 QUuid DataSourceController::registerDataSource(const QString &dataSourceName) noexcept
79 {
79 {
80 auto dataSourceUid = QUuid::createUuid();
80 auto dataSourceUid = QUuid::createUuid();
81 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
81 impl->m_DataSources.insert(dataSourceUid, dataSourceName);
82
82
83 return dataSourceUid;
83 return dataSourceUid;
84 }
84 }
85
85
86 void DataSourceController::setDataSourceItem(
86 void DataSourceController::setDataSourceItem(
87 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
87 const QUuid &dataSourceUid, std::unique_ptr<DataSourceItem> dataSourceItem) noexcept
88 {
88 {
89 if (!dataSourceItem) {
89 if (!dataSourceItem) {
90 qCWarning(LOG_DataSourceController())
90 qCWarning(LOG_DataSourceController())
91 << tr("Data source item can't be registered (null item)");
91 << tr("Data source item can't be registered (null item)");
92 return;
92 return;
93 }
93 }
94
94
95 if (impl->m_DataSources.contains(dataSourceUid)) {
95 if (impl->m_DataSources.contains(dataSourceUid)) {
96 // The data provider is implicitly converted to a shared_ptr
96 // The data provider is implicitly converted to a shared_ptr
97 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
97 impl->m_DataSourceItems.insert(std::make_pair(dataSourceUid, std::move(dataSourceItem)));
98
98
99 // Retrieves the data source item to emit the signal with it
99 // Retrieves the data source item to emit the signal with it
100 auto it = impl->m_DataSourceItems.find(dataSourceUid);
100 auto it = impl->m_DataSourceItems.find(dataSourceUid);
101 if (it != impl->m_DataSourceItems.end()) {
101 if (it != impl->m_DataSourceItems.end()) {
102 emit dataSourceItemSet(it->second.get());
102 emit dataSourceItemSet(it->second.get());
103 }
103 }
104 }
104 }
105 else {
105 else {
106 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
106 qCWarning(LOG_DataSourceController()) << tr("Can't set data source item for uid %1 : no "
107 "data source has been registered with the uid")
107 "data source has been registered with the uid")
108 .arg(dataSourceUid.toString());
108 .arg(dataSourceUid.toString());
109 }
109 }
110 }
110 }
111
111
112 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
112 void DataSourceController::setDataProvider(const QUuid &dataSourceUid,
113 std::unique_ptr<IDataProvider> dataProvider) noexcept
113 std::unique_ptr<IDataProvider> dataProvider) noexcept
114 {
114 {
115 if (impl->m_DataSources.contains(dataSourceUid)) {
115 if (impl->m_DataSources.contains(dataSourceUid)) {
116 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
116 impl->m_DataProviders.insert(std::make_pair(dataSourceUid, std::move(dataProvider)));
117 }
117 }
118 else {
118 else {
119 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
119 qCWarning(LOG_DataSourceController()) << tr("Can't set data provider for uid %1 : no data "
120 "source has been registered with the uid")
120 "source has been registered with the uid")
121 .arg(dataSourceUid.toString());
121 .arg(dataSourceUid.toString());
122 }
122 }
123 }
123 }
124
124
125 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
125 void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
126 const DataSourceItem &productItem) noexcept
126 const DataSourceItem &productItem) noexcept
127 {
127 {
128 if (productItem.type() == DataSourceItemType::PRODUCT
128 if (productItem.type() == DataSourceItemType::PRODUCT
129 || productItem.type() == DataSourceItemType::COMPONENT) {
129 || productItem.type() == DataSourceItemType::COMPONENT) {
130 /// Retrieves the data provider of the data source (if any)
130 /// Retrieves the data provider of the data source (if any)
131 auto it = impl->m_DataProviders.find(dataSourceUid);
131 auto it = impl->m_DataProviders.find(dataSourceUid);
132 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
132 auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
133
133
134 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
134 emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
135 dataProvider);
135 dataProvider);
136 }
136 }
137 else {
137 else {
138 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
138 qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
139 }
139 }
140 }
140 }
141
141
142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData) const
142 QByteArray DataSourceController::mimeDataForProductsData(const QVariantList &productsData) const
143 {
143 {
144 QByteArray encodedData;
144 QByteArray encodedData;
145 QDataStream stream{&encodedData, QIODevice::WriteOnly};
145 QDataStream stream{&encodedData, QIODevice::WriteOnly};
146
146
147 stream << productsData;
147 stream << productsData;
148
148
149 return encodedData;
149 return encodedData;
150 }
150 }
151
151
152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData) const
152 QVariantList DataSourceController::productsDataForMimeData(const QByteArray &mimeData) const
153 {
153 {
154 QDataStream stream{mimeData};
154 QDataStream stream{mimeData};
155
155
156 QVariantList productList;
156 QVariantList productList;
157 stream >> productList;
157 stream >> productList;
158
158
159 return productList;
159 return productList;
160 }
160 }
161
161
162 void DataSourceController::initialize()
162 void DataSourceController::initialize()
163 {
163 {
164 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
164 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init")
165 << QThread::currentThread();
165 << QThread::currentThread();
166 impl->m_WorkingMutex.lock();
166 impl->m_WorkingMutex.lock();
167 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
167 qCDebug(LOG_DataSourceController()) << tr("DataSourceController init END");
168 }
168 }
169
169
170 void DataSourceController::finalize()
170 void DataSourceController::finalize()
171 {
171 {
172 impl->m_WorkingMutex.unlock();
172 impl->m_WorkingMutex.unlock();
173 }
173 }
174
174
175 void DataSourceController::requestVariable(const QVariantHash &productData)
175 void DataSourceController::requestVariable(const QVariantHash &productData)
176 {
176 {
177 DataSourceItem *sourceItem = impl->findDataSourceItem(productData);
177 auto sourceItem = impl->findDataSourceItem(productData);
178
178
179 if (sourceItem) {
179 if (sourceItem) {
180 auto sourceName = sourceItem->rootItem().name();
180 auto sourceName = sourceItem->rootItem().name();
181 auto sourceId = impl->m_DataSources.key(sourceName);
181 auto sourceId = impl->m_DataSources.key(sourceName);
182 loadProductItem(sourceId, *sourceItem);
182 loadProductItem(sourceId, *sourceItem);
183 }
183 }
184 else {
184 else {
185 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
185 qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found");
186 }
186 }
187 }
187 }
188
188
189 void DataSourceController::waitForFinish()
189 void DataSourceController::waitForFinish()
190 {
190 {
191 QMutexLocker locker{&impl->m_WorkingMutex};
191 QMutexLocker locker{&impl->m_WorkingMutex};
192 }
192 }
@@ -1,163 +1,162
1 #include <DataSource/DataSourceItem.h>
1 #include <DataSource/DataSourceItem.h>
2 #include <DataSource/DataSourceItemAction.h>
2 #include <DataSource/DataSourceItemAction.h>
3
3
4 #include <QVector>
4 #include <QVector>
5
5
6 const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
6 const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
7
7
8 struct DataSourceItem::DataSourceItemPrivate {
8 struct DataSourceItem::DataSourceItemPrivate {
9 explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
9 explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
10 : m_Parent{nullptr}, m_Children{}, m_Type{type}, m_Data{std::move(data)}, m_Actions{}
10 : m_Parent{nullptr}, m_Children{}, m_Type{type}, m_Data{std::move(data)}, m_Actions{}
11 {
11 {
12 }
12 }
13
13
14 DataSourceItem *m_Parent;
14 DataSourceItem *m_Parent;
15 std::vector<std::unique_ptr<DataSourceItem> > m_Children;
15 std::vector<std::unique_ptr<DataSourceItem> > m_Children;
16 DataSourceItemType m_Type;
16 DataSourceItemType m_Type;
17 QVariantHash m_Data;
17 QVariantHash m_Data;
18 std::vector<std::unique_ptr<DataSourceItemAction> > m_Actions;
18 std::vector<std::unique_ptr<DataSourceItemAction> > m_Actions;
19 };
19 };
20
20
21 DataSourceItem::DataSourceItem(DataSourceItemType type, const QString &name)
21 DataSourceItem::DataSourceItem(DataSourceItemType type, const QString &name)
22 : DataSourceItem{type, QVariantHash{{NAME_DATA_KEY, name}}}
22 : DataSourceItem{type, QVariantHash{{NAME_DATA_KEY, name}}}
23 {
23 {
24 }
24 }
25
25
26 DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
26 DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
27 : impl{spimpl::make_unique_impl<DataSourceItemPrivate>(type, std::move(data))}
27 : impl{spimpl::make_unique_impl<DataSourceItemPrivate>(type, std::move(data))}
28 {
28 {
29 }
29 }
30
30
31 QVector<DataSourceItemAction *> DataSourceItem::actions() const noexcept
31 QVector<DataSourceItemAction *> DataSourceItem::actions() const noexcept
32 {
32 {
33 auto result = QVector<DataSourceItemAction *>{};
33 auto result = QVector<DataSourceItemAction *>{};
34
34
35 std::transform(std::cbegin(impl->m_Actions), std::cend(impl->m_Actions),
35 std::transform(std::cbegin(impl->m_Actions), std::cend(impl->m_Actions),
36 std::back_inserter(result), [](const auto &action) { return action.get(); });
36 std::back_inserter(result), [](const auto &action) { return action.get(); });
37
37
38 return result;
38 return result;
39 }
39 }
40
40
41 void DataSourceItem::addAction(std::unique_ptr<DataSourceItemAction> action) noexcept
41 void DataSourceItem::addAction(std::unique_ptr<DataSourceItemAction> action) noexcept
42 {
42 {
43 action->setDataSourceItem(this);
43 action->setDataSourceItem(this);
44 impl->m_Actions.push_back(std::move(action));
44 impl->m_Actions.push_back(std::move(action));
45 }
45 }
46
46
47 void DataSourceItem::appendChild(std::unique_ptr<DataSourceItem> child) noexcept
47 void DataSourceItem::appendChild(std::unique_ptr<DataSourceItem> child) noexcept
48 {
48 {
49 child->impl->m_Parent = this;
49 child->impl->m_Parent = this;
50 impl->m_Children.push_back(std::move(child));
50 impl->m_Children.push_back(std::move(child));
51 }
51 }
52
52
53 DataSourceItem *DataSourceItem::child(int childIndex) const noexcept
53 DataSourceItem *DataSourceItem::child(int childIndex) const noexcept
54 {
54 {
55 if (childIndex < 0 || childIndex >= childCount()) {
55 if (childIndex < 0 || childIndex >= childCount()) {
56 return nullptr;
56 return nullptr;
57 }
57 }
58 else {
58 else {
59 return impl->m_Children.at(childIndex).get();
59 return impl->m_Children.at(childIndex).get();
60 }
60 }
61 }
61 }
62
62
63 int DataSourceItem::childCount() const noexcept
63 int DataSourceItem::childCount() const noexcept
64 {
64 {
65 return impl->m_Children.size();
65 return impl->m_Children.size();
66 }
66 }
67
67
68 QVariant DataSourceItem::data(const QString &key) const noexcept
68 QVariant DataSourceItem::data(const QString &key) const noexcept
69 {
69 {
70 return impl->m_Data.value(key);
70 return impl->m_Data.value(key);
71 }
71 }
72
72
73 QVariantHash DataSourceItem::data() const noexcept
73 QVariantHash DataSourceItem::data() const noexcept
74 {
74 {
75 return impl->m_Data;
75 return impl->m_Data;
76 }
76 }
77
77
78 bool DataSourceItem::isRoot() const noexcept
78 bool DataSourceItem::isRoot() const noexcept
79 {
79 {
80 return impl->m_Parent == nullptr;
80 return impl->m_Parent == nullptr;
81 }
81 }
82
82
83 QString DataSourceItem::name() const noexcept
83 QString DataSourceItem::name() const noexcept
84 {
84 {
85 return data(NAME_DATA_KEY).toString();
85 return data(NAME_DATA_KEY).toString();
86 }
86 }
87
87
88 DataSourceItem *DataSourceItem::parentItem() const noexcept
88 DataSourceItem *DataSourceItem::parentItem() const noexcept
89 {
89 {
90 return impl->m_Parent;
90 return impl->m_Parent;
91 }
91 }
92
92
93 const DataSourceItem &DataSourceItem::rootItem() const noexcept
93 const DataSourceItem &DataSourceItem::rootItem() const noexcept
94 {
94 {
95 return isRoot() ? *this : parentItem()->rootItem();
95 return isRoot() ? *this : parentItem()->rootItem();
96 }
96 }
97
97
98 void DataSourceItem::setData(const QString &key, const QVariant &value, bool append) noexcept
98 void DataSourceItem::setData(const QString &key, const QVariant &value, bool append) noexcept
99 {
99 {
100 auto it = impl->m_Data.constFind(key);
100 auto it = impl->m_Data.constFind(key);
101 if (append && it != impl->m_Data.constEnd()) {
101 if (append && it != impl->m_Data.constEnd()) {
102 // Case of an existing value to which we want to add to the new value
102 // Case of an existing value to which we want to add to the new value
103 if (it->canConvert<QVariantList>()) {
103 if (it->canConvert<QVariantList>()) {
104 auto variantList = it->value<QVariantList>();
104 auto variantList = it->value<QVariantList>();
105 variantList.append(value);
105 variantList.append(value);
106
106
107 impl->m_Data.insert(key, variantList);
107 impl->m_Data.insert(key, variantList);
108 }
108 }
109 else {
109 else {
110 impl->m_Data.insert(key, QVariantList{*it, value});
110 impl->m_Data.insert(key, QVariantList{*it, value});
111 }
111 }
112 }
112 }
113 else {
113 else {
114 // Other cases :
114 // Other cases :
115 // - new value in map OR
115 // - new value in map OR
116 // - replacement of an existing value (not appending)
116 // - replacement of an existing value (not appending)
117 impl->m_Data.insert(key, value);
117 impl->m_Data.insert(key, value);
118 }
118 }
119 }
119 }
120
120
121 DataSourceItemType DataSourceItem::type() const noexcept
121 DataSourceItemType DataSourceItem::type() const noexcept
122 {
122 {
123 return impl->m_Type;
123 return impl->m_Type;
124 }
124 }
125
125
126 DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursive)
126 DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursive)
127 {
127 {
128 for (const auto &child : impl->m_Children) {
128 for (const auto &child : impl->m_Children) {
129 if (child->impl->m_Data == data) {
129 if (child->impl->m_Data == data) {
130 return child.get();
130 return child.get();
131 }
131 }
132
132
133 if (recursive) {
133 if (recursive) {
134 auto foundItem = child->findItem(data, true);
134 if (auto foundItem = child->findItem(data, true)) {
135 if (foundItem) {
136 return foundItem;
135 return foundItem;
137 }
136 }
138 }
137 }
139 }
138 }
140
139
141 return nullptr;
140 return nullptr;
142 }
141 }
143
142
144 bool DataSourceItem::operator==(const DataSourceItem &other)
143 bool DataSourceItem::operator==(const DataSourceItem &other)
145 {
144 {
146 // Compares items' attributes
145 // Compares items' attributes
147 if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
146 if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
148 // Compares contents of items' children
147 // Compares contents of items' children
149 return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
148 return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
150 std::cbegin(other.impl->m_Children),
149 std::cbegin(other.impl->m_Children),
151 [](const auto &itemChild, const auto &otherChild) {
150 [](const auto &itemChild, const auto &otherChild) {
152 return *itemChild == *otherChild;
151 return *itemChild == *otherChild;
153 });
152 });
154 }
153 }
155 else {
154 else {
156 return false;
155 return false;
157 }
156 }
158 }
157 }
159
158
160 bool DataSourceItem::operator!=(const DataSourceItem &other)
159 bool DataSourceItem::operator!=(const DataSourceItem &other)
161 {
160 {
162 return !(*this == other);
161 return !(*this == other);
163 }
162 }
@@ -1,368 +1,368
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2 #include <Variable/VariableController.h>
2 #include <Variable/VariableController.h>
3 #include <Variable/VariableModel.h>
3 #include <Variable/VariableModel.h>
4
4
5 #include <Common/DateUtils.h>
5 #include <Common/DateUtils.h>
6 #include <Common/MimeTypesDef.h>
6 #include <Common/MimeTypesDef.h>
7 #include <Common/StringUtils.h>
7 #include <Common/StringUtils.h>
8
8
9 #include <Data/IDataSeries.h>
9 #include <Data/IDataSeries.h>
10
10
11 #include <QDataStream>
11 #include <QDataStream>
12 #include <QMimeData>
12 #include <QMimeData>
13 #include <QSize>
13 #include <QSize>
14 #include <unordered_map>
14 #include <unordered_map>
15
15
16 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
16 Q_LOGGING_CATEGORY(LOG_VariableModel, "VariableModel")
17
17
18 namespace {
18 namespace {
19
19
20 // Column indexes
20 // Column indexes
21 const auto NAME_COLUMN = 0;
21 const auto NAME_COLUMN = 0;
22 const auto TSTART_COLUMN = 1;
22 const auto TSTART_COLUMN = 1;
23 const auto TEND_COLUMN = 2;
23 const auto TEND_COLUMN = 2;
24 const auto NBPOINTS_COLUMN = 3;
24 const auto NBPOINTS_COLUMN = 3;
25 const auto UNIT_COLUMN = 4;
25 const auto UNIT_COLUMN = 4;
26 const auto MISSION_COLUMN = 5;
26 const auto MISSION_COLUMN = 5;
27 const auto PLUGIN_COLUMN = 6;
27 const auto PLUGIN_COLUMN = 6;
28 const auto NB_COLUMNS = 7;
28 const auto NB_COLUMNS = 7;
29
29
30 // Column properties
30 // Column properties
31 const auto DEFAULT_HEIGHT = 25;
31 const auto DEFAULT_HEIGHT = 25;
32 const auto DEFAULT_WIDTH = 100;
32 const auto DEFAULT_WIDTH = 100;
33
33
34 struct ColumnProperties {
34 struct ColumnProperties {
35 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
35 ColumnProperties(const QString &name = {}, int width = DEFAULT_WIDTH,
36 int height = DEFAULT_HEIGHT)
36 int height = DEFAULT_HEIGHT)
37 : m_Name{name}, m_Width{width}, m_Height{height}
37 : m_Name{name}, m_Width{width}, m_Height{height}
38 {
38 {
39 }
39 }
40
40
41 QString m_Name;
41 QString m_Name;
42 int m_Width;
42 int m_Width;
43 int m_Height;
43 int m_Height;
44 };
44 };
45
45
46 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
46 const auto COLUMN_PROPERTIES = QHash<int, ColumnProperties>{
47 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
47 {NAME_COLUMN, {QObject::tr("Name")}}, {TSTART_COLUMN, {QObject::tr("tStart"), 180}},
48 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
48 {TEND_COLUMN, {QObject::tr("tEnd"), 180}}, {NBPOINTS_COLUMN, {QObject::tr("Nb points")}},
49 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
49 {UNIT_COLUMN, {QObject::tr("Unit")}}, {MISSION_COLUMN, {QObject::tr("Mission")}},
50 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
50 {PLUGIN_COLUMN, {QObject::tr("Plugin")}}};
51
51
52 /// Format for datetimes
52 /// Format for datetimes
53 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
53 const auto DATETIME_FORMAT = QStringLiteral("dd/MM/yyyy \nhh:mm:ss:zzz");
54
54
55 QString uniqueName(const QString &defaultName,
55 QString uniqueName(const QString &defaultName,
56 const std::vector<std::shared_ptr<Variable> > &variables)
56 const std::vector<std::shared_ptr<Variable> > &variables)
57 {
57 {
58 auto forbiddenNames = std::vector<QString>(variables.size());
58 auto forbiddenNames = std::vector<QString>(variables.size());
59 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
59 std::transform(variables.cbegin(), variables.cend(), forbiddenNames.begin(),
60 [](const auto &variable) { return variable->name(); });
60 [](const auto &variable) { return variable->name(); });
61 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
61 auto uniqueName = StringUtils::uniqueName(defaultName, forbiddenNames);
62 Q_ASSERT(!uniqueName.isEmpty());
62 Q_ASSERT(!uniqueName.isEmpty());
63
63
64 return uniqueName;
64 return uniqueName;
65 }
65 }
66
66
67 } // namespace
67 } // namespace
68
68
69 struct VariableModel::VariableModelPrivate {
69 struct VariableModel::VariableModelPrivate {
70 /// Variables created in SciQlop
70 /// Variables created in SciQlop
71 std::vector<std::shared_ptr<Variable> > m_Variables;
71 std::vector<std::shared_ptr<Variable> > m_Variables;
72 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
72 std::unordered_map<std::shared_ptr<Variable>, double> m_VariableToProgress;
73 VariableController *m_VariableController;
73 VariableController *m_VariableController;
74
74
75 /// Return the row index of the variable. -1 if it's not found
75 /// Return the row index of the variable. -1 if it's not found
76 int indexOfVariable(Variable *variable) const noexcept;
76 int indexOfVariable(Variable *variable) const noexcept;
77 };
77 };
78
78
79 VariableModel::VariableModel(VariableController *parent)
79 VariableModel::VariableModel(VariableController *parent)
80 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
80 : QAbstractTableModel{parent}, impl{spimpl::make_unique_impl<VariableModelPrivate>()}
81 {
81 {
82 impl->m_VariableController = parent;
82 impl->m_VariableController = parent;
83 }
83 }
84
84
85 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
85 void VariableModel::addVariable(std::shared_ptr<Variable> variable) noexcept
86 {
86 {
87 auto insertIndex = rowCount();
87 auto insertIndex = rowCount();
88 beginInsertRows({}, insertIndex, insertIndex);
88 beginInsertRows({}, insertIndex, insertIndex);
89
89
90 // Generates unique name for the variable
90 // Generates unique name for the variable
91 variable->setName(uniqueName(variable->name(), impl->m_Variables));
91 variable->setName(uniqueName(variable->name(), impl->m_Variables));
92
92
93 impl->m_Variables.push_back(variable);
93 impl->m_Variables.push_back(variable);
94 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
94 connect(variable.get(), &Variable::updated, this, &VariableModel::onVariableUpdated);
95
95
96 endInsertRows();
96 endInsertRows();
97 }
97 }
98
98
99 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
99 bool VariableModel::containsVariable(std::shared_ptr<Variable> variable) const noexcept
100 {
100 {
101 auto end = impl->m_Variables.cend();
101 auto end = impl->m_Variables.cend();
102 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
102 return std::find(impl->m_Variables.cbegin(), end, variable) != end;
103 }
103 }
104
104
105 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
105 std::shared_ptr<Variable> VariableModel::createVariable(const QString &name,
106 const QVariantHash &metadata) noexcept
106 const QVariantHash &metadata) noexcept
107 {
107 {
108 auto variable = std::make_shared<Variable>(name, metadata);
108 auto variable = std::make_shared<Variable>(name, metadata);
109 addVariable(variable);
109 addVariable(variable);
110
110
111 return variable;
111 return variable;
112 }
112 }
113
113
114 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
114 void VariableModel::deleteVariable(std::shared_ptr<Variable> variable) noexcept
115 {
115 {
116 if (!variable) {
116 if (!variable) {
117 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
117 qCCritical(LOG_Variable()) << "Can't delete a null variable from the model";
118 return;
118 return;
119 }
119 }
120
120
121 // Finds variable in the model
121 // Finds variable in the model
122 auto begin = impl->m_Variables.cbegin();
122 auto begin = impl->m_Variables.cbegin();
123 auto end = impl->m_Variables.cend();
123 auto end = impl->m_Variables.cend();
124 auto it = std::find(begin, end, variable);
124 auto it = std::find(begin, end, variable);
125 if (it != end) {
125 if (it != end) {
126 auto removeIndex = std::distance(begin, it);
126 auto removeIndex = std::distance(begin, it);
127
127
128 // Deletes variable
128 // Deletes variable
129 beginRemoveRows({}, removeIndex, removeIndex);
129 beginRemoveRows({}, removeIndex, removeIndex);
130 impl->m_Variables.erase(it);
130 impl->m_Variables.erase(it);
131 endRemoveRows();
131 endRemoveRows();
132 }
132 }
133 else {
133 else {
134 qCritical(LOG_VariableModel())
134 qCritical(LOG_VariableModel())
135 << tr("Can't delete variable %1 from the model: the variable is not in the model")
135 << tr("Can't delete variable %1 from the model: the variable is not in the model")
136 .arg(variable->name());
136 .arg(variable->name());
137 }
137 }
138
138
139 // Removes variable from progress map
139 // Removes variable from progress map
140 impl->m_VariableToProgress.erase(variable);
140 impl->m_VariableToProgress.erase(variable);
141 }
141 }
142
142
143
143
144 std::shared_ptr<Variable> VariableModel::variable(int index) const
144 std::shared_ptr<Variable> VariableModel::variable(int index) const
145 {
145 {
146 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
146 return (index >= 0 && index < impl->m_Variables.size()) ? impl->m_Variables[index] : nullptr;
147 }
147 }
148
148
149 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
149 std::vector<std::shared_ptr<Variable> > VariableModel::variables() const
150 {
150 {
151 return impl->m_Variables;
151 return impl->m_Variables;
152 }
152 }
153
153
154 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
154 void VariableModel::setDataProgress(std::shared_ptr<Variable> variable, double progress)
155 {
155 {
156 if (progress > 0.0) {
156 if (progress > 0.0) {
157 impl->m_VariableToProgress[variable] = progress;
157 impl->m_VariableToProgress[variable] = progress;
158 }
158 }
159 else {
159 else {
160 impl->m_VariableToProgress.erase(variable);
160 impl->m_VariableToProgress.erase(variable);
161 }
161 }
162 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
162 auto modelIndex = createIndex(impl->indexOfVariable(variable.get()), NAME_COLUMN);
163
163
164 emit dataChanged(modelIndex, modelIndex);
164 emit dataChanged(modelIndex, modelIndex);
165 }
165 }
166
166
167 int VariableModel::columnCount(const QModelIndex &parent) const
167 int VariableModel::columnCount(const QModelIndex &parent) const
168 {
168 {
169 Q_UNUSED(parent);
169 Q_UNUSED(parent);
170
170
171 return NB_COLUMNS;
171 return NB_COLUMNS;
172 }
172 }
173
173
174 int VariableModel::rowCount(const QModelIndex &parent) const
174 int VariableModel::rowCount(const QModelIndex &parent) const
175 {
175 {
176 Q_UNUSED(parent);
176 Q_UNUSED(parent);
177
177
178 return impl->m_Variables.size();
178 return impl->m_Variables.size();
179 }
179 }
180
180
181 QVariant VariableModel::data(const QModelIndex &index, int role) const
181 QVariant VariableModel::data(const QModelIndex &index, int role) const
182 {
182 {
183 if (!index.isValid()) {
183 if (!index.isValid()) {
184 return QVariant{};
184 return QVariant{};
185 }
185 }
186
186
187 if (index.row() < 0 || index.row() >= rowCount()) {
187 if (index.row() < 0 || index.row() >= rowCount()) {
188 return QVariant{};
188 return QVariant{};
189 }
189 }
190
190
191 if (role == Qt::DisplayRole) {
191 if (role == Qt::DisplayRole) {
192 if (auto variable = impl->m_Variables.at(index.row()).get()) {
192 if (auto variable = impl->m_Variables.at(index.row()).get()) {
193 switch (index.column()) {
193 switch (index.column()) {
194 case NAME_COLUMN:
194 case NAME_COLUMN:
195 return variable->name();
195 return variable->name();
196 case TSTART_COLUMN: {
196 case TSTART_COLUMN: {
197 auto range = variable->realRange();
197 auto range = variable->realRange();
198 return range != INVALID_RANGE
198 return range != INVALID_RANGE
199 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
199 ? DateUtils::dateTime(range.m_TStart).toString(DATETIME_FORMAT)
200 : QVariant{};
200 : QVariant{};
201 }
201 }
202 case TEND_COLUMN: {
202 case TEND_COLUMN: {
203 auto range = variable->realRange();
203 auto range = variable->realRange();
204 return range != INVALID_RANGE
204 return range != INVALID_RANGE
205 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
205 ? DateUtils::dateTime(range.m_TEnd).toString(DATETIME_FORMAT)
206 : QVariant{};
206 : QVariant{};
207 }
207 }
208 case NBPOINTS_COLUMN:
208 case NBPOINTS_COLUMN:
209 return variable->nbPoints();
209 return variable->nbPoints();
210 case UNIT_COLUMN:
210 case UNIT_COLUMN:
211 return variable->metadata().value(QStringLiteral("units"));
211 return variable->metadata().value(QStringLiteral("units"));
212 case MISSION_COLUMN:
212 case MISSION_COLUMN:
213 return variable->metadata().value(QStringLiteral("mission"));
213 return variable->metadata().value(QStringLiteral("mission"));
214 case PLUGIN_COLUMN:
214 case PLUGIN_COLUMN:
215 return variable->metadata().value(QStringLiteral("plugin"));
215 return variable->metadata().value(QStringLiteral("plugin"));
216 default:
216 default:
217 // No action
217 // No action
218 break;
218 break;
219 }
219 }
220
220
221 qWarning(LOG_VariableModel())
221 qWarning(LOG_VariableModel())
222 << tr("Can't get data (unknown column %1)").arg(index.column());
222 << tr("Can't get data (unknown column %1)").arg(index.column());
223 }
223 }
224 else {
224 else {
225 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
225 qWarning(LOG_VariableModel()) << tr("Can't get data (no variable)");
226 }
226 }
227 }
227 }
228 else if (role == VariableRoles::ProgressRole) {
228 else if (role == VariableRoles::ProgressRole) {
229 if (auto variable = impl->m_Variables.at(index.row())) {
229 if (auto variable = impl->m_Variables.at(index.row())) {
230
230
231 auto it = impl->m_VariableToProgress.find(variable);
231 auto it = impl->m_VariableToProgress.find(variable);
232 if (it != impl->m_VariableToProgress.cend()) {
232 if (it != impl->m_VariableToProgress.cend()) {
233 return it->second;
233 return it->second;
234 }
234 }
235 }
235 }
236 }
236 }
237
237
238 return QVariant{};
238 return QVariant{};
239 }
239 }
240
240
241 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
241 QVariant VariableModel::headerData(int section, Qt::Orientation orientation, int role) const
242 {
242 {
243 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
243 if (role != Qt::DisplayRole && role != Qt::SizeHintRole) {
244 return QVariant{};
244 return QVariant{};
245 }
245 }
246
246
247 if (orientation == Qt::Horizontal) {
247 if (orientation == Qt::Horizontal) {
248 auto propertiesIt = COLUMN_PROPERTIES.find(section);
248 auto propertiesIt = COLUMN_PROPERTIES.find(section);
249 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
249 if (propertiesIt != COLUMN_PROPERTIES.cend()) {
250 // Role is either DisplayRole or SizeHintRole
250 // Role is either DisplayRole or SizeHintRole
251 return (role == Qt::DisplayRole)
251 return (role == Qt::DisplayRole)
252 ? QVariant{propertiesIt->m_Name}
252 ? QVariant{propertiesIt->m_Name}
253 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
253 : QVariant{QSize{propertiesIt->m_Width, propertiesIt->m_Height}};
254 }
254 }
255 else {
255 else {
256 qWarning(LOG_VariableModel())
256 qWarning(LOG_VariableModel())
257 << tr("Can't get header data (unknown column %1)").arg(section);
257 << tr("Can't get header data (unknown column %1)").arg(section);
258 }
258 }
259 }
259 }
260
260
261 return QVariant{};
261 return QVariant{};
262 }
262 }
263
263
264 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
264 Qt::ItemFlags VariableModel::flags(const QModelIndex &index) const
265 {
265 {
266 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
266 return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
267 }
267 }
268
268
269 Qt::DropActions VariableModel::supportedDropActions() const
269 Qt::DropActions VariableModel::supportedDropActions() const
270 {
270 {
271 return Qt::CopyAction | Qt::MoveAction;
271 return Qt::CopyAction | Qt::MoveAction;
272 }
272 }
273
273
274 Qt::DropActions VariableModel::supportedDragActions() const
274 Qt::DropActions VariableModel::supportedDragActions() const
275 {
275 {
276 return Qt::CopyAction | Qt::MoveAction;
276 return Qt::CopyAction | Qt::MoveAction;
277 }
277 }
278
278
279 QStringList VariableModel::mimeTypes() const
279 QStringList VariableModel::mimeTypes() const
280 {
280 {
281 return {MIME_TYPE_VARIABLE_LIST};
281 return {MIME_TYPE_VARIABLE_LIST};
282 }
282 }
283
283
284 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
284 QMimeData *VariableModel::mimeData(const QModelIndexList &indexes) const
285 {
285 {
286 auto mimeData = new QMimeData;
286 auto mimeData = new QMimeData;
287
287
288 QList<std::shared_ptr<Variable> > variableList;
288 QList<std::shared_ptr<Variable> > variableList;
289
289
290 for (const auto &index : indexes) {
290 for (const auto &index : indexes) {
291 if (index.column() == 0) { // only the first column
291 if (index.column() == 0) { // only the first column
292 auto variable = impl->m_Variables.at(index.row());
292 auto variable = impl->m_Variables.at(index.row());
293 if (variable.get() && index.isValid()) {
293 if (variable.get() && index.isValid()) {
294 variableList << variable;
294 variableList << variable;
295 }
295 }
296 }
296 }
297 }
297 }
298
298
299 auto encodedData = impl->m_VariableController->mimeDataForVariables(variableList);
299 auto encodedData = impl->m_VariableController->mimeDataForVariables(variableList);
300 mimeData->setData(MIME_TYPE_VARIABLE_LIST, encodedData);
300 mimeData->setData(MIME_TYPE_VARIABLE_LIST, encodedData);
301
301
302 return mimeData;
302 return mimeData;
303 }
303 }
304
304
305 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
305 bool VariableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
306 int column, const QModelIndex &parent) const
306 int column, const QModelIndex &parent) const
307 {
307 {
308 // drop of a product
308 // drop of a product
309 return data->hasFormat(MIME_TYPE_PRODUCT_LIST);
309 return data->hasFormat(MIME_TYPE_PRODUCT_LIST);
310 }
310 }
311
311
312 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
312 bool VariableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
313 const QModelIndex &parent)
313 const QModelIndex &parent)
314 {
314 {
315 bool dropDone = false;
315 auto dropDone = false;
316
316
317 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
317 if (data->hasFormat(MIME_TYPE_PRODUCT_LIST)) {
318 QDataStream stream(data->data(MIME_TYPE_PRODUCT_LIST));
318 QDataStream stream(data->data(MIME_TYPE_PRODUCT_LIST));
319
319
320 QVariantList productList;
320 QVariantList productList;
321 stream >> productList;
321 stream >> productList;
322
322
323 for (auto metaData : productList) {
323 for (auto metaData : productList) {
324 emit requestVariable(metaData.toHash());
324 emit requestVariable(metaData.toHash());
325 }
325 }
326
326
327 dropDone = true;
327 dropDone = true;
328 }
328 }
329
329
330 return dropDone;
330 return dropDone;
331 }
331 }
332
332
333 void VariableModel::abortProgress(const QModelIndex &index)
333 void VariableModel::abortProgress(const QModelIndex &index)
334 {
334 {
335 if (auto variable = impl->m_Variables.at(index.row())) {
335 if (auto variable = impl->m_Variables.at(index.row())) {
336 emit abortProgessRequested(variable);
336 emit abortProgessRequested(variable);
337 }
337 }
338 }
338 }
339
339
340 void VariableModel::onVariableUpdated() noexcept
340 void VariableModel::onVariableUpdated() noexcept
341 {
341 {
342 // Finds variable that has been updated in the model
342 // Finds variable that has been updated in the model
343 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
343 if (auto updatedVariable = dynamic_cast<Variable *>(sender())) {
344 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
344 auto updatedVariableIndex = impl->indexOfVariable(updatedVariable);
345
345
346 if (updatedVariableIndex > -1) {
346 if (updatedVariableIndex > -1) {
347 emit dataChanged(createIndex(updatedVariableIndex, 0),
347 emit dataChanged(createIndex(updatedVariableIndex, 0),
348 createIndex(updatedVariableIndex, columnCount() - 1));
348 createIndex(updatedVariableIndex, columnCount() - 1));
349 }
349 }
350 }
350 }
351 }
351 }
352
352
353 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
353 int VariableModel::VariableModelPrivate::indexOfVariable(Variable *variable) const noexcept
354 {
354 {
355 auto begin = std::cbegin(m_Variables);
355 auto begin = std::cbegin(m_Variables);
356 auto end = std::cend(m_Variables);
356 auto end = std::cend(m_Variables);
357 auto it
357 auto it
358 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
358 = std::find_if(begin, end, [variable](const auto &var) { return var.get() == variable; });
359
359
360 if (it != end) {
360 if (it != end) {
361 // Gets the index of the variable in the model: we assume here that views have the same
361 // Gets the index of the variable in the model: we assume here that views have the same
362 // order as the model
362 // order as the model
363 return std::distance(begin, it);
363 return std::distance(begin, it);
364 }
364 }
365 else {
365 else {
366 return -1;
366 return -1;
367 }
367 }
368 }
368 }
@@ -1,309 +1,331
1 #include "DragDropHelper.h"
1 #include "DragDropHelper.h"
2 #include "SqpApplication.h"
2 #include "SqpApplication.h"
3 #include "Visualization/VisualizationDragDropContainer.h"
3 #include "Visualization/VisualizationDragDropContainer.h"
4 #include "Visualization/VisualizationDragWidget.h"
4 #include "Visualization/VisualizationDragWidget.h"
5 #include "Visualization/VisualizationWidget.h"
5 #include "Visualization/VisualizationWidget.h"
6 #include "Visualization/operations/FindVariableOperation.h"
6 #include "Visualization/operations/FindVariableOperation.h"
7
7
8 #include "Variable/VariableController.h"
8 #include "Variable/VariableController.h"
9
9
10 #include "Common/MimeTypesDef.h"
10 #include "Common/MimeTypesDef.h"
11 #include "Common/VisualizationDef.h"
11 #include "Common/VisualizationDef.h"
12
12
13 #include <QDir>
13 #include <QDir>
14 #include <QDragEnterEvent>
14 #include <QDragEnterEvent>
15 #include <QDragMoveEvent>
15 #include <QDragMoveEvent>
16 #include <QScrollArea>
16 #include <QScrollArea>
17 #include <QScrollBar>
17 #include <QScrollBar>
18 #include <QTimer>
18 #include <QTimer>
19 #include <QVBoxLayout>
19 #include <QVBoxLayout>
20
20
21 const int SCROLL_SPEED = 5;
21 const int SCROLL_SPEED = 5;
22 const int SCROLL_ZONE_SIZE = 50;
22 const int SCROLL_ZONE_SIZE = 50;
23
23
24 Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper")
24 Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper")
25
25
26 struct DragDropScroller::DragDropScrollerPrivate {
26 struct DragDropScroller::DragDropScrollerPrivate {
27
27
28 QList<QScrollArea *> m_ScrollAreas;
28 QList<QScrollArea *> m_ScrollAreas;
29 QScrollArea *m_CurrentScrollArea = nullptr;
29 QScrollArea *m_CurrentScrollArea = nullptr;
30 std::unique_ptr<QTimer> m_Timer = nullptr;
30 std::unique_ptr<QTimer> m_Timer = nullptr;
31
31
32 enum class ScrollDirection { up, down, unknown };
32 enum class ScrollDirection { up, down, unknown };
33 ScrollDirection m_Direction = ScrollDirection::unknown;
33 ScrollDirection m_Direction = ScrollDirection::unknown;
34
34
35 explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
35 explicit DragDropScrollerPrivate() : m_Timer{std::make_unique<QTimer>()}
36 {
36 {
37 m_Timer->setInterval(0);
37 m_Timer->setInterval(0);
38 }
38 }
39 };
39 };
40
40
41 DragDropScroller::DragDropScroller(QObject *parent)
41 DragDropScroller::DragDropScroller(QObject *parent)
42 : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
42 : QObject{parent}, impl{spimpl::make_unique_impl<DragDropScrollerPrivate>()}
43 {
43 {
44 connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
44 connect(impl->m_Timer.get(), &QTimer::timeout, this, &DragDropScroller::onTimer);
45 }
45 }
46
46
47 void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
47 void DragDropScroller::addScrollArea(QScrollArea *scrollArea)
48 {
48 {
49 impl->m_ScrollAreas << scrollArea;
49 impl->m_ScrollAreas << scrollArea;
50 scrollArea->viewport()->setAcceptDrops(true);
50 scrollArea->viewport()->setAcceptDrops(true);
51 }
51 }
52
52
53 void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
53 void DragDropScroller::removeScrollArea(QScrollArea *scrollArea)
54 {
54 {
55 impl->m_ScrollAreas.removeAll(scrollArea);
55 impl->m_ScrollAreas.removeAll(scrollArea);
56 scrollArea->viewport()->setAcceptDrops(false);
56 scrollArea->viewport()->setAcceptDrops(false);
57 }
57 }
58
58
59 bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
59 bool DragDropScroller::eventFilter(QObject *obj, QEvent *event)
60 {
60 {
61 if (event->type() == QEvent::DragMove) {
61 if (event->type() == QEvent::DragMove) {
62 auto w = static_cast<QWidget *>(obj);
62 auto w = static_cast<QWidget *>(obj);
63
63
64 if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
64 if (impl->m_CurrentScrollArea && impl->m_CurrentScrollArea->isAncestorOf(w)) {
65 auto moveEvent = static_cast<QDragMoveEvent *>(event);
65 auto moveEvent = static_cast<QDragMoveEvent *>(event);
66
66
67 auto pos = moveEvent->pos();
67 auto pos = moveEvent->pos();
68 if (impl->m_CurrentScrollArea->viewport() != w) {
68 if (impl->m_CurrentScrollArea->viewport() != w) {
69 auto globalPos = w->mapToGlobal(moveEvent->pos());
69 auto globalPos = w->mapToGlobal(moveEvent->pos());
70 pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
70 pos = impl->m_CurrentScrollArea->viewport()->mapFromGlobal(globalPos);
71 }
71 }
72
72
73 auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
73 auto isInTopZone = pos.y() > impl->m_CurrentScrollArea->viewport()->size().height()
74 - SCROLL_ZONE_SIZE;
74 - SCROLL_ZONE_SIZE;
75 auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
75 auto isInBottomZone = pos.y() < SCROLL_ZONE_SIZE;
76
76
77 if (!isInTopZone && !isInBottomZone) {
77 if (!isInTopZone && !isInBottomZone) {
78 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
78 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
79 impl->m_Timer->stop();
79 impl->m_Timer->stop();
80 }
80 }
81 else if (!impl->m_Timer->isActive()) {
81 else if (!impl->m_Timer->isActive()) {
82 impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
82 impl->m_Direction = isInTopZone ? DragDropScrollerPrivate::ScrollDirection::up
83 : DragDropScrollerPrivate::ScrollDirection::down;
83 : DragDropScrollerPrivate::ScrollDirection::down;
84 impl->m_Timer->start();
84 impl->m_Timer->start();
85 }
85 }
86 }
86 }
87 }
87 }
88 else if (event->type() == QEvent::DragEnter) {
88 else if (event->type() == QEvent::DragEnter) {
89 auto w = static_cast<QWidget *>(obj);
89 auto w = static_cast<QWidget *>(obj);
90
90
91 for (auto scrollArea : impl->m_ScrollAreas) {
91 for (auto scrollArea : impl->m_ScrollAreas) {
92 if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
92 if (impl->m_CurrentScrollArea != scrollArea && scrollArea->isAncestorOf(w)) {
93 auto enterEvent = static_cast<QDragEnterEvent *>(event);
93 auto enterEvent = static_cast<QDragEnterEvent *>(event);
94 enterEvent->acceptProposedAction();
94 enterEvent->acceptProposedAction();
95 enterEvent->setDropAction(Qt::IgnoreAction);
95 enterEvent->setDropAction(Qt::IgnoreAction);
96 impl->m_CurrentScrollArea = scrollArea;
96 impl->m_CurrentScrollArea = scrollArea;
97 break;
97 break;
98 }
98 }
99 }
99 }
100 }
100 }
101 else if (event->type() == QEvent::DragLeave) {
101 else if (event->type() == QEvent::DragLeave) {
102 if (impl->m_CurrentScrollArea) {
102 if (impl->m_CurrentScrollArea) {
103 if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
103 if (!QRect(QPoint(), impl->m_CurrentScrollArea->size())
104 .contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
104 .contains(impl->m_CurrentScrollArea->mapFromGlobal(QCursor::pos()))) {
105 impl->m_CurrentScrollArea = nullptr;
105 impl->m_CurrentScrollArea = nullptr;
106 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
106 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
107 impl->m_Timer->stop();
107 impl->m_Timer->stop();
108 }
108 }
109 }
109 }
110 }
110 }
111 else if (event->type() == QEvent::Drop) {
111 else if (event->type() == QEvent::Drop) {
112 if (impl->m_CurrentScrollArea) {
112 if (impl->m_CurrentScrollArea) {
113 impl->m_CurrentScrollArea = nullptr;
113 impl->m_CurrentScrollArea = nullptr;
114 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
114 impl->m_Direction = DragDropScrollerPrivate::ScrollDirection::unknown;
115 impl->m_Timer->stop();
115 impl->m_Timer->stop();
116 }
116 }
117 }
117 }
118
118
119 return false;
119 return false;
120 }
120 }
121
121
122 void DragDropScroller::onTimer()
122 void DragDropScroller::onTimer()
123 {
123 {
124 if (impl->m_CurrentScrollArea) {
124 if (impl->m_CurrentScrollArea) {
125 auto mvt = 0;
125 auto mvt = 0;
126 switch (impl->m_Direction) {
126 switch (impl->m_Direction) {
127 case DragDropScrollerPrivate::ScrollDirection::up:
127 case DragDropScrollerPrivate::ScrollDirection::up:
128 mvt = SCROLL_SPEED;
128 mvt = SCROLL_SPEED;
129 break;
129 break;
130 case DragDropScrollerPrivate::ScrollDirection::down:
130 case DragDropScrollerPrivate::ScrollDirection::down:
131 mvt = -SCROLL_SPEED;
131 mvt = -SCROLL_SPEED;
132 break;
132 break;
133 default:
133 default:
134 break;
134 break;
135 }
135 }
136
136
137 impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
137 impl->m_CurrentScrollArea->verticalScrollBar()->setValue(
138 impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
138 impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt);
139 }
139 }
140 }
140 }
141
141
142 struct DragDropHelper::DragDropHelperPrivate {
142 struct DragDropHelper::DragDropHelperPrivate {
143
143
144 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
144 VisualizationDragWidget *m_CurrentDragWidget = nullptr;
145 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
145 std::unique_ptr<QWidget> m_PlaceHolder = nullptr;
146 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
146 std::unique_ptr<DragDropScroller> m_DragDropScroller = nullptr;
147 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
147 QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using
148 // QTemporaryFile to have a name which is not generated.
148 // QTemporaryFile to have a name which is not generated.
149
149
150 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
150 VisualizationDragWidget *m_HighlightedDragWidget = nullptr;
151
151
152 QMetaObject::Connection m_DragWidgetDestroyedConnection;
153 QMetaObject::Connection m_HighlightedWidgetDestroyedConnection;
154
152 explicit DragDropHelperPrivate()
155 explicit DragDropHelperPrivate()
153 : m_PlaceHolder{std::make_unique<QWidget>()},
156 : m_PlaceHolder{std::make_unique<QWidget>()},
154 m_DragDropScroller{std::make_unique<DragDropScroller>()}
157 m_DragDropScroller{std::make_unique<DragDropScroller>()}
155 {
158 {
156 m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
159 m_PlaceHolder->setStyleSheet("background-color: #BBD5EE; border:2px solid #2A7FD4");
157 sqpApp->installEventFilter(m_DragDropScroller.get());
160 sqpApp->installEventFilter(m_DragDropScroller.get());
158
161
159
162
160 m_ImageTempUrl = QDir::temp().absoluteFilePath("Scqlop_graph.png");
163 m_ImageTempUrl = QDir::temp().absoluteFilePath("Scqlop_graph.png");
161 }
164 }
162
165
163 void preparePlaceHolder() const
166 void preparePlaceHolder() const
164 {
167 {
165 if (m_CurrentDragWidget) {
168 if (m_CurrentDragWidget) {
166 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
169 m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size());
167 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
170 m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy());
168 }
171 }
169 else {
172 else {
170 // Configuration of the placeHolder when there is no dragWidget
173 // Configuration of the placeHolder when there is no dragWidget
171 // (for instance with a drag from a variable)
174 // (for instance with a drag from a variable)
172
175
173 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
176 m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT);
174 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
177 m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
175 }
178 }
176 }
179 }
177 };
180 };
178
181
179
182
180 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
183 DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl<DragDropHelperPrivate>()}
181 {
184 {
182 }
185 }
183
186
184 DragDropHelper::~DragDropHelper()
187 DragDropHelper::~DragDropHelper()
185 {
188 {
186 QFile::remove(impl->m_ImageTempUrl);
189 QFile::remove(impl->m_ImageTempUrl);
187 }
190 }
188
191
189 void DragDropHelper::resetDragAndDrop()
192 void DragDropHelper::resetDragAndDrop()
190 {
193 {
191 setCurrentDragWidget(nullptr);
194 setCurrentDragWidget(nullptr);
192 impl->m_HighlightedDragWidget = nullptr;
195 impl->m_HighlightedDragWidget = nullptr;
193 }
196 }
194
197
195 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
198 void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget)
196 {
199 {
200 if (impl->m_CurrentDragWidget) {
201
202 QObject::disconnect(impl->m_DragWidgetDestroyedConnection);
203 }
204
205 if (dragWidget) {
206 // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed
207 impl->m_DragWidgetDestroyedConnection
208 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
209 [this]() { impl->m_CurrentDragWidget = nullptr; });
210 }
211
197 impl->m_CurrentDragWidget = dragWidget;
212 impl->m_CurrentDragWidget = dragWidget;
198 }
213 }
199
214
200 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
215 VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const
201 {
216 {
202 return impl->m_CurrentDragWidget;
217 return impl->m_CurrentDragWidget;
203 }
218 }
204
219
205
220
206 QWidget &DragDropHelper::placeHolder() const
221 QWidget &DragDropHelper::placeHolder() const
207 {
222 {
208 return *impl->m_PlaceHolder;
223 return *impl->m_PlaceHolder;
209 }
224 }
210
225
211 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
226 void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index)
212 {
227 {
213 removePlaceHolder();
228 removePlaceHolder();
214 impl->preparePlaceHolder();
229 impl->preparePlaceHolder();
215 layout->insertWidget(index, impl->m_PlaceHolder.get());
230 layout->insertWidget(index, impl->m_PlaceHolder.get());
216 impl->m_PlaceHolder->show();
231 impl->m_PlaceHolder->show();
217 }
232 }
218
233
219 void DragDropHelper::removePlaceHolder()
234 void DragDropHelper::removePlaceHolder()
220 {
235 {
221 auto parentWidget = impl->m_PlaceHolder->parentWidget();
236 auto parentWidget = impl->m_PlaceHolder->parentWidget();
222 if (parentWidget) {
237 if (parentWidget) {
223 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
238 parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get());
224 impl->m_PlaceHolder->setParent(nullptr);
239 impl->m_PlaceHolder->setParent(nullptr);
225 impl->m_PlaceHolder->hide();
240 impl->m_PlaceHolder->hide();
226 }
241 }
227 }
242 }
228
243
229 bool DragDropHelper::isPlaceHolderSet() const
244 bool DragDropHelper::isPlaceHolderSet() const
230 {
245 {
231 return impl->m_PlaceHolder->parentWidget();
246 return impl->m_PlaceHolder->parentWidget();
232 }
247 }
233
248
234 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
249 void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea)
235 {
250 {
236 impl->m_DragDropScroller->addScrollArea(scrollArea);
251 impl->m_DragDropScroller->addScrollArea(scrollArea);
237 }
252 }
238
253
239 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
254 void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea)
240 {
255 {
241 impl->m_DragDropScroller->removeScrollArea(scrollArea);
256 impl->m_DragDropScroller->removeScrollArea(scrollArea);
242 }
257 }
243
258
244 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
259 QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const
245 {
260 {
246 image.save(impl->m_ImageTempUrl);
261 image.save(impl->m_ImageTempUrl);
247 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
262 return QUrl::fromLocalFile(impl->m_ImageTempUrl);
248 }
263 }
249
264
250 void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
265 void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget)
251 {
266 {
252 if (impl->m_HighlightedDragWidget) {
267 if (impl->m_HighlightedDragWidget) {
253 impl->m_HighlightedDragWidget->highlightForMerge(false);
268 impl->m_HighlightedDragWidget->highlightForMerge(false);
269 QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection);
254 }
270 }
255
271
256 if (dragWidget) {
272 if (dragWidget) {
257 impl->m_HighlightedDragWidget = dragWidget;
273 dragWidget->highlightForMerge(true);
258 impl->m_HighlightedDragWidget->highlightForMerge(true);
274
275 // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed
276 impl->m_DragWidgetDestroyedConnection
277 = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed,
278 [this]() { impl->m_HighlightedDragWidget = nullptr; });
259 }
279 }
280
281 impl->m_HighlightedDragWidget = dragWidget;
260 }
282 }
261
283
262 bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
284 bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData,
263 VisualizationDragDropContainer *dropContainer)
285 VisualizationDragDropContainer *dropContainer)
264 {
286 {
265 if (!mimeData || !dropContainer) {
287 if (!mimeData || !dropContainer) {
266 qCWarning(LOG_DragDropHelper()) << QObject::tr(
288 qCWarning(LOG_DragDropHelper()) << QObject::tr(
267 "DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
289 "DragDropHelper::checkMimeDataForVisualization, invalid input parameters.");
268 Q_ASSERT(false);
290 Q_ASSERT(false);
269 }
291 }
270
292
271 auto result = true;
293 auto result = true;
272
294
273 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
295 if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) {
274 auto variables = sqpApp->variableController().variablesForMimeData(
296 auto variables = sqpApp->variableController().variablesForMimeData(
275 mimeData->data(MIME_TYPE_VARIABLE_LIST));
297 mimeData->data(MIME_TYPE_VARIABLE_LIST));
276
298
277 if (variables.count() == 1) {
299 if (variables.count() == 1) {
278 // Check that the viariable is not already in a graph
300 // Check that the viariable is not already in a graph
279
301
280 // Search for the top level VisualizationWidget
302 // Search for the top level VisualizationWidget
281 auto parent = dropContainer->parentWidget();
303 auto parent = dropContainer->parentWidget();
282 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
304 while (parent && qobject_cast<VisualizationWidget *>(parent) == nullptr) {
283 parent = parent->parentWidget();
305 parent = parent->parentWidget();
284 }
306 }
285
307
286 if (parent) {
308 if (parent) {
287 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
309 auto visualizationWidget = static_cast<VisualizationWidget *>(parent);
288
310
289 FindVariableOperation findVariableOperation{variables.first()};
311 FindVariableOperation findVariableOperation{variables.first()};
290 visualizationWidget->accept(&findVariableOperation);
312 visualizationWidget->accept(&findVariableOperation);
291 auto variableContainers = findVariableOperation.result();
313 auto variableContainers = findVariableOperation.result();
292 if (!variableContainers.empty()) {
314 if (!variableContainers.empty()) {
293 result = false;
315 result = false;
294 }
316 }
295 }
317 }
296 else {
318 else {
297 qCWarning(LOG_DragDropHelper()) << QObject::tr(
319 qCWarning(LOG_DragDropHelper()) << QObject::tr(
298 "DragDropHelper::checkMimeDataForVisualization, the parent "
320 "DragDropHelper::checkMimeDataForVisualization, the parent "
299 "VisualizationWidget cannot be found.");
321 "VisualizationWidget cannot be found.");
300 result = false;
322 result = false;
301 }
323 }
302 }
324 }
303 else {
325 else {
304 result = false;
326 result = false;
305 }
327 }
306 }
328 }
307
329
308 return result;
330 return result;
309 }
331 }
@@ -1,380 +1,381
1 #include "Visualization/VisualizationDragDropContainer.h"
1 #include "Visualization/VisualizationDragDropContainer.h"
2 #include "DragDropHelper.h"
2 #include "DragDropHelper.h"
3 #include "SqpApplication.h"
3 #include "SqpApplication.h"
4 #include "Visualization/VisualizationDragWidget.h"
4 #include "Visualization/VisualizationDragWidget.h"
5
5
6 #include "Common/VisualizationDef.h"
6 #include "Common/VisualizationDef.h"
7
7
8 #include <QDrag>
8 #include <QDrag>
9 #include <QDragEnterEvent>
9 #include <QDragEnterEvent>
10 #include <QVBoxLayout>
10 #include <QVBoxLayout>
11
11
12 #include <cmath>
12 #include <cmath>
13 #include <memory>
13 #include <memory>
14
14
15 Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer")
15 Q_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer, "VisualizationDragDropContainer")
16
16
17 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
17 struct VisualizationDragDropContainer::VisualizationDragDropContainerPrivate {
18
18
19 QVBoxLayout *m_Layout;
19 QVBoxLayout *m_Layout;
20 QStringList m_AcceptedMimeTypes;
20 QStringList m_AcceptedMimeTypes;
21 QStringList m_MergeAllowedMimeTypes;
21 QStringList m_MergeAllowedMimeTypes;
22 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
22 VisualizationDragDropContainer::AcceptMimeDataFunction m_AcceptMimeDataFun
23 = [](auto mimeData) { return true; };
23 = [](auto mimeData) { return true; };
24 int m_MinContainerHeight = 0;
24 int m_MinContainerHeight = 0;
25
25
26 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
26 explicit VisualizationDragDropContainerPrivate(QWidget *widget)
27 {
27 {
28 m_Layout = new QVBoxLayout(widget);
28 m_Layout = new QVBoxLayout(widget);
29 m_Layout->setContentsMargins(0, 0, 0, 0);
29 m_Layout->setContentsMargins(0, 0, 0, 0);
30 }
30 }
31
31
32 bool acceptMimeData(const QMimeData *data) const
32 bool acceptMimeData(const QMimeData *data) const
33 {
33 {
34 for (const auto &type : m_AcceptedMimeTypes) {
34 for (const auto &type : m_AcceptedMimeTypes) {
35 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
35 if (data->hasFormat(type) && m_AcceptMimeDataFun(data)) {
36 return true;
36 return true;
37 }
37 }
38 }
38 }
39
39
40 return false;
40 return false;
41 }
41 }
42
42
43 bool allowMergeMimeData(const QMimeData *data) const
43 bool allowMergeMimeData(const QMimeData *data) const
44 {
44 {
45 for (const auto &type : m_MergeAllowedMimeTypes) {
45 for (const auto &type : m_MergeAllowedMimeTypes) {
46 if (data->hasFormat(type)) {
46 if (data->hasFormat(type)) {
47 return true;
47 return true;
48 }
48 }
49 }
49 }
50
50
51 return false;
51 return false;
52 }
52 }
53
53
54 bool hasPlaceHolder() const
54 bool hasPlaceHolder() const
55 {
55 {
56 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
56 return sqpApp->dragDropHelper().placeHolder().parentWidget() == m_Layout->parentWidget();
57 }
57 }
58
58
59 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
59 VisualizationDragWidget *getChildDragWidgetAt(const QWidget *parent, const QPoint &pos) const
60 {
60 {
61 VisualizationDragWidget *dragWidget = nullptr;
61 VisualizationDragWidget *dragWidget = nullptr;
62
62
63 for (auto child : parent->children()) {
63 for (auto child : parent->children()) {
64 auto widget = qobject_cast<VisualizationDragWidget *>(child);
64 auto widget = qobject_cast<VisualizationDragWidget *>(child);
65 if (widget && widget->isVisible()) {
65 if (widget && widget->isVisible()) {
66 if (widget->frameGeometry().contains(pos)) {
66 if (widget->frameGeometry().contains(pos)) {
67 dragWidget = widget;
67 dragWidget = widget;
68 break;
68 break;
69 }
69 }
70 }
70 }
71 }
71 }
72
72
73 return dragWidget;
73 return dragWidget;
74 }
74 }
75
75
76 bool cursorIsInContainer(QWidget *container) const
76 bool cursorIsInContainer(QWidget *container) const
77 {
77 {
78 auto adustNum = 18; // to be safe, in case of scrollbar on the side
78 return container->isAncestorOf(sqpApp->widgetAt(QCursor::pos()));
79 auto containerRect = QRect(QPoint(), container->contentsRect().size())
80 .adjusted(adustNum, adustNum, -adustNum, -adustNum);
81 return containerRect.contains(container->mapFromGlobal(QCursor::pos()));
82 }
79 }
83
80
84 int countDragWidget(const QWidget *parent) const
81 int countDragWidget(const QWidget *parent) const
85 {
82 {
86 auto nbGraph = 0;
83 auto nbGraph = 0;
87 for (auto child : parent->children()) {
84 for (auto child : parent->children()) {
88 if (qobject_cast<VisualizationDragWidget *>(child)) {
85 if (qobject_cast<VisualizationDragWidget *>(child)) {
89 nbGraph += 1;
86 nbGraph += 1;
90 }
87 }
91 }
88 }
92
89
93 return nbGraph;
90 return nbGraph;
94 }
91 }
95
92
96 void findPlaceHolderPosition(const QPoint &pos, bool canMerge,
93 void findPlaceHolderPosition(const QPoint &pos, bool canMerge,
97 const VisualizationDragDropContainer *container);
94 const VisualizationDragDropContainer *container);
98 };
95 };
99
96
100 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
97 VisualizationDragDropContainer::VisualizationDragDropContainer(QWidget *parent)
101 : QWidget{parent},
98 : QWidget{parent},
102 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
99 impl{spimpl::make_unique_impl<VisualizationDragDropContainerPrivate>(this)}
103 {
100 {
104 setAcceptDrops(true);
101 setAcceptDrops(true);
105 }
102 }
106
103
107 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
104 void VisualizationDragDropContainer::addDragWidget(VisualizationDragWidget *dragWidget)
108 {
105 {
109 impl->m_Layout->addWidget(dragWidget);
106 impl->m_Layout->addWidget(dragWidget);
110 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
107 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
111 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
108 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
112 &VisualizationDragDropContainer::startDrag);
109 &VisualizationDragDropContainer::startDrag);
113 }
110 }
114
111
115 void VisualizationDragDropContainer::insertDragWidget(int index,
112 void VisualizationDragDropContainer::insertDragWidget(int index,
116 VisualizationDragWidget *dragWidget)
113 VisualizationDragWidget *dragWidget)
117 {
114 {
118 impl->m_Layout->insertWidget(index, dragWidget);
115 impl->m_Layout->insertWidget(index, dragWidget);
119 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
116 disconnect(dragWidget, &VisualizationDragWidget::dragDetected, nullptr, nullptr);
120 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
117 connect(dragWidget, &VisualizationDragWidget::dragDetected, this,
121 &VisualizationDragDropContainer::startDrag);
118 &VisualizationDragDropContainer::startDrag);
122 }
119 }
123
120
124 void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes)
121 void VisualizationDragDropContainer::setAcceptedMimeTypes(const QStringList &mimeTypes)
125 {
122 {
126 impl->m_AcceptedMimeTypes = mimeTypes;
123 impl->m_AcceptedMimeTypes = mimeTypes;
127 }
124 }
128
125
129 void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes)
126 void VisualizationDragDropContainer::setMergeAllowedMimeTypes(const QStringList &mimeTypes)
130 {
127 {
131 impl->m_MergeAllowedMimeTypes = mimeTypes;
128 impl->m_MergeAllowedMimeTypes = mimeTypes;
132 }
129 }
133
130
134 int VisualizationDragDropContainer::countDragWidget() const
131 int VisualizationDragDropContainer::countDragWidget() const
135 {
132 {
136 return impl->countDragWidget(this);
133 return impl->countDragWidget(this);
137 }
134 }
138
135
139 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
136 void VisualizationDragDropContainer::setAcceptMimeDataFunction(
140 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
137 VisualizationDragDropContainer::AcceptMimeDataFunction fun)
141 {
138 {
142 impl->m_AcceptMimeDataFun = fun;
139 impl->m_AcceptMimeDataFun = fun;
143 }
140 }
144
141
145 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
142 void VisualizationDragDropContainer::startDrag(VisualizationDragWidget *dragWidget,
146 const QPoint &dragPosition)
143 const QPoint &dragPosition)
147 {
144 {
148 auto &helper = sqpApp->dragDropHelper();
145 auto &helper = sqpApp->dragDropHelper();
149 helper.resetDragAndDrop();
146 helper.resetDragAndDrop();
150
147
151 // Note: The management of the drag object is done by Qt
148 // Note: The management of the drag object is done by Qt
152 auto drag = new QDrag{dragWidget};
149 auto drag = new QDrag{dragWidget};
153 drag->setHotSpot(dragPosition);
150 drag->setHotSpot(dragPosition);
154
151
155 auto mimeData = dragWidget->mimeData();
152 auto mimeData = dragWidget->mimeData();
156 drag->setMimeData(mimeData);
153 drag->setMimeData(mimeData);
157
154
158 auto pixmap = QPixmap(dragWidget->size());
155 auto pixmap = QPixmap(dragWidget->size());
159 dragWidget->render(&pixmap);
156 dragWidget->render(&pixmap);
160 drag->setPixmap(pixmap);
157 drag->setPixmap(pixmap);
161
158
162 auto image = pixmap.toImage();
159 auto image = pixmap.toImage();
163 mimeData->setImageData(image);
160 mimeData->setImageData(image);
164 mimeData->setUrls({helper.imageTemporaryUrl(image)});
161 mimeData->setUrls({helper.imageTemporaryUrl(image)});
165
162
166 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
163 if (impl->m_Layout->indexOf(dragWidget) >= 0) {
167 helper.setCurrentDragWidget(dragWidget);
164 helper.setCurrentDragWidget(dragWidget);
168
165
169 if (impl->cursorIsInContainer(this)) {
166 if (impl->cursorIsInContainer(this)) {
170 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
167 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
171 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
168 helper.insertPlaceHolder(impl->m_Layout, dragWidgetIndex);
172 dragWidget->setVisible(false);
169 dragWidget->setVisible(false);
173 }
170 }
171 else {
172 // The drag starts directly outside the drop zone
173 // do not add the placeHolder
174 }
174
175
175 // Note: The exec() is blocking on windows but not on linux and macOS
176 // Note: The exec() is blocking on windows but not on linux and macOS
176 drag->exec(Qt::MoveAction | Qt::CopyAction);
177 drag->exec(Qt::MoveAction | Qt::CopyAction);
177 }
178 }
178 else {
179 else {
179 qCWarning(LOG_VisualizationDragDropContainer())
180 qCWarning(LOG_VisualizationDragDropContainer())
180 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
181 << tr("VisualizationDragDropContainer::startDrag, drag aborted, the specified "
181 "VisualizationDragWidget is not found in this container.");
182 "VisualizationDragWidget is not found in this container.");
182 }
183 }
183 }
184 }
184
185
185 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
186 void VisualizationDragDropContainer::dragEnterEvent(QDragEnterEvent *event)
186 {
187 {
187 if (impl->acceptMimeData(event->mimeData())) {
188 if (impl->acceptMimeData(event->mimeData())) {
188 event->acceptProposedAction();
189 event->acceptProposedAction();
189
190
190 auto &helper = sqpApp->dragDropHelper();
191 auto &helper = sqpApp->dragDropHelper();
191
192
192 if (!impl->hasPlaceHolder()) {
193 if (!impl->hasPlaceHolder()) {
193 auto dragWidget = helper.getCurrentDragWidget();
194 auto dragWidget = helper.getCurrentDragWidget();
194
195
195 if (dragWidget) {
196 if (dragWidget) {
196 // If the drag&drop is internal to the visualization, entering the container hide
197 // If the drag&drop is internal to the visualization, entering the container hide
197 // the dragWidget which was made visible by the dragLeaveEvent
198 // the dragWidget which was made visible by the dragLeaveEvent
198 auto parentWidget
199 auto parentWidget
199 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
200 = qobject_cast<VisualizationDragDropContainer *>(dragWidget->parentWidget());
200 if (parentWidget) {
201 if (parentWidget) {
201 dragWidget->setVisible(false);
202 dragWidget->setVisible(false);
202 }
203 }
203 }
204 }
204
205
205 auto canMerge = impl->allowMergeMimeData(event->mimeData());
206 auto canMerge = impl->allowMergeMimeData(event->mimeData());
206 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
207 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
207 }
208 }
208 else {
209 else {
209 // do nothing
210 // do nothing
210 }
211 }
211 }
212 }
212 else {
213 else {
213 event->ignore();
214 event->ignore();
214 }
215 }
215
216
216 QWidget::dragEnterEvent(event);
217 QWidget::dragEnterEvent(event);
217 }
218 }
218
219
219 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
220 void VisualizationDragDropContainer::dragLeaveEvent(QDragLeaveEvent *event)
220 {
221 {
221 Q_UNUSED(event);
222 Q_UNUSED(event);
222
223
223 auto &helper = sqpApp->dragDropHelper();
224 auto &helper = sqpApp->dragDropHelper();
224
225
225 if (!impl->cursorIsInContainer(this)) {
226 if (!impl->cursorIsInContainer(this)) {
226 helper.removePlaceHolder();
227 helper.removePlaceHolder();
227 helper.setHightlightedDragWidget(nullptr);
228 helper.setHightlightedDragWidget(nullptr);
228 impl->m_MinContainerHeight = 0;
229 impl->m_MinContainerHeight = 0;
229
230
230 auto dragWidget = helper.getCurrentDragWidget();
231 auto dragWidget = helper.getCurrentDragWidget();
231 if (dragWidget) {
232 if (dragWidget) {
232 // dragWidget has a value only if the drag is started from the visualization
233 // dragWidget has a value only if the drag is started from the visualization
233 // In that case, shows the drag widget at its original place
234 // In that case, shows the drag widget at its original place
234 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
235 // So the drag widget doesn't stay hidden if the drop occurs outside the visualization
235 // drop zone (It is not possible to catch a drop event outside of the application)
236 // drop zone (It is not possible to catch a drop event outside of the application)
236
237
237 if (dragWidget) {
238 if (dragWidget) {
238 dragWidget->setVisible(true);
239 dragWidget->setVisible(true);
239 }
240 }
240 }
241 }
241 }
242 }
242 else {
243 else {
243 // Leave event probably received for a child widget.
244 // Leave event probably received for a child widget.
244 // Do nothing.
245 // Do nothing.
245 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
246 // Note: The DragLeave event, doesn't have any mean to determine who sent it.
246 }
247 }
247
248
248 QWidget::dragLeaveEvent(event);
249 QWidget::dragLeaveEvent(event);
249 }
250 }
250
251
251 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
252 void VisualizationDragDropContainer::dragMoveEvent(QDragMoveEvent *event)
252 {
253 {
253 if (impl->acceptMimeData(event->mimeData())) {
254 if (impl->acceptMimeData(event->mimeData())) {
254 auto canMerge = impl->allowMergeMimeData(event->mimeData());
255 auto canMerge = impl->allowMergeMimeData(event->mimeData());
255 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
256 impl->findPlaceHolderPosition(event->pos(), canMerge, this);
256 }
257 }
257 else {
258 else {
258 event->ignore();
259 event->ignore();
259 }
260 }
260
261
261 QWidget::dragMoveEvent(event);
262 QWidget::dragMoveEvent(event);
262 }
263 }
263
264
264 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
265 void VisualizationDragDropContainer::dropEvent(QDropEvent *event)
265 {
266 {
266 if (impl->acceptMimeData(event->mimeData())) {
267 if (impl->acceptMimeData(event->mimeData())) {
267 auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget();
268 auto dragWidget = sqpApp->dragDropHelper().getCurrentDragWidget();
268 if (impl->hasPlaceHolder()) {
269 if (impl->hasPlaceHolder()) {
269 auto &helper = sqpApp->dragDropHelper();
270 auto &helper = sqpApp->dragDropHelper();
270
271
271 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
272 auto droppedIndex = impl->m_Layout->indexOf(&helper.placeHolder());
272
273
273 if (dragWidget) {
274 if (dragWidget) {
274 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
275 auto dragWidgetIndex = impl->m_Layout->indexOf(dragWidget);
275 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
276 if (dragWidgetIndex >= 0 && dragWidgetIndex < droppedIndex) {
276 // Correction of the index if the drop occurs in the same container
277 // Correction of the index if the drop occurs in the same container
277 // and if the drag is started from the visualization (in that case, the
278 // and if the drag is started from the visualization (in that case, the
278 // dragWidget is hidden)
279 // dragWidget is hidden)
279 droppedIndex -= 1;
280 droppedIndex -= 1;
280 }
281 }
281
282
282 dragWidget->setVisible(true);
283 dragWidget->setVisible(true);
283 }
284 }
284
285
285 event->acceptProposedAction();
286 event->acceptProposedAction();
286
287
287 helper.removePlaceHolder();
288 helper.removePlaceHolder();
288
289
289 emit dropOccured(droppedIndex, event->mimeData());
290 emit dropOccured(droppedIndex, event->mimeData());
290 }
291 }
291 else {
292 else {
292 qCWarning(LOG_VisualizationDragDropContainer())
293 qCWarning(LOG_VisualizationDragDropContainer())
293 << tr("VisualizationDragDropContainer::dropEvent, couldn't drop because the "
294 << tr("VisualizationDragDropContainer::dropEvent, couldn't drop because the "
294 "placeHolder is not found.");
295 "placeHolder is not found.");
295 // Q_ASSERT(false);
296 // Q_ASSERT(false);
296 }
297 }
297 }
298 }
298 else {
299 else {
299 event->ignore();
300 event->ignore();
300 }
301 }
301
302
302 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
303 sqpApp->dragDropHelper().setHightlightedDragWidget(nullptr);
303 impl->m_MinContainerHeight = 0;
304 impl->m_MinContainerHeight = 0;
304
305
305 QWidget::dropEvent(event);
306 QWidget::dropEvent(event);
306 }
307 }
307
308
308
309
309 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
310 void VisualizationDragDropContainer::VisualizationDragDropContainerPrivate::findPlaceHolderPosition(
310 const QPoint &pos, bool canMerge, const VisualizationDragDropContainer *container)
311 const QPoint &pos, bool canMerge, const VisualizationDragDropContainer *container)
311 {
312 {
312 auto &helper = sqpApp->dragDropHelper();
313 auto &helper = sqpApp->dragDropHelper();
313
314
314 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
315 auto dragWidgetHovered = getChildDragWidgetAt(container, pos);
315 if (dragWidgetHovered) {
316 if (dragWidgetHovered) {
316 auto nbDragWidget = countDragWidget(container);
317 auto nbDragWidget = countDragWidget(container);
317 if (nbDragWidget > 0) {
318 if (nbDragWidget > 0) {
318
319
319 if (m_MinContainerHeight == 0) {
320 if (m_MinContainerHeight == 0) {
320 m_MinContainerHeight = container->size().height();
321 m_MinContainerHeight = container->size().height();
321 }
322 }
322
323
323 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
324 m_MinContainerHeight = qMin(m_MinContainerHeight, container->size().height());
324 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
325 auto graphHeight = qMax(m_MinContainerHeight / nbDragWidget, GRAPH_MINIMUM_HEIGHT);
325
326
326 auto posY = pos.y();
327 auto posY = pos.y();
327 auto dropIndex = floor(posY / graphHeight);
328 auto dropIndex = floor(posY / graphHeight);
328 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
329 auto zoneSize = qMin(graphHeight / 4.0, 75.0);
329
330
330
331
331 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
332 auto isOnTop = posY < dropIndex * graphHeight + zoneSize;
332 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
333 auto isOnBottom = posY > (dropIndex + 1) * graphHeight - zoneSize;
333
334
334 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
335 auto placeHolderIndex = m_Layout->indexOf(&(helper.placeHolder()));
335
336
336 if (isOnTop || isOnBottom || !canMerge) {
337 if (isOnTop || isOnBottom || !canMerge) {
337 if (isOnBottom) {
338 if (isOnBottom) {
338 dropIndex += 1;
339 dropIndex += 1;
339 }
340 }
340
341
341 if (helper.getCurrentDragWidget()) {
342 if (helper.getCurrentDragWidget()) {
342 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
343 auto dragWidgetIndex = m_Layout->indexOf(helper.getCurrentDragWidget());
343 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
344 if (dragWidgetIndex >= 0 && dragWidgetIndex <= dropIndex) {
344 // Correction of the index if the drop occurs in the same container
345 // Correction of the index if the drop occurs in the same container
345 // and if the drag is started from the visualization (in that case, the
346 // and if the drag is started from the visualization (in that case, the
346 // dragWidget is hidden)
347 // dragWidget is hidden)
347 dropIndex += 1;
348 dropIndex += 1;
348 }
349 }
349 }
350 }
350
351
351 if (dropIndex != placeHolderIndex) {
352 if (dropIndex != placeHolderIndex) {
352 helper.insertPlaceHolder(m_Layout, dropIndex);
353 helper.insertPlaceHolder(m_Layout, dropIndex);
353 }
354 }
354
355
355 helper.setHightlightedDragWidget(nullptr);
356 helper.setHightlightedDragWidget(nullptr);
356 }
357 }
357 else if (canMerge) {
358 else if (canMerge) {
358 // drop on the middle -> merge
359 // drop on the middle -> merge
359 if (hasPlaceHolder()) {
360 if (hasPlaceHolder()) {
360 helper.removePlaceHolder();
361 helper.removePlaceHolder();
361 }
362 }
362
363
363 helper.setHightlightedDragWidget(dragWidgetHovered);
364 helper.setHightlightedDragWidget(dragWidgetHovered);
364 }
365 }
365 }
366 }
366 else {
367 else {
367 qCWarning(LOG_VisualizationDragDropContainer())
368 qCWarning(LOG_VisualizationDragDropContainer())
368 << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the "
369 << tr("VisualizationDragDropContainer::dragMoveEvent, no widget found in the "
369 "container");
370 "container");
370 }
371 }
371 }
372 }
372 else if (!hasPlaceHolder()) {
373 else if (!hasPlaceHolder()) {
373 // Drop on an empty container, just add the placeHolder at the top
374 // Drop on an empty container, just add the placeHolder at the top
374 helper.insertPlaceHolder(m_Layout, 0);
375 helper.insertPlaceHolder(m_Layout, 0);
375 }
376 }
376 else {
377 else {
377 // No hovered drag widget, the mouse is probably hover the placeHolder
378 // No hovered drag widget, the mouse is probably hover the placeHolder
378 // Do nothing
379 // Do nothing
379 }
380 }
380 }
381 }
General Comments 3
Under Review
author

Pull request updated. Auto status change to "Under Review"

Changed commits:
  * 1 added
  * 0 removed

Changed files:
  * A core/tests/meson.build
You need to be logged in to leave comments. Login now