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