Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgslayoutmultiframe.h
3 : : --------------------
4 : : begin : October 2017
5 : : copyright : (C) 2017 by Nyall Dawson
6 : : email : nyall dot dawson 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 QGSLAYOUTMULTIFRAME_H
17 : : #define QGSLAYOUTMULTIFRAME_H
18 : :
19 : : #include "qgis_core.h"
20 : : #include "qgis_sip.h"
21 : : #include "qgslayoutobject.h"
22 : : #include "qgslayoutundocommand.h"
23 : : #include "qgsapplication.h"
24 : : #include <QIcon>
25 : : #include <QObject>
26 : : #include <QSizeF>
27 : : #include <QPointF>
28 : :
29 : : class QgsLayoutFrame;
30 : : class QgsLayoutItem;
31 : : class QgsLayout;
32 : : class QDomDocument;
33 : : class QDomElement;
34 : : class QRectF;
35 : : class QPainter;
36 : : class QStyleOptionGraphicsItem;
37 : : class QgsRenderContext;
38 : : class QgsLayoutItemRenderContext;
39 : :
40 : : /**
41 : : * \ingroup core
42 : : * \class QgsLayoutMultiFrame
43 : : * \brief Abstract base class for layout items with the ability to distribute the content to
44 : : * several frames (QgsLayoutFrame items).
45 : : * \since QGIS 3.0
46 : : */
47 : :
48 : : class CORE_EXPORT QgsLayoutMultiFrame: public QgsLayoutObject, public QgsLayoutUndoObjectInterface
49 : : {
50 : : #ifdef SIP_RUN
51 : : #include "qgslayoutitemhtml.h"
52 : : #include "qgslayoutitemattributetable.h"
53 : : #include "qgslayoutitemmanualtable.h"
54 : : #include "qgslayoutitemtexttable.h"
55 : : #endif
56 : :
57 : :
58 : : #ifdef SIP_RUN
59 : : SIP_CONVERT_TO_SUBCLASS_CODE
60 : : // the conversions have to be static, because they're using multiple inheritance
61 : : // (seen in PyQt4 .sip files for some QGraphicsItem classes)
62 : : if ( QgsLayoutMultiFrame *mf = qobject_cast< QgsLayoutMultiFrame *>( sipCpp ) )
63 : : {
64 : : switch ( mf->type() )
65 : : {
66 : : // really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
67 : : case QGraphicsItem::UserType + 112:
68 : : sipType = sipType_QgsLayoutItemHtml;
69 : : *sipCppRet = static_cast<QgsLayoutItemHtml *>( sipCpp );
70 : : break;
71 : : case QGraphicsItem::UserType + 113:
72 : : sipType = sipType_QgsLayoutItemAttributeTable;
73 : : *sipCppRet = static_cast<QgsLayoutItemAttributeTable *>( sipCpp );
74 : : break;
75 : : case QGraphicsItem::UserType + 114:
76 : : sipType = sipType_QgsLayoutItemTextTable;
77 : : *sipCppRet = static_cast<QgsLayoutItemTextTable *>( sipCpp );
78 : : break;
79 : : case QGraphicsItem::UserType + 116:
80 : : sipType = sipType_QgsLayoutItemManualTable;
81 : : *sipCppRet = static_cast<QgsLayoutItemManualTable *>( sipCpp );
82 : : break;
83 : : default:
84 : : sipType = 0;
85 : : }
86 : : }
87 : : else
88 : : {
89 : : sipType = 0;
90 : : }
91 : : SIP_END
92 : : #endif
93 : :
94 : 0 : Q_OBJECT
95 : :
96 : : public:
97 : :
98 : : /**
99 : : * Specifies the behavior for creating new frames to fit the multiframe's content
100 : : */
101 : : enum ResizeMode
102 : : {
103 : : UseExistingFrames = 0, //!< Don't automatically create new frames, just use existing frames
104 : : ExtendToNextPage, //!< Creates new full page frames on the following page(s) until the entire multiframe content is visible
105 : : RepeatOnEveryPage, //!< Repeats the same frame on every page
106 : : RepeatUntilFinished /*!< creates new frames with the same position and dimensions as the existing frame on the following page(s),
107 : : until the entire multiframe content is visible */
108 : : };
109 : :
110 : : //! Multiframe item undo commands, used for collapsing undo commands
111 : : enum UndoCommand
112 : : {
113 : : UndoHtmlBreakDistance, //!< HTML page break distance
114 : : UndoHtmlSource, //!< HTML source
115 : : UndoHtmlStylesheet, //!< HTML stylesheet
116 : : UndoTableCellStyle, //!< Table cell style
117 : : UndoTableMaximumFeatures, //!< Maximum features in table
118 : : UndoTableMargin, //!< Table margins
119 : : UndoTableHeaderFontColor, //!< Table header font color
120 : : UndoTableContentFontColor, //!< Table content font color
121 : : UndoTableGridStrokeWidth, //!< Table grid stroke width
122 : : UndoTableGridColor, //!< Table grid color
123 : : UndoTableBackgroundColor, //!< Table background color
124 : : UndoNone = -1, //!< No command suppression
125 : : };
126 : :
127 : : /**
128 : : * Construct a new multiframe item, attached to the specified \a layout.
129 : : */
130 : : QgsLayoutMultiFrame( QgsLayout *layout SIP_TRANSFERTHIS );
131 : :
132 : : ~QgsLayoutMultiFrame() override;
133 : :
134 : : /**
135 : : * Returns the multiframe identification string. This is a unique random string set for the multiframe
136 : : * upon creation.
137 : : * \note There is no corresponding setter for the uuid - it's created automatically.
138 : : */
139 : 0 : QString uuid() const { return mUuid; }
140 : :
141 : : /**
142 : : * Returns the total size of the multiframe's content, in layout units.
143 : : */
144 : : virtual QSizeF totalSize() const = 0;
145 : :
146 : : /**
147 : : * Returns unique multiframe type id.
148 : : */
149 : : virtual int type() const = 0;
150 : :
151 : : /**
152 : : * Returns the item's icon.
153 : : */
154 : : virtual QIcon icon() const { return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItem.svg" ) ); }
155 : :
156 : : /**
157 : : * Returns the fixed size for a frame, if desired. If the fixed frame size changes,
158 : : * the sizes of all frames can be recalculated by calling recalculateFrameRects().
159 : : * \param frameIndex frame number
160 : : * \returns fixed size for frame. If the size has a width or height of 0, then
161 : : * the frame size is not fixed in that direction and frames can have variable width
162 : : * or height accordingly.
163 : : * \see minFrameSize()
164 : : * \see recalculateFrameRects()
165 : : */
166 : : virtual QSizeF fixedFrameSize( int frameIndex = -1 ) const;
167 : :
168 : : /**
169 : : * Returns the minimum size for a frames, if desired. If the minimum
170 : : * size changes, the sizes of all frames can be recalculated by calling
171 : : * recalculateFrameRects().
172 : : * \param frameIndex frame number
173 : : * \returns minimum size for frame. If the size has a width or height of 0, then
174 : : * the frame size has no minimum in that direction.
175 : : * \see fixedFrameSize()
176 : : * \see recalculateFrameRects()
177 : : */
178 : : virtual QSizeF minFrameSize( int frameIndex = -1 ) const;
179 : :
180 : : /**
181 : : * Renders a portion of the multiframe's content into a render \a context.
182 : : * \param context destination render painter
183 : : * \param renderExtent visible extent of content to render into the painter.
184 : : * \param frameIndex frame number for content
185 : : */
186 : : virtual void render( QgsLayoutItemRenderContext &context, const QRectF &renderExtent, int frameIndex ) = 0;
187 : :
188 : : /**
189 : : * Adds a \a frame to the multiframe.
190 : : *
191 : : * If \a recalcFrameSizes is set to TRUE, then a recalculation of all existing frame sizes will be forced.
192 : : *
193 : : * \see removeFrame()
194 : : */
195 : : virtual void addFrame( QgsLayoutFrame *frame SIP_TRANSFER, bool recalcFrameSizes = true );
196 : :
197 : : /**
198 : : * Finds the optimal position to break a frame at.
199 : : * \param yPos maximum vertical position for break, in layout units.
200 : : * \returns the optimal breakable position which occurs in the multi frame close
201 : : * to and before the specified yPos
202 : : */
203 : : virtual double findNearbyPageBreak( double yPos );
204 : :
205 : : /**
206 : : * Removes a frame by \a index from the multiframe. This method automatically removes the frame from the
207 : : * layout too.
208 : : *
209 : : * If \a removeEmptyPages is set to TRUE, then pages which are empty after the frame is removed will
210 : : * also be removed from the layout.
211 : : *
212 : : * \see addFrame()
213 : : * \see deleteFrames()
214 : : */
215 : : void removeFrame( int index, bool removeEmptyPages = false );
216 : :
217 : : /**
218 : : * Removes and deletes all child frames.
219 : : * \see removeFrame()
220 : : */
221 : : void deleteFrames();
222 : :
223 : : /**
224 : : * Sets the resize \a mode for the multiframe, and recalculates frame sizes to match.
225 : : * \see resizeMode()
226 : : */
227 : : void setResizeMode( ResizeMode mode );
228 : :
229 : : /**
230 : : * Returns the resize mode for the multiframe.
231 : : * \see setResizeMode()
232 : : */
233 : 0 : ResizeMode resizeMode() const { return mResizeMode; }
234 : :
235 : : /**
236 : : * Stores the multiframe state in a DOM element.
237 : : * \param parentElement parent DOM element (e.g. 'Layout' element)
238 : : * \param document DOM document
239 : : * \param context read write context
240 : : * \param includeFrames set to TRUE to write state information about child frames into DOM
241 : : * \see readXml()
242 : : */
243 : : bool writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context, bool includeFrames = false ) const;
244 : :
245 : : /**
246 : : * Sets the item state from a DOM element.
247 : : * \param itemElement is the DOM node corresponding to item (e.g. 'LayoutItem' element)
248 : : * \param document DOM document
249 : : * \param context read write context
250 : : * \param includeFrames set to TRUE to read state information about child frames from DOM
251 : : * \see writeXml()
252 : : */
253 : : bool readXml( const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context, bool includeFrames = false );
254 : :
255 : : /**
256 : : * Returns a list of all child frames for this multiframe.
257 : : * \see frameCount()
258 : : */
259 : : QList<QgsLayoutFrame *> frames() const;
260 : :
261 : : /**
262 : : * Returns the number of frames associated with this multiframe.
263 : : * \see frames()
264 : : */
265 : 0 : int frameCount() const { return mFrameItems.size(); }
266 : :
267 : : /**
268 : : * Returns the child frame at a specified \a index from the multiframe.
269 : : * \see frameIndex()
270 : : */
271 : : QgsLayoutFrame *frame( int index ) const;
272 : :
273 : : /**
274 : : * Returns the index of a \a frame within the multiframe.
275 : : * \returns index for frame if found, -1 if frame not found in multiframe
276 : : * \see frame()
277 : : */
278 : : int frameIndex( QgsLayoutFrame *frame ) const;
279 : :
280 : : /**
281 : : * Creates a new frame and adds it to the multi frame and layout.
282 : : * \param currentFrame an existing QgsLayoutFrame from which to copy the size
283 : : * and general frame properties (e.g., frame style, background, rendering settings).
284 : : * \param pos position of top-left corner of the new frame, in layout units
285 : : * \param size size of the new frame, in layout units
286 : : */
287 : : QgsLayoutFrame *createNewFrame( QgsLayoutFrame *currentFrame, QPointF pos, QSizeF size );
288 : :
289 : : /**
290 : : * Returns the multiframe display name.
291 : : */
292 : : virtual QString displayName() const;
293 : :
294 : : QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = nullptr ) override SIP_FACTORY;
295 : :
296 : : QgsExpressionContext createExpressionContext() const override;
297 : :
298 : : /**
299 : : * Starts new undo command for this item.
300 : : * The \a commandText should be a capitalized, imperative tense description (e.g. "Add Map Item").
301 : : * If specified, multiple consecutive commands for this item with the same \a command will
302 : : * be collapsed into a single undo command in the layout history.
303 : : * \see endCommand()
304 : : * \see cancelCommand()
305 : : */
306 : : void beginCommand( const QString &commandText, UndoCommand command = UndoNone );
307 : :
308 : : /**
309 : : * Completes the current item command and push it onto the layout's undo stack.
310 : : * \see beginCommand()
311 : : * \see cancelCommand()
312 : : */
313 : : void endCommand();
314 : :
315 : : /**
316 : : * Cancels the current item command and discards it.
317 : : * \see beginCommand()
318 : : * \see endCommand()
319 : : */
320 : : void cancelCommand();
321 : :
322 : : /**
323 : : * Called after all pending items have been restored from XML. Multiframes can use
324 : : * this method to run steps which must take place after all items have been restored to the layout,
325 : : * such as connecting to signals emitted by other items, which may not have existed in the layout
326 : : * at the time readXml() was called. E.g. a scalebar can use this to connect to its linked
327 : : * map item after restoration from XML.
328 : : * \see readXml()
329 : : */
330 : : virtual void finalizeRestoreFromXml();
331 : :
332 : : public slots:
333 : :
334 : : /**
335 : : * Refreshes the multiframe, causing a recalculation of any property overrides.
336 : : */
337 : : void refresh() override;
338 : :
339 : : /**
340 : : * Forces a redraw of all child frames.
341 : : */
342 : : void update();
343 : :
344 : : /**
345 : : * Recalculates the portion of the multiframe item which is shown in each of its
346 : : * component frames. If the resize mode is set to anything but UseExistingFrames then
347 : : * this may cause new frames to be added or frames to be removed, in order to fit
348 : : * the current size of the multiframe's content.
349 : : * \see recalculateFrameRects()
350 : : */
351 : : virtual void recalculateFrameSizes();
352 : :
353 : : /**
354 : : * Forces a recalculation of all the associated frame's scene rectangles. This
355 : : * method is useful for multiframes which implement a minFrameSize() or
356 : : * fixedFrameSize() method.
357 : : * \see minFrameSize()
358 : : * \see fixedFrameSize()
359 : : * \see recalculateFrameSizes
360 : : */
361 : : void recalculateFrameRects();
362 : :
363 : : /**
364 : : * Refreshes a data defined \a property for the multi frame by reevaluating the property's value
365 : : * and redrawing the item with this new value. If \a property is set to
366 : : * QgsLayoutObject::AllProperties then all data defined properties for the item will be
367 : : * refreshed.
368 : : */
369 : : virtual void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
370 : :
371 : : signals:
372 : :
373 : : /**
374 : : * Emitted when the contents of the multi frame have changed and the frames
375 : : * must be redrawn.
376 : : */
377 : : void contentsChanged();
378 : :
379 : : protected:
380 : :
381 : : /**
382 : : * Stores multiframe state within an XML DOM element.
383 : : * \param element is the DOM element to store the multiframe's properties in
384 : : * \param document DOM document
385 : : * \param context read write context
386 : : * \see writeXml()
387 : : * \see readPropertiesFromElement()
388 : : */
389 : : virtual bool writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const;
390 : :
391 : : /**
392 : : * Sets multiframe state from a DOM element.
393 : : * \param element is the DOM element for the multiframe
394 : : * \param document DOM document
395 : : * \param context read write context
396 : : * \see writePropertiesToElement()
397 : : *
398 : : * Note that item subclasses should not rely on all other items being present in the
399 : : * layout at the time this method is called. Instead, any connections and links to
400 : : * other items must be made in the finalizeRestoreFromXml() method. E.g. when restoring
401 : : * a scalebar, the connection to the linked map's signals should be implemented
402 : : * in finalizeRestoreFromXml(), not readPropertiesFromElement().
403 : : *
404 : : * \see readXml()
405 : : */
406 : : virtual bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
407 : :
408 : : QList<QgsLayoutFrame *> mFrameItems;
409 : :
410 : : ResizeMode mResizeMode = UseExistingFrames;
411 : :
412 : : private slots:
413 : :
414 : : /**
415 : : * Adapts to changed number of layout pages if resize type is RepeatOnEveryPage.
416 : : */
417 : : void handlePageChange();
418 : :
419 : : /**
420 : : * Called when a frame is removed. Updates frame list and recalculates
421 : : * content of remaining frames.
422 : : */
423 : : void handleFrameRemoval( QgsLayoutFrame *frame );
424 : :
425 : :
426 : : private:
427 : : QgsLayoutMultiFrame() = delete;
428 : :
429 : : bool mIsRecalculatingSize = false;
430 : :
431 : : bool mBlockUpdates = false;
432 : : bool mBlockUndoCommands = false;
433 : :
434 : : QList< QString > mFrameUuids;
435 : : QList< QString > mFrameTemplateUuids;
436 : :
437 : : //! Unique id
438 : : QString mUuid;
439 : : QString mTemplateUuid;
440 : : friend class QgsLayoutFrame;
441 : : friend class QgsLayout;
442 : : };
443 : :
444 : :
445 : : #endif // QGSLAYOUTMULTIFRAME_H
|