QGIS API Documentation  2.99.0-Master (ef89a62)
qgscomposermultiframe.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposermultiframe.cpp
3  ------------------------------------------------------------
4  begin : July 2012
5  copyright : (C) 2012 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
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 #include "qgscomposermultiframe.h"
17 #include "qgscomposerframe.h"
18 #include "qgscomposition.h"
19 #include <QtCore>
20 
22  : QgsComposerObject( c )
23  , mResizeMode( UseExistingFrames )
24  , mCreateUndoCommands( createUndoCommands )
25  , mIsRecalculatingSize( false )
26 {
27  mComposition->addMultiFrame( this );
29 }
30 
32  : QgsComposerObject( nullptr )
34  , mCreateUndoCommands( false )
35  , mIsRecalculatingSize( false )
36 {
37 }
38 
40 {
41  deleteFrames();
42 }
43 
45 {
46  Q_UNUSED( frameIndex ); return QSizeF( 0, 0 );
47 }
48 
50 {
51  Q_UNUSED( frameIndex ); return QSizeF( 0, 0 );
52 }
53 
55 {
56  return yPos;
57 }
58 
60 {
61  if ( mode != mResizeMode )
62  {
63  mResizeMode = mode;
65  emit changed();
66  }
67 }
68 
70 {
71  if ( mFrameItems.size() < 1 )
72  {
73  return;
74  }
75 
76  QSizeF size = totalSize();
77  double totalHeight = size.height();
78 
79  if ( totalHeight < 1 )
80  {
81  return;
82  }
83 
84  double currentY = 0;
85  double currentHeight = 0;
86  QgsComposerFrame *currentItem = nullptr;
87 
88  for ( int i = 0; i < mFrameItems.size(); ++i )
89  {
90  if ( mResizeMode != RepeatOnEveryPage && currentY >= totalHeight )
91  {
92  if ( mResizeMode == RepeatUntilFinished || mResizeMode == ExtendToNextPage ) //remove unneeded frames in extent mode
93  {
94  bool removingPages = true;
95  for ( int j = mFrameItems.size(); j > i; --j )
96  {
97  int numPagesBefore = mComposition->numPages();
98  removeFrame( j - 1, removingPages );
99  //if removing the frame didn't also remove the page, then stop removing pages
100  removingPages = removingPages && ( mComposition->numPages() < numPagesBefore );
101  }
102  return;
103  }
104  }
105 
106  currentItem = mFrameItems.value( i );
107  currentHeight = currentItem->rect().height();
109  {
110  currentItem->setContentSection( QRectF( 0, 0, currentItem->rect().width(), currentHeight ) );
111  }
112  else
113  {
114  currentHeight = findNearbyPageBreak( currentY + currentHeight ) - currentY;
115  currentItem->setContentSection( QRectF( 0, currentY, currentItem->rect().width(), currentHeight ) );
116  }
117  currentItem->update();
118  currentY += currentHeight;
119  }
120 
121  //at end of frames but there is still content left. Add other pages if ResizeMode ==
123  {
124  while ( ( mResizeMode == RepeatOnEveryPage ) || currentY < totalHeight )
125  {
126  //find out on which page the lower left point of the last frame is
127  int page = qFloor( ( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1;
128 
130  {
131  if ( page >= mComposition->numPages() )
132  {
133  break;
134  }
135  }
136  else
137  {
138  //add an extra page if required
139  if ( mComposition->numPages() < ( page + 1 ) )
140  {
141  mComposition->setNumPages( page + 1 );
142  }
143  }
144 
145  double frameHeight = 0;
147  {
148  frameHeight = currentItem->rect().height();
149  }
150  else //mResizeMode == ExtendToNextPage
151  {
152  frameHeight = ( currentY + mComposition->paperHeight() ) > totalHeight ? totalHeight - currentY : mComposition->paperHeight();
153  }
154 
155  double newFrameY = page * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
157  {
158  newFrameY += currentItem->pos().y() - ( page - 1 ) * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
159  }
160 
161  //create new frame
162  QgsComposerFrame *newFrame = createNewFrame( currentItem,
163  QPointF( currentItem->pos().x(), newFrameY ),
164  QSizeF( currentItem->rect().width(), frameHeight ) );
165 
167  {
168  newFrame->setContentSection( QRectF( 0, 0, newFrame->rect().width(), newFrame->rect().height() ) );
169  currentY += frameHeight;
170  }
171  else
172  {
173  double contentHeight = findNearbyPageBreak( currentY + newFrame->rect().height() ) - currentY;
174  newFrame->setContentSection( QRectF( 0, currentY, newFrame->rect().width(), contentHeight ) );
175  currentY += contentHeight;
176  }
177 
178  currentItem = newFrame;
179  }
180  }
181 }
182 
184 {
185  if ( mFrameItems.size() < 1 )
186  {
187  //no frames, nothing to do
188  return;
189  }
190 
191  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
192  {
193  frame->setSceneRect( QRectF( frame->scenePos().x(), frame->scenePos().y(),
194  frame->rect().width(), frame->rect().height() ) );
195  }
196 }
197 
199 {
200  if ( !currentFrame )
201  {
202  return nullptr;
203  }
204 
205  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, pos.x(),
206  pos.y(), size.width(), size.height() );
207 
208  //copy some settings from the parent frame
209  newFrame->setBackgroundColor( currentFrame->backgroundColor() );
210  newFrame->setBackgroundEnabled( currentFrame->hasBackground() );
211  newFrame->setBlendMode( currentFrame->blendMode() );
212  newFrame->setFrameEnabled( currentFrame->hasFrame() );
213  newFrame->setFrameStrokeColor( currentFrame->frameStrokeColor() );
214  newFrame->setFrameJoinStyle( currentFrame->frameJoinStyle() );
215  newFrame->setFrameStrokeWidth( currentFrame->frameStrokeWidth() );
216  newFrame->setItemOpacity( currentFrame->itemOpacity() );
217  newFrame->setHideBackgroundIfEmpty( currentFrame->hideBackgroundIfEmpty() );
218 
219  addFrame( newFrame, false );
220 
221  return newFrame;
222 }
223 
225 {
226  return tr( "<frame>" );
227 }
228 
230 {
231  QgsComposerFrame *frame = dynamic_cast<QgsComposerFrame *>( item );
232  if ( !frame )
233  {
234  return;
235  }
236  int index = mFrameItems.indexOf( frame );
237  if ( index == -1 )
238  {
239  return;
240  }
241 
242  mFrameItems.removeAt( index );
243  if ( !mFrameItems.isEmpty() )
244  {
245  if ( resizeMode() != QgsComposerMultiFrame::RepeatOnEveryPage && !mIsRecalculatingSize )
246  {
247  //removing a frame forces the multi frame to UseExistingFrames resize mode
248  //otherwise the frame may not actually be removed, leading to confusing ui behavior
250  emit changed();
252  }
253  }
254 }
255 
257 {
258  if ( mComposition->numPages() < 1 )
259  {
260  return;
261  }
262 
264  {
265  return;
266  }
267 
268  //remove items beginning on non-existing pages
269  for ( int i = mFrameItems.size() - 1; i >= 0; --i )
270  {
272  int page = frame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
273  if ( page > ( mComposition->numPages() - 1 ) )
274  {
275  removeFrame( i );
276  }
277  }
278 
279  //page number of the last item
280  QgsComposerFrame *lastFrame = mFrameItems.last();
281  int lastItemPage = lastFrame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
282 
283  for ( int i = lastItemPage + 1; i < mComposition->numPages(); ++i )
284  {
285  //copy last frame to current page
286  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, lastFrame->pos().x(),
287  lastFrame->pos().y() + mComposition->paperHeight() + mComposition->spaceBetweenPages(),
288  lastFrame->rect().width(), lastFrame->rect().height() );
289  addFrame( newFrame, false );
290  lastFrame = newFrame;
291  }
292 
294  update();
295 }
296 
297 void QgsComposerMultiFrame::removeFrame( int i, const bool removeEmptyPages )
298 {
299  if ( i >= mFrameItems.count() )
300  {
301  return;
302  }
303 
304  QgsComposerFrame *frameItem = mFrameItems.at( i );
305  if ( mComposition )
306  {
307  mIsRecalculatingSize = true;
308  int pageNumber = frameItem->page();
309  //remove item, but don't create undo command
310  mComposition->removeComposerItem( frameItem, false );
311  //if frame was the only item on the page, remove the page
312  if ( removeEmptyPages && mComposition->pageIsEmpty( pageNumber ) )
313  {
315  }
316  mIsRecalculatingSize = false;
317  }
318  mFrameItems.removeAt( i );
319 }
320 
322 {
323  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
324  {
325  frame->update();
326  }
327 }
328 
330 {
331  ResizeMode bkResizeMode = mResizeMode;
334  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
335  {
336  mComposition->removeComposerItem( frame, false );
337  delete frame;
338  }
340  mFrameItems.clear();
341  mResizeMode = bkResizeMode;
342 }
343 
345 {
346  if ( i >= mFrameItems.size() )
347  {
348  return nullptr;
349  }
350  return mFrameItems.at( i );
351 }
352 
354 {
355  return mFrameItems.indexOf( frame );
356 }
357 
358 bool QgsComposerMultiFrame::_writeXml( QDomElement &elem, QDomDocument &doc, bool ignoreFrames ) const
359 {
360  elem.setAttribute( QStringLiteral( "resizeMode" ), mResizeMode );
361  if ( !ignoreFrames )
362  {
363  QList<QgsComposerFrame *>::const_iterator frameIt = mFrameItems.constBegin();
364  for ( ; frameIt != mFrameItems.constEnd(); ++frameIt )
365  {
366  ( *frameIt )->writeXml( elem, doc );
367  }
368  }
369  QgsComposerObject::writeXml( elem, doc );
370  return true;
371 }
372 
373 bool QgsComposerMultiFrame::_readXml( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
374 {
375  QgsComposerObject::readXml( itemElem, doc );
376 
377  mResizeMode = static_cast< ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() );
378  if ( !ignoreFrames )
379  {
380  QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
381  for ( int i = 0; i < frameList.size(); ++i )
382  {
383  QDomElement frameElem = frameList.at( i ).toElement();
384  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, 0, 0, 0, 0 );
385  newFrame->readXml( frameElem, doc );
386  addFrame( newFrame, false );
387  }
388 
389  //TODO - think there should be a recalculateFrameSizes() call here
390  }
391  return true;
392 }
ResizeMode
Specifies the behavior for creating new frames to fit the multiframe&#39;s content.
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of it&#39;s component frames...
A base class for objects which belong to a map composition.
virtual void setFrameStrokeWidth(const double strokeWidth)
Sets frame stroke width.
void setHideBackgroundIfEmpty(const bool hideBackgroundIfEmpty)
Sets whether the background and frame stroke should be hidden if this frame is empty.
virtual QSizeF minFrameSize(const int frameIndex=-1) const
Returns the minimum size for a frames, if desired.
bool pageIsEmpty(const int page) const
Returns whether a page is empty, ie, it contains no items except for the background paper item...
A item that forms part of a map composition.
QColor frameStrokeColor() const
Returns the frame&#39;s stroke color.
Creates new full page frames on the following page(s) until the entire multiframe content is visible...
int numPages() const
Returns the number of pages in the composition.
QColor backgroundColor() const
Gets the background color for this item.
virtual QString displayName() const
Get multiframe display name.
virtual void setFrameEnabled(const bool drawFrame)
Set whether this item has a frame drawn around it or not.
virtual QSizeF totalSize() const =0
Returns the total size of the multiframe&#39;s content.
double itemOpacity() const
Returns the item&#39;s opacity.
void setBlendMode(const QPainter::CompositionMode blendMode)
Sets the item&#39;s composition blending mode.
QgsComposerObject(QgsComposition *composition)
Constructor.
int page() const
Gets the page the item is currently on.
void setNumPages(const int pages)
Sets the number of pages for the composition.
void recalculateFrameRects()
Forces a recalculation of all the associated frame&#39;s scene rectangles.
QgsComposerMultiFrame(QgsComposition *c, bool createUndoCommands)
Construct a new multiframe item.
void setFrameJoinStyle(const Qt::PenJoinStyle style)
Sets join style used when drawing the item&#39;s frame.
virtual void addFrame(QgsComposerFrame *frame, bool recalcFrameSizes=true)=0
Adds a frame to the multiframe.
bool _readXml(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false)
Restores state information about base multiframe object from a DOM element.
void itemRemoved(QgsComposerItem *)
Is emitted when a composer item has been removed from the scene.
virtual bool readXml(const QDomElement &itemElem, const QDomDocument &doc)
Sets item state from DOM element.
QgsComposerFrame * createNewFrame(QgsComposerFrame *currentFrame, QPointF pos, QSizeF size)
Creates a new frame and adds it to the multi frame and composition.
Repeats the same frame on every page.
bool mCreateUndoCommands
True: creates QgsMultiFrameCommands on internal changes (e.g. changing frames )
void removeFrame(int i, const bool removeEmptyPages=false)
Removes a frame from the multiframe.
void removeComposerItem(QgsComposerItem *item, const bool createCommand=true, const bool removeGroupItems=true)
Remove item from the graphics scene. Additionally to QGraphicsScene::removeItem, this function consid...
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
bool hasBackground() const
Whether this item has a Background or not.
void setBackgroundEnabled(const bool drawBackground)
Set whether this item has a Background drawn around it or not.
Graphics scene for map printing.
void handlePageChange()
Adapts to changed number of composition pages if resize type is RepeatOnEveryPage.
Frame item for a composer multiframe item.
bool _writeXml(QDomElement &elem, QDomDocument &doc, bool ignoreFrames=false) const
Stores state information about base multiframe object in DOM element.
void nPagesChanged()
QgsComposition * mComposition
void deleteFrames()
Removes and deletes all child frames.
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets item state from DOM element.
void setItemOpacity(const double opacity)
Sets the item&#39;s opacity.
virtual void setFrameStrokeColor(const QColor &color)
Sets frame stroke color.
Don&#39;t automatically create new frames, just use existing frames.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color for this item.
bool hideBackgroundIfEmpty() const
Returns whether the background and frame stroke should be hidden if this frame is empty...
virtual QSizeF fixedFrameSize(const int frameIndex=-1) const
Returns the fixed size for a frame, if desired.
void setResizeMode(ResizeMode mode)
Sets the resize mode for the multiframe, and recalculates frame sizes to match.
QList< QgsComposerFrame * > mFrameItems
int frameIndex(QgsComposerFrame *frame) const
Returns the index of a frame within the multiframe.
double paperHeight() const
Height of paper item.
bool hasFrame() const
Whether this item has a frame or not.
void update()
Forces a redraw of all child frames.
void setContentSection(const QRectF &section)
Sets the visible part of the multiframe&#39;s content which is visible within this frame (relative to the...
virtual bool writeXml(QDomElement &elem, QDomDocument &doc) const
Stores item state in DOM element.
virtual double findNearbyPageBreak(double yPos)
Finds the optimal position to break a frame at.
QgsComposerFrame * frame(int i) const
Returns a child frame from the multiframe.
QPainter::CompositionMode blendMode() const
Returns the item&#39;s composition blending mode.
void addMultiFrame(QgsComposerMultiFrame *multiFrame)
Adds multiframe. The object is owned by QgsComposition until removeMultiFrame is called.
ResizeMode resizeMode() const
Returns the resize mode for the multiframe.
void handleFrameRemoval(QgsComposerItem *item)
Called before a frame is going to be removed.
double spaceBetweenPages() const
Returns the vertical space between pages in a composer view.
Qt::PenJoinStyle frameJoinStyle() const
Returns the join style used for drawing the item&#39;s frame.
void changed()
Emitted when the properties of a multi frame have changed, and the GUI item widget must be updated...
double frameStrokeWidth() const
Returns the frame&#39;s stroke width.