QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 
44 void QgsComposerMultiFrame::render( QPainter *p, const QRectF &renderExtent )
45 {
46  //base implementation does nothing
47  Q_UNUSED( p );
48  Q_UNUSED( renderExtent );
49 }
50 
51 void QgsComposerMultiFrame::render( QPainter *painter, const QRectF &renderExtent, const int frameIndex )
52 {
53  Q_UNUSED( frameIndex );
54  //base implementation ignores frameIndex
56  render( painter, renderExtent );
58 }
59 
61 {
62  if ( mode != mResizeMode )
63  {
64  mResizeMode = mode;
66  emit changed();
67  }
68 }
69 
71 {
72  if ( mFrameItems.size() < 1 )
73  {
74  return;
75  }
76 
77  QSizeF size = totalSize();
78  double totalHeight = size.height();
79 
80  if ( totalHeight < 1 )
81  {
82  return;
83  }
84 
85  double currentY = 0;
86  double currentHeight = 0;
87  QgsComposerFrame* currentItem = nullptr;
88 
89  for ( int i = 0; i < mFrameItems.size(); ++i )
90  {
91  if ( mResizeMode != RepeatOnEveryPage && currentY >= totalHeight )
92  {
93  if ( mResizeMode == RepeatUntilFinished || mResizeMode == ExtendToNextPage ) //remove unneeded frames in extent mode
94  {
95  bool removingPages = true;
96  for ( int j = mFrameItems.size(); j > i; --j )
97  {
98  int numPagesBefore = mComposition->numPages();
99  removeFrame( j - 1, removingPages );
100  //if removing the frame didn't also remove the page, then stop removing pages
101  removingPages = removingPages && ( mComposition->numPages() < numPagesBefore );
102  }
103  return;
104  }
105  }
106 
107  currentItem = mFrameItems.value( i );
108  currentHeight = currentItem->rect().height();
110  {
111  currentItem->setContentSection( QRectF( 0, 0, currentItem->rect().width(), currentHeight ) );
112  }
113  else
114  {
115  currentHeight = findNearbyPageBreak( currentY + currentHeight ) - currentY;
116  currentItem->setContentSection( QRectF( 0, currentY, currentItem->rect().width(), currentHeight ) );
117  }
118  currentItem->update();
119  currentY += currentHeight;
120  }
121 
122  //at end of frames but there is still content left. Add other pages if ResizeMode ==
124  {
125  while (( mResizeMode == RepeatOnEveryPage ) || currentY < totalHeight )
126  {
127  //find out on which page the lower left point of the last frame is
128  int page = qFloor(( currentItem->pos().y() + currentItem->rect().height() ) / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() ) ) + 1;
129 
131  {
132  if ( page >= mComposition->numPages() )
133  {
134  break;
135  }
136  }
137  else
138  {
139  //add an extra page if required
140  if ( mComposition->numPages() < ( page + 1 ) )
141  {
142  mComposition->setNumPages( page + 1 );
143  }
144  }
145 
146  double frameHeight = 0;
148  {
149  frameHeight = currentItem->rect().height();
150  }
151  else //mResizeMode == ExtendToNextPage
152  {
153  frameHeight = ( currentY + mComposition->paperHeight() ) > totalHeight ? totalHeight - currentY : mComposition->paperHeight();
154  }
155 
156  double newFrameY = page * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
158  {
159  newFrameY += currentItem->pos().y() - ( page - 1 ) * ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
160  }
161 
162  //create new frame
163  QgsComposerFrame* newFrame = createNewFrame( currentItem,
164  QPointF( currentItem->pos().x(), newFrameY ),
165  QSizeF( currentItem->rect().width(), frameHeight ) );
166 
168  {
169  newFrame->setContentSection( QRectF( 0, 0, newFrame->rect().width(), newFrame->rect().height() ) );
170  currentY += frameHeight;
171  }
172  else
173  {
174  double contentHeight = findNearbyPageBreak( currentY + newFrame->rect().height() ) - currentY;
175  newFrame->setContentSection( QRectF( 0, currentY, newFrame->rect().width(), contentHeight ) );
176  currentY += contentHeight;
177  }
178 
179  currentItem = newFrame;
180  }
181  }
182 }
183 
185 {
186  if ( mFrameItems.size() < 1 )
187  {
188  //no frames, nothing to do
189  return;
190  }
191 
192  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
193  {
194  frame->setSceneRect( QRectF( frame->scenePos().x(), frame->scenePos().y(),
195  frame->rect().width(), frame->rect().height() ) );
196  }
197 }
198 
200 {
201  if ( !currentFrame )
202  {
203  return nullptr;
204  }
205 
206  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, pos.x(),
207  pos.y(), size.width(), size.height() );
208 
209  //copy some settings from the parent frame
210  newFrame->setBackgroundColor( currentFrame->backgroundColor() );
211  newFrame->setBackgroundEnabled( currentFrame->hasBackground() );
212  newFrame->setBlendMode( currentFrame->blendMode() );
213  newFrame->setFrameEnabled( currentFrame->hasFrame() );
214  newFrame->setFrameOutlineColor( currentFrame->frameOutlineColor() );
215  newFrame->setFrameJoinStyle( currentFrame->frameJoinStyle() );
216  newFrame->setFrameOutlineWidth( currentFrame->frameOutlineWidth() );
217  newFrame->setTransparency( currentFrame->transparency() );
218  newFrame->setHideBackgroundIfEmpty( currentFrame->hideBackgroundIfEmpty() );
219 
220  addFrame( newFrame, false );
221 
222  return newFrame;
223 }
224 
226 {
227  return tr( "<frame>" );
228 }
229 
231 {
232  QgsComposerFrame* frame = dynamic_cast<QgsComposerFrame*>( item );
233  if ( !frame )
234  {
235  return;
236  }
237  int index = mFrameItems.indexOf( frame );
238  if ( index == -1 )
239  {
240  return;
241  }
242 
243  mFrameItems.removeAt( index );
244  if ( !mFrameItems.isEmpty() )
245  {
246  if ( resizeMode() != QgsComposerMultiFrame::RepeatOnEveryPage && !mIsRecalculatingSize )
247  {
248  //removing a frame forces the multi frame to UseExistingFrames resize mode
249  //otherwise the frame may not actually be removed, leading to confusing ui behaviour
251  emit changed();
253  }
254  }
255 }
256 
258 {
259  if ( mComposition->numPages() < 1 )
260  {
261  return;
262  }
263 
265  {
266  return;
267  }
268 
269  //remove items beginning on non-existing pages
270  for ( int i = mFrameItems.size() - 1; i >= 0; --i )
271  {
273  int page = frame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
274  if ( page > ( mComposition->numPages() - 1 ) )
275  {
276  removeFrame( i );
277  }
278  }
279 
280  //page number of the last item
281  QgsComposerFrame* lastFrame = mFrameItems.last();
282  int lastItemPage = lastFrame->pos().y() / ( mComposition->paperHeight() + mComposition->spaceBetweenPages() );
283 
284  for ( int i = lastItemPage + 1; i < mComposition->numPages(); ++i )
285  {
286  //copy last frame to current page
287  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, lastFrame->pos().x(),
288  lastFrame->pos().y() + mComposition->paperHeight() + mComposition->spaceBetweenPages(),
289  lastFrame->rect().width(), lastFrame->rect().height() );
290  addFrame( newFrame, false );
291  lastFrame = newFrame;
292  }
293 
295  update();
296 }
297 
298 void QgsComposerMultiFrame::removeFrame( int i, const bool removeEmptyPages )
299 {
300  if ( i >= mFrameItems.count() )
301  {
302  return;
303  }
304 
305  QgsComposerFrame* frameItem = mFrameItems.at( i );
306  if ( mComposition )
307  {
308  mIsRecalculatingSize = true;
309  int pageNumber = frameItem->page();
310  //remove item, but don't create undo command
311  mComposition->removeComposerItem( frameItem, false );
312  //if frame was the only item on the page, remove the page
313  if ( removeEmptyPages && mComposition->pageIsEmpty( pageNumber ) )
314  {
316  }
317  mIsRecalculatingSize = false;
318  }
319  mFrameItems.removeAt( i );
320 }
321 
323 {
324  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
325  {
326  frame->update();
327  }
328 }
329 
331 {
332  ResizeMode bkResizeMode = mResizeMode;
334  QObject::disconnect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
335  Q_FOREACH ( QgsComposerFrame* frame, mFrameItems )
336  {
337  mComposition->removeComposerItem( frame, false );
338  delete frame;
339  }
340  QObject::connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
341  mFrameItems.clear();
342  mResizeMode = bkResizeMode;
343 }
344 
346 {
347  if ( i >= mFrameItems.size() )
348  {
349  return nullptr;
350  }
351  return mFrameItems.at( i );
352 }
353 
355 {
356  return mFrameItems.indexOf( frame );
357 }
358 
359 bool QgsComposerMultiFrame::_writeXML( QDomElement& elem, QDomDocument& doc, bool ignoreFrames ) const
360 {
361  elem.setAttribute( "resizeMode", mResizeMode );
362  if ( !ignoreFrames )
363  {
365  for ( ; frameIt != mFrameItems.constEnd(); ++frameIt )
366  {
367  ( *frameIt )->writeXML( elem, doc );
368  }
369  }
370  QgsComposerObject::writeXML( elem, doc );
371  return true;
372 }
373 
374 bool QgsComposerMultiFrame::_readXML( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames )
375 {
376  QgsComposerObject::readXML( itemElem, doc );
377 
378  mResizeMode = static_cast< ResizeMode >( itemElem.attribute( "resizeMode", "0" ).toInt() );
379  if ( !ignoreFrames )
380  {
381  QDomNodeList frameList = itemElem.elementsByTagName( "ComposerFrame" );
382  for ( int i = 0; i < frameList.size(); ++i )
383  {
384  QDomElement frameElem = frameList.at( i ).toElement();
385  QgsComposerFrame* newFrame = new QgsComposerFrame( mComposition, this, 0, 0, 0, 0 );
386  newFrame->readXML( frameElem, doc );
387  addFrame( newFrame, false );
388  }
389 
390  //TODO - think there should be a recalculateFrameSizes() call here
391  }
392  return true;
393 }
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...
void clear()
QDomNodeList elementsByTagName(const QString &tagname) const
static unsigned index
A base class for objects which belong to a map composition.
QString attribute(const QString &name, const QString &defValue) const
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...
const T & at(int i) const
A item that forms part of a map composition.
void removeAt(int i)
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:515
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.
virtual bool writeXML(QDomElement &elem, QDomDocument &doc) const
Stores item state in DOM element.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString tr(const char *sourceText, const char *disambiguation, int n)
void setBlendMode(const QPainter::CompositionMode blendMode)
Sets the item&#39;s composition blending mode.
void update(const QRectF &rect)
int size() const
QgsComposerObject(QgsComposition *composition)
Constructor.
T value(int i) const
int indexOf(const T &value, int from) const
QDomElement toElement() const
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false)
Restores state information about base multiframe object from a DOM element.
double frameOutlineWidth() const
Returns the frame&#39;s outline width.
int page() const
Gets the page the item is currently on.
QPointF pos() const
void setNumPages(const int pages)
Sets the number of pages for the composition.
int count(const T &value) const
qreal x() const
qreal y() const
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.
void setAttribute(const QString &name, const QString &value)
int toInt(bool *ok, int base) const
QgsComposerFrame * createNewFrame(QgsComposerFrame *currentFrame, QPointF pos, QSizeF size)
Creates a new frame and adds it to the multi frame and composition.
bool isEmpty() const
virtual void setFrameOutlineColor(const QColor &color)
Sets frame outline color.
QList< QgsComposerFrame * > mFrameItems
bool mCreateUndoCommands
True: creates QgsMultiFrameCommands on internal changes (e.g.
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.
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.
virtual void setFrameOutlineWidth(const double outlineWidth)
Sets frame outline width.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:516
QgsComposition * mComposition
void deleteFrames()
Removes and deletes all child frames.
qreal width() const
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...
virtual bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
Sets item state from DOM element.
T & last()
void setResizeMode(ResizeMode mode)
Sets the resize mode for the multiframe, and recalculates frame sizes to match.
virtual Q_DECL_DEPRECATED void render(QPainter *p, const QRectF &renderExtent)
Renders a portion of the multiframe&#39;s content into a painter.
int frameIndex(QgsComposerFrame *frame) const
Returns the index of a frame within the multiframe.
double paperHeight() const
Height of paper item.
qreal height() const
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...
int size() const
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets item state from DOM element.
const_iterator constEnd() const
const_iterator constBegin() const
qreal height() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.
ResizeMode resizeMode() const
Returns the resize mode for the multiframe.
void handleFrameRemoval(QgsComposerItem *item)
Called before a frame is going to be removed.
QPointF scenePos() const
qreal width() const
double spaceBetweenPages() const
Returns the vertical space between pages in a composer view.
bool _writeXML(QDomElement &elem, QDomDocument &doc, bool ignoreFrames=false) const
Stores state information about base multiframe object in DOM element.
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...
QDomNode at(int index) const
QRectF rect() const
int transparency() const
Returns the item&#39;s transparency.