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