QGIS API Documentation  2.99.0-Master (6a61179)
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 );
28  connect( mComposition, SIGNAL( nPagesChanged() ), this, SLOT( handlePageChange() ) );
29 }
30 
32  : QgsComposerObject( nullptr )
34  , mCreateUndoCommands( false )
35  , mIsRecalculatingSize( false )
36 {
37 }
38 
40 {
41  deleteFrames();
42 }
43 
45 {
46  if ( mode != mResizeMode )
47  {
48  mResizeMode = mode;
50  emit changed();
51  }
52 }
53 
55 {
56  if ( mFrameItems.size() < 1 )
57  {
58  return;
59  }
60 
61  QSizeF size = totalSize();
62  double totalHeight = size.height();
63 
64  if ( totalHeight < 1 )
65  {
66  return;
67  }
68 
69  double currentY = 0;
70  double currentHeight = 0;
71  QgsComposerFrame* currentItem = nullptr;
72 
73  for ( int i = 0; i < mFrameItems.size(); ++i )
74  {
75  if ( mResizeMode != RepeatOnEveryPage && currentY >= totalHeight )
76  {
77  if ( mResizeMode == RepeatUntilFinished || mResizeMode == ExtendToNextPage ) //remove unneeded frames in extent mode
78  {
79  bool removingPages = true;
80  for ( int j = mFrameItems.size(); j > i; --j )
81  {
82  int numPagesBefore = mComposition->numPages();
83  removeFrame( j - 1, removingPages );
84  //if removing the frame didn't also remove the page, then stop removing pages
85  removingPages = removingPages && ( mComposition->numPages() < numPagesBefore );
86  }
87  return;
88  }
89  }
90 
91  currentItem = mFrameItems.value( i );
92  currentHeight = currentItem->rect().height();
94  {
95  currentItem->setContentSection( QRectF( 0, 0, currentItem->rect().width(), currentHeight ) );
96  }
97  else
98  {
99  currentHeight = findNearbyPageBreak( currentY + currentHeight ) - currentY;
100  currentItem->setContentSection( QRectF( 0, currentY, currentItem->rect().width(), currentHeight ) );
101  }
102  currentItem->update();
103  currentY += currentHeight;
104  }
105 
106  //at end of frames but there is still content left. Add other pages if ResizeMode ==
108  {
109  while (( mResizeMode == RepeatOnEveryPage ) || currentY < totalHeight )
110  {
111  //find out on which page the lower left point of the last frame is
112  int page = qFloor(( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1;
113 
115  {
116  if ( page >= mComposition->numPages() )
117  {
118  break;
119  }
120  }
121  else
122  {
123  //add an extra page if required
124  if ( mComposition->numPages() < ( page + 1 ) )
125  {
126  mComposition->setNumPages( page + 1 );
127  }
128  }
129 
130  double frameHeight = 0;
132  {
133  frameHeight = currentItem->rect().height();
134  }
135  else //mResizeMode == ExtendToNextPage
136  {
137  frameHeight = ( currentY + mComposition->paperHeight() ) > totalHeight ? totalHeight - currentY : mComposition->paperHeight();
138  }
139 
140  double newFrameY = page * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
142  {
143  newFrameY += currentItem->pos().y() - ( page - 1 ) * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
144  }
145 
146  //create new frame
147  QgsComposerFrame* newFrame = createNewFrame( currentItem,
148  QPointF( currentItem->pos().x(), newFrameY ),
149  QSizeF( currentItem->rect().width(), frameHeight ) );
150 
152  {
153  newFrame->setContentSection( QRectF( 0, 0, newFrame->rect().width(), newFrame->rect().height() ) );
154  currentY += frameHeight;
155  }
156  else
157  {
158  double contentHeight = findNearbyPageBreak( currentY + newFrame->rect().height() ) - currentY;
159  newFrame->setContentSection( QRectF( 0, currentY, newFrame->rect().width(), contentHeight ) );
160  currentY += contentHeight;
161  }
162 
163  currentItem = newFrame;
164  }
165  }
166 }
167 
169 {
170  if ( mFrameItems.size() < 1 )
171  {
172  //no frames, nothing to do
173  return;
174  }
175 
176  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
177  {
178  frame->setSceneRect( QRectF( frame->scenePos().x(), frame->scenePos().y(),
179  frame->rect().width(), frame->rect().height() ) );
180  }
181 }
182 
184 {
185  if ( !currentFrame )
186  {
187  return nullptr;
188  }
189 
190  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, pos.x(),
191  pos.y(), size.width(), size.height() );
192 
193  //copy some settings from the parent frame
194  newFrame->setBackgroundColor( currentFrame->backgroundColor() );
195  newFrame->setBackgroundEnabled( currentFrame->hasBackground() );
196  newFrame->setBlendMode( currentFrame->blendMode() );
197  newFrame->setFrameEnabled( currentFrame->hasFrame() );
198  newFrame->setFrameOutlineColor( currentFrame->frameOutlineColor() );
199  newFrame->setFrameJoinStyle( currentFrame->frameJoinStyle() );
200  newFrame->setFrameOutlineWidth( currentFrame->frameOutlineWidth() );
201  newFrame->setTransparency( currentFrame->transparency() );
202  newFrame->setHideBackgroundIfEmpty( currentFrame->hideBackgroundIfEmpty() );
203 
204  addFrame( newFrame, false );
205 
206  return newFrame;
207 }
208 
210 {
211  return tr( "<frame>" );
212 }
213 
215 {
216  QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
217  if ( !frame )
218  {
219  return;
220  }
221  int index = mFrameItems.indexOf( frame );
222  if ( index == -1 )
223  {
224  return;
225  }
226 
227  mFrameItems.removeAt( index );
228  if ( !mFrameItems.isEmpty() )
229  {
230  if ( resizeMode() != QgsComposerMultiFrame::RepeatOnEveryPage && !mIsRecalculatingSize )
231  {
232  //removing a frame forces the multi frame to UseExistingFrames resize mode
233  //otherwise the frame may not actually be removed, leading to confusing ui behaviour
235  emit changed();
237  }
238  }
239 }
240 
242 {
243  if ( mComposition->numPages() < 1 )
244  {
245  return;
246  }
247 
249  {
250  return;
251  }
252 
253  //remove items beginning on non-existing pages
254  for ( int i = mFrameItems.size() - 1; i >= 0; --i )
255  {
257  int page = frame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
258  if ( page > ( mComposition->numPages() - 1 ) )
259  {
260  removeFrame( i );
261  }
262  }
263 
264  //page number of the last item
265  QgsComposerFrame* lastFrame = mFrameItems.last();
266  int lastItemPage = lastFrame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
267 
268  for ( int i = lastItemPage + 1; i < mComposition->numPages(); ++i )
269  {
270  //copy last frame to current page
271  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, lastFrame->pos().x(),
272  lastFrame->pos().y() + mComposition->paperHeight() + mComposition->spaceBetweenPages(),
273  lastFrame->rect().width(), lastFrame->rect().height() );
274  addFrame( newFrame, false );
275  lastFrame = newFrame;
276  }
277 
279  update();
280 }
281 
282 void QgsComposerMultiFrame::removeFrame( int i, const bool removeEmptyPages )
283 {
284  if ( i >= mFrameItems.count() )
285  {
286  return;
287  }
288 
289  QgsComposerFrame* frameItem = mFrameItems.at( i );
290  if ( mComposition )
291  {
292  mIsRecalculatingSize = true;
293  int pageNumber = frameItem->page();
294  //remove item, but don't create undo command
295  mComposition->removeComposerItem( frameItem, false );
296  //if frame was the only item on the page, remove the page
297  if ( removeEmptyPages && mComposition->pageIsEmpty( pageNumber ) )
298  {
300  }
301  mIsRecalculatingSize = false;
302  }
303  mFrameItems.removeAt( i );
304 }
305 
307 {
308  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
309  {
310  frame->update();
311  }
312 }
313 
315 {
316  ResizeMode bkResizeMode = mResizeMode;
318  QObject::disconnect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
319  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
320  {
321  mComposition->removeComposerItem( frame, false );
322  delete frame;
323  }
324  QObject::connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
325  mFrameItems.clear();
326  mResizeMode = bkResizeMode;
327 }
328 
330 {
331  if ( i >= mFrameItems.size() )
332  {
333  return nullptr;
334  }
335  return mFrameItems.at( i );
336 }
337 
339 {
340  return mFrameItems.indexOf( frame );
341 }
342 
343 bool QgsComposerMultiFrame::_writeXml( QDomElement& elem, QDomDocument& doc, bool ignoreFrames ) const
344 {
345  elem.setAttribute( QStringLiteral( "resizeMode" ), mResizeMode );
346  if ( !ignoreFrames )
347  {
348  QList<QgsComposerFrame*>::const_iterator frameIt = mFrameItems.constBegin();
349  for ( ; frameIt != mFrameItems.constEnd(); ++frameIt )
350  {
351  ( *frameIt )->writeXml( elem, doc );
352  }
353  }
354  QgsComposerObject::writeXml( elem, doc );
355  return true;
356 }
357 
358 bool QgsComposerMultiFrame::_readXml( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames )
359 {
360  QgsComposerObject::readXml( itemElem, doc );
361 
362  mResizeMode = static_cast< ResizeMode >( itemElem.attribute( QStringLiteral( "resizeMode" ), QStringLiteral( "0" ) ).toInt() );
363  if ( !ignoreFrames )
364  {
365  QDomNodeList frameList = itemElem.elementsByTagName( QStringLiteral( "ComposerFrame" ) );
366  for ( int i = 0; i < frameList.size(); ++i )
367  {
368  QDomElement frameElem = frameList.at( i ).toElement();
369  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, 0, 0, 0, 0 );
370  newFrame->readXml( frameElem, doc );
371  addFrame( newFrame, false );
372  }
373 
374  //TODO - think there should be a recalculateFrameSizes() call here
375  }
376  return true;
377 }
ResizeMode
Specifies the behaviour 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...
static unsigned index
A base class for objects which belong to a map composition.
void setHideBackgroundIfEmpty(const bool hideBackgroundIfEmpty)
Sets whether the background and frame border should be hidden if this frame is empty.
virtual double findNearbyPageBreak(double yPos)
Finds the optimal position to break a frame at.
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.
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.
void setBlendMode(const QPainter::CompositionMode blendMode)
Sets the item&#39;s composition blending mode.
QgsComposerObject(QgsComposition *composition)
Constructor.
double frameOutlineWidth() const
Returns the frame&#39;s outline width.
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.
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.
virtual void setFrameOutlineColor(const QColor &color)
Sets frame outline color.
Repeats the same frame on every page.
QList< QgsComposerFrame * > mFrameItems
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.
virtual void setFrameOutlineWidth(const double outlineWidth)
Sets frame outline width.
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.
Don&#39;t automatically create new frames, just use existing frames.
void setBackgroundColor(const QColor &backgroundColor)
Sets the background color for this item.
QColor frameOutlineColor() const
Returns the frame&#39;s outline color.
bool hideBackgroundIfEmpty() const
Returns whether the background and frame border should be hidden if this frame is empty...
void setResizeMode(ResizeMode mode)
Sets the resize mode for the multiframe, and recalculates frame sizes to match.
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.
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.
void setTransparency(const int transparency)
Sets the item&#39;s transparency.
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...
int transparency() const
Returns the item&#39;s transparency.