Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsmapthemecollection.h 3 : : -------------------------------------- 4 : : Date : September 2014 5 : : Copyright : (C) 2014 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 QGSMAPTHEMECOLLECTION_H 17 : : #define QGSMAPTHEMECOLLECTION_H 18 : : 19 : : #include "qgis_core.h" 20 : : #include "qgis_sip.h" 21 : : #include <QMap> 22 : : #include <QObject> 23 : : #include <QPointer> 24 : : #include <QSet> 25 : : #include <QStringList> 26 : : 27 : : #include "qgsmaplayer.h" 28 : : 29 : : class QDomDocument; 30 : : class QgsLayerTreeModel; 31 : : class QgsLayerTreeNode; 32 : : class QgsLayerTreeGroup; 33 : : class QgsLayerTreeLayer; 34 : : class QgsProject; 35 : : 36 : : /** 37 : : * \class QgsMapThemeCollection 38 : : * \ingroup core 39 : : * \brief Container class that allows storage of map themes consisting of visible 40 : : * map layers and layer styles. 41 : : * \since QGIS 2.12 42 : : */ 43 : : 44 : : class CORE_EXPORT QgsMapThemeCollection : public QObject 45 : : { 46 : : Q_OBJECT 47 : : 48 : : Q_PROPERTY( QStringList mapThemes READ mapThemes NOTIFY mapThemesChanged ) 49 : : Q_PROPERTY( QgsProject *project READ project WRITE setProject NOTIFY projectChanged ) 50 : : 51 : : public: 52 : : 53 : : /** 54 : : * \ingroup core 55 : : * \brief Individual record of a visible layer in a map theme record. 56 : : * \since QGIS 3.0 57 : : */ 58 : 0 : class CORE_EXPORT MapThemeLayerRecord 59 : : { 60 : : public: 61 : : //! Initialize layer record with a map layer - it will be stored as a weak pointer 62 : 0 : MapThemeLayerRecord( QgsMapLayer *l = nullptr ): mLayer( l ) {} 63 : : 64 : : bool operator==( const QgsMapThemeCollection::MapThemeLayerRecord &other ) const 65 : : { 66 : : return mLayer == other.mLayer && isVisible == other.isVisible && 67 : : usingCurrentStyle == other.usingCurrentStyle && currentStyle == other.currentStyle && 68 : : usingLegendItems == other.usingLegendItems && checkedLegendItems == other.checkedLegendItems && 69 : : expandedLegendItems == other.expandedLegendItems && expandedLayerNode == other.expandedLayerNode; 70 : : } 71 : : bool operator!=( const QgsMapThemeCollection::MapThemeLayerRecord &other ) const 72 : : { 73 : : return !( *this == other ); 74 : : } 75 : : 76 : : //! Returns map layer or NULLPTR if the layer does not exist anymore 77 : 0 : QgsMapLayer *layer() const { return mLayer; } 78 : : 79 : : //! Sets the map layer for this record 80 : : void setLayer( QgsMapLayer *layer ); 81 : : 82 : : /** 83 : : * TRUE if the layer is visible in the associated theme. 84 : : * \since QGIS 3.14 85 : : */ 86 : 0 : bool isVisible = true; 87 : : 88 : : //! Whether current style is valid and should be applied 89 : 0 : bool usingCurrentStyle = false; 90 : : //! Name of the current style of the layer 91 : : QString currentStyle; 92 : : //! Whether checkedLegendItems should be applied 93 : 0 : bool usingLegendItems = false; 94 : : //! Rule keys of check legend items in layer tree model 95 : : QSet<QString> checkedLegendItems; 96 : : 97 : : /** 98 : : * Rule keys of expanded legend items in layer tree view. 99 : : * \since QGIS 3.2 100 : : */ 101 : : QSet<QString> expandedLegendItems; 102 : : 103 : : /** 104 : : * Whether the layer's tree node is expanded 105 : : * (only to be applied if the parent MapThemeRecord has the information about expanded nodes stored) 106 : : * \since QGIS 3.2 107 : : */ 108 : 0 : bool expandedLayerNode = false; 109 : : private: 110 : : //! Weak pointer to the layer 111 : : QgsWeakMapLayerPointer mLayer; 112 : : }; 113 : : 114 : : /** 115 : : * \ingroup core 116 : : * \brief Individual map theme record of visible layers and styles. 117 : : * 118 : : * \since QGIS 3.0, Previously called PresetRecord 119 : : */ 120 : 0 : class CORE_EXPORT MapThemeRecord 121 : : { 122 : : public: 123 : : 124 : : bool operator==( const QgsMapThemeCollection::MapThemeRecord &other ) const 125 : : { 126 : : return validLayerRecords() == other.validLayerRecords() && 127 : : mHasExpandedStateInfo == other.mHasExpandedStateInfo && 128 : : mExpandedGroupNodes == other.mExpandedGroupNodes && mCheckedGroupNodes == other.mCheckedGroupNodes; 129 : : } 130 : : bool operator!=( const QgsMapThemeCollection::MapThemeRecord &other ) const 131 : : { 132 : : return !( *this == other ); 133 : : } 134 : : 135 : : //! Returns a list of records for all visible layer belonging to the theme. 136 : 0 : QList<QgsMapThemeCollection::MapThemeLayerRecord> layerRecords() const { return mLayerRecords; } 137 : : 138 : : //! Sets layer records for the theme. 139 : 0 : void setLayerRecords( const QList<QgsMapThemeCollection::MapThemeLayerRecord> &records ) { mLayerRecords = records; } 140 : : 141 : : //! Removes a record for \a layer if present. 142 : : void removeLayerRecord( QgsMapLayer *layer ); 143 : : 144 : : //! Add a new record for a layer. 145 : : void addLayerRecord( const QgsMapThemeCollection::MapThemeLayerRecord &record ); 146 : : 147 : : /** 148 : : * Returns set with only records for valid layers 149 : : */ 150 : : QHash<QgsMapLayer *, QgsMapThemeCollection::MapThemeLayerRecord> validLayerRecords() const SIP_SKIP; 151 : : 152 : : /** 153 : : * Returns whether information about expanded/collapsed state of nodes has been recorded 154 : : * and thus whether expandedGroupNodes() and expandedLegendItems + expandedLayerNode from layer records are valid. 155 : : * \since QGIS 3.2 156 : : */ 157 : 0 : bool hasExpandedStateInfo() const { return mHasExpandedStateInfo; } 158 : : 159 : : /** 160 : : * Returns whether information about checked/unchecked state of groups has been recorded 161 : : * and thus whether checkedGroupNodes() is valid. 162 : : * \note Not available in Python bindings 163 : : * \since QGIS 3.10.1 164 : : */ 165 : 0 : bool hasCheckedStateInfo() const { return mHasCheckedStateInfo; } SIP_SKIP; 166 : : 167 : : /** 168 : : * Sets whether the map theme contains valid expanded/collapsed state of nodes 169 : : * \since QGIS 3.2 170 : : */ 171 : 0 : void setHasExpandedStateInfo( bool hasInfo ) { mHasExpandedStateInfo = hasInfo; } 172 : : 173 : : /** 174 : : * Sets whether the map theme contains valid checked/unchecked state of group nodes 175 : : * \note Not available in Python bindings 176 : : * \since QGIS 3.10.1 177 : : */ 178 : 0 : void setHasCheckedStateInfo( bool hasInfo ) { mHasCheckedStateInfo = hasInfo; } SIP_SKIP; 179 : : 180 : : /** 181 : : * Returns a set of group identifiers for group nodes that should have expanded state (other group nodes should be collapsed). 182 : : * The returned value is valid only when hasExpandedStateInfo() returns TRUE. 183 : : * Group identifiers are built using group names, a sub-group name is prepended by parent group's identifier 184 : : * and a forward slash, e.g. "level1/level2" 185 : : * \since QGIS 3.2 186 : : */ 187 : 0 : QSet<QString> expandedGroupNodes() const { return mExpandedGroupNodes; } 188 : : 189 : : /** 190 : : * Returns a set of group identifiers for group nodes that should have checked state (other group nodes should be unchecked). 191 : : * The returned value is valid only when hasCheckedStateInfo() returns TRUE. 192 : : * Group identifiers are built using group names, a sub-group name is prepended by parent group's identifier 193 : : * and a forward slash, e.g. "level1/level2" 194 : : * \since QGIS 3.10.1 195 : : */ 196 : 0 : QSet<QString> checkedGroupNodes() const { return mCheckedGroupNodes; } 197 : : 198 : : /** 199 : : * Sets a set of group identifiers for group nodes that should have expanded state. See expandedGroupNodes(). 200 : : * \since QGIS 3.2 201 : : */ 202 : 0 : void setExpandedGroupNodes( const QSet<QString> &expandedGroupNodes ) { mExpandedGroupNodes = expandedGroupNodes; } 203 : : 204 : : /** 205 : : * Sets a set of group identifiers for group nodes that should have checked state. See checkedGroupNodes(). 206 : : * \since QGIS 3.10.1 207 : : */ 208 : 0 : void setCheckedGroupNodes( const QSet<QString> &checkedGroupNodes ) { mCheckedGroupNodes = checkedGroupNodes; } 209 : : 210 : : private: 211 : : //! Layer-specific records for the theme. Only visible layers are listed. 212 : : QList<MapThemeLayerRecord> mLayerRecords; 213 : : 214 : : //! Whether the information about expanded/collapsed state of groups, layers and legend items has been stored 215 : 0 : bool mHasExpandedStateInfo = false; 216 : : //! Whether the information about checked/unchecked state of groups, layers and legend items has been stored 217 : 0 : bool mHasCheckedStateInfo = false; 218 : : 219 : : /** 220 : : * Which groups should be expanded. Each group is identified by its name (sub-groups IDs are prepended with parent 221 : : * group and forward slash - e.g. "level1/level2/level3"). 222 : : */ 223 : : QSet<QString> mExpandedGroupNodes; 224 : : 225 : : /** 226 : : * Which groups should be checked. Each group is identified by its name (sub-groups IDs are prepended with parent 227 : : * group and forward slash - e.g. "level1/level2/level3"). 228 : : */ 229 : : QSet<QString> mCheckedGroupNodes; 230 : : 231 : : friend class QgsMapThemeCollection; 232 : : }; 233 : : 234 : : /** 235 : : * Create map theme collection that handles themes of the given project. 236 : : */ 237 : : QgsMapThemeCollection( QgsProject *project = nullptr ); 238 : : 239 : : /** 240 : : * Returns whether a map theme with a matching name exists. 241 : : * \since QGIS 3.0 242 : : */ 243 : : bool hasMapTheme( const QString &name ) const; 244 : : 245 : : /** 246 : : * Inserts a new map theme to the collection. 247 : : * \see update() 248 : : */ 249 : : void insert( const QString &name, const QgsMapThemeCollection::MapThemeRecord &state ); 250 : : 251 : : /** 252 : : * Updates a map theme within the collection. 253 : : * \param name name of map theme to update 254 : : * \param state map theme record to replace existing map theme 255 : : * \see insert() 256 : : */ 257 : : void update( const QString &name, const QgsMapThemeCollection::MapThemeRecord &state ); 258 : : 259 : : /** 260 : : * Removes an existing map theme from collection. 261 : : * \since QGIS 3.0 262 : : */ 263 : : void removeMapTheme( const QString &name ); 264 : : 265 : : /** 266 : : * Renames the existing map theme called \a name to \a newName. 267 : : * Returns TRUE if the rename was successful, or FALSE if it failed (e.g. due to a duplicate name for \a newName). 268 : : * \since QGIS 3.14 269 : : */ 270 : : bool renameMapTheme( const QString &name, const QString &newName ); 271 : : 272 : : //! Removes all map themes from the collection. 273 : : void clear(); 274 : : 275 : : /** 276 : : * Returns a list of existing map theme names. 277 : : * \since QGIS 3.0 278 : : */ 279 : : QStringList mapThemes() const; 280 : : 281 : : /** 282 : : * Returns the recorded state of a map theme. 283 : : * \since QGIS 3.0 284 : : */ 285 : 0 : QgsMapThemeCollection::MapThemeRecord mapThemeState( const QString &name ) const { return mMapThemes[name]; } 286 : : 287 : : /** 288 : : * Returns the list of layer IDs that are visible for the specified map theme. 289 : : * 290 : : * \note The order of the returned list is not guaranteed to reflect the order of layers 291 : : * in the canvas. 292 : : * \since QGIS 3.0 293 : : */ 294 : : QStringList mapThemeVisibleLayerIds( const QString &name ) const; 295 : : 296 : : /** 297 : : * Returns the list of layers that are visible for the specified map theme. 298 : : * 299 : : * \note The order of the returned list is not guaranteed to reflect the order of layers 300 : : * in the canvas. 301 : : * \since QGIS 3.0 302 : : */ 303 : : QList<QgsMapLayer *> mapThemeVisibleLayers( const QString &name ) const; 304 : : 305 : : /** 306 : : * Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme. 307 : : * \since QGIS 3.0 308 : : */ 309 : : QMap<QString, QString> mapThemeStyleOverrides( const QString &name ); 310 : : 311 : : /** 312 : : * Reads the map theme collection state from XML 313 : : * \param doc DOM document 314 : : * \see writeXml 315 : : */ 316 : : void readXml( const QDomDocument &doc ); 317 : : 318 : : /** 319 : : * Writes the map theme collection state to XML. 320 : : * \param doc DOM document 321 : : * \see readXml 322 : : */ 323 : : void writeXml( QDomDocument &doc ); 324 : : 325 : : /** 326 : : * Static method to create theme from the current state of layer visibilities in layer tree, 327 : : * current style of layers and check state of legend items (from a layer tree model). 328 : : * \since QGIS 3.0 329 : : */ 330 : : static QgsMapThemeCollection::MapThemeRecord createThemeFromCurrentState( QgsLayerTreeGroup *root, QgsLayerTreeModel *model ); 331 : : 332 : : /** 333 : : * Apply theme given by its name and modify layer tree, current style of layers and checked 334 : : * legend items of passed layer tree model. 335 : : * \since QGIS 3.0 336 : : */ 337 : : void applyTheme( const QString &name, QgsLayerTreeGroup *root, QgsLayerTreeModel *model ); 338 : : 339 : : /** 340 : : * The QgsProject on which this map theme collection works. 341 : : * 342 : : * \since QGIS 3.0 343 : : */ 344 : : QgsProject *project(); 345 : : 346 : : /** 347 : : * \copydoc project() 348 : : * \since QGIS 3.0 349 : : */ 350 : : void setProject( QgsProject *project ); 351 : : 352 : : /** 353 : : * Returns the master layer order (this will always match the project's QgsProject::layerOrder() ). 354 : : * All map themes will maintain the same layer order as the master layer order. 355 : : * \see masterVisibleLayers() 356 : : * \since QGIS 3.0 357 : : */ 358 : : QList< QgsMapLayer * > masterLayerOrder() const; 359 : : 360 : : /** 361 : : * Returns the master list of visible layers. The order of returned layers will always match those 362 : : * of masterLayerOrder(), but the returned layers are filtered to only include those visible 363 : : * in the project's layer tree. 364 : : * \see masterLayerOrder() 365 : : * \since QGIS 3.0 366 : : */ 367 : : QList< QgsMapLayer * > masterVisibleLayers() const; 368 : : 369 : : signals: 370 : : 371 : : /** 372 : : * Emitted when map themes within the collection are changed. 373 : : * \since QGIS 3.0 374 : : */ 375 : : void mapThemesChanged(); 376 : : 377 : : /** 378 : : * Emitted when a map theme changes definition. 379 : : * \since QGIS 3.0 380 : : */ 381 : : void mapThemeChanged( const QString &theme ); 382 : : 383 : : /** 384 : : * Emitted when a map theme within the collection is renamed. 385 : : * \since QGIS 3.14 386 : : */ 387 : : void mapThemeRenamed( const QString &name, const QString &newName ); 388 : : 389 : : /** 390 : : * Emitted when the project changes 391 : : * 392 : : * \copydoc project() 393 : : * \since QGIS 3.0 394 : : */ 395 : : void projectChanged(); 396 : : 397 : : private slots: 398 : : 399 : : /** 400 : : * Handles updates of the map theme collection when layers are removed from the registry 401 : : */ 402 : : void registryLayersRemoved( const QStringList &layerIDs ); 403 : : 404 : : //! Update style name if a stored style gets renamed 405 : : void layerStyleRenamed( const QString &oldName, const QString &newName ); 406 : : 407 : : private: 408 : : 409 : : /** 410 : : * Apply check states of legend nodes of a given layer as defined in the map theme. 411 : : */ 412 : : void applyMapThemeCheckedLegendNodesToLayer( const MapThemeLayerRecord &layerRec, QgsMapLayer *layer ); 413 : : 414 : : /** 415 : : * Reconnects all map theme layers to handle style renames 416 : : */ 417 : : void reconnectToLayersStyleManager(); 418 : : 419 : : static bool findRecordForLayer( QgsMapLayer *layer, const MapThemeRecord &rec, MapThemeLayerRecord &layerRec ); 420 : : static MapThemeLayerRecord createThemeLayerRecord( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model ); 421 : : static void createThemeFromCurrentState( QgsLayerTreeGroup *parent, QgsLayerTreeModel *model, MapThemeRecord &rec ); 422 : : static void applyThemeToLayer( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model, const MapThemeRecord &rec ); 423 : : static void applyThemeToGroup( QgsLayerTreeGroup *parent, QgsLayerTreeModel *model, const MapThemeRecord &rec ); 424 : : 425 : : typedef QMap<QString, MapThemeRecord> MapThemeRecordMap; 426 : : MapThemeRecordMap mMapThemes; 427 : : //! project used to retrieve layers from layer IDs 428 : : QgsProject *mProject = nullptr; 429 : : }; 430 : : 431 : : 432 : : #endif // QGSMAPTHEMECOLLECTION_H