QGIS API Documentation  2.99.0-Master (19b062c)
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  , mCreateUndoCommands( createUndoCommands )
24 {
25  mComposition->addMultiFrame( this );
27 }
28 
30  : QgsComposerObject( nullptr )
31 {
32 }
33 
35 {
36  deleteFrames();
37 }
38 
40 {
41  Q_UNUSED( frameIndex ); return QSizeF( 0, 0 );
42 }
43 
45 {
46  Q_UNUSED( frameIndex ); return QSizeF( 0, 0 );
47 }
48 
50 {
51  return yPos;
52 }
53 
55 {
56  if ( mode != mResizeMode )
57  {
58  mResizeMode = mode;
60  emit changed();
61  }
62 }
63 
65 {
66  if ( mFrameItems.empty() )
67  {
68  return;
69  }
70 
71  QSizeF size = totalSize();
72  double totalHeight = size.height();
73 
74  if ( totalHeight < 1 )
75  {
76  return;
77  }
78 
79  double currentY = 0;
80  double currentHeight = 0;
81  QgsComposerFrame *currentItem = nullptr;
82 
83  for ( int i = 0; i < mFrameItems.size(); ++i )
84  {
85  if ( mResizeMode != RepeatOnEveryPage && currentY >= totalHeight )
86  {
87  if ( mResizeMode == RepeatUntilFinished || mResizeMode == ExtendToNextPage ) //remove unneeded frames in extent mode
88  {
89  bool removingPages = true;
90  for ( int j = mFrameItems.size(); j > i; --j )
91  {
92  int numPagesBefore = mComposition->numPages();
93  removeFrame( j - 1, removingPages );
94  //if removing the frame didn't also remove the page, then stop removing pages
95  removingPages = removingPages && ( mComposition->numPages() < numPagesBefore );
96  }
97  return;
98  }
99  }
100 
101  currentItem = mFrameItems.value( i );
102  currentHeight = currentItem->rect().height();
104  {
105  currentItem->setContentSection( QRectF( 0, 0, currentItem->rect().width(), currentHeight ) );
106  }
107  else
108  {
109  currentHeight = findNearbyPageBreak( currentY + currentHeight ) - currentY;
110  currentItem->setContentSection( QRectF( 0, currentY, currentItem->rect().width(), currentHeight ) );
111  }
112  currentItem->update();
113  currentY += currentHeight;
114  }
115 
116  //at end of frames but there is still content left. Add other pages if ResizeMode ==
118  {
119  while ( ( mResizeMode == RepeatOnEveryPage ) || currentY < totalHeight )
120  {
121  //find out on which page the lower left point of the last frame is
122  int page = std::floor( ( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1;
123 
125  {
126  if ( page >= mComposition->numPages() )
127  {
128  break;
129  }
130  }
131  else
132  {
133  //add an extra page if required
134  if ( mComposition->numPages() < ( page + 1 ) )
135  {
136  mComposition->setNumPages( page + 1 );
137  }
138  }
139 
140  double frameHeight = 0;
142  {
143  frameHeight = currentItem->rect().height();
144  }
145  else //mResizeMode == ExtendToNextPage
146  {
147  frameHeight = ( currentY + mComposition->paperHeight() ) > totalHeight ? totalHeight - currentY : mComposition->paperHeight();
148  }
149 
150  double newFrameY = page * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
152  {
153  newFrameY += currentItem->pos().y() - ( page - 1 ) * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
154  }
155 
156  //create new frame
157  QgsComposerFrame *newFrame = createNewFrame( currentItem,
158  QPointF( currentItem->pos().x(), newFrameY ),
159  QSizeF( currentItem->rect().width(), frameHeight ) );
160 
162  {
163  newFrame->setContentSection( QRectF( 0, 0, newFrame->rect().width(), newFrame->rect().height() ) );
164  currentY += frameHeight;
165  }
166  else
167  {
168  double contentHeight = findNearbyPageBreak( currentY + newFrame->rect().height() ) - currentY;
169  newFrame->setContentSection( QRectF( 0, currentY, newFrame->rect().width(), contentHeight ) );
170  currentY += contentHeight;
171  }
172 
173  currentItem = newFrame;
174  }
175  }
176 }
177 
179 {
180  if ( mFrameItems.empty() )
181  {
182  //no frames, nothing to do
183  return;
184  }
185 
186  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
187  {
188  frame->setSceneRect( QRectF( frame->scenePos().x(), frame->scenePos().y(),
189  frame->rect().width(), frame->rect().height() ) );
190  }
191 }
192 
194 {
195  if ( !currentFrame )
196  {
197  return nullptr;
198  }
199 
200  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, pos.x(),
201  pos.y(), size.width(), size.height() );
202 
203  //copy some settings from the parent frame
204  newFrame->setBackgroundColor( currentFrame->backgroundColor() );
205  newFrame->setBackgroundEnabled( currentFrame->hasBackground() );
206  newFrame->setBlendMode( currentFrame->blendMode() );
207  newFrame->setFrameEnabled( currentFrame->hasFrame() );
208  newFrame->setFrameStrokeColor( currentFrame->frameStrokeColor() );
209  newFrame->setFrameJoinStyle( currentFrame->frameJoinStyle() );
210  newFrame->setFrameStrokeWidth( currentFrame->frameStrokeWidth() );
211  newFrame->setItemOpacity( currentFrame->itemOpacity() );
212  newFrame->setHideBackgroundIfEmpty( currentFrame->hideBackgroundIfEmpty() );
213 
214  addFrame( newFrame, false );
215 
216  return newFrame;
217 }
218 
220 {
221  return tr( "<frame>" );
222 }
223 
225 {
226  QgsComposerFrame *frame = dynamic_cast<QgsComposerFrame *>( item );
227  if ( !frame )
228  {
229  return;
230  }
231  int index = mFrameItems.indexOf( frame );
232  if ( index == -1 )
233  {
234  return;
235  }
236 
237  mFrameItems.removeAt( index );
238  if ( !mFrameItems.isEmpty() )
239  {
240  if ( resizeMode() != QgsComposerMultiFrame::RepeatOnEveryPage && !mIsRecalculatingSize )
241  {
242  //removing a frame forces the multi frame to UseExistingFrames resize mode
243  //otherwise the frame may not actually be removed, leading to confusing ui behavior
245  emit changed();
247  }
248  }
249 }
250 
252 {
253  if ( mComposition->numPages() < 1 )
254  {
255  return;
256  }
257 
259  {
260  return;
261  }
262 
263  //remove items beginning on non-existing pages
264  for ( int i = mFrameItems.size() - 1; i >= 0; --i )
265  {
267  int page = frame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
268  if ( page > ( mComposition->numPages() - 1 ) )
269  {
270  removeFrame( i );
271  }
272  }
273 
274  //page number of the last item
275  QgsComposerFrame *lastFrame = mFrameItems.last();
276  int lastItemPage = lastFrame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
277 
278  for ( int i = lastItemPage + 1; i < mComposition->numPages(); ++i )
279  {
280  //copy last frame to current page
281  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, lastFrame->pos().x(),
282  lastFrame->pos().y() + mComposition->paperHeight() + mComposition->spaceBetweenPages(),
283  lastFrame->rect().width(), lastFrame->rect().height() );
284  addFrame( newFrame, false );
285  lastFrame = newFrame;
286  }
287 
289  update();
290 }
291 
292 void QgsComposerMultiFrame::removeFrame( int i, const bool removeEmptyPages )
293 {
294  if ( i >= mFrameItems.count() )
295  {
296  return;
297  }
298 
299  QgsComposerFrame *frameItem = mFrameItems.at( i );
300  if ( mComposition )
301  {
302  mIsRecalculatingSize = true;
303  int pageNumber = frameItem->page();
304  //remove item, but don't create undo command
305  mComposition->removeComposerItem( frameItem, false );
306  //if frame was the only item on the page, remove the page
307  if ( removeEmptyPages && mComposition->pageIsEmpty( pageNumber ) )
308  {
310  }
311  mIsRecalculatingSize = false;
312  }
313  mFrameItems.removeAt( i );
314 }
315 
317 {
318  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
319  {
320  frame->update();
321  }
322 }
323 
325 {
326  ResizeMode bkResizeMode = mResizeMode;
329  Q_FOREACH ( QgsComposerFrame *frame, mFrameItems )
330  {
331  mComposition->removeComposerItem( frame, false );
332  delete frame;
333  }
335  mFrameItems.clear();
336  mResizeMode = bkResizeMode;
337 }
338 
340 {
341  if ( i >= mFrameItems.size() )
342  {
343  return nullptr;
344  }
345  return mFrameItems.at( i );
346 }
347 
349 {
350  return mFrameItems.indexOf( frame );
351 }
352 
353 bool QgsComposerMultiFrame::_writeXml( QDomElement &elem, QDomDocument &doc, bool ignoreFrames ) const
354 {
355  elem.setAttribute( QStringLiteral( "resizeMode" ), mResizeMode );
356  if ( !ignoreFrames )
357  {
358  QList<QgsComposerFrame *>::const_iterator frameIt = mFrameItems.constBegin();
359  for ( ; frameIt != mFrameItems.constEnd(); ++frameIt )
360  {
361  ( *frameIt )->writeXml( elem, doc );
362  }
363  }
364  QgsComposerObject::writeXml( elem, doc );
365  return true;
366 }
367 
368 bool QgsComposerMultiFrame::_readXml( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
369 {
370  QgsComposerObject::readXml( itemElem, doc );
371 
372  mResizeMode = static_cast< ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() );
373  if ( !ignoreFrames )
374  {
375  QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
376  for ( int i = 0; i < frameList.size(); ++i )
377  {
378  QDomElement frameElem = frameList.at( i ).toElement();
379  QgsComposerFrame *newFrame = new QgsComposerFrame( mComposition, this, 0, 0, 0, 0 );
380  newFrame->readXml( frameElem, doc );
381  addFrame( newFrame, false );
382  }
383 
384  //TODO - think there should be a recalculateFrameSizes() call here
385  }
386  return true;
387 }
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.
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.