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 
QDeclarativeCategory(QObject * parent)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 
QDeclarativeCategory(const QPlaceCategory & category,QDeclarativeGeoServiceProvider * plugin,QObject * parent)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 
~QDeclarativeCategory()94 QDeclarativeCategory::~QDeclarativeCategory() {}
95 
96 // From QQmlParserStatus
componentComplete()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 */
setPlugin(QDeclarativeGeoServiceProvider * plugin)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(m_plugin, SIGNAL(attached()),
132                 this, SLOT(pluginReady()));
133     }
134 }
135 
plugin() const136 QDeclarativeGeoServiceProvider *QDeclarativeCategory::plugin() const
137 {
138     return m_plugin;
139 }
140 
141 /*!
142     \internal
143 */
pluginReady()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(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
150                          .arg(m_plugin->name()).arg(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 */
setCategory(const QPlaceCategory & category)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 
category()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 */
setCategoryId(const QString & id)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 
categoryId() const203 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 */
setName(const QString & name)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 
name() const221 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 */
visibility() const255 QDeclarativeCategory::Visibility QDeclarativeCategory::visibility() const
256 {
257     return static_cast<QDeclarativeCategory::Visibility>(m_category.visibility());
258 }
259 
setVisibility(Visibility visibility)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 */
icon() const275 QDeclarativePlaceIcon *QDeclarativeCategory::icon() const
276 {
277     return m_icon;
278 }
279 
setIcon(QDeclarativePlaceIcon * icon)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 */
errorString() const298 QString QDeclarativeCategory::errorString() const
299 {
300     return m_errorString;
301 }
302 
setStatus(Status status,const QString & errorString)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 */
status() const337 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 */
save(const QString & parentId)347 void QDeclarativeCategory::save(const QString &parentId)
348 {
349     QPlaceManager *placeManager = manager();
350     if (!placeManager)
351         return;
352 
353     m_reply = placeManager->saveCategory(category(), parentId);
354     connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
355     setStatus(QDeclarativeCategory::Saving);
356 }
357 
358 /*!
359     \qmlmethod void Category::remove()
360 
361     This method permanently removes the category from the backend service.
362 */
remove()363 void QDeclarativeCategory::remove()
364 {
365     QPlaceManager *placeManager = manager();
366     if (!placeManager)
367         return;
368 
369     m_reply = placeManager->removeCategory(m_category.categoryId());
370     connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
371     setStatus(QDeclarativeCategory::Removing);
372 }
373 
374 /*!
375     \internal
376 */
replyFinished()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 *>(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(QDeclarativeCategory::Ready);
411     } else {
412         QString errorString = m_reply->errorString();
413 
414         m_reply->deleteLater();
415         m_reply = 0;
416 
417         setStatus(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 */
manager()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(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET));
440         return 0;
441     }
442 
443     QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider();
444     if (!serviceProvider) {
445         setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_NOT_VALID));
446         return 0;
447     }
448     QPlaceManager *placeManager = serviceProvider->placeManager();
449     if (!placeManager) {
450         setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
451                          .arg(m_plugin->name()).arg(serviceProvider->errorString()));
452         return 0;
453     }
454 
455     return placeManager;
456 }
457 
458 QT_END_NAMESPACE
459