Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmaplayerref.h
3 : : --------------------------------------
4 : : Date : January 2017
5 : : Copyright : (C) 2017 by Martin Dobias
6 : : Email : wonder dot sk at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : :
16 : : #ifndef QGSMAPLAYERREF_H
17 : : #define QGSMAPLAYERREF_H
18 : :
19 : : #define SIP_NO_FILE
20 : :
21 : : #include <QPointer>
22 : :
23 : : #include "qgsmaplayer.h"
24 : : #include "qgsdataprovider.h"
25 : : #include "qgsproject.h"
26 : : #include <utility>
27 : :
28 : : /**
29 : : * Internal structure to keep weak pointer to QgsMapLayer or layerId
30 : : * if the layer is not available yet.
31 : : * \note not available in Python bindings
32 : : */
33 : : template<typename TYPE>
34 : 1 : struct _LayerRef
35 : : {
36 : :
37 : : /**
38 : : * Flag for match type in weak resolution
39 : : * \since QGIS 3.12
40 : : */
41 : : enum MatchType
42 : : {
43 : : Name = 1 << 2, //! Match layer name
44 : : Provider = 1 << 3, //! Match layer provider name
45 : : Source = 1 << 4, //! Match layer source
46 : : All = Provider | Source //!< Match all
47 : : };
48 : :
49 : :
50 : : /**
51 : : * Constructor for a layer reference from an existing map layer.
52 : : * The layerId, source, name and provider members will automatically
53 : : * be populated from this layer.
54 : : */
55 : 1 : _LayerRef( TYPE *l = nullptr )
56 : 1 : : layer( l )
57 : 1 : , layerId( l ? l->id() : QString() )
58 : 1 : , source( l ? l->publicSource() : QString() )
59 : 1 : , name( l ? l->name() : QString() )
60 : 1 : , provider( l && l->dataProvider() ? l->dataProvider()->name() : QString() )
61 : 1 : {}
62 : :
63 : : /**
64 : : * Constructor for a weak layer reference, using a combination of layer ID,
65 : : * \a name, public \a source and \a provider key.
66 : : */
67 : 0 : _LayerRef( const QString &id, const QString &name = QString(), const QString &source = QString(), const QString &provider = QString() )
68 : 0 : : layer()
69 : 0 : , layerId( id )
70 : 0 : , source( source )
71 : 0 : , name( name )
72 : 0 : , provider( provider )
73 : 0 : {}
74 : :
75 : : /**
76 : : * Sets the reference to point to a specified layer.
77 : : */
78 : 0 : void setLayer( TYPE *l )
79 : : {
80 : 0 : layer = l;
81 : 0 : layerId = l ? l->id() : QString();
82 : 0 : source = l ? l->publicSource() : QString();
83 : 0 : name = l ? l->name() : QString();
84 : 0 : provider = l && l->dataProvider() ? l->dataProvider()->name() : QString();
85 : 0 : }
86 : :
87 : : /**
88 : : * Returns TRUE if the layer reference is resolved and contains a reference to an existing
89 : : * map layer.
90 : : */
91 : 1 : operator bool() const
92 : : {
93 : 1 : return static_cast< bool >( layer.data() );
94 : : }
95 : :
96 : : /**
97 : : * Forwards the to map layer.
98 : : */
99 : 1 : TYPE *operator->() const
100 : : {
101 : 1 : return layer.data();
102 : : }
103 : :
104 : : /**
105 : : * Returns a pointer to the layer, or NULLPTR if the reference has not yet been matched
106 : : * to a layer.
107 : : */
108 : 1 : TYPE *get() const
109 : : {
110 : 1 : return layer.data();
111 : : }
112 : :
113 : : //! Weak pointer to map layer
114 : : QPointer<TYPE> layer;
115 : :
116 : : //! Original layer ID
117 : : QString layerId;
118 : :
119 : : //! Weak reference to layer public source
120 : : QString source;
121 : : //! Weak reference to layer name
122 : : QString name;
123 : : //! Weak reference to layer provider
124 : : QString provider;
125 : :
126 : : /**
127 : : * Returns TRUE if a layer matches the weak references to layer public source,
128 : : * layer name and data provider contained in this layer reference.
129 : : * \see resolveWeakly()
130 : : */
131 : : bool layerMatchesSource( QgsMapLayer *layer ) const
132 : : {
133 : : if ( layer->publicSource() != source ||
134 : : layer->name() != name )
135 : : return false;
136 : :
137 : : if ( layer->providerType() != provider )
138 : : return false;
139 : :
140 : : return true;
141 : : }
142 : :
143 : : /**
144 : : * Resolves the map layer by attempting to find a layer with matching ID
145 : : * within a \a project. If found, this reference will be updated to match
146 : : * the found layer and the layer will be returned. If no matching layer is
147 : : * found, NULLPTR is returned.
148 : : * \see resolveWeakly()
149 : : */
150 : 0 : TYPE *resolve( const QgsProject *project )
151 : : {
152 : 0 : if ( project && !layerId.isEmpty() )
153 : : {
154 : 0 : if ( TYPE *l = qobject_cast<TYPE *>( project->mapLayer( layerId ) ) )
155 : : {
156 : 0 : setLayer( l );
157 : 0 : return l;
158 : : }
159 : 0 : }
160 : 0 : return nullptr;
161 : 0 : }
162 : :
163 : 0 : bool layerMatchesWeakly( QgsMapLayer *layer, MatchType matchType = MatchType::All )
164 : : {
165 : : // First match the name
166 : 0 : if ( matchType & MatchType::Name && ( layer->name().isEmpty() || layer->name() != name ) )
167 : : {
168 : 0 : return false;
169 : : }
170 : : else
171 : : {
172 : : // We have found a match by name, now check the other
173 : : // criteria
174 : 0 : if ( matchType & MatchType::Provider && layer->providerType() != provider )
175 : : {
176 : 0 : return false;
177 : : }
178 : 0 : if ( matchType & MatchType::Source && layer->publicSource() != source )
179 : : {
180 : 0 : return false;
181 : : }
182 : : // All tests passed
183 : 0 : return true;
184 : : }
185 : 0 : }
186 : :
187 : : /**
188 : : * Resolves the map layer by attempting to find a matching layer
189 : : * in a \a project using a weak match.
190 : : *
191 : : * First, the layer is attempted to match to project layers using the
192 : : * layer's ID (calling this method implicitly calls resolve()).
193 : : *
194 : : * Failing a match by layer ID, the layer will be matched by using
195 : : * the weak references to layer public source, layer name and data
196 : : * provider contained in this layer reference.
197 : : *
198 : : * The \a matchType enum can used to refine the matching criteria
199 : : * when using the weak reference and include layer name,
200 : : * provider name and layer source in the equality test,
201 : : * by default they are all checked.
202 : : *
203 : : * If a matching layer is found, this reference will be updated to match
204 : : * the found layer and the layer will be returned. If no matching layer is
205 : : * found, NULLPTR is returned.
206 : : * \see resolve()
207 : : * \see layerMatchesSource()
208 : : * \see layerMatchesWeakly()
209 : : * \see resolveByIdOrNameOnly()
210 : : */
211 : 0 : TYPE *resolveWeakly( const QgsProject *project, MatchType matchType = MatchType::All )
212 : : {
213 : : // first try matching by layer ID
214 : 0 : if ( resolve( project ) )
215 : 0 : return layer;
216 : :
217 : 0 : if ( project )
218 : : {
219 : 0 : QList<QgsMapLayer *> layers;
220 : : // If matching by name ...
221 : 0 : if ( matchType & MatchType::Name )
222 : : {
223 : 0 : if ( name.isEmpty() )
224 : : {
225 : 0 : return nullptr;
226 : : }
227 : 0 : layers = project->mapLayersByName( name );
228 : 0 : }
229 : : else // ... we need all layers
230 : : {
231 : 0 : layers = project->mapLayers().values();
232 : : }
233 : 0 : for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
234 : : {
235 : 0 : if ( TYPE *tl = qobject_cast< TYPE *>( *it ) )
236 : : {
237 : 0 : if ( layerMatchesWeakly( tl, matchType ) )
238 : : {
239 : 0 : setLayer( tl );
240 : 0 : return tl;
241 : : }
242 : 0 : }
243 : 0 : }
244 : 0 : }
245 : 0 : return nullptr;
246 : 0 : }
247 : :
248 : : /**
249 : : * Resolves the map layer by attempting to find a matching layer
250 : : * in a \a project using a weak match.
251 : : *
252 : : * First, the layer is attempted to match to project layers using the
253 : : * layer's ID (calling this method implicitly calls resolve()).
254 : : *
255 : : * Failing a match by layer ID, the layer will be matched by using
256 : : * the weak references to layer public source, layer name and data
257 : : * provider contained in this layer reference.
258 : : *
259 : : * Failing a match by weak reference, the layer will be matched by using
260 : : * the name only.
261 : : *
262 : : * If a matching layer is found, this reference will be updated to match
263 : : * the found layer and the layer will be returned. If no matching layer is
264 : : * found, NULLPTR is returned.
265 : : * \see resolve()
266 : : * \see layerMatchesSource()
267 : : * \see resolveWeakly()
268 : : * \since QGIS 3.8
269 : : */
270 : 0 : TYPE *resolveByIdOrNameOnly( const QgsProject *project )
271 : : {
272 : : // first try by matching by layer ID, or weakly by source, name and provider
273 : 0 : if ( resolveWeakly( project ) )
274 : 0 : return layer;
275 : :
276 : : // fallback to checking by name only
277 : 0 : if ( project && !name.isEmpty() )
278 : : {
279 : 0 : const QList<QgsMapLayer *> layers = project->mapLayersByName( name );
280 : 0 : for ( QgsMapLayer *l : layers )
281 : : {
282 : 0 : if ( TYPE *tl = qobject_cast< TYPE *>( l ) )
283 : : {
284 : 0 : setLayer( tl );
285 : 0 : return tl;
286 : : }
287 : : }
288 : 0 : }
289 : 0 : return nullptr;
290 : 0 : }
291 : :
292 : :
293 : : };
294 : :
295 : : typedef _LayerRef<QgsMapLayer> QgsMapLayerRef;
296 : :
297 : : #endif // QGSMAPLAYERREF_H
|