1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #ifndef QPLACEMANAGERENGINE_TEST_H |
30 | #define QPLACEMANAGERENGINE_TEST_H |
31 | |
32 | #include <QtCore/QDateTime> |
33 | #include <QtCore/QFile> |
34 | #include <QtCore/QJsonDocument> |
35 | #include <QtCore/QJsonObject> |
36 | #include <QtCore/QJsonArray> |
37 | #include <QtCore/QUuid> |
38 | #include <QtPositioning/QGeoCoordinate> |
39 | #include <QtPositioning/QGeoLocation> |
40 | #include <QtLocation/QPlaceContentReply> |
41 | #include <QtLocation/QPlaceManager> |
42 | #include <QtLocation/QPlaceManagerEngine> |
43 | #include <QtLocation/QPlaceReply> |
44 | #include <QtLocation/QPlaceDetailsReply> |
45 | #include <QtLocation/QPlaceEditorial> |
46 | #include <QtLocation/QPlaceIdReply> |
47 | #include <QtLocation/QPlaceImage> |
48 | #include <QtLocation/QPlaceSearchSuggestionReply> |
49 | #include <QtLocation/QPlaceSearchReply> |
50 | #include <QtLocation/QPlaceResult> |
51 | #include <QtLocation/QPlaceCategory> |
52 | #include <QtLocation/QPlace> |
53 | #include <QtLocation/QPlaceReview> |
54 | #include <QtLocation/private/qplace_p.h> |
55 | #include <QtTest/QTest> |
56 | |
57 | QT_BEGIN_NAMESPACE |
58 | |
59 | inline uint qHash(const QPlaceCategory &category) |
60 | { |
61 | return qHash(uuid: QUuid(category.categoryId().toLatin1())); |
62 | } |
63 | |
64 | QT_END_NAMESPACE |
65 | |
66 | QT_USE_NAMESPACE |
67 | |
68 | class QPlacePrivateDefaultAlt : public QPlacePrivateDefault |
69 | { |
70 | public: |
71 | QPlacePrivateDefaultAlt() {} |
72 | QPlacePrivateDefaultAlt(const QPlacePrivateDefaultAlt &other) |
73 | : QPlacePrivateDefault(other) |
74 | { |
75 | } |
76 | ~QPlacePrivateDefaultAlt() {} |
77 | |
78 | QPlaceAttribute extendedAttribute(const QString &attributeType) const override |
79 | { |
80 | if (attributeType == QStringLiteral("x_provider" )) { |
81 | QPlaceAttribute a; |
82 | a.setLabel(QStringLiteral("x_provider" )); |
83 | a.setText(QStringLiteral("QPlacePrivateDefaultAlt" )); |
84 | return a; |
85 | } else { |
86 | return QPlacePrivateDefault::extendedAttribute(attributeType); |
87 | } |
88 | } |
89 | }; |
90 | |
91 | class QPlaceAlt : public QPlace |
92 | { |
93 | public: |
94 | QPlaceAlt() : QPlace(QSharedDataPointer<QPlacePrivate>(new QPlacePrivateDefaultAlt())) |
95 | { |
96 | } |
97 | }; |
98 | |
99 | class PlaceReply : public QPlaceReply |
100 | { |
101 | Q_OBJECT |
102 | |
103 | friend class QPlaceManagerEngineTest; |
104 | |
105 | public: |
106 | PlaceReply(QObject *parent = 0) |
107 | : QPlaceReply(parent) |
108 | { } |
109 | |
110 | Q_INVOKABLE void emitFinished() |
111 | { |
112 | emit finished(); |
113 | } |
114 | }; |
115 | |
116 | class ContentReply : public QPlaceContentReply |
117 | { |
118 | Q_OBJECT |
119 | |
120 | friend class QPlaceManagerEngineTest; |
121 | |
122 | public: |
123 | ContentReply(QObject *parent = 0) |
124 | : QPlaceContentReply(parent) |
125 | {} |
126 | |
127 | Q_INVOKABLE void emitError() |
128 | { |
129 | emit error(error: error(), errorString: errorString()); |
130 | } |
131 | |
132 | Q_INVOKABLE void emitFinished() |
133 | { |
134 | emit finished(); |
135 | } |
136 | }; |
137 | |
138 | class DetailsReply : public QPlaceDetailsReply |
139 | { |
140 | Q_OBJECT |
141 | |
142 | friend class QPlaceManagerEngineTest; |
143 | |
144 | public: |
145 | DetailsReply(QObject *parent = 0) |
146 | : QPlaceDetailsReply(parent) |
147 | { } |
148 | |
149 | Q_INVOKABLE void emitError() |
150 | { |
151 | emit error(error: error(), errorString: errorString()); |
152 | } |
153 | |
154 | Q_INVOKABLE void emitFinished() |
155 | { |
156 | emit finished(); |
157 | } |
158 | }; |
159 | |
160 | class IdReply : public QPlaceIdReply |
161 | { |
162 | Q_OBJECT |
163 | |
164 | friend class QPlaceManagerEngineTest; |
165 | |
166 | public: |
167 | IdReply(QPlaceIdReply::OperationType type, QObject *parent = 0) |
168 | : QPlaceIdReply(type, parent) |
169 | { } |
170 | |
171 | Q_INVOKABLE void emitError() |
172 | { |
173 | emit error(error: error(), errorString: errorString()); |
174 | } |
175 | |
176 | Q_INVOKABLE void emitFinished() |
177 | { |
178 | emit finished(); |
179 | } |
180 | }; |
181 | |
182 | class PlaceSearchReply : public QPlaceSearchReply |
183 | { |
184 | Q_OBJECT |
185 | |
186 | public: |
187 | PlaceSearchReply(const QList<QPlaceSearchResult> &results, QObject *parent = 0) |
188 | : QPlaceSearchReply(parent) |
189 | { |
190 | setResults(results); |
191 | } |
192 | |
193 | Q_INVOKABLE void emitError() |
194 | { |
195 | emit error(error: error(), errorString: errorString()); |
196 | } |
197 | |
198 | Q_INVOKABLE void emitFinished() |
199 | { |
200 | emit finished(); |
201 | } |
202 | }; |
203 | |
204 | class SuggestionReply : public QPlaceSearchSuggestionReply |
205 | { |
206 | Q_OBJECT |
207 | |
208 | public: |
209 | SuggestionReply(const QStringList &suggestions, QObject *parent = 0) |
210 | : QPlaceSearchSuggestionReply(parent) |
211 | { |
212 | setSuggestions(suggestions); |
213 | } |
214 | |
215 | Q_INVOKABLE void emitError() |
216 | { |
217 | emit error(error: error(), errorString: errorString()); |
218 | } |
219 | |
220 | Q_INVOKABLE void emitFinished() |
221 | { |
222 | emit finished(); |
223 | } |
224 | }; |
225 | |
226 | class QPlaceManagerEngineTest : public QPlaceManagerEngine |
227 | { |
228 | Q_OBJECT |
229 | public: |
230 | QPlaceManagerEngineTest(const QVariantMap ¶meters) |
231 | : QPlaceManagerEngine(parameters) |
232 | { |
233 | m_locales << QLocale(); |
234 | if (parameters.value(QStringLiteral("initializePlaceData" ), adefaultValue: false).toBool()) { |
235 | QFile placeData(QFINDTESTDATA("place_data.json" )); |
236 | QVERIFY(placeData.exists()); |
237 | if (placeData.open(flags: QIODevice::ReadOnly)) { |
238 | QJsonDocument document = QJsonDocument::fromJson(json: placeData.readAll()); |
239 | |
240 | if (document.isObject()) { |
241 | QJsonObject o = document.object(); |
242 | |
243 | if (o.contains(QStringLiteral("categories" ))) { |
244 | QJsonArray categories = o.value(QStringLiteral("categories" )).toArray(); |
245 | |
246 | for (int i = 0; i < categories.count(); ++i) { |
247 | QJsonObject c = categories.at(i).toObject(); |
248 | |
249 | QPlaceCategory category; |
250 | |
251 | category.setName(c.value(QStringLiteral("name" )).toString()); |
252 | category.setCategoryId(c.value(QStringLiteral("id" )).toString()); |
253 | |
254 | m_categories.insert(akey: category.categoryId(), avalue: category); |
255 | |
256 | const QString parentId = c.value(QStringLiteral("parentId" )).toString(); |
257 | m_childCategories[parentId].append(t: category.categoryId()); |
258 | } |
259 | } |
260 | |
261 | if (o.contains(QStringLiteral("places" ))) { |
262 | QJsonArray places = o.value(QStringLiteral("places" )).toArray(); |
263 | |
264 | for (int i = 0; i < places.count(); ++i) { |
265 | QJsonObject p = places.at(i).toObject(); |
266 | |
267 | QPlace place; |
268 | if (p.value(QStringLiteral("alternateImplementation" )).toBool(defaultValue: false)) { |
269 | place = QPlaceAlt(); |
270 | QPlaceAttribute att; |
271 | att.setLabel(QStringLiteral("x_provider" )); |
272 | att.setText(QStringLiteral("42" )); // Doesn't matter, wont be used. |
273 | place.setExtendedAttribute(QStringLiteral("x_provider" ), attribute: att); |
274 | } |
275 | |
276 | place.setName(p.value(QStringLiteral("name" )).toString()); |
277 | place.setPlaceId(p.value(QStringLiteral("id" )).toString()); |
278 | |
279 | QList<QPlaceCategory> categories; |
280 | QJsonArray ca = p.value(QStringLiteral("categories" )).toArray(); |
281 | for (int j = 0; j < ca.count(); ++j) { |
282 | QPlaceCategory c = m_categories.value(akey: ca.at(i: j).toString()); |
283 | if (!c.isEmpty()) |
284 | categories.append(t: c); |
285 | } |
286 | place.setCategories(categories); |
287 | |
288 | QGeoCoordinate coordinate; |
289 | QJsonObject lo = p.value(QStringLiteral("location" )).toObject(); |
290 | coordinate.setLatitude(lo.value(QStringLiteral("latitude" )).toDouble()); |
291 | coordinate.setLongitude(lo.value(QStringLiteral("longitude" )).toDouble()); |
292 | |
293 | QGeoLocation location; |
294 | location.setCoordinate(coordinate); |
295 | |
296 | place.setLocation(location); |
297 | |
298 | m_places.insert(akey: place.placeId(), avalue: place); |
299 | |
300 | QStringList recommendations; |
301 | QJsonArray ra = p.value(QStringLiteral("recommendations" )).toArray(); |
302 | for (int j = 0; j < ra.count(); ++j) |
303 | recommendations.append(t: ra.at(i: j).toString()); |
304 | m_placeRecommendations.insert(akey: place.placeId(), avalue: recommendations); |
305 | |
306 | QJsonArray revArray = p.value(QStringLiteral("reviews" )).toArray(); |
307 | QList<QPlaceReview> reviews; |
308 | for (int j = 0; j < revArray.count(); ++j) { |
309 | QJsonObject ro = revArray.at(i: j).toObject(); |
310 | QPlaceReview review; |
311 | if (ro.contains(QStringLiteral("title" ))) |
312 | review.setTitle(ro.value(QStringLiteral("title" )).toString()); |
313 | if (ro.contains(QStringLiteral("text" ))) |
314 | review.setText(ro.value(QStringLiteral("text" )).toString()); |
315 | |
316 | if (ro.contains(QStringLiteral("language" ))) |
317 | review.setLanguage(ro.value(key: "language" ).toString()); |
318 | |
319 | if (ro.contains(QStringLiteral("rating" ))) |
320 | review.setRating(ro.value(key: "rating" ).toDouble()); |
321 | |
322 | if (ro.contains(QStringLiteral("dateTime" ))) |
323 | review.setDateTime(QDateTime::fromString( |
324 | s: ro.value(QStringLiteral("dateTime" )).toString(), |
325 | QStringLiteral("hh:mm dd-MM-yyyy" ))); |
326 | if (ro.contains(QStringLiteral("reviewId" ))) |
327 | review.setReviewId(ro.value(key: "reviewId" ).toString()); |
328 | |
329 | reviews << review; |
330 | } |
331 | m_placeReviews.insert(akey: place.placeId(), avalue: reviews); |
332 | |
333 | QJsonArray imgArray = p.value(QStringLiteral("images" )).toArray(); |
334 | QList<QPlaceImage> images; |
335 | for (int j = 0; j < imgArray.count(); ++j) { |
336 | QJsonObject imgo = imgArray.at(i: j).toObject(); |
337 | QPlaceImage image; |
338 | if (imgo.contains(QStringLiteral("url" ))) |
339 | image.setUrl(imgo.value(QStringLiteral("url" )).toString()); |
340 | |
341 | if (imgo.contains(key: "imageId" )) |
342 | image.setImageId(imgo.value(QStringLiteral("imageId" )).toString()); |
343 | |
344 | if (imgo.contains(key: "mimeType" )) |
345 | image.setMimeType(imgo.value(QStringLiteral("mimeType" )).toString()); |
346 | |
347 | images << image; |
348 | } |
349 | |
350 | m_placeImages.insert(akey: place.placeId(), avalue: images); |
351 | |
352 | QJsonArray edArray = p.value(QStringLiteral("editorials" )).toArray(); |
353 | QList<QPlaceEditorial> editorials; |
354 | for (int j = 0; j < edArray.count(); ++j) { |
355 | QJsonObject edo = edArray.at(i: j).toObject(); |
356 | QPlaceEditorial editorial; |
357 | if (edo.contains(QStringLiteral("title" ))) |
358 | editorial.setTitle(edo.value(QStringLiteral("title" )).toString()); |
359 | |
360 | if (edo.contains(QStringLiteral("text" ))) |
361 | editorial.setText(edo.value(QStringLiteral("text" )).toString()); |
362 | |
363 | if (edo.contains(QStringLiteral("language" ))) |
364 | editorial.setLanguage(edo.value(QStringLiteral("language" )).toString()); |
365 | |
366 | editorials << editorial; |
367 | } |
368 | |
369 | m_placeEditorials.insert(akey: place.placeId(), avalue: editorials); |
370 | } |
371 | } |
372 | } |
373 | } |
374 | } |
375 | } |
376 | |
377 | QPlaceDetailsReply *getPlaceDetails(const QString &placeId) override |
378 | { |
379 | DetailsReply *reply = new DetailsReply(this); |
380 | |
381 | if (placeId.isEmpty() || !m_places.contains(akey: placeId)) { |
382 | reply->setError(error: QPlaceReply::PlaceDoesNotExistError, errorString: tr(s: "Place does not exist" )); |
383 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
384 | } else { |
385 | reply->setPlace(m_places.value(akey: placeId)); |
386 | } |
387 | |
388 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
389 | |
390 | return reply; |
391 | } |
392 | |
393 | QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &query) override |
394 | { |
395 | ContentReply *reply = new ContentReply(this); |
396 | if (query.placeId().isEmpty() || !m_places.contains(akey: query.placeId())) { |
397 | reply->setError(error: QPlaceReply::PlaceDoesNotExistError, errorString: tr(s: "Place does not exist" )); |
398 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
399 | |
400 | } else { |
401 | QPlaceContent::Collection collection; |
402 | int totalCount = 0; |
403 | switch (query.contentType()) { |
404 | case QPlaceContent::ReviewType: |
405 | totalCount = m_placeReviews.value(akey: query.placeId()).count(); |
406 | break; |
407 | case QPlaceContent::ImageType: |
408 | totalCount = m_placeImages.value(akey: query.placeId()).count(); |
409 | break; |
410 | case QPlaceContent::EditorialType: |
411 | totalCount = m_placeEditorials.value(akey: query.placeId()).count(); |
412 | default: |
413 | //do nothing |
414 | break; |
415 | } |
416 | |
417 | QVariantMap context = query.contentContext().toMap(); |
418 | |
419 | int offset = context.value(QStringLiteral("offset" ), adefaultValue: 0).toInt(); |
420 | int max = (query.limit() == -1) ? totalCount |
421 | : qMin(a: offset + query.limit(), b: totalCount); |
422 | for (int i = offset; i < max; ++i) { |
423 | switch (query.contentType()) { |
424 | case QPlaceContent::ReviewType: |
425 | collection.insert(akey: i, avalue: m_placeReviews.value(akey: query.placeId()).at(i)); |
426 | break; |
427 | case QPlaceContent::ImageType: |
428 | collection.insert(akey: i, avalue: m_placeImages.value(akey: query.placeId()).at(i)); |
429 | break; |
430 | case QPlaceContent::EditorialType: |
431 | collection.insert(akey: i, avalue: m_placeEditorials.value(akey: query.placeId()).at(i)); |
432 | default: |
433 | //do nothing |
434 | break; |
435 | } |
436 | } |
437 | |
438 | reply->setContent(collection); |
439 | reply->setTotalCount(totalCount); |
440 | |
441 | if (max != totalCount) { |
442 | context.clear(); |
443 | context.insert(QStringLiteral("offset" ), avalue: offset + query.limit()); |
444 | QPlaceContentRequest request = query; |
445 | request.setContentContext(context); |
446 | reply->setNextPageRequest(request); |
447 | } |
448 | if (offset > 0) { |
449 | context.clear(); |
450 | context.insert(QStringLiteral("offset" ), avalue: qMin(a: 0, b: offset - query.limit())); |
451 | QPlaceContentRequest request = query; |
452 | request.setContentContext(context); |
453 | reply->setPreviousPageRequest(request); |
454 | } |
455 | } |
456 | |
457 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
458 | return reply; |
459 | } |
460 | |
461 | QPlaceSearchReply *search(const QPlaceSearchRequest &query) override |
462 | { |
463 | QList<QPlaceSearchResult> results; |
464 | |
465 | if (!query.searchTerm().isEmpty()) { |
466 | foreach (const QPlace &place, m_places) { |
467 | if (!place.name().contains(s: query.searchTerm(), cs: Qt::CaseInsensitive)) |
468 | continue; |
469 | |
470 | QPlaceResult r; |
471 | r.setPlace(place); |
472 | r.setTitle(place.name()); |
473 | |
474 | results.append(t: r); |
475 | } |
476 | } else if (!query.categories().isEmpty()) { |
477 | const auto &categoryList = query.categories(); |
478 | const QSet<QPlaceCategory> categories(categoryList.cbegin(), categoryList.cend()); |
479 | for (const QPlace &place : qAsConst(t&: m_places)) { |
480 | const auto &placeCategoryList = place.categories(); |
481 | const QSet<QPlaceCategory> placeCategories(placeCategoryList.cbegin(), placeCategoryList.cend()); |
482 | if (!placeCategories.intersects(other: categories)) |
483 | continue; |
484 | |
485 | QPlaceResult r; |
486 | r.setPlace(place); |
487 | r.setTitle(place.name()); |
488 | |
489 | results.append(t: r); |
490 | } |
491 | } else if (!query.recommendationId().isEmpty()) { |
492 | QStringList recommendations = m_placeRecommendations.value(akey: query.recommendationId()); |
493 | foreach (const QString &id, recommendations) { |
494 | QPlaceResult r; |
495 | r.setPlace(m_places.value(akey: id)); |
496 | r.setTitle(r.place().name()); |
497 | |
498 | results.append(t: r); |
499 | } |
500 | } |
501 | |
502 | PlaceSearchReply *reply = new PlaceSearchReply(results, this); |
503 | |
504 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
505 | |
506 | return reply; |
507 | } |
508 | |
509 | QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &query) override |
510 | { |
511 | QStringList suggestions; |
512 | if (query.searchTerm() == QLatin1String("test" )) { |
513 | suggestions << QStringLiteral("test1" ); |
514 | suggestions << QStringLiteral("test2" ); |
515 | suggestions << QStringLiteral("test3" ); |
516 | } |
517 | |
518 | SuggestionReply *reply = new SuggestionReply(suggestions, this); |
519 | |
520 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
521 | |
522 | return reply; |
523 | } |
524 | |
525 | QPlaceIdReply *savePlace(const QPlace &place) override |
526 | { |
527 | IdReply *reply = new IdReply(QPlaceIdReply::SavePlace, this); |
528 | |
529 | if (!place.placeId().isEmpty() && !m_places.contains(akey: place.placeId())) { |
530 | reply->setError(error: QPlaceReply::PlaceDoesNotExistError, errorString: tr(s: "Place does not exist" )); |
531 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
532 | } else if (!place.placeId().isEmpty()) { |
533 | m_places.insert(akey: place.placeId(), avalue: place); |
534 | reply->setId(place.placeId()); |
535 | } else { |
536 | QPlace p = place; |
537 | p.setPlaceId(QUuid::createUuid().toString()); |
538 | m_places.insert(akey: p.placeId(), avalue: p); |
539 | |
540 | reply->setId(p.placeId()); |
541 | } |
542 | |
543 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
544 | |
545 | return reply; |
546 | } |
547 | |
548 | QPlaceIdReply *removePlace(const QString &placeId) override |
549 | { |
550 | IdReply *reply = new IdReply(QPlaceIdReply::RemovePlace, this); |
551 | reply->setId(placeId); |
552 | |
553 | if (!m_places.contains(akey: placeId)) { |
554 | reply->setError(error: QPlaceReply::PlaceDoesNotExistError, errorString: tr(s: "Place does not exist" )); |
555 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
556 | } else { |
557 | m_places.remove(akey: placeId); |
558 | } |
559 | |
560 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
561 | |
562 | return reply; |
563 | } |
564 | |
565 | QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId) override |
566 | { |
567 | IdReply *reply = new IdReply(QPlaceIdReply::SaveCategory, this); |
568 | |
569 | if ((!category.categoryId().isEmpty() && !m_categories.contains(akey: category.categoryId())) || |
570 | (!parentId.isEmpty() && !m_categories.contains(akey: parentId))) { |
571 | reply->setError(error: QPlaceReply::CategoryDoesNotExistError, errorString: tr(s: "Category does not exist" )); |
572 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
573 | } else if (!category.categoryId().isEmpty()) { |
574 | m_categories.insert(akey: category.categoryId(), avalue: category); |
575 | QStringList children = m_childCategories.value(akey: parentId); |
576 | |
577 | for (QStringList &c : m_childCategories) |
578 | c.removeAll(t: category.categoryId()); |
579 | |
580 | if (!children.contains(str: category.categoryId())) { |
581 | children.append(t: category.categoryId()); |
582 | m_childCategories.insert(akey: parentId, avalue: children); |
583 | } |
584 | reply->setId(category.categoryId()); |
585 | } else { |
586 | QPlaceCategory c = category; |
587 | c.setCategoryId(QUuid::createUuid().toString()); |
588 | m_categories.insert(akey: c.categoryId(), avalue: c); |
589 | QStringList children = m_childCategories.value(akey: parentId); |
590 | if (!children.contains(str: c.categoryId())) { |
591 | children.append(t: c.categoryId()); |
592 | m_childCategories.insert(akey: parentId, avalue: children); |
593 | } |
594 | |
595 | reply->setId(c.categoryId()); |
596 | } |
597 | |
598 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
599 | |
600 | return reply; |
601 | } |
602 | |
603 | QPlaceIdReply *removeCategory(const QString &categoryId) override |
604 | { |
605 | IdReply *reply = new IdReply(QPlaceIdReply::RemoveCategory, this); |
606 | reply->setId(categoryId); |
607 | |
608 | if (!m_categories.contains(akey: categoryId)) { |
609 | reply->setError(error: QPlaceReply::CategoryDoesNotExistError, errorString: tr(s: "Category does not exist" )); |
610 | QMetaObject::invokeMethod(obj: reply, member: "emitError" , type: Qt::QueuedConnection); |
611 | } else { |
612 | m_categories.remove(akey: categoryId); |
613 | |
614 | for (auto &c : m_childCategories) |
615 | c.removeAll(t: categoryId); |
616 | } |
617 | |
618 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
619 | |
620 | return reply; |
621 | } |
622 | |
623 | QPlaceReply *initializeCategories() override |
624 | { |
625 | QPlaceReply *reply = new PlaceReply(this); |
626 | |
627 | QMetaObject::invokeMethod(obj: reply, member: "emitFinished" , type: Qt::QueuedConnection); |
628 | |
629 | return reply; |
630 | } |
631 | |
632 | QString parentCategoryId(const QString &categoryId) const override |
633 | { |
634 | for (auto i = m_childCategories.cbegin(), end = m_childCategories.cend(); i != end; ++i) { |
635 | if (i.value().contains(str: categoryId)) |
636 | return i.key(); |
637 | } |
638 | |
639 | return QString(); |
640 | } |
641 | |
642 | virtual QStringList childCategoryIds(const QString &categoryId) const override |
643 | { |
644 | return m_childCategories.value(akey: categoryId); |
645 | } |
646 | |
647 | virtual QPlaceCategory category(const QString &categoryId) const override |
648 | { |
649 | return m_categories.value(akey: categoryId); |
650 | } |
651 | |
652 | QList<QPlaceCategory> childCategories(const QString &parentId) const override |
653 | { |
654 | QList<QPlaceCategory> categories; |
655 | |
656 | foreach (const QString &id, m_childCategories.value(parentId)) |
657 | categories.append(t: m_categories.value(akey: id)); |
658 | |
659 | return categories; |
660 | } |
661 | |
662 | QList<QLocale> locales() const override |
663 | { |
664 | return m_locales; |
665 | } |
666 | |
667 | void setLocales(const QList<QLocale> &locales) override |
668 | { |
669 | m_locales = locales; |
670 | } |
671 | |
672 | QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override |
673 | { |
674 | QList<QPair<int, QUrl> > candidates; |
675 | |
676 | QMap<QString, int> sizeDictionary; |
677 | sizeDictionary.insert(QStringLiteral("s" ), avalue: 20); |
678 | sizeDictionary.insert(QStringLiteral("m" ), avalue: 30); |
679 | sizeDictionary.insert(QStringLiteral("l" ), avalue: 50); |
680 | |
681 | QStringList sizeKeys; |
682 | sizeKeys << QStringLiteral("s" ) << QStringLiteral("m" ) << QStringLiteral("l" ); |
683 | |
684 | foreach (const QString &sizeKey, sizeKeys) |
685 | { |
686 | if (icon.parameters().contains(akey: sizeKey)) |
687 | candidates.append(t: QPair<int, QUrl>(sizeDictionary.value(akey: sizeKey), |
688 | icon.parameters().value(akey: sizeKey).toUrl())); |
689 | } |
690 | |
691 | if (candidates.isEmpty()) |
692 | return QUrl(); |
693 | else if (candidates.count() == 1) { |
694 | return candidates.first().second; |
695 | } else { |
696 | //we assume icons are squarish so we can use height to |
697 | //determine which particular icon to return |
698 | int requestedHeight = size.height(); |
699 | |
700 | for (int i = 0; i < candidates.count() - 1; ++i) { |
701 | int thresholdHeight = (candidates.at(i).first + candidates.at(i: i+1).first) / 2; |
702 | if (requestedHeight < thresholdHeight) |
703 | return candidates.at(i).second; |
704 | } |
705 | return candidates.last().second; |
706 | } |
707 | } |
708 | |
709 | QPlace compatiblePlace(const QPlace &original) const override |
710 | { |
711 | QPlace place; |
712 | place.setName(original.name()); |
713 | return place; |
714 | } |
715 | |
716 | private: |
717 | QList<QLocale> m_locales; |
718 | QHash<QString, QPlace> m_places; |
719 | QHash<QString, QPlaceCategory> m_categories; |
720 | QHash<QString, QStringList> m_childCategories; |
721 | QHash<QString, QStringList> m_placeRecommendations; |
722 | QHash<QString, QList<QPlaceReview> > m_placeReviews; |
723 | QHash<QString, QList<QPlaceImage> > m_placeImages; |
724 | QHash<QString, QList<QPlaceEditorial> > m_placeEditorials; |
725 | }; |
726 | |
727 | #endif |
728 | |