##// END OF EJS Templates
Drop of events on a catalogue
trabillard -
r1285:964109cb8b70
parent child
Show More
@@ -1,77 +1,83
1 1 #ifndef SCIQLOP_CATALOGUECONTROLLER_H
2 2 #define SCIQLOP_CATALOGUECONTROLLER_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <QLoggingCategory>
9 9 #include <QObject>
10 10 #include <QUuid>
11 11
12 12 #include <Common/spimpl.h>
13 13
14 14 #include <memory>
15 15
16 16 class DBCatalogue;
17 17 class DBEvent;
18 18 class DBEventProduct;
19 19
20 20 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController)
21 21
22 22 class DataSourceItem;
23 23 class Variable;
24 24
25 25 /**
26 26 * @brief The CatalogueController class aims to handle catalogues and event using the CatalogueAPI
27 27 * library.
28 28 */
29 29 class SCIQLOP_CORE_EXPORT CatalogueController : public QObject {
30 30 Q_OBJECT
31 31 public:
32 32 explicit CatalogueController(QObject *parent = 0);
33 33 virtual ~CatalogueController();
34 34
35 35 // DB
36 36 QStringList getRepositories() const;
37 37 void addDB(const QString &dbPath);
38 38 void saveDB(const QString &destinationPath, const QString &repository);
39 39
40 40 // Event
41 41 /// retrieveEvents with empty repository retrieve them from the default repository
42 42 std::list<std::shared_ptr<DBEvent> > retrieveEvents(const QString &repository) const;
43 43 std::list<std::shared_ptr<DBEvent> > retrieveAllEvents() const;
44 44 std::list<std::shared_ptr<DBEvent> >
45 45 retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const;
46 46 void addEvent(std::shared_ptr<DBEvent> event);
47 47 void updateEvent(std::shared_ptr<DBEvent> event);
48 48 void updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct);
49 49 void removeEvent(std::shared_ptr<DBEvent> event);
50 50 // void trashEvent(std::shared_ptr<DBEvent> event);
51 51 // void restore(QUuid eventId);
52 52 void saveEvent(std::shared_ptr<DBEvent> event);
53 53
54 54 // Catalogue
55 55 // bool createCatalogue(const QString &name, QVector<QUuid> eventList);
56 56 /// retrieveEvents with empty repository retrieve them from the default repository
57 57 std::list<std::shared_ptr<DBCatalogue> > retrieveCatalogues(const QString &repository
58 58 = QString()) const;
59 59 void updateCatalogue(std::shared_ptr<DBCatalogue> catalogue);
60 60 void removeCatalogue(std::shared_ptr<DBCatalogue> catalogue);
61 61 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue);
62 62
63 63 void saveAll();
64 64
65 /// Returns the MIME data associated to a list of variables
66 QByteArray mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const;
67
68 /// Returns the list of variables contained in a MIME data
69 QVector<std::shared_ptr<DBEvent> > eventsForMimeData(const QByteArray &mimeData) const;
70
65 71 public slots:
66 72 /// Manage init/end of the controller
67 73 void initialize();
68 74 void finalize();
69 75
70 76 private:
71 77 void waitForFinish();
72 78
73 79 class CatalogueControllerPrivate;
74 80 spimpl::unique_impl_ptr<CatalogueControllerPrivate> impl;
75 81 };
76 82
77 83 #endif // SCIQLOP_CATALOGUECONTROLLER_H
@@ -1,334 +1,375
1 1 #include <Catalogue/CatalogueController.h>
2 2
3 3 #include <Variable/Variable.h>
4 4
5 5 #include <CatalogueDao.h>
6 6
7 7 #include <ComparaisonPredicate.h>
8 8 #include <CompoundPredicate.h>
9 9 #include <DBCatalogue.h>
10 10 #include <DBEvent.h>
11 11 #include <DBEventProduct.h>
12 12 #include <DBTag.h>
13 13 #include <IRequestPredicate.h>
14 14
15 #include <QDataStream>
15 16 #include <QMutex>
16 17 #include <QThread>
17 18
18 19 #include <QDir>
19 20 #include <QStandardPaths>
20 21
21 22 Q_LOGGING_CATEGORY(LOG_CatalogueController, "CatalogueController")
22 23
23 24 namespace {
24 25
25 26 static QString REPOSITORY_WORK_SUFFIX = QString{"_work"};
26 27 static QString REPOSITORY_TRASH_SUFFIX = QString{"_trash"};
27 28 }
28 29
29 30 class CatalogueController::CatalogueControllerPrivate {
30 31
31 32 public:
32 33 explicit CatalogueControllerPrivate(CatalogueController *parent) : m_Q{parent} {}
33 34
34 35 QMutex m_WorkingMutex;
35 36 CatalogueDao m_CatalogueDao;
36 37
37 38 QStringList m_RepositoryList;
38 39 CatalogueController *m_Q;
39 40
40 41 void copyDBtoDB(const QString &dbFrom, const QString &dbTo);
41 42 QString toWorkRepository(QString repository);
42 43 QString toSyncRepository(QString repository);
43 44 void savAllDB();
44 45
45 46 void saveEvent(std::shared_ptr<DBEvent> event, bool persist = true);
46 47 void saveCatalogue(std::shared_ptr<DBCatalogue> catalogue, bool persist = true);
47 48 };
48 49
49 50 CatalogueController::CatalogueController(QObject *parent)
50 51 : impl{spimpl::make_unique_impl<CatalogueControllerPrivate>(this)}
51 52 {
52 53 qCDebug(LOG_CatalogueController()) << tr("CatalogueController construction")
53 54 << QThread::currentThread();
54 55 }
55 56
56 57 CatalogueController::~CatalogueController()
57 58 {
58 59 qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction")
59 60 << QThread::currentThread();
60 61 this->waitForFinish();
61 62 }
62 63
63 64 QStringList CatalogueController::getRepositories() const
64 65 {
65 66 return impl->m_RepositoryList;
66 67 }
67 68
68 69 void CatalogueController::addDB(const QString &dbPath)
69 70 {
70 71 QDir dbDir(dbPath);
71 72 if (dbDir.exists()) {
72 73 auto dirName = dbDir.dirName();
73 74
74 75 if (std::find(impl->m_RepositoryList.cbegin(), impl->m_RepositoryList.cend(), dirName)
75 76 != impl->m_RepositoryList.cend()) {
76 77 qCCritical(LOG_CatalogueController())
77 78 << tr("Impossible to addDB that is already loaded");
78 79 }
79 80
80 81 if (!impl->m_CatalogueDao.addDB(dbPath, dirName)) {
81 82 qCCritical(LOG_CatalogueController())
82 83 << tr("Impossible to addDB %1 from %2 ").arg(dirName, dbPath);
83 84 }
84 85 else {
85 86 impl->m_RepositoryList << dirName;
86 87 impl->copyDBtoDB(dirName, impl->toWorkRepository(dirName));
87 88 }
88 89 }
89 90 else {
90 91 qCCritical(LOG_CatalogueController()) << tr("Impossible to addDB that not exists: ")
91 92 << dbPath;
92 93 }
93 94 }
94 95
95 96 void CatalogueController::saveDB(const QString &destinationPath, const QString &repository)
96 97 {
97 98 if (!impl->m_CatalogueDao.saveDB(destinationPath, repository)) {
98 99 qCCritical(LOG_CatalogueController())
99 100 << tr("Impossible to saveDB %1 from %2 ").arg(repository, destinationPath);
100 101 }
101 102 }
102 103
103 104 std::list<std::shared_ptr<DBEvent> >
104 105 CatalogueController::retrieveEvents(const QString &repository) const
105 106 {
106 107 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
107 108
108 109 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
109 110 auto events = impl->m_CatalogueDao.getEvents(impl->toWorkRepository(dbDireName));
110 111 for (auto event : events) {
111 112 eventsShared.push_back(std::make_shared<DBEvent>(event));
112 113 }
113 114 return eventsShared;
114 115 }
115 116
116 117 std::list<std::shared_ptr<DBEvent> > CatalogueController::retrieveAllEvents() const
117 118 {
118 119 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
119 120 for (auto repository : impl->m_RepositoryList) {
120 121 eventsShared.splice(eventsShared.end(), retrieveEvents(repository));
121 122 }
122 123
123 124 return eventsShared;
124 125 }
125 126
126 127 std::list<std::shared_ptr<DBEvent> >
127 128 CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr<DBCatalogue> catalogue) const
128 129 {
129 130 auto eventsShared = std::list<std::shared_ptr<DBEvent> >{};
130 131 auto events = impl->m_CatalogueDao.getCatalogueEvents(*catalogue);
131 132 for (auto event : events) {
132 133 eventsShared.push_back(std::make_shared<DBEvent>(event));
133 134 }
134 135 return eventsShared;
135 136 }
136 137
137 138 void CatalogueController::updateEvent(std::shared_ptr<DBEvent> event)
138 139 {
139 140 event->setRepository(impl->toWorkRepository(event->getRepository()));
140 141
141 142 impl->m_CatalogueDao.updateEvent(*event);
142 143 }
143 144
144 145 void CatalogueController::updateEventProduct(std::shared_ptr<DBEventProduct> eventProduct)
145 146 {
146 147 impl->m_CatalogueDao.updateEventProduct(*eventProduct);
147 148 }
148 149
149 150 void CatalogueController::removeEvent(std::shared_ptr<DBEvent> event)
150 151 {
151 152 // Remove it from both repository and repository_work
152 153 event->setRepository(impl->toWorkRepository(event->getRepository()));
153 154 impl->m_CatalogueDao.removeEvent(*event);
154 155 event->setRepository(impl->toSyncRepository(event->getRepository()));
155 156 impl->m_CatalogueDao.removeEvent(*event);
156 157 }
157 158
158 159 void CatalogueController::addEvent(std::shared_ptr<DBEvent> event)
159 160 {
160 161 event->setRepository(impl->toWorkRepository(event->getRepository()));
161 162
162 163 auto eventTemp = *event;
163 164 impl->m_CatalogueDao.addEvent(eventTemp);
164 165
165 166 // Call update is necessary at the creation of add Event if it has some tags or some event
166 167 // products
167 168 if (!event->getEventProducts().empty() || !event->getTags().empty()) {
168 169
169 170 auto eventProductsTemp = eventTemp.getEventProducts();
170 171 auto eventProductTempUpdated = std::list<DBEventProduct>{};
171 172 for (auto eventProductTemp : eventProductsTemp) {
172 173 eventProductTemp.setEvent(eventTemp);
173 174 eventProductTempUpdated.push_back(eventProductTemp);
174 175 }
175 176 eventTemp.setEventProducts(eventProductTempUpdated);
176 177
177 178 impl->m_CatalogueDao.updateEvent(eventTemp);
178 179 }
179 180 }
180 181
181 182 void CatalogueController::saveEvent(std::shared_ptr<DBEvent> event)
182 183 {
183 184 impl->saveEvent(event, true);
184 185 }
185 186
186 187 std::list<std::shared_ptr<DBCatalogue> >
187 188 CatalogueController::retrieveCatalogues(const QString &repository) const
188 189 {
189 190 QString dbDireName = repository.isEmpty() ? REPOSITORY_DEFAULT : repository;
190 191
191 192 auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
192 193 auto catalogues = impl->m_CatalogueDao.getCatalogues(impl->toWorkRepository(dbDireName));
193 194 for (auto catalogue : catalogues) {
194 195 cataloguesShared.push_back(std::make_shared<DBCatalogue>(catalogue));
195 196 }
196 197 return cataloguesShared;
197 198 }
198 199
199 200 void CatalogueController::updateCatalogue(std::shared_ptr<DBCatalogue> catalogue)
200 201 {
201 202 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
202 203
203 204 impl->m_CatalogueDao.updateCatalogue(*catalogue);
204 205 }
205 206
206 207 void CatalogueController::removeCatalogue(std::shared_ptr<DBCatalogue> catalogue)
207 208 {
208 209 // Remove it from both repository and repository_work
209 210 catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository()));
210 211 impl->m_CatalogueDao.removeCatalogue(*catalogue);
211 212 catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository()));
212 213 impl->m_CatalogueDao.removeCatalogue(*catalogue);
213 214 }
214 215
215 216 void CatalogueController::saveCatalogue(std::shared_ptr<DBCatalogue> catalogue)
216 217 {
217 218 impl->saveCatalogue(catalogue, true);
218 219 }
219 220
220 221 void CatalogueController::saveAll()
221 222 {
222 223 for (auto repository : impl->m_RepositoryList) {
223 224 // Save Event
224 225 auto events = this->retrieveEvents(repository);
225 226 for (auto event : events) {
226 227 impl->saveEvent(event, false);
227 228 }
228 229
229 230 // Save Catalogue
230 231 auto catalogues = this->retrieveCatalogues(repository);
231 232 for (auto catalogue : catalogues) {
232 233 impl->saveCatalogue(catalogue, false);
233 234 }
234 235 }
235 236
236 237 impl->savAllDB();
237 238 }
238 239
240 QByteArray
241 CatalogueController::mimeDataForEvents(const QVector<std::shared_ptr<DBEvent> > &events) const
242 {
243 auto encodedData = QByteArray{};
244
245 QMap<QString, QVariantList> idsPerRepository;
246 for (auto event : events) {
247 idsPerRepository[event->getRepository()] << event->getUniqId();
248 }
249
250 QDataStream stream{&encodedData, QIODevice::WriteOnly};
251 stream << idsPerRepository;
252
253 return encodedData;
254 }
255
256 QVector<std::shared_ptr<DBEvent> >
257 CatalogueController::eventsForMimeData(const QByteArray &mimeData) const
258 {
259 auto events = QVector<std::shared_ptr<DBEvent> >{};
260 QDataStream stream{mimeData};
261
262 QMap<QString, QVariantList> idsPerRepository;
263 stream >> idsPerRepository;
264
265 for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) {
266 auto repository = it.key();
267 auto allRepositoryEvent = retrieveEvents(repository);
268 for (auto uuid : it.value()) {
269 for (auto repositoryEvent : allRepositoryEvent) {
270 if (uuid.toUuid() == repositoryEvent->getUniqId()) {
271 events << repositoryEvent;
272 }
273 }
274 }
275 }
276
277 return events;
278 }
279
239 280 void CatalogueController::initialize()
240 281 {
241 282 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init")
242 283 << QThread::currentThread();
243 284 impl->m_WorkingMutex.lock();
244 285 impl->m_CatalogueDao.initialize();
245 286 auto defaultRepositoryLocation
246 287 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
247 288
248 289 QDir defaultRepositoryLocationDir;
249 290 if (defaultRepositoryLocationDir.mkpath(defaultRepositoryLocation)) {
250 291 defaultRepositoryLocationDir.cd(defaultRepositoryLocation);
251 292 auto defaultRepository = defaultRepositoryLocationDir.absoluteFilePath(REPOSITORY_DEFAULT);
252 293 qCInfo(LOG_CatalogueController()) << tr("Persistant data loading from: ")
253 294 << defaultRepository;
254 295 this->addDB(defaultRepository);
255 296 }
256 297 else {
257 298 qCWarning(LOG_CatalogueController())
258 299 << tr("Cannot load the persistent default repository from ")
259 300 << defaultRepositoryLocation;
260 301 }
261 302
262 303 qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END");
263 304 }
264 305
265 306 void CatalogueController::finalize()
266 307 {
267 308 impl->m_WorkingMutex.unlock();
268 309 }
269 310
270 311 void CatalogueController::waitForFinish()
271 312 {
272 313 QMutexLocker locker{&impl->m_WorkingMutex};
273 314 }
274 315
275 316 void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom,
276 317 const QString &dbTo)
277 318 {
278 319 // auto cataloguesShared = std::list<std::shared_ptr<DBCatalogue> >{};
279 320 auto catalogues = m_CatalogueDao.getCatalogues(dbFrom);
280 321 auto events = m_CatalogueDao.getEvents(dbFrom);
281 322 for (auto catalogue : catalogues) {
282 323 m_CatalogueDao.copyCatalogue(catalogue, dbTo, true);
283 324 }
284 325
285 326 for (auto event : events) {
286 327 m_CatalogueDao.copyEvent(event, dbTo, true);
287 328 }
288 329 }
289 330
290 331 QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QString repository)
291 332 {
292 333 auto syncRepository = toSyncRepository(repository);
293 334
294 335 return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX);
295 336 }
296 337
297 338 QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository)
298 339 {
299 340 auto syncRepository = repository;
300 341 if (repository.endsWith(REPOSITORY_WORK_SUFFIX)) {
301 342 syncRepository.remove(REPOSITORY_WORK_SUFFIX);
302 343 }
303 344 else if (repository.endsWith(REPOSITORY_TRASH_SUFFIX)) {
304 345 syncRepository.remove(REPOSITORY_TRASH_SUFFIX);
305 346 }
306 347 return syncRepository;
307 348 }
308 349
309 350 void CatalogueController::CatalogueControllerPrivate::savAllDB()
310 351 {
311 352 for (auto repository : m_RepositoryList) {
312 353 auto defaultRepositoryLocation
313 354 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
314 355 m_CatalogueDao.saveDB(defaultRepositoryLocation, repository);
315 356 }
316 357 }
317 358
318 359 void CatalogueController::CatalogueControllerPrivate::saveEvent(std::shared_ptr<DBEvent> event,
319 360 bool persist)
320 361 {
321 362 m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()), true);
322 363 if (persist) {
323 364 savAllDB();
324 365 }
325 366 }
326 367
327 368 void CatalogueController::CatalogueControllerPrivate::saveCatalogue(
328 369 std::shared_ptr<DBCatalogue> catalogue, bool persist)
329 370 {
330 371 m_CatalogueDao.copyCatalogue(*catalogue, toSyncRepository(catalogue->getRepository()), true);
331 372 if (persist) {
332 373 savAllDB();
333 374 }
334 375 }
@@ -1,34 +1,35
1 1 #ifndef SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
2 2 #define SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QVariant>
6 6 #include <QVector>
7 7
8 8 class QMimeData;
9 9
10 10 class CatalogueAbstractTreeItem {
11 11 public:
12 12 constexpr static const int DEFAULT_TYPE = -1;
13 13
14 14 CatalogueAbstractTreeItem(int type = DEFAULT_TYPE);
15 15 virtual ~CatalogueAbstractTreeItem();
16 16
17 17 void addChild(CatalogueAbstractTreeItem *child);
18 18 QVector<CatalogueAbstractTreeItem *> children() const;
19 19 CatalogueAbstractTreeItem *parent() const;
20 20
21 21 int type() const;
22 22 QString text(int column = 0) const;
23 23
24 24 virtual QVariant data(int column, int role) const;
25 25 virtual Qt::ItemFlags flags(int column) const;
26 26 virtual bool setData(int column, int role, const QVariant &value);
27 27 virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action);
28 virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action);
28 29
29 30 private:
30 31 class CatalogueAbstractTreeItemPrivate;
31 32 spimpl::unique_impl_ptr<CatalogueAbstractTreeItemPrivate> impl;
32 33 };
33 34
34 35 #endif // SCIQLOP_CATALOGUEABSTRACTTREEITEM_H
@@ -1,27 +1,28
1 1 #ifndef SCIQLOP_CATALOGUETREEITEM_H
2 2 #define SCIQLOP_CATALOGUETREEITEM_H
3 3
4 4 #include <Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h>
5 5 #include <Common/spimpl.h>
6 6
7 7 class DBCatalogue;
8 8
9 9
10 10 class CatalogueTreeItem : public CatalogueAbstractTreeItem {
11 11 public:
12 12 CatalogueTreeItem(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon, int type);
13 13
14 14 QVariant data(int column, int role) const override;
15 15 bool setData(int column, int role, const QVariant &value) override;
16 16 Qt::ItemFlags flags(int column) const override;
17 17 bool canDropMimeData(const QMimeData *data, Qt::DropAction action) override;
18 bool dropMimeData(const QMimeData *data, Qt::DropAction action) override;
18 19
19 20 /// Returns the catalogue represented by the item
20 21 std::shared_ptr<DBCatalogue> catalogue() const;
21 22
22 23 private:
23 24 class CatalogueTreeItemPrivate;
24 25 spimpl::unique_impl_ptr<CatalogueTreeItemPrivate> impl;
25 26 };
26 27
27 28 #endif // SCIQLOP_CATALOGUETREEITEM_H
@@ -1,55 +1,56
1 1 #ifndef SCIQLOP_CATALOGUETREEMODEL_H
2 2 #define SCIQLOP_CATALOGUETREEMODEL_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QAbstractItemModel>
6 6
7 7 class CatalogueAbstractTreeItem;
8 8
9 9 /**
10 10 * @brief Model to display catalogue items based on QTreeWidgetItem
11 11 * @warning Do not use the method QTreeWidgetItem::treeWidget for an item added to this model or the
12 12 * application will crash
13 13 */
14 14 class CatalogueTreeModel : public QAbstractItemModel {
15 15 Q_OBJECT
16 16
17 17 signals:
18 18 void itemRenamed(const QModelIndex &index);
19 void itemDropped(const QModelIndex &parentIndex);
19 20
20 21 public:
21 22 CatalogueTreeModel(QObject *parent = nullptr);
22 23
23 24 enum class Column { Name, Validation, Count };
24 25
25 26 QModelIndex addTopLevelItem(CatalogueAbstractTreeItem *item);
26 27 QVector<CatalogueAbstractTreeItem *> topLevelItems() const;
27 28
28 29 void addChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex);
29 30
30 31 CatalogueAbstractTreeItem *item(const QModelIndex &index) const;
31 32 QModelIndex indexOf(CatalogueAbstractTreeItem *item, int column = 0) const;
32 33
33 34 // model
34 35 QModelIndex index(int row, int column,
35 36 const QModelIndex &parent = QModelIndex()) const override;
36 37 QModelIndex parent(const QModelIndex &index) const override;
37 38 int rowCount(const QModelIndex &parent) const override;
38 39 int columnCount(const QModelIndex &parent) const override;
39 40 Qt::ItemFlags flags(const QModelIndex &index) const override;
40 41 QVariant data(const QModelIndex &index, int role) const override;
41 42 bool setData(const QModelIndex &index, const QVariant &value, int role) override;
42 43
43 44 bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
44 45 const QModelIndex &parent) const override;
45 46 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
46 47 const QModelIndex &parent) override;
47 48 Qt::DropActions supportedDropActions() const;
48 49 QStringList mimeTypes() const;
49 50
50 51 private:
51 52 class CatalogueTreeModelPrivate;
52 53 spimpl::unique_impl_ptr<CatalogueTreeModelPrivate> impl;
53 54 };
54 55
55 56 #endif // CATALOGUETREEMODEL_H
@@ -1,442 +1,444
1 1 #include "Catalogue/CatalogueEventsModel.h"
2 2
3 #include <Catalogue/CatalogueController.h>
3 4 #include <Common/DateUtils.h>
4 5 #include <Common/MimeTypesDef.h>
5 6 #include <DBEvent.h>
6 7 #include <DBEventProduct.h>
7 8 #include <DBTag.h>
8 9 #include <Data/SqpRange.h>
9 10 #include <SqpApplication.h>
10 11 #include <Time/TimeController.h>
11 12
12 13 #include <list>
13 14 #include <unordered_map>
14 15
15 16 #include <QHash>
16 17 #include <QMimeData>
17 18
18 19 Q_LOGGING_CATEGORY(LOG_CatalogueEventsModel, "CatalogueEventsModel")
19 20
20 21 const auto EVENT_ITEM_TYPE = 1;
21 22 const auto EVENT_PRODUCT_ITEM_TYPE = 2;
22 23
23 24 struct CatalogueEventsModel::CatalogueEventsModelPrivate {
24 25 QVector<std::shared_ptr<DBEvent> > m_Events;
25 26 std::unordered_map<DBEvent *, QVector<std::shared_ptr<DBEventProduct> > > m_EventProducts;
26 27 std::unordered_set<std::shared_ptr<DBEvent> > m_EventsWithChanges;
27 28
28 29 QStringList columnNames()
29 30 {
30 31 return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
31 32 tr("Tags"), tr("Product"), tr("")};
32 33 }
33 34
34 35 QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
35 36 {
36 37 if (col == (int)CatalogueEventsModel::Column::Validation) {
37 38 return m_EventsWithChanges.find(event) != m_EventsWithChanges.cend() ? true
38 39 : QVariant();
39 40 }
40 41
41 42 return eventData(col, event);
42 43 }
43 44
44 45 QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
45 46 {
46 47 switch (static_cast<Column>(col)) {
47 48 case CatalogueEventsModel::Column::Name:
48 49 return event->getName();
49 50 case CatalogueEventsModel::Column::TStart:
50 51 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
51 52 : QVariant{};
52 53 case CatalogueEventsModel::Column::TEnd:
53 54 return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
54 55 : QVariant{};
55 56 case CatalogueEventsModel::Column::Product:
56 57 return QString::number(nbEventProducts(event)) + " product(s)";
57 58 case CatalogueEventsModel::Column::Tags: {
58 59 QString tagList;
59 60 auto tags = event->getTags();
60 61 for (auto tag : tags) {
61 62 tagList += tag.getName();
62 63 tagList += ' ';
63 64 }
64 65
65 66 return tagList;
66 67 }
67 68 case CatalogueEventsModel::Column::Validation:
68 69 return QVariant();
69 70 default:
70 71 break;
71 72 }
72 73
73 74 Q_ASSERT(false);
74 75 return QStringLiteral("Unknown Data");
75 76 }
76 77
77 78 void parseEventProduct(const std::shared_ptr<DBEvent> &event)
78 79 {
79 80 for (auto product : event->getEventProducts()) {
80 81 m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
81 82 }
82 83 }
83 84
84 85 int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
85 86 {
86 87 auto eventProductsIt = m_EventProducts.find(event.get());
87 88 if (eventProductsIt != m_EventProducts.cend()) {
88 89 return m_EventProducts.at(event.get()).count();
89 90 }
90 91 else {
91 92 return 0;
92 93 }
93 94 }
94 95
95 96 QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
96 97 {
97 98 switch (static_cast<Column>(col)) {
98 99 case CatalogueEventsModel::Column::Name:
99 100 return eventProduct->getProductId();
100 101 case CatalogueEventsModel::Column::TStart:
101 102 return DateUtils::dateTime(eventProduct->getTStart());
102 103 case CatalogueEventsModel::Column::TEnd:
103 104 return DateUtils::dateTime(eventProduct->getTEnd());
104 105 case CatalogueEventsModel::Column::Product:
105 106 return eventProduct->getProductId();
106 107 case CatalogueEventsModel::Column::Tags:
107 108 return QString();
108 109 case CatalogueEventsModel::Column::Validation:
109 110 return QVariant();
110 111 default:
111 112 break;
112 113 }
113 114
114 115 Q_ASSERT(false);
115 116 return QStringLiteral("Unknown Data");
116 117 }
117 118 };
118 119
119 120 CatalogueEventsModel::CatalogueEventsModel(QObject *parent)
120 121 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
121 122 {
122 123 }
123 124
124 125 void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
125 126 {
126 127 beginResetModel();
127 128
128 129 impl->m_Events = events;
129 130 impl->m_EventProducts.clear();
130 131 impl->m_EventsWithChanges.clear();
131 132 for (auto event : events) {
132 133 impl->parseEventProduct(event);
133 134 }
134 135
135 136 endResetModel();
136 137 }
137 138
138 139 std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
139 140 {
140 141 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
141 142 return impl->m_Events.value(index.row());
142 143 }
143 144 else {
144 145 return nullptr;
145 146 }
146 147 }
147 148
148 149 std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
149 150 {
150 151 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
151 152 return getEvent(index.parent());
152 153 }
153 154 else {
154 155 return nullptr;
155 156 }
156 157 }
157 158
158 159 std::shared_ptr<DBEventProduct>
159 160 CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
160 161 {
161 162 if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
162 163 auto event = static_cast<DBEvent *>(index.internalPointer());
163 164 return impl->m_EventProducts.at(event).value(index.row());
164 165 }
165 166 else {
166 167 return nullptr;
167 168 }
168 169 }
169 170
170 171 void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
171 172 {
172 173 beginInsertRows(QModelIndex(), impl->m_Events.count() - 1, impl->m_Events.count() - 1);
173 174 impl->m_Events.append(event);
174 175 impl->parseEventProduct(event);
175 176 endInsertRows();
176 177 }
177 178
178 179 void CatalogueEventsModel::removeEvent(const std::shared_ptr<DBEvent> &event)
179 180 {
180 181 auto index = impl->m_Events.indexOf(event);
181 182 if (index >= 0) {
182 183 beginRemoveRows(QModelIndex(), index, index);
183 184 impl->m_Events.removeAt(index);
184 185 impl->m_EventProducts.erase(event.get());
185 186 impl->m_EventsWithChanges.erase(event);
186 187 endRemoveRows();
187 188 }
188 189 }
189 190
190 191 QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
191 192 {
192 193 return impl->m_Events;
193 194 }
194 195
195 196 void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &event)
196 197 {
197 198 auto eventIndex = indexOf(event);
198 199 if (eventIndex.isValid()) {
199 200
200 201 // Refreshes the event line
201 202 auto colCount = columnCount();
202 203 emit dataChanged(eventIndex, index(eventIndex.row(), colCount));
203 204
204 205 // Also refreshes its children event products
205 206 auto childCount = rowCount(eventIndex);
206 207 emit dataChanged(index(0, 0, eventIndex), index(childCount, colCount, eventIndex));
207 208 }
208 209 else {
209 210 qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
210 211 }
211 212 }
212 213
213 214 QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
214 215 {
215 216 auto row = impl->m_Events.indexOf(event);
216 217 if (row >= 0) {
217 218 return index(row, 0);
218 219 }
219 220
220 221 return QModelIndex();
221 222 }
222 223
223 224 void CatalogueEventsModel::setEventHasChanges(const std::shared_ptr<DBEvent> &event,
224 225 bool hasChanges)
225 226 {
226 227 if (hasChanges) {
227 228 impl->m_EventsWithChanges.insert(event);
228 229 }
229 230 else {
230 231 impl->m_EventsWithChanges.erase(event);
231 232 }
232 233 }
233 234
234 235 bool CatalogueEventsModel::eventsHasChanges(const std::shared_ptr<DBEvent> &event) const
235 236 {
236 237 return impl->m_EventsWithChanges.find(event) != impl->m_EventsWithChanges.cend();
237 238 }
238 239
239 240 QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
240 241 {
241 242 if (!hasIndex(row, column, parent)) {
242 243 return QModelIndex();
243 244 }
244 245
245 246 switch (itemTypeOf(parent)) {
246 247 case CatalogueEventsModel::ItemType::Root:
247 248 return createIndex(row, column);
248 249 case CatalogueEventsModel::ItemType::Event: {
249 250 auto event = getEvent(parent);
250 251 return createIndex(row, column, event.get());
251 252 }
252 253 case CatalogueEventsModel::ItemType::EventProduct:
253 254 break;
254 255 default:
255 256 break;
256 257 }
257 258
258 259 return QModelIndex();
259 260 }
260 261
261 262 QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
262 263 {
263 264 switch (itemTypeOf(index)) {
264 265 case CatalogueEventsModel::ItemType::EventProduct: {
265 266 auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
266 267 auto it
267 268 = std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
268 269 [parentEvent](auto event) { return event.get() == parentEvent; });
269 270
270 271 if (it != impl->m_Events.cend()) {
271 272 return createIndex(it - impl->m_Events.cbegin(), 0);
272 273 }
273 274 else {
274 275 return QModelIndex();
275 276 }
276 277 }
277 278 case CatalogueEventsModel::ItemType::Root:
278 279 break;
279 280 case CatalogueEventsModel::ItemType::Event:
280 281 break;
281 282 default:
282 283 break;
283 284 }
284 285
285 286 return QModelIndex();
286 287 }
287 288
288 289 int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
289 290 {
290 291 if (parent.column() > 0) {
291 292 return 0;
292 293 }
293 294
294 295 switch (itemTypeOf(parent)) {
295 296 case CatalogueEventsModel::ItemType::Root:
296 297 return impl->m_Events.count();
297 298 case CatalogueEventsModel::ItemType::Event: {
298 299 auto event = getEvent(parent);
299 300 return impl->m_EventProducts[event.get()].count();
300 301 }
301 302 case CatalogueEventsModel::ItemType::EventProduct:
302 303 break;
303 304 default:
304 305 break;
305 306 }
306 307
307 308 return 0;
308 309 }
309 310
310 311 int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
311 312 {
312 313 return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
313 314 }
314 315
315 316 Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
316 317 {
317 318 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
318 319 }
319 320
320 321 QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
321 322 {
322 323 if (index.isValid()) {
323 324
324 325 auto type = itemTypeOf(index);
325 326 if (type == CatalogueEventsModel::ItemType::Event) {
326 327 auto event = getEvent(index);
327 328 switch (role) {
328 329 case Qt::DisplayRole:
329 330 return impl->eventData(index.column(), event);
330 331 break;
331 332 }
332 333 }
333 334 else if (type == CatalogueEventsModel::ItemType::EventProduct) {
334 335 auto product = getEventProduct(index);
335 336 switch (role) {
336 337 case Qt::DisplayRole:
337 338 return impl->eventProductData(index.column(), product);
338 339 break;
339 340 }
340 341 }
341 342 }
342 343
343 344 return QVariant{};
344 345 }
345 346
346 347 QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
347 348 {
348 349 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
349 350 return impl->columnNames().value(section);
350 351 }
351 352
352 353 return QVariant();
353 354 }
354 355
355 356 void CatalogueEventsModel::sort(int column, Qt::SortOrder order)
356 357 {
357 358 std::sort(impl->m_Events.begin(), impl->m_Events.end(),
358 359 [this, column, order](auto e1, auto e2) {
359 360 auto data1 = impl->sortData(column, e1);
360 361 auto data2 = impl->sortData(column, e2);
361 362
362 363 auto result = data1.toString() < data2.toString();
363 364
364 365 return order == Qt::AscendingOrder ? result : !result;
365 366 });
366 367
367 368 emit dataChanged(QModelIndex(), QModelIndex());
368 369 emit modelSorted();
369 370 }
370 371
371 372 Qt::DropActions CatalogueEventsModel::supportedDragActions() const
372 373 {
373 return Qt::CopyAction | Qt::MoveAction;
374 return Qt::CopyAction;
374 375 }
375 376
376 377 QStringList CatalogueEventsModel::mimeTypes() const
377 378 {
378 379 return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE};
379 380 }
380 381
381 382 QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
382 383 {
383 384 auto mimeData = new QMimeData;
384 385
385 386 bool isFirst = true;
386 387
387 388 QVector<std::shared_ptr<DBEvent> > eventList;
388 389 QVector<std::shared_ptr<DBEventProduct> > eventProductList;
389 390
390 391 SqpRange firstTimeRange;
391 392 for (const auto &index : indexes) {
392 393 if (index.column() == 0) { // only the first column
393 394
394 395 auto type = itemTypeOf(index);
395 396 if (type == ItemType::Event) {
396 397 auto event = getEvent(index);
397 398 eventList << event;
398 399
399 400 if (isFirst) {
400 401 isFirst = false;
401 402 firstTimeRange.m_TStart = event->getTStart();
402 403 firstTimeRange.m_TEnd = event->getTEnd();
403 404 }
404 405 }
405 406 else if (type == ItemType::EventProduct) {
406 407 auto product = getEventProduct(index);
407 408 eventProductList << product;
408 409
409 410 if (isFirst) {
410 411 isFirst = false;
411 412 firstTimeRange.m_TStart = product->getTStart();
412 413 firstTimeRange.m_TEnd = product->getTEnd();
413 414 }
414 415 }
415 416 }
416 417 }
417 418
418 auto eventsEncodedData
419 = QByteArray{}; // sqpApp->catalogueController().->mimeDataForEvents(eventList); //TODO
420 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
419 if (!eventList.isEmpty() && eventProductList.isEmpty()) {
420 auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList);
421 mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData);
422 }
421 423
422 424 if (eventList.count() + eventProductList.count() == 1) {
423 425 // No time range MIME data if multiple events are dragged
424 426 auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
425 427 mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
426 428 }
427 429
428 430 return mimeData;
429 431 }
430 432
431 433 CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
432 434 {
433 435 if (!index.isValid()) {
434 436 return ItemType::Root;
435 437 }
436 438 else if (index.internalPointer() == nullptr) {
437 439 return ItemType::Event;
438 440 }
439 441 else {
440 442 return ItemType::EventProduct;
441 443 }
442 444 }
@@ -1,295 +1,313
1 1 #include "Catalogue/CatalogueSideBarWidget.h"
2 2 #include "ui_CatalogueSideBarWidget.h"
3 3 #include <SqpApplication.h>
4 4
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h>
8 8 #include <Catalogue/CatalogueTreeItems/CatalogueTreeItem.h>
9 9 #include <Catalogue/CatalogueTreeModel.h>
10 10 #include <CatalogueDao.h>
11 11 #include <ComparaisonPredicate.h>
12 12 #include <DBCatalogue.h>
13 13
14 14 #include <QMenu>
15 15
16 16 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
17 17
18 18
19 19 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
20 20 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
21 21 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
22 22 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
23 23
24 24
25 25 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
26 26
27 27 CatalogueTreeModel *m_TreeModel = nullptr;
28 28
29 29 void configureTreeWidget(QTreeView *treeView);
30 30 QModelIndex addDatabaseItem(const QString &name);
31 31 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
32 32 void addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
33 33 const QModelIndex &databaseIndex);
34 34
35 35 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
36 36 void setHasChanges(bool value, const QModelIndex &index, QTreeView *treeView);
37 37 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
38
39 int selectionType(QTreeView *treeView) const
40 {
41 auto selectedItems = treeView->selectionModel()->selectedRows();
42 if (selectedItems.isEmpty()) {
43 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
44 }
45 else {
46 auto firstIndex = selectedItems.first();
47 auto firstItem = m_TreeModel->item(firstIndex);
48 if (!firstItem) {
49 Q_ASSERT(false);
50 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
51 }
52 auto selectionType = firstItem->type();
53
54 for (auto itemIndex : selectedItems) {
55 auto item = m_TreeModel->item(itemIndex);
56 if (!item || item->type() != selectionType) {
57 // Incoherent multi selection
58 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
59 break;
60 }
61 }
62
63 return selectionType;
64 }
65 }
66
67 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
68 {
69 QVector<std::shared_ptr<DBCatalogue> > catalogues;
70 auto selectedItems = treeView->selectionModel()->selectedRows();
71 for (auto itemIndex : selectedItems) {
72 auto item = m_TreeModel->item(itemIndex);
73 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
74 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
75 }
76 }
77
78 return catalogues;
79 }
80
81 QStringList selectedRepositories(QTreeView *treeView) const
82 {
83 QStringList repositories;
84 auto selectedItems = treeView->selectionModel()->selectedRows();
85 for (auto itemIndex : selectedItems) {
86 auto item = m_TreeModel->item(itemIndex);
87 if (item && item->type() == DATABASE_ITEM_TYPE) {
88 repositories.append(item->text());
89 }
90 }
91
92 return repositories;
93 }
38 94 };
39 95
40 96 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
41 97 : QWidget(parent),
42 98 ui(new Ui::CatalogueSideBarWidget),
43 99 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
44 100 {
45 101 ui->setupUi(this);
46 102
47 103 impl->m_TreeModel = new CatalogueTreeModel(this);
48 104 ui->treeView->setModel(impl->m_TreeModel);
49 105
50 106 impl->configureTreeWidget(ui->treeView);
51 107
52 108 ui->treeView->header()->setStretchLastSection(false);
53 109 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
54 110 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
55 111
56 112 auto emitSelection = [this]() {
57 113
58 auto selectedItems = ui->treeView->selectionModel()->selectedRows();
59 if (selectedItems.isEmpty()) {
60 emit this->selectionCleared();
114 auto selectionType = impl->selectionType(ui->treeView);
115
116 switch (selectionType) {
117 case CATALOGUE_ITEM_TYPE:
118 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
119 break;
120 case DATABASE_ITEM_TYPE:
121 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
122 break;
123 case ALL_EVENT_ITEM_TYPE:
124 emit this->allEventsSelected();
125 break;
126 case TRASH_ITEM_TYPE:
127 emit this->trashSelected();
128 break;
129 default:
130 emit this->selectionCleared();
131 break;
61 132 }
62 else {
63 QVector<std::shared_ptr<DBCatalogue> > catalogues;
64 QStringList databases;
65 auto firstIndex = selectedItems.first();
66 auto firstItem = impl->m_TreeModel->item(firstIndex);
67 if (!firstItem) {
68 Q_ASSERT(false);
69 return;
70 }
71 auto selectionType = firstItem->type();
72
73 for (auto itemIndex : selectedItems) {
74 auto item = impl->m_TreeModel->item(itemIndex);
75 if (item && item->type() == selectionType) {
76 switch (selectionType) {
77 case CATALOGUE_ITEM_TYPE:
78 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
79 break;
80 case DATABASE_ITEM_TYPE:
81 databases.append(item->text());
82 case ALL_EVENT_ITEM_TYPE: // fallthrough
83 case TRASH_ITEM_TYPE: // fallthrough
84 default:
85 break;
86 }
87 }
88 else {
89 // Incoherent multi selection
90 selectionType = -1;
91 break;
92 }
93 }
94
95 switch (selectionType) {
96 case CATALOGUE_ITEM_TYPE:
97 emit this->catalogueSelected(catalogues);
98 break;
99 case DATABASE_ITEM_TYPE:
100 emit this->databaseSelected(databases);
101 break;
102 case ALL_EVENT_ITEM_TYPE:
103 emit this->allEventsSelected();
104 break;
105 case TRASH_ITEM_TYPE:
106 emit this->trashSelected();
107 break;
108 default:
109 emit this->selectionCleared();
110 break;
111 }
112 }
113
114
115 133 };
116 134
117 135 connect(ui->treeView, &QTreeView::clicked, emitSelection);
118 136 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, emitSelection);
119 137 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [emitSelection, this](auto index) {
120 138 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
121 139 if (selectedIndexes.contains(index)) {
122 140 emitSelection();
123 141 }
124 142
125 143 auto item = impl->m_TreeModel->item(index);
126 144 impl->setHasChanges(true, index, ui->treeView);
127 145 });
128 146
129 147 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
130 148 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
131 149 &CatalogueSideBarWidget::onContextMenuRequested);
132 150 }
133 151
134 152 CatalogueSideBarWidget::~CatalogueSideBarWidget()
135 153 {
136 154 delete ui;
137 155 }
138 156
139 157 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
140 158 bool hasChanges)
141 159 {
142 160 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
143 161 auto index = impl->m_TreeModel->indexOf(catalogueItem);
144 162 impl->setHasChanges(hasChanges, index, ui->treeView);
145 163 // catalogueItem->refresh();
146 164 }
147 165 }
148 166
149 167 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
150 168 {
151 169 QMenu menu{this};
152 170
153 171 auto currentIndex = ui->treeView->currentIndex();
154 172 auto currentItem = impl->m_TreeModel->item(currentIndex);
155 173 if (!currentItem) {
156 174 return;
157 175 }
158 176
159 177 switch (currentItem->type()) {
160 178 case CATALOGUE_ITEM_TYPE:
161 179 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
162 180 break;
163 181 case DATABASE_ITEM_TYPE:
164 182 break;
165 183 case ALL_EVENT_ITEM_TYPE:
166 184 break;
167 185 case TRASH_ITEM_TYPE:
168 186 menu.addAction("Empty Trash", []() {
169 187 // TODO
170 188 });
171 189 break;
172 190 default:
173 191 break;
174 192 }
175 193
176 194 if (!menu.isEmpty()) {
177 195 menu.exec(ui->treeView->mapToGlobal(pos));
178 196 }
179 197 }
180 198
181 199 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
182 200 {
183 201 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
184 202 ALL_EVENT_ITEM_TYPE};
185 203 m_TreeModel->addTopLevelItem(allEventsItem);
186 204
187 205 auto trashItem
188 206 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
189 207 m_TreeModel->addTopLevelItem(trashItem);
190 208
191 209 auto separator = new QFrame{treeView};
192 210 separator->setFrameShape(QFrame::HLine);
193 211 auto separatorItem
194 212 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
195 213 separatorItem->setEnabled(false);
196 214 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
197 215 treeView->setIndexWidget(separatorIndex, separator);
198 216
199 217 auto repositories = sqpApp->catalogueController().getRepositories();
200 218 for (auto dbname : repositories) {
201 219 auto dbIndex = addDatabaseItem(dbname);
202 220 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
203 221 for (auto catalogue : catalogues) {
204 222 addCatalogueItem(catalogue, dbIndex);
205 223 }
206 224 }
207 225
208 226 treeView->expandAll();
209 227 }
210 228
211 229 QModelIndex
212 230 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
213 231 {
214 232 auto databaseItem
215 233 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
216 234 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
217 235
218 236 return databaseIndex;
219 237 }
220 238
221 239 CatalogueAbstractTreeItem *
222 240 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
223 241 {
224 242 for (auto item : m_TreeModel->topLevelItems()) {
225 243 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
226 244 return item;
227 245 }
228 246 }
229 247
230 248 return nullptr;
231 249 }
232 250
233 251 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
234 252 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
235 253 {
236 254 auto catalogueItem
237 255 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
238 256 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
239 257 }
240 258
241 259 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
242 260 const std::shared_ptr<DBCatalogue> &catalogue) const
243 261 {
244 262 for (auto item : m_TreeModel->topLevelItems()) {
245 263 if (item->type() == DATABASE_ITEM_TYPE) {
246 264 for (auto childItem : item->children()) {
247 265 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
248 266 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
249 267 if (catalogueItem->catalogue() == catalogue) {
250 268 return catalogueItem;
251 269 }
252 270 }
253 271 else {
254 272 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
255 273 "structure. A database item should "
256 274 "only contain catalogues.";
257 275 Q_ASSERT(false);
258 276 }
259 277 }
260 278 }
261 279 }
262 280
263 281 return nullptr;
264 282 }
265 283
266 284 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(bool value,
267 285 const QModelIndex &index,
268 286 QTreeView *treeView)
269 287 {
270 288 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
271 289 if (value) {
272 290 if (!hasChanges(validationIndex, treeView)) {
273 291 auto widget = CatalogueExplorerHelper::buildValidationWidget(
274 292 treeView,
275 293 [this, validationIndex, treeView]() {
276 294 setHasChanges(false, validationIndex, treeView);
277 295 },
278 296 [this, validationIndex, treeView]() {
279 297 setHasChanges(false, validationIndex, treeView);
280 298 });
281 299 treeView->setIndexWidget(validationIndex, widget);
282 300 }
283 301 }
284 302 else {
285 303 // Note: the widget is destroyed
286 304 treeView->setIndexWidget(validationIndex, nullptr);
287 305 }
288 306 }
289 307
290 308 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
291 309 QTreeView *treeView)
292 310 {
293 311 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
294 312 return treeView->indexWidget(validationIndex) != nullptr;
295 313 }
@@ -1,74 +1,81
1 1 #include "Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h"
2 2
3 3 struct CatalogueAbstractTreeItem::CatalogueAbstractTreeItemPrivate {
4 4 int m_Type;
5 5 QVector<CatalogueAbstractTreeItem *> m_Children;
6 6 CatalogueAbstractTreeItem *m_Parent = nullptr;
7 7
8 8 CatalogueAbstractTreeItemPrivate(int type) : m_Type(type) {}
9 9 };
10 10
11 11 CatalogueAbstractTreeItem::CatalogueAbstractTreeItem(int type)
12 12 : impl{spimpl::make_unique_impl<CatalogueAbstractTreeItemPrivate>(type)}
13 13 {
14 14 }
15 15
16 16 CatalogueAbstractTreeItem::~CatalogueAbstractTreeItem()
17 17 {
18 18 qDeleteAll(impl->m_Children);
19 19 }
20 20
21 21 void CatalogueAbstractTreeItem::addChild(CatalogueAbstractTreeItem *child)
22 22 {
23 23 impl->m_Children << child;
24 24 child->impl->m_Parent = this;
25 25 }
26 26
27 27 QVector<CatalogueAbstractTreeItem *> CatalogueAbstractTreeItem::children() const
28 28 {
29 29 return impl->m_Children;
30 30 }
31 31
32 32 CatalogueAbstractTreeItem *CatalogueAbstractTreeItem::parent() const
33 33 {
34 34 return impl->m_Parent;
35 35 }
36 36
37 37 int CatalogueAbstractTreeItem::type() const
38 38 {
39 39 return impl->m_Type;
40 40 }
41 41
42 42 QString CatalogueAbstractTreeItem::text(int column) const
43 43 {
44 44 return data(0, Qt::DisplayRole).toString();
45 45 }
46 46
47 47 QVariant CatalogueAbstractTreeItem::data(int column, int role) const
48 48 {
49 49 Q_UNUSED(column);
50 50 Q_UNUSED(role);
51 51 return QVariant();
52 52 }
53 53
54 54 Qt::ItemFlags CatalogueAbstractTreeItem::flags(int column) const
55 55 {
56 56 Q_UNUSED(column);
57 57 return Qt::NoItemFlags;
58 58 }
59 59
60 60 bool CatalogueAbstractTreeItem::setData(int column, int role, const QVariant &value)
61 61 {
62 62 Q_UNUSED(column);
63 63 Q_UNUSED(role);
64 64 Q_UNUSED(value);
65 65
66 66 return false;
67 67 }
68 68
69 69 bool CatalogueAbstractTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action)
70 70 {
71 71 Q_UNUSED(data);
72 72 Q_UNUSED(action);
73 73 return false;
74 74 }
75
76 bool CatalogueAbstractTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
77 {
78 Q_UNUSED(data);
79 Q_UNUSED(action);
80 return false;
81 }
@@ -1,85 +1,95
1 1 #include "Catalogue/CatalogueTreeItems/CatalogueTreeItem.h"
2 2 #include <Catalogue/CatalogueExplorerHelper.h>
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <QIcon>
7 7 #include <QMimeData>
8 8 #include <SqpApplication.h>
9 9
10 10 #include <memory>
11 11
12 12 #include <DBCatalogue.h>
13 13
14 14 struct CatalogueTreeItem::CatalogueTreeItemPrivate {
15 15
16 16 std::shared_ptr<DBCatalogue> m_Catalogue;
17 17 QIcon m_Icon;
18 18
19 19 CatalogueTreeItemPrivate(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon)
20 20 : m_Catalogue(catalogue), m_Icon(icon)
21 21 {
22 22 }
23 23 };
24 24
25 25
26 26 CatalogueTreeItem::CatalogueTreeItem(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon,
27 27 int type)
28 28 : CatalogueAbstractTreeItem(type),
29 29 impl{spimpl::make_unique_impl<CatalogueTreeItemPrivate>(catalogue, icon)}
30 30 {
31 31 }
32 32
33 33 QVariant CatalogueTreeItem::data(int column, int role) const
34 34 {
35 35 if (column == 0) {
36 36 switch (role) {
37 37 case Qt::EditRole: // fallthrough
38 38 case Qt::DisplayRole:
39 39 return impl->m_Catalogue->getName();
40 40 case Qt::DecorationRole:
41 41 return impl->m_Icon;
42 42 default:
43 43 break;
44 44 }
45 45 }
46 46
47 47 return QVariant();
48 48 }
49 49
50 50 bool CatalogueTreeItem::setData(int column, int role, const QVariant &value)
51 51 {
52 52 bool result = false;
53 53
54 54 if (role == Qt::EditRole && column == 0) {
55 55 auto newName = value.toString();
56 56 if (newName != impl->m_Catalogue->getName()) {
57 57 impl->m_Catalogue->setName(newName);
58 58 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
59 59 result = true;
60 60 }
61 61 }
62 62
63 63 return result;
64 64 }
65 65
66 66 Qt::ItemFlags CatalogueTreeItem::flags(int column) const
67 67 {
68 68 if (column == 0) {
69 69 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable
70 70 | Qt::ItemIsDropEnabled;
71 71 }
72 72 else {
73 73 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
74 74 }
75 75 }
76 76
77 77 bool CatalogueTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action)
78 78 {
79 79 return data->hasFormat(MIME_TYPE_EVENT_LIST);
80 80 }
81 81
82 bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
83 {
84 Q_ASSERT(canDropMimeData(data, action));
85
86 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
87 // impl->m_Catalogue->addEvents(events); TODO: move events in the new catalogue
88 // Warning: Check that the events aren't already in the catalogue
89 // Also check for the repository !!!
90 }
91
82 92 std::shared_ptr<DBCatalogue> CatalogueTreeItem::catalogue() const
83 93 {
84 94 return impl->m_Catalogue;
85 95 }
@@ -1,190 +1,201
1 1 #include "Catalogue/CatalogueTreeModel.h"
2 2 #include <Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h>
3 3
4 4 #include <QMimeData>
5 5 #include <memory>
6 6
7 7 #include <Common/MimeTypesDef.h>
8 8
9 9 struct CatalogueTreeModel::CatalogueTreeModelPrivate {
10 10 std::unique_ptr<CatalogueAbstractTreeItem> m_RootItem = nullptr;
11 11
12 12 CatalogueTreeModelPrivate() : m_RootItem{std::make_unique<CatalogueAbstractTreeItem>()} {}
13 13 };
14 14
15 15 CatalogueTreeModel::CatalogueTreeModel(QObject *parent)
16 16 : QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueTreeModelPrivate>()}
17 17 {
18 18 }
19 19
20 20 QModelIndex CatalogueTreeModel::addTopLevelItem(CatalogueAbstractTreeItem *item)
21 21 {
22 22 auto nbTopLevelItems = impl->m_RootItem->children().count();
23 23 beginInsertRows(QModelIndex(), nbTopLevelItems, nbTopLevelItems);
24 24 impl->m_RootItem->addChild(item);
25 25 endInsertRows();
26 26
27 27 emit dataChanged(QModelIndex(), QModelIndex());
28 28
29 29 return index(nbTopLevelItems, 0);
30 30 }
31 31
32 32 QVector<CatalogueAbstractTreeItem *> CatalogueTreeModel::topLevelItems() const
33 33 {
34 34 return impl->m_RootItem->children();
35 35 }
36 36
37 37 void CatalogueTreeModel::addChildItem(CatalogueAbstractTreeItem *child,
38 38 const QModelIndex &parentIndex)
39 39 {
40 40 auto parentItem = item(parentIndex);
41 41 int c = parentItem->children().count();
42 42 beginInsertRows(parentIndex, c, c);
43 43 parentItem->addChild(child);
44 44 endInsertRows();
45 45
46 emit dataChanged(QModelIndex(), QModelIndex());
46 emit dataChanged(parentIndex, parentIndex);
47 47 }
48 48
49 49 CatalogueAbstractTreeItem *CatalogueTreeModel::item(const QModelIndex &index) const
50 50 {
51 51 return static_cast<CatalogueAbstractTreeItem *>(index.internalPointer());
52 52 }
53 53
54 54 QModelIndex CatalogueTreeModel::indexOf(CatalogueAbstractTreeItem *item, int column) const
55 55 {
56 56 auto parentItem = item->parent();
57 57 if (!parentItem) {
58 58 return QModelIndex();
59 59 }
60 60
61 61 auto row = parentItem->children().indexOf(item);
62 62 return createIndex(row, column, item);
63 63 }
64 64
65 65 QModelIndex CatalogueTreeModel::index(int row, int column, const QModelIndex &parent) const
66 66 {
67 67 if (column > 0) {
68 68 int a = 0;
69 69 }
70 70
71 71 if (!hasIndex(row, column, parent)) {
72 72 return QModelIndex();
73 73 }
74 74
75 75 CatalogueAbstractTreeItem *parentItem = nullptr;
76 76
77 77 if (!parent.isValid()) {
78 78 parentItem = impl->m_RootItem.get();
79 79 }
80 80 else {
81 81 parentItem = item(parent);
82 82 }
83 83
84 84 auto childItem = parentItem->children().value(row);
85 85 if (childItem) {
86 86 return createIndex(row, column, childItem);
87 87 }
88 88
89 89 return QModelIndex();
90 90 }
91 91
92 92
93 93 QModelIndex CatalogueTreeModel::parent(const QModelIndex &index) const
94 94 {
95 95 if (!index.isValid()) {
96 96 return QModelIndex();
97 97 }
98 98
99 99 auto childItem = item(index);
100 100 auto parentItem = childItem->parent();
101 101
102 102 if (parentItem == nullptr || parentItem->parent() == nullptr) {
103 103 return QModelIndex();
104 104 }
105 105
106 106 auto row = parentItem->parent()->children().indexOf(parentItem);
107 107 return createIndex(row, 0, parentItem);
108 108 }
109 109
110 110 int CatalogueTreeModel::rowCount(const QModelIndex &parent) const
111 111 {
112 112 CatalogueAbstractTreeItem *parentItem = nullptr;
113 113
114 114 if (!parent.isValid()) {
115 115 parentItem = impl->m_RootItem.get();
116 116 }
117 117 else {
118 118 parentItem = item(parent);
119 119 }
120 120
121 121 return parentItem->children().count();
122 122 }
123 123
124 124 int CatalogueTreeModel::columnCount(const QModelIndex &parent) const
125 125 {
126 126 return (int)Column::Count;
127 127 }
128 128
129 129 Qt::ItemFlags CatalogueTreeModel::flags(const QModelIndex &index) const
130 130 {
131 131 auto treeItem = item(index);
132 132 if (treeItem) {
133 133 return treeItem->flags(index.column());
134 134 }
135 135
136 136 return Qt::NoItemFlags;
137 137 }
138 138
139 139 QVariant CatalogueTreeModel::data(const QModelIndex &index, int role) const
140 140 {
141 141 auto treeItem = item(index);
142 142 if (treeItem) {
143 143 return treeItem->data(index.column(), role);
144 144 }
145 145
146 146 return QModelIndex();
147 147 }
148 148
149 149 bool CatalogueTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
150 150 {
151 151 auto treeItem = item(index);
152 152 if (treeItem) {
153 153 auto result = treeItem->setData(index.column(), role, value);
154 154
155 155 if (result && index.column() == (int)Column::Name) {
156 156 emit itemRenamed(index);
157 157 }
158 158
159 159 return result;
160 160 }
161 161
162 162 return false;
163 163 }
164 164 bool CatalogueTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row,
165 165 int column, const QModelIndex &parent) const
166 166 {
167 167 auto draggedIndex = parent;
168 168 auto draggedItem = item(draggedIndex);
169 169 if (draggedItem) {
170 170 return draggedItem->canDropMimeData(data, action);
171 171 }
172 172
173 173 return false;
174 174 }
175 175
176 176 bool CatalogueTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row,
177 177 int column, const QModelIndex &parent)
178 178 {
179 return false;
179 bool result = false;
180
181 auto draggedIndex = parent;
182 auto draggedItem = item(draggedIndex);
183 if (draggedItem) {
184 result = draggedItem->dropMimeData(data, action);
185 if (result) {
186 emit itemDropped(draggedIndex);
187 }
188 }
189
190 return result;
180 191 }
181 192
182 193 Qt::DropActions CatalogueTreeModel::supportedDropActions() const
183 194 {
184 195 return Qt::CopyAction | Qt::MoveAction;
185 196 }
186 197
187 198 QStringList CatalogueTreeModel::mimeTypes() const
188 199 {
189 200 return {MIME_TYPE_EVENT_LIST};
190 201 }
@@ -1,97 +1,100
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <ui version="4.0">
3 3 <class>CatalogueSideBarWidget</class>
4 4 <widget class="QWidget" name="CatalogueSideBarWidget">
5 5 <property name="geometry">
6 6 <rect>
7 7 <x>0</x>
8 8 <y>0</y>
9 9 <width>330</width>
10 10 <height>523</height>
11 11 </rect>
12 12 </property>
13 13 <property name="windowTitle">
14 14 <string>Form</string>
15 15 </property>
16 16 <layout class="QVBoxLayout" name="verticalLayout">
17 17 <property name="leftMargin">
18 18 <number>0</number>
19 19 </property>
20 20 <property name="topMargin">
21 21 <number>0</number>
22 22 </property>
23 23 <property name="rightMargin">
24 24 <number>0</number>
25 25 </property>
26 26 <property name="bottomMargin">
27 27 <number>0</number>
28 28 </property>
29 29 <item>
30 30 <layout class="QHBoxLayout" name="horizontalLayout">
31 31 <item>
32 32 <widget class="QToolButton" name="btnAdd">
33 33 <property name="text">
34 34 <string>+</string>
35 35 </property>
36 36 <property name="icon">
37 37 <iconset resource="../../resources/sqpguiresources.qrc">
38 38 <normaloff>:/icones/add.png</normaloff>:/icones/add.png</iconset>
39 39 </property>
40 40 <property name="autoRaise">
41 41 <bool>true</bool>
42 42 </property>
43 43 </widget>
44 44 </item>
45 45 <item>
46 46 <widget class="QToolButton" name="btnRemove">
47 47 <property name="text">
48 48 <string> - </string>
49 49 </property>
50 50 <property name="icon">
51 51 <iconset resource="../../resources/sqpguiresources.qrc">
52 52 <normaloff>:/icones/remove.png</normaloff>:/icones/remove.png</iconset>
53 53 </property>
54 54 <property name="autoRaise">
55 55 <bool>true</bool>
56 56 </property>
57 57 </widget>
58 58 </item>
59 59 <item>
60 60 <spacer name="horizontalSpacer">
61 61 <property name="orientation">
62 62 <enum>Qt::Horizontal</enum>
63 63 </property>
64 64 <property name="sizeHint" stdset="0">
65 65 <size>
66 66 <width>40</width>
67 67 <height>20</height>
68 68 </size>
69 69 </property>
70 70 </spacer>
71 71 </item>
72 72 </layout>
73 73 </item>
74 74 <item>
75 75 <widget class="QTreeView" name="treeView">
76 76 <property name="acceptDrops">
77 77 <bool>true</bool>
78 78 </property>
79 79 <property name="dragDropMode">
80 80 <enum>QAbstractItemView::DragDrop</enum>
81 81 </property>
82 <property name="selectionMode">
83 <enum>QAbstractItemView::ExtendedSelection</enum>
84 </property>
82 85 <attribute name="headerVisible">
83 86 <bool>false</bool>
84 87 </attribute>
85 88 <attribute name="headerStretchLastSection">
86 89 <bool>false</bool>
87 90 </attribute>
88 91 </widget>
89 92 </item>
90 93 </layout>
91 94 </widget>
92 95 <resources>
93 96 <include location="../../resources/sqpguiresources.qrc"/>
94 97 <include location="../../resources/sqpguiresources.qrc"/>
95 98 </resources>
96 99 <connections/>
97 100 </ui>
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Status change > Approved

You need to be logged in to leave comments. Login now