| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. |
| 4 | ** Contact: http://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtLocation module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
| 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 http://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at http://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or later as published by the Free |
| 28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
| 29 | ** the packaging of this file. Please review the following information to |
| 30 | ** ensure the GNU General Public License version 2.0 requirements will be |
| 31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| 32 | ** |
| 33 | ** $QT_END_LICENSE$ |
| 34 | ** |
| 35 | ****************************************************************************/ |
| 36 | |
| 37 | #include "qdeclarativecategory_p.h" |
| 38 | #include "qdeclarativeplaceicon_p.h" |
| 39 | #include "qdeclarativegeoserviceprovider_p.h" |
| 40 | #include "error_messages_p.h" |
| 41 | |
| 42 | #include <QtQml/QQmlInfo> |
| 43 | #include <QtLocation/QGeoServiceProvider> |
| 44 | #include <QtLocation/QPlaceManager> |
| 45 | #include <QCoreApplication> |
| 46 | |
| 47 | QT_BEGIN_NAMESPACE |
| 48 | |
| 49 | /*! |
| 50 | \qmltype Category |
| 51 | \instantiates QDeclarativeCategory |
| 52 | \inqmlmodule QtLocation |
| 53 | \ingroup qml-QtLocation5-places |
| 54 | \ingroup qml-QtLocation5-places-data |
| 55 | |
| 56 | \since QtLocation 5.5 |
| 57 | |
| 58 | \brief The Category type represents a category that a \l Place can be associated with. |
| 59 | |
| 60 | Categories are used to search for places based on the categories they are associated with. The |
| 61 | list of available categories can be obtained from the \l CategoryModel. The |
| 62 | \l PlaceSearchModel has a \l {PlaceSearchModel::categories}{categories} property that is used |
| 63 | to limit the search results to places with the specified categories. |
| 64 | |
| 65 | If the \l Plugin supports it, categories can be created or removed. To create a new category |
| 66 | construct a new Category object and set its properties, then invoke the \l save() method. |
| 67 | |
| 68 | \snippet declarative/maps.qml QtLocation import |
| 69 | \codeline |
| 70 | \snippet declarative/places.qml Category |
| 71 | \dots 0 |
| 72 | \snippet declarative/places.qml Category save |
| 73 | |
| 74 | To remove a category ensure that the \l plugin and categoryId properties are set and call the |
| 75 | \l remove() method. |
| 76 | |
| 77 | \sa CategoryModel |
| 78 | */ |
| 79 | |
| 80 | QDeclarativeCategory::QDeclarativeCategory(QObject *parent) |
| 81 | : QObject(parent), m_icon(0), m_plugin(0), m_reply(0), m_complete(false), m_status(Ready) |
| 82 | { |
| 83 | } |
| 84 | |
| 85 | QDeclarativeCategory::QDeclarativeCategory(const QPlaceCategory &category, |
| 86 | QDeclarativeGeoServiceProvider *plugin, |
| 87 | QObject *parent) |
| 88 | : QObject(parent), m_category(category), m_icon(0), m_plugin(plugin), m_reply(0), |
| 89 | m_complete(false), m_status(Ready) |
| 90 | { |
| 91 | setCategory(category); |
| 92 | } |
| 93 | |
| 94 | QDeclarativeCategory::~QDeclarativeCategory() {} |
| 95 | |
| 96 | // From QQmlParserStatus |
| 97 | void QDeclarativeCategory::componentComplete() |
| 98 | { |
| 99 | // delayed instantiation of QObject based properties. |
| 100 | if (!m_icon) { |
| 101 | m_icon = new QDeclarativePlaceIcon(this); |
| 102 | m_icon->setPlugin(m_plugin); |
| 103 | } |
| 104 | |
| 105 | m_complete = true; |
| 106 | } |
| 107 | |
| 108 | /*! |
| 109 | \qmlproperty Plugin Category::plugin |
| 110 | |
| 111 | This property holds the location based service to which the category belongs. |
| 112 | */ |
| 113 | void QDeclarativeCategory::setPlugin(QDeclarativeGeoServiceProvider *plugin) |
| 114 | { |
| 115 | if (m_plugin == plugin) |
| 116 | return; |
| 117 | |
| 118 | m_plugin = plugin; |
| 119 | if (m_complete) |
| 120 | emit pluginChanged(); |
| 121 | |
| 122 | if (m_icon && m_icon->parent() == this && !m_icon->plugin()) |
| 123 | m_icon->setPlugin(m_plugin); |
| 124 | |
| 125 | if (!m_plugin) |
| 126 | return; |
| 127 | |
| 128 | if (m_plugin->isAttached()) { |
| 129 | pluginReady(); |
| 130 | } else { |
| 131 | connect(sender: m_plugin, SIGNAL(attached()), |
| 132 | receiver: this, SLOT(pluginReady())); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | QDeclarativeGeoServiceProvider *QDeclarativeCategory::plugin() const |
| 137 | { |
| 138 | return m_plugin; |
| 139 | } |
| 140 | |
| 141 | /*! |
| 142 | \internal |
| 143 | */ |
| 144 | void QDeclarativeCategory::pluginReady() |
| 145 | { |
| 146 | QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); |
| 147 | QPlaceManager *placeManager = serviceProvider->placeManager(); |
| 148 | if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { |
| 149 | setStatus(status: Error, errorString: QCoreApplication::translate(context: CONTEXT_NAME, key: PLUGIN_ERROR) |
| 150 | .arg(a: m_plugin->name()).arg(a: serviceProvider->errorString())); |
| 151 | return; |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | |
| 156 | /*! |
| 157 | \qmlproperty QPlaceCategory Category::category |
| 158 | \keyword Category::category |
| 159 | |
| 160 | For details on how to use this property to interface between C++ and QML see |
| 161 | "\l {Category - QPlaceCategory} {Interfaces between C++ and QML Code}". |
| 162 | */ |
| 163 | void QDeclarativeCategory::setCategory(const QPlaceCategory &category) |
| 164 | { |
| 165 | QPlaceCategory previous = m_category; |
| 166 | m_category = category; |
| 167 | |
| 168 | if (category.name() != previous.name()) |
| 169 | emit nameChanged(); |
| 170 | |
| 171 | if (category.categoryId() != previous.categoryId()) |
| 172 | emit categoryIdChanged(); |
| 173 | |
| 174 | if (m_icon && m_icon->parent() == this) { |
| 175 | m_icon->setPlugin(m_plugin); |
| 176 | m_icon->setIcon(m_category.icon()); |
| 177 | } else if (!m_icon || m_icon->parent() != this) { |
| 178 | m_icon = new QDeclarativePlaceIcon(m_category.icon(), m_plugin, this); |
| 179 | emit iconChanged(); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | QPlaceCategory QDeclarativeCategory::category() |
| 184 | { |
| 185 | m_category.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); |
| 186 | return m_category; |
| 187 | } |
| 188 | |
| 189 | /*! |
| 190 | \qmlproperty string Category::categoryId |
| 191 | |
| 192 | This property holds the identifier of the category. The categoryId is a string which uniquely |
| 193 | identifies this category within the categories \l plugin. |
| 194 | */ |
| 195 | void QDeclarativeCategory::setCategoryId(const QString &id) |
| 196 | { |
| 197 | if (m_category.categoryId() != id) { |
| 198 | m_category.setCategoryId(id); |
| 199 | emit categoryIdChanged(); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | QString QDeclarativeCategory::categoryId() const |
| 204 | { |
| 205 | return m_category.categoryId(); |
| 206 | } |
| 207 | |
| 208 | /*! |
| 209 | \qmlproperty string Category::name |
| 210 | |
| 211 | This property holds string based name of the category. |
| 212 | */ |
| 213 | void QDeclarativeCategory::setName(const QString &name) |
| 214 | { |
| 215 | if (m_category.name() != name) { |
| 216 | m_category.setName(name); |
| 217 | emit nameChanged(); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | QString QDeclarativeCategory::name() const |
| 222 | { |
| 223 | return m_category.name(); |
| 224 | } |
| 225 | |
| 226 | /*! |
| 227 | \qmlproperty enumeration Category::visibility |
| 228 | |
| 229 | This property holds the visibility of the category. It can be one of: |
| 230 | |
| 231 | \table |
| 232 | \row |
| 233 | \li Category.UnspecifiedVisibility |
| 234 | \li The visibility of the category is unspecified. If saving a category, the |
| 235 | plugin will automatically set a default visibility to the category saved in the backend. |
| 236 | This default is dependent on the plugin implementation. |
| 237 | \row |
| 238 | \li Category.DeviceVisibility |
| 239 | \li The category is limited to the current device. The category will not be transferred |
| 240 | off of the device. |
| 241 | \row |
| 242 | \li Category.PrivateVisibility |
| 243 | \li The category is private to the current user. The category may be transferred to an |
| 244 | online service but is only ever visible to the current user. |
| 245 | \row |
| 246 | \li Category.PublicVisibility |
| 247 | \li The category is public. |
| 248 | \endtable |
| 249 | |
| 250 | Note that visibility does not affect how \l{Place}s associated with |
| 251 | the category are displayed in the user-interface of an application |
| 252 | on the device. Instead, it defines the sharing semantics of the |
| 253 | category. |
| 254 | */ |
| 255 | QDeclarativeCategory::Visibility QDeclarativeCategory::visibility() const |
| 256 | { |
| 257 | return static_cast<QDeclarativeCategory::Visibility>(m_category.visibility()); |
| 258 | } |
| 259 | |
| 260 | void QDeclarativeCategory::setVisibility(Visibility visibility) |
| 261 | { |
| 262 | if (static_cast<QDeclarativeCategory::Visibility>(m_category.visibility()) == visibility) |
| 263 | return; |
| 264 | |
| 265 | m_category.setVisibility(static_cast<QLocation::Visibility>(visibility)); |
| 266 | emit visibilityChanged(); |
| 267 | } |
| 268 | |
| 269 | /*! |
| 270 | \qmlproperty PlaceIcon Category::icon |
| 271 | |
| 272 | This property holds the image source associated with the category. To display the icon you can use |
| 273 | the \l Image type. |
| 274 | */ |
| 275 | QDeclarativePlaceIcon *QDeclarativeCategory::icon() const |
| 276 | { |
| 277 | return m_icon; |
| 278 | } |
| 279 | |
| 280 | void QDeclarativeCategory::setIcon(QDeclarativePlaceIcon *icon) |
| 281 | { |
| 282 | if (m_icon == icon) |
| 283 | return; |
| 284 | |
| 285 | if (m_icon && m_icon->parent() == this) |
| 286 | delete m_icon; |
| 287 | |
| 288 | m_icon = icon; |
| 289 | emit iconChanged(); |
| 290 | } |
| 291 | |
| 292 | /*! |
| 293 | \qmlmethod string Category::errorString() |
| 294 | |
| 295 | Returns a string description of the error of the last operation. |
| 296 | If the last operation completed successfully then the string is empty. |
| 297 | */ |
| 298 | QString QDeclarativeCategory::errorString() const |
| 299 | { |
| 300 | return m_errorString; |
| 301 | } |
| 302 | |
| 303 | void QDeclarativeCategory::setStatus(Status status, const QString &errorString) |
| 304 | { |
| 305 | Status originalStatus = m_status; |
| 306 | m_status = status; |
| 307 | m_errorString = errorString; |
| 308 | |
| 309 | if (originalStatus != m_status) |
| 310 | emit statusChanged(); |
| 311 | } |
| 312 | |
| 313 | /*! |
| 314 | \qmlproperty enumeration Category::status |
| 315 | |
| 316 | This property holds the status of the category. It can be one of: |
| 317 | |
| 318 | \table |
| 319 | \row |
| 320 | \li Category.Ready |
| 321 | \li No error occurred during the last operation, further operations may be performed on |
| 322 | the category. |
| 323 | \row |
| 324 | \li Category.Saving |
| 325 | \li The category is currently being saved, no other operations may be performed until the |
| 326 | current operation completes. |
| 327 | \row |
| 328 | \li Category.Removing |
| 329 | \li The category is currently being removed, no other operations can be performed until |
| 330 | the current operation completes. |
| 331 | \row |
| 332 | \li Category.Error |
| 333 | \li An error occurred during the last operation, further operations can still be |
| 334 | performed on the category. |
| 335 | \endtable |
| 336 | */ |
| 337 | QDeclarativeCategory::Status QDeclarativeCategory::status() const |
| 338 | { |
| 339 | return m_status; |
| 340 | } |
| 341 | |
| 342 | /*! |
| 343 | \qmlmethod void Category::save() |
| 344 | |
| 345 | This method saves the category to the backend service. |
| 346 | */ |
| 347 | void QDeclarativeCategory::save(const QString &parentId) |
| 348 | { |
| 349 | QPlaceManager *placeManager = manager(); |
| 350 | if (!placeManager) |
| 351 | return; |
| 352 | |
| 353 | m_reply = placeManager->saveCategory(category: category(), parentId); |
| 354 | connect(sender: m_reply, SIGNAL(finished()), receiver: this, SLOT(replyFinished())); |
| 355 | setStatus(status: QDeclarativeCategory::Saving); |
| 356 | } |
| 357 | |
| 358 | /*! |
| 359 | \qmlmethod void Category::remove() |
| 360 | |
| 361 | This method permanently removes the category from the backend service. |
| 362 | */ |
| 363 | void QDeclarativeCategory::remove() |
| 364 | { |
| 365 | QPlaceManager *placeManager = manager(); |
| 366 | if (!placeManager) |
| 367 | return; |
| 368 | |
| 369 | m_reply = placeManager->removeCategory(categoryId: m_category.categoryId()); |
| 370 | connect(sender: m_reply, SIGNAL(finished()), receiver: this, SLOT(replyFinished())); |
| 371 | setStatus(status: QDeclarativeCategory::Removing); |
| 372 | } |
| 373 | |
| 374 | /*! |
| 375 | \internal |
| 376 | */ |
| 377 | void QDeclarativeCategory::replyFinished() |
| 378 | { |
| 379 | if (!m_reply) |
| 380 | return; |
| 381 | |
| 382 | if (m_reply->error() == QPlaceReply::NoError) { |
| 383 | switch (m_reply->type()) { |
| 384 | case (QPlaceReply::IdReply) : { |
| 385 | QPlaceIdReply *idReply = qobject_cast<QPlaceIdReply *>(object: m_reply); |
| 386 | |
| 387 | switch (idReply->operationType()) { |
| 388 | case QPlaceIdReply::SaveCategory: |
| 389 | setCategoryId(idReply->id()); |
| 390 | break; |
| 391 | case QPlaceIdReply::RemoveCategory: |
| 392 | setCategoryId(QString()); |
| 393 | break; |
| 394 | default: |
| 395 | //Other operation types shouldn't ever be received. |
| 396 | break; |
| 397 | } |
| 398 | break; |
| 399 | } |
| 400 | default: |
| 401 | //other types of replies shouldn't ever be received. |
| 402 | break; |
| 403 | } |
| 404 | |
| 405 | m_errorString.clear(); |
| 406 | |
| 407 | m_reply->deleteLater(); |
| 408 | m_reply = 0; |
| 409 | |
| 410 | setStatus(status: QDeclarativeCategory::Ready); |
| 411 | } else { |
| 412 | QString errorString = m_reply->errorString(); |
| 413 | |
| 414 | m_reply->deleteLater(); |
| 415 | m_reply = 0; |
| 416 | |
| 417 | setStatus(status: QDeclarativeCategory::Error, errorString); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | /*! |
| 422 | \internal |
| 423 | Helper function to return the manager, this manager is intended to be used to perform the next |
| 424 | operation. Sets status to Error and an appropriate m_errorString if the manager cannot be |
| 425 | obtained. |
| 426 | */ |
| 427 | QPlaceManager *QDeclarativeCategory::manager() |
| 428 | { |
| 429 | if (m_status != QDeclarativeCategory::Ready && m_status != QDeclarativeCategory::Error) |
| 430 | return 0; |
| 431 | |
| 432 | if (m_reply) { |
| 433 | m_reply->abort(); |
| 434 | m_reply->deleteLater(); |
| 435 | m_reply = 0; |
| 436 | } |
| 437 | |
| 438 | if (!m_plugin) { |
| 439 | setStatus(status: Error, errorString: QCoreApplication::translate(context: CONTEXT_NAME, key: PLUGIN_PROPERTY_NOT_SET)); |
| 440 | return 0; |
| 441 | } |
| 442 | |
| 443 | QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); |
| 444 | if (!serviceProvider) { |
| 445 | setStatus(status: Error, errorString: QCoreApplication::translate(context: CONTEXT_NAME, key: PLUGIN_NOT_VALID)); |
| 446 | return 0; |
| 447 | } |
| 448 | QPlaceManager *placeManager = serviceProvider->placeManager(); |
| 449 | if (!placeManager) { |
| 450 | setStatus(status: Error, errorString: QCoreApplication::translate(context: CONTEXT_NAME, key: PLUGIN_ERROR) |
| 451 | .arg(a: m_plugin->name()).arg(a: serviceProvider->errorString())); |
| 452 | return 0; |
| 453 | } |
| 454 | |
| 455 | return placeManager; |
| 456 | } |
| 457 | |
| 458 | QT_END_NAMESPACE |
| 459 | |