1 /*************************************************************************** 2 qgslayoutitemguiregistry.h 3 -------------------------- 4 begin : June 2017 5 copyright : (C) 2017 by Nyall Dawson 6 email : nyall dot dawson at gmail dot com 7 ***************************************************************************/ 8 /*************************************************************************** 9 * * 10 * This program is free software; you can redistribute it and/or modify * 11 * it under the terms of the GNU General Public License as published by * 12 * the Free Software Foundation; either version 2 of the License, or * 13 * (at your option) any later version. * 14 * * 15 ***************************************************************************/ 16 #ifndef QGSLAYOUTITEMGUIREGISTRY_H 17 #define QGSLAYOUTITEMGUIREGISTRY_H 18 19 #include "qgis_gui.h" 20 #include "qgis_sip.h" 21 #include "qgsapplication.h" 22 #include "qgspathresolver.h" 23 #include "qgslayoutitemregistry.h" 24 #include <QGraphicsItem> //for QGraphicsItem::UserType 25 #include <QIcon> 26 #include <functional> 27 28 #include "qgslayoutitem.h" // temporary 29 30 class QgsLayout; 31 class QgsLayoutView; 32 class QgsLayoutItem; 33 class QgsLayoutViewRubberBand; 34 class QgsLayoutItemBaseWidget; 35 36 /** 37 * \ingroup gui 38 * \brief Stores GUI metadata about one layout item class. 39 * 40 * This is a companion to QgsLayoutItemAbstractMetadata, storing only 41 * the components related to the GUI behavior of a layout item. 42 * 43 * \note In C++ you can use QgsLayoutItemGuiMetadata convenience class. 44 * \since QGIS 3.0 45 */ 46 class GUI_EXPORT QgsLayoutItemAbstractGuiMetadata 47 { 48 public: 49 50 //! Flags for controlling how a items behave in the GUI 51 enum Flag 52 { 53 FlagNoCreationTools = 1 << 1, //!< Do not show item creation tools for the item type 54 }; Q_DECLARE_FLAGS(Flags,Flag)55 Q_DECLARE_FLAGS( Flags, Flag ) 56 57 /** 58 * Constructor for QgsLayoutItemAbstractGuiMetadata with the specified class \a type. 59 * 60 * \a visibleName should be set to a translated, user visible name identifying the corresponding layout item. 61 * 62 * An optional \a groupId can be set, which allows grouping of related layout item classes. See QgsLayoutItemGuiMetadata for details. 63 * 64 * If \a isNodeBased is TRUE, then the corresponding item is a node based item. 65 */ 66 QgsLayoutItemAbstractGuiMetadata( int type, const QString &visibleName, const QString &groupId = QString(), bool isNodeBased = false, Flags flags = QgsLayoutItemAbstractGuiMetadata::Flags() ) 67 : mType( type ) 68 , mGroupId( groupId ) 69 , mIsNodeBased( isNodeBased ) 70 , mName( visibleName ) 71 , mFlags( flags ) 72 {} 73 74 virtual ~QgsLayoutItemAbstractGuiMetadata() = default; 75 76 /** 77 * Returns the unique item type code for the layout item class. 78 */ type()79 int type() const { return mType; } 80 81 /** 82 * Returns item flags. 83 */ flags()84 Flags flags() const { return mFlags; } 85 86 /** 87 * Returns the item group ID, if set. 88 */ groupId()89 QString groupId() const { return mGroupId; } 90 91 /** 92 * Returns TRUE if the associated item is a node based item. 93 */ isNodeBased()94 bool isNodeBased() const { return mIsNodeBased; } 95 96 /** 97 * Returns a translated, user visible name identifying the corresponding layout item. 98 */ visibleName()99 QString visibleName() const { return mName; } 100 101 /** 102 * Returns an icon representing creation of the layout item type. 103 */ creationIcon()104 virtual QIcon creationIcon() const { return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddBasicRectangle.svg" ) ); } 105 106 /* 107 * IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not 108 * the case. 109 * As per Phil Thomson's advice on https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039450.html: 110 * 111 * " 112 * /Factory/ is used when the instance returned is guaranteed to be new to Python. 113 * In this case it isn't because it has already been seen when being returned by QgsProcessingAlgorithm::createInstance() 114 * (However for a different sub-class implemented in C++ then it would be the first time it was seen 115 * by Python so the /Factory/ on create() would be correct.) 116 * 117 * You might try using /TransferBack/ on create() instead - that might be the best compromise. 118 * " 119 */ 120 121 /** 122 * Creates a configuration widget for an \a item of this type. Can return NULLPTR if no configuration GUI is required. 123 */ createItemWidget(QgsLayoutItem * item)124 virtual QgsLayoutItemBaseWidget *createItemWidget( QgsLayoutItem *item ) SIP_TRANSFERBACK { Q_UNUSED( item ) return nullptr; } 125 126 /** 127 * Creates a rubber band for use when creating layout items of this type. Can return NULLPTR if no rubber band 128 * should be created. The default behavior is to create a rectangular rubber band. 129 * \see createNodeRubberBand() 130 */ 131 virtual QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) SIP_TRANSFERBACK; 132 133 /** 134 * Creates a rubber band for use when creating layout node based items of this type. Can return NULLPTR if no rubber band 135 * should be created. The default behavior is to return NULLPTR. 136 * \see createRubberBand() 137 */ 138 virtual QAbstractGraphicsShapeItem *createNodeRubberBand( QgsLayoutView *view ) SIP_TRANSFERBACK; 139 140 /** 141 * Creates an instance of the corresponding item type. 142 */ 143 virtual QgsLayoutItem *createItem( QgsLayout *layout ) SIP_TRANSFERBACK; 144 145 /** 146 * Called when a newly created item of the associated type has been added to a layout. 147 * 148 * This is only called for additions which result from GUI operations - i.e. it is not 149 * called for items added from templates. 150 */ 151 virtual void newItemAddedToLayout( QgsLayoutItem *item ); 152 153 private: 154 155 int mType = -1; 156 QString mGroupId; 157 bool mIsNodeBased = false; 158 QString mName; 159 Flags mFlags; 160 161 }; 162 163 //! Layout item configuration widget creation function 164 typedef std::function<QgsLayoutItemBaseWidget *( QgsLayoutItem * )> QgsLayoutItemWidgetFunc SIP_SKIP; 165 166 //! Layout rubber band creation function 167 typedef std::function<QgsLayoutViewRubberBand *( QgsLayoutView * )> QgsLayoutItemRubberBandFunc SIP_SKIP; 168 169 //! Layout node based rubber band creation function 170 typedef std::function<QAbstractGraphicsShapeItem *( QgsLayoutView * )> QgsLayoutNodeItemRubberBandFunc SIP_SKIP; 171 172 //! Layout item added to layout callback 173 typedef std::function<void ( QgsLayoutItem *, const QVariantMap & )> QgsLayoutItemAddedToLayoutFunc SIP_SKIP; 174 175 #ifndef SIP_RUN 176 177 /** 178 * \ingroup gui 179 * \brief Convenience metadata class that uses static functions to handle layout item GUI behavior. 180 * \note not available in Python bindings 181 * \since QGIS 3.0 182 */ 183 class GUI_EXPORT QgsLayoutItemGuiMetadata : public QgsLayoutItemAbstractGuiMetadata 184 { 185 public: 186 187 /** 188 * Constructor for QgsLayoutItemGuiMetadata with the specified class \a type 189 * and \a creationIcon, and function pointers for the various 190 * configuration widget creation functions. 191 * 192 * \a visibleName should be set to a translated, user visible name identifying the corresponding layout item. 193 * 194 * An optional \a groupId can be set, which allows grouping of related layout item classes. See QgsLayoutItemGuiMetadata for details. 195 * 196 * If \a isNodeBased is TRUE, then the corresponding item is a node based item. 197 */ 198 QgsLayoutItemGuiMetadata( int type, const QString &visibleName, const QIcon &creationIcon, 199 const QgsLayoutItemWidgetFunc &pfWidget = nullptr, 200 const QgsLayoutItemRubberBandFunc &pfRubberBand = nullptr, const QString &groupId = QString(), 201 bool isNodeBased = false, 202 QgsLayoutItemAbstractGuiMetadata::Flags flags = QgsLayoutItemAbstractGuiMetadata::Flags(), 203 const QgsLayoutItemCreateFunc &pfCreateFunc = nullptr ) QgsLayoutItemAbstractGuiMetadata(type,visibleName,groupId,isNodeBased,flags)204 : QgsLayoutItemAbstractGuiMetadata( type, visibleName, groupId, isNodeBased, flags ) 205 , mIcon( creationIcon ) 206 , mWidgetFunc( pfWidget ) 207 , mRubberBandFunc( pfRubberBand ) 208 , mCreateFunc( pfCreateFunc ) 209 {} 210 211 /** 212 * Returns the classes' configuration widget creation function. 213 * \see setWidgetFunction() 214 */ widgetFunction()215 QgsLayoutItemWidgetFunc widgetFunction() const { return mWidgetFunc; } 216 217 /** 218 * Sets the classes' configuration widget creation \a function. 219 * \see widgetFunction() 220 */ setWidgetFunction(const QgsLayoutItemWidgetFunc & function)221 void setWidgetFunction( const QgsLayoutItemWidgetFunc &function ) { mWidgetFunc = function; } 222 223 /** 224 * Returns the classes' rubber band creation function. 225 * \see setRubberBandCreationFunction() 226 */ rubberBandCreationFunction()227 QgsLayoutItemRubberBandFunc rubberBandCreationFunction() const { return mRubberBandFunc; } 228 229 /** 230 * Sets the classes' rubber band creation \a function. 231 * \see rubberBandCreationFunction() 232 */ setRubberBandCreationFunction(const QgsLayoutItemRubberBandFunc & function)233 void setRubberBandCreationFunction( const QgsLayoutItemRubberBandFunc &function ) { mRubberBandFunc = function; } 234 235 /** 236 * Returns the classes' node based rubber band creation function. 237 * \see setNodeRubberBandCreationFunction() 238 */ nodeRubberBandCreationFunction()239 QgsLayoutNodeItemRubberBandFunc nodeRubberBandCreationFunction() const { return mNodeRubberBandFunc; } 240 241 /** 242 * Sets the classes' node based rubber band creation \a function. 243 * \see nodeRubberBandCreationFunction() 244 */ setNodeRubberBandCreationFunction(const QgsLayoutNodeItemRubberBandFunc & function)245 void setNodeRubberBandCreationFunction( const QgsLayoutNodeItemRubberBandFunc &function ) { mNodeRubberBandFunc = function; } 246 247 /** 248 * Returns the classes' item creation function. 249 * \see setItemCreationFunction() 250 */ itemCreationFunction()251 QgsLayoutItemCreateFunc itemCreationFunction() const { return mCreateFunc; } 252 253 /** 254 * Sets the classes' item creation \a function. 255 * \see itemCreationFunction() 256 */ setItemCreationFunction(const QgsLayoutItemCreateFunc & function)257 void setItemCreationFunction( const QgsLayoutItemCreateFunc &function ) { mCreateFunc = function; } 258 259 /** 260 * Returns the classes' item added to layout function. 261 * \see setItemAddedToLayoutFunction() 262 */ itemAddToLayoutFunction()263 QgsLayoutItemAddedToLayoutFunc itemAddToLayoutFunction() const { return mAddedToLayoutFunc; } 264 265 /** 266 * Sets the classes' item creation \a function. 267 * \see itemAddToLayoutFunction() 268 */ setItemAddedToLayoutFunction(const QgsLayoutItemAddedToLayoutFunc & function)269 void setItemAddedToLayoutFunction( const QgsLayoutItemAddedToLayoutFunc &function ) { mAddedToLayoutFunc = function; } 270 creationIcon()271 QIcon creationIcon() const override { return mIcon.isNull() ? QgsLayoutItemAbstractGuiMetadata::creationIcon() : mIcon; } createItemWidget(QgsLayoutItem * item)272 QgsLayoutItemBaseWidget *createItemWidget( QgsLayoutItem *item ) override { return mWidgetFunc ? mWidgetFunc( item ) : nullptr; } createRubberBand(QgsLayoutView * view)273 QgsLayoutViewRubberBand *createRubberBand( QgsLayoutView *view ) override { return mRubberBandFunc ? mRubberBandFunc( view ) : nullptr; } createNodeRubberBand(QgsLayoutView * view)274 QAbstractGraphicsShapeItem *createNodeRubberBand( QgsLayoutView *view ) override { return mNodeRubberBandFunc ? mNodeRubberBandFunc( view ) : nullptr; } 275 276 QgsLayoutItem *createItem( QgsLayout *layout ) override; 277 void newItemAddedToLayout( QgsLayoutItem *item ) override; 278 279 /** 280 * Called when a newly created item of the associated type has been added to a layout. 281 * 282 * This is only called for additions which result from GUI operations - i.e. it is not 283 * called for items added from templates. 284 * 285 * The \a properties map will be filled with any custom properties which were specified during 286 * the item creation. 287 * 288 * \since QGIS 3.18 289 */ 290 void newItemAddedToLayout( QgsLayoutItem *item, const QVariantMap &properties ); 291 292 protected: 293 QIcon mIcon; 294 QgsLayoutItemWidgetFunc mWidgetFunc = nullptr; 295 QgsLayoutItemRubberBandFunc mRubberBandFunc = nullptr; 296 QgsLayoutNodeItemRubberBandFunc mNodeRubberBandFunc = nullptr; 297 QgsLayoutItemCreateFunc mCreateFunc = nullptr; 298 QgsLayoutItemAddedToLayoutFunc mAddedToLayoutFunc = nullptr; 299 300 }; 301 302 #endif 303 304 /** 305 * \ingroup gui 306 * \brief Stores GUI metadata about a group of layout item classes. 307 * 308 * QgsLayoutItemGuiGroup stores settings about groups of related layout item classes 309 * which should be presented to users grouped together. 310 * 311 * For instance, the various basic shape creation tools would use QgsLayoutItemGuiGroup 312 * to display grouped within designer dialogs. 313 * 314 * \since QGIS 3.0 315 */ 316 class GUI_EXPORT QgsLayoutItemGuiGroup 317 { 318 public: 319 320 /** 321 * Constructor for QgsLayoutItemGuiGroup. 322 */ 323 QgsLayoutItemGuiGroup( const QString &id = QString(), const QString &name = QString(), const QIcon &icon = QIcon() ) id(id)324 : id( id ) 325 , name( name ) 326 , icon( icon ) 327 {} 328 329 /** 330 * Unique (untranslated) group ID string. 331 */ 332 QString id; 333 334 /** 335 * Translated group name. 336 */ 337 QString name; 338 339 /** 340 * Icon for group. 341 */ 342 QIcon icon; 343 344 }; 345 346 347 /** 348 * \ingroup core 349 * \class QgsLayoutItemGuiRegistry 350 * \brief Registry of available layout item GUI behavior. 351 * 352 * QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through 353 * QgsGui::layoutItemGuiRegistry(). 354 * 355 * This acts as a companion to QgsLayoutItemRegistry, handling only 356 * the components related to the GUI behavior of layout items. 357 * 358 * \since QGIS 3.0 359 */ 360 class GUI_EXPORT QgsLayoutItemGuiRegistry : public QObject 361 { 362 Q_OBJECT 363 364 public: 365 366 /** 367 * Creates a new empty item GUI registry. 368 * 369 * QgsLayoutItemGuiRegistry is not usually directly created, but rather accessed through 370 * QgsGui::layoutItemGuiRegistry(). 371 */ 372 QgsLayoutItemGuiRegistry( QObject *parent = nullptr ); 373 374 ~QgsLayoutItemGuiRegistry() override; 375 376 //! QgsLayoutItemGuiRegistry cannot be copied. 377 QgsLayoutItemGuiRegistry( const QgsLayoutItemGuiRegistry &rh ) = delete; 378 //! QgsLayoutItemGuiRegistry cannot be copied. 379 QgsLayoutItemGuiRegistry &operator=( const QgsLayoutItemGuiRegistry &rh ) = delete; 380 381 /** 382 * Returns the metadata for the specified item \a metadataId. Returns NULLPTR if 383 * a corresponding \a metadataId was not found in the registry. 384 */ 385 QgsLayoutItemAbstractGuiMetadata *itemMetadata( int metadataId ) const; 386 387 /** 388 * Returns the GUI item metadata ID which corresponds to the specified layout item \a type. 389 * 390 * In the case that multiple GUI metadata classes exist for a single layout item \a type then 391 * only the first encountered GUI metadata ID will be returned. 392 * 393 * Returns -1 if no matching metadata is found in the GUI registry. 394 * 395 * \since QGIS 3.18 396 */ 397 int metadataIdForItemType( int type ) const; 398 399 /** 400 * Registers the gui metadata for a new layout item type. Takes ownership of the metadata instance. 401 */ 402 bool addLayoutItemGuiMetadata( QgsLayoutItemAbstractGuiMetadata *metadata SIP_TRANSFER ); 403 404 /** 405 * Registers a new item group with the registry. This must be done before calling 406 * addLayoutItemGuiMetadata() for any item types associated with the group. 407 * 408 * Returns TRUE if group was added, or FALSE if group could not be added (e.g. due to 409 * duplicate id value). 410 * 411 * \see itemGroup() 412 */ 413 bool addItemGroup( const QgsLayoutItemGuiGroup &group ); 414 415 /** 416 * Returns a reference to the item group with matching \a id. 417 * \see addItemGroup() 418 */ 419 const QgsLayoutItemGuiGroup &itemGroup( const QString &id ); 420 421 /* 422 * IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not 423 * the case. 424 * As per Phil Thomson's advice on https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039450.html: 425 * 426 * " 427 * /Factory/ is used when the instance returned is guaranteed to be new to Python. 428 * In this case it isn't because it has already been seen when being returned by QgsProcessingAlgorithm::createInstance() 429 * (However for a different sub-class implemented in C++ then it would be the first time it was seen 430 * by Python so the /Factory/ on create() would be correct.) 431 * 432 * You might try using /TransferBack/ on create() instead - that might be the best compromise. 433 * " 434 */ 435 436 /** 437 * Creates a new instance of a layout item given the item metadata \a metadataId, target \a layout. 438 */ 439 QgsLayoutItem *createItem( int metadataId, QgsLayout *layout ) const SIP_TRANSFERBACK; 440 441 /** 442 * Called when a newly created item of the associated metadata \a metadataId has been added to a layout. 443 * 444 * This is only called for additions which result from GUI operations - i.e. it is not 445 * called for items added from templates. 446 * 447 * Since QGIS 3.18 the optional \a properties argument can be used to pass custom properties to the 448 * QgsLayoutItemGuiMetadata::newItemAddedToLayout() function. 449 */ 450 void newItemAddedToLayout( int metadataId, QgsLayoutItem *item, const QVariantMap &properties = QVariantMap() ); 451 452 /* 453 * IMPORTANT: While it seems like /Factory/ would be the correct annotations here, that's not 454 * the case. 455 * As per Phil Thomson's advice on https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039450.html: 456 * 457 * " 458 * /Factory/ is used when the instance returned is guaranteed to be new to Python. 459 * In this case it isn't because it has already been seen when being returned by QgsProcessingAlgorithm::createInstance() 460 * (However for a different sub-class implemented in C++ then it would be the first time it was seen 461 * by Python so the /Factory/ on create() would be correct.) 462 * 463 * You might try using /TransferBack/ on create() instead - that might be the best compromise. 464 * " 465 */ 466 467 /** 468 * Creates a new instance of a layout item configuration widget for the specified \a item. 469 */ 470 QgsLayoutItemBaseWidget *createItemWidget( QgsLayoutItem *item ) const SIP_TRANSFERBACK; 471 472 /** 473 * Creates a new rubber band item for the specified item \a metadataId and destination \a view. 474 * \note not available from Python bindings 475 * \see createNodeItemRubberBand() 476 */ 477 QgsLayoutViewRubberBand *createItemRubberBand( int metadataId, QgsLayoutView *view ) const SIP_SKIP; 478 479 /** 480 * Creates a rubber band for the specified item \a metadataId and destination \a view. 481 * Can return NULLPTR if no node based rubber band should be created or is applicable for the item. 482 * \see createItemRubberBand() 483 * \note not available from Python bindings 484 */ 485 QAbstractGraphicsShapeItem *createNodeItemRubberBand( int metadataId, QgsLayoutView *view ) SIP_SKIP; 486 487 /** 488 * Returns a list of available item metadata ids handled by the registry. 489 */ 490 QList< int > itemMetadataIds() const; 491 492 signals: 493 494 /** 495 * Emitted whenever a new item type is added to the registry, with the specified 496 * \a metadataId. 497 */ 498 void typeAdded( int metadataId ); 499 500 private: 501 #ifdef SIP_RUN 502 QgsLayoutItemGuiRegistry( const QgsLayoutItemGuiRegistry &rh ); 503 #endif 504 505 QMap< int, QgsLayoutItemAbstractGuiMetadata *> mMetadata; 506 507 QMap< QString, QgsLayoutItemGuiGroup > mItemGroups; 508 509 }; 510 511 #endif //QGSLAYOUTITEMGUIREGISTRY_H 512 513 514 515