QGIS API Documentation  3.4.3-Madeira (2f64a3c)
qgslayoutitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitem.cpp
3  -------------------
4  begin : June 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgslayoutitem.h"
18 #include "qgslayout.h"
19 #include "qgslayoututils.h"
20 #include "qgspagesizeregistry.h"
22 #include "qgslayoutmodel.h"
23 #include "qgssymbollayerutils.h"
24 #include "qgslayoutitemgroup.h"
25 #include "qgspainting.h"
26 #include "qgslayouteffect.h"
27 #include "qgslayoutundostack.h"
29 #include "qgslayoutitempage.h"
30 #include "qgsimageoperation.h"
31 #include <QPainter>
32 #include <QStyleOptionGraphicsItem>
33 #include <QUuid>
34 
35 #define CACHE_SIZE_LIMIT 5000
36 
38  : mRenderContext( context )
39  , mViewScaleFactor( viewScaleFactor )
40 {
41 }
42 
43 
44 
45 QgsLayoutItem::QgsLayoutItem( QgsLayout *layout, bool manageZValue )
46  : QgsLayoutObject( layout )
47  , QGraphicsRectItem( nullptr )
48  , mUuid( QUuid::createUuid().toString() )
49 {
50  setZValue( QgsLayout::ZItem );
51 
52  // needed to access current view transform during paint operations
53  setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption | QGraphicsItem::ItemIsSelectable );
54 
55  setCacheMode( QGraphicsItem::DeviceCoordinateCache );
56 
57  //record initial position
58  QgsUnitTypes::LayoutUnit initialUnits = layout ? layout->units() : QgsUnitTypes::LayoutMillimeters;
59  mItemPosition = QgsLayoutPoint( scenePos().x(), scenePos().y(), initialUnits );
60  mItemSize = QgsLayoutSize( rect().width(), rect().height(), initialUnits );
61 
62  // required to initially setup background/frame style
63  refreshBackgroundColor( false );
64  refreshFrame( false );
65 
66  initConnectionsToLayout();
67 
68  //let z-Value be managed by layout
69  if ( mLayout && manageZValue )
70  {
71  mLayoutManagesZValue = true;
72  mLayout->itemsModel()->addItemAtTop( this );
73  }
74  else
75  {
76  mLayoutManagesZValue = false;
77  }
78 
79  // Setup layout effect
80  mEffect.reset( new QgsLayoutEffect() );
81  if ( mLayout )
82  {
83  mEffect->setEnabled( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagUseAdvancedEffects );
84  connect( &mLayout->renderContext(), &QgsLayoutRenderContext::flagsChanged, this, [ = ]( QgsLayoutRenderContext::Flags flags )
85  {
86  mEffect->setEnabled( flags & QgsLayoutRenderContext::FlagUseAdvancedEffects );
87  } );
88  }
89  setGraphicsEffect( mEffect.get() );
90 }
91 
93 {
94  cleanup();
95 }
96 
98 {
99  if ( mLayout && mLayoutManagesZValue )
100  {
101  mLayout->itemsModel()->removeItem( this );
102  }
103 }
104 
106 {
107  //return id, if it's not empty
108  if ( !id().isEmpty() )
109  {
110  return id();
111  }
112 
113  //for unnamed items, default to item type
114  if ( QgsLayoutItemAbstractMetadata *metadata = QgsApplication::layoutItemRegistry()->itemMetadata( type() ) )
115  {
116  return tr( "<%1>" ).arg( metadata->visibleName() );
117  }
118 
119  return tr( "<item>" );
120 }
121 
123 {
125 }
126 
127 QgsLayoutItem::Flags QgsLayoutItem::itemFlags() const
128 {
129  return nullptr;
130 }
131 
132 void QgsLayoutItem::setId( const QString &id )
133 {
134  if ( id == mId )
135  {
136  return;
137  }
138 
139  if ( !shouldBlockUndoCommands() )
140  mLayout->undoStack()->beginCommand( this, tr( "Change Item ID" ) );
141 
142  mId = id;
143 
144  if ( !shouldBlockUndoCommands() )
145  mLayout->undoStack()->endCommand();
146 
147  setToolTip( id );
148 
149  //inform model that id data has changed
150  if ( mLayout )
151  {
152  mLayout->itemsModel()->updateItemDisplayName( this );
153  }
154 
155  emit changed();
156 }
157 
158 void QgsLayoutItem::setSelected( bool selected )
159 {
160  QGraphicsRectItem::setSelected( selected );
161  //inform model that id data has changed
162  if ( mLayout )
163  {
164  mLayout->itemsModel()->updateItemSelectStatus( this );
165  }
166 }
167 
168 void QgsLayoutItem::setVisibility( const bool visible )
169 {
170  if ( visible == isVisible() )
171  {
172  //nothing to do
173  return;
174  }
175 
176  std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
177  if ( !shouldBlockUndoCommands() )
178  {
179  command.reset( createCommand( visible ? tr( "Show Item" ) : tr( "Hide Item" ), 0 ) );
180  command->saveBeforeState();
181  }
182 
183  QGraphicsItem::setVisible( visible );
184 
185  if ( command )
186  {
187  command->saveAfterState();
188  mLayout->undoStack()->push( command.release() );
189  }
190 
191  //inform model that visibility has changed
192  if ( mLayout )
193  {
194  mLayout->itemsModel()->updateItemVisibility( this );
195  }
196 }
197 
199 {
200  if ( locked == mIsLocked )
201  {
202  return;
203  }
204 
205  if ( !shouldBlockUndoCommands() )
206  mLayout->undoStack()->beginCommand( this, locked ? tr( "Lock Item" ) : tr( "Unlock Item" ) );
207 
208  mIsLocked = locked;
209 
210  if ( !shouldBlockUndoCommands() )
211  mLayout->undoStack()->endCommand();
212 
213  //inform model that id data has changed
214  if ( mLayout )
215  {
216  mLayout->itemsModel()->updateItemLockStatus( this );
217  }
218 
219  update();
220  emit lockChanged();
221 }
222 
224 {
225  return !mParentGroupUuid.isEmpty() && mLayout && static_cast< bool >( mLayout->itemByUuid( mParentGroupUuid ) );
226 }
227 
229 {
230  if ( !mLayout || mParentGroupUuid.isEmpty() )
231  return nullptr;
232 
233  return qobject_cast< QgsLayoutItemGroup * >( mLayout->itemByUuid( mParentGroupUuid ) );
234 }
235 
237 {
238  if ( !group )
239  mParentGroupUuid.clear();
240  else
241  mParentGroupUuid = group->uuid();
242  setFlag( QGraphicsItem::ItemIsSelectable, !static_cast< bool>( group ) ); //item in groups cannot be selected
243 }
244 
245 void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
246 {
247  if ( !painter || !painter->device() || !shouldDrawItem() )
248  {
249  return;
250  }
251 
252  //TODO - remember to disable saving/restoring on graphics view!!
253 
254  if ( shouldDrawDebugRect() )
255  {
256  drawDebugRect( painter );
257  return;
258  }
259 
260  bool previewRender = !mLayout || mLayout->renderContext().isPreviewRender();
261  double destinationDpi = previewRender ? QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle ) * 25.4 : mLayout->renderContext().dpi();
262  bool useImageCache = false;
263  bool forceRasterOutput = containsAdvancedEffects() && ( !mLayout || !( mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagForceVectorOutput ) );
264 
265  if ( useImageCache || forceRasterOutput )
266  {
267  double widthInPixels = 0;
268  double heightInPixels = 0;
269 
270  if ( previewRender )
271  {
272  widthInPixels = boundingRect().width() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle );
273  heightInPixels = boundingRect().height() * QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle );
274  }
275  else
276  {
277  double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutPixels ).length() : destinationDpi / 25.4;
278  widthInPixels = boundingRect().width() * layoutUnitsToPixels;
279  heightInPixels = boundingRect().height() * layoutUnitsToPixels;
280  }
281 
282  // limit size of image for better performance
283  if ( previewRender && ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT ) )
284  {
285  double scale = 1.0;
286  if ( widthInPixels > heightInPixels )
287  {
288  scale = widthInPixels / CACHE_SIZE_LIMIT;
289  widthInPixels = CACHE_SIZE_LIMIT;
290  heightInPixels /= scale;
291  }
292  else
293  {
294  scale = heightInPixels / CACHE_SIZE_LIMIT;
295  heightInPixels = CACHE_SIZE_LIMIT;
296  widthInPixels /= scale;
297  }
298  destinationDpi = destinationDpi / scale;
299  }
300 
301  if ( previewRender && !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
302  {
303  // can reuse last cached image
304  QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
305  painter->save();
306  preparePainter( painter );
307  double cacheScale = destinationDpi / mItemCacheDpi;
308  painter->scale( cacheScale / context.scaleFactor(), cacheScale / context.scaleFactor() );
309  painter->drawImage( boundingRect().x() * context.scaleFactor() / cacheScale,
310  boundingRect().y() * context.scaleFactor() / cacheScale, mItemCachedImage );
311  painter->restore();
312  return;
313  }
314  else
315  {
316  QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
317  image.fill( Qt::transparent );
318  image.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
319  image.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
320  QPainter p( &image );
321 
322  preparePainter( &p );
325  // painter is already scaled to dots
326  // need to translate so that item origin is at 0,0 in painter coordinates (not bounding rect origin)
327  p.translate( -boundingRect().x() * context.scaleFactor(), -boundingRect().y() * context.scaleFactor() );
328  drawBackground( context );
329  double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle );
330  QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
331  draw( itemRenderContext );
332  drawFrame( context );
333  p.end();
334 
335  QgsImageOperation::multiplyOpacity( image, mEvaluatedOpacity );
336 
337  painter->save();
338  // scale painter from mm to dots
339  painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
340  painter->drawImage( boundingRect().x() * context.scaleFactor(),
341  boundingRect().y() * context.scaleFactor(), image );
342  painter->restore();
343 
344  if ( previewRender )
345  {
346  mItemCacheDpi = destinationDpi;
347  mItemCachedImage = image;
348  }
349  }
350  }
351  else
352  {
353  // no caching or flattening
354  painter->save();
355  preparePainter( painter );
356  QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
358  drawBackground( context );
359 
360  // scale painter from mm to dots
361  painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
362  double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle );
363  QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
364  draw( itemRenderContext );
365 
366  painter->scale( context.scaleFactor(), context.scaleFactor() );
367  drawFrame( context );
368 
369  painter->restore();
370  }
371 }
372 
374 {
375  if ( point == mReferencePoint )
376  {
377  return;
378  }
379 
380  mReferencePoint = point;
381 
382  //also need to adjust stored position
383  updateStoredItemPosition();
385 }
386 
387 void QgsLayoutItem::attemptResize( const QgsLayoutSize &s, bool includesFrame )
388 {
389  if ( !mLayout )
390  {
391  mItemSize = s;
392  setRect( 0, 0, s.width(), s.height() );
393  return;
394  }
395 
396  QgsLayoutSize size = s;
397 
398  if ( includesFrame )
399  {
400  //adjust position to account for frame size
401  double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), size.units() ).length();
402  size.setWidth( size.width() - 2 * bleed );
403  size.setHeight( size.height() - 2 * bleed );
404  }
405 
406  QgsLayoutSize evaluatedSize = applyDataDefinedSize( size );
407  QSizeF targetSizeLayoutUnits = mLayout->convertToLayoutUnits( evaluatedSize );
408  QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
409  actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
410  actualSizeLayoutUnits = applyItemSizeConstraint( actualSizeLayoutUnits );
411 
412  if ( actualSizeLayoutUnits == rect().size() )
413  {
414  return;
415  }
416 
417  QgsLayoutSize actualSizeTargetUnits = mLayout->convertFromLayoutUnits( actualSizeLayoutUnits, size.units() );
418  mItemSize = actualSizeTargetUnits;
419 
420  setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
422  emit sizePositionChanged();
423 }
424 
425 void QgsLayoutItem::attemptMove( const QgsLayoutPoint &p, bool useReferencePoint, bool includesFrame, int page )
426 {
427  if ( !mLayout )
428  {
429  mItemPosition = p;
430  setPos( p.toQPointF() );
431  return;
432  }
433 
434  QgsLayoutPoint point = p;
435  if ( page >= 0 )
436  {
437  point = mLayout->pageCollection()->pagePositionToAbsolute( page, p );
438  }
439 
440  if ( includesFrame )
441  {
442  //adjust position to account for frame size
443  double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), point.units() ).length();
444  point.setX( point.x() + bleed );
445  point.setY( point.y() + bleed );
446  }
447 
448  QgsLayoutPoint evaluatedPoint = point;
449  if ( !useReferencePoint )
450  {
451  evaluatedPoint = topLeftToReferencePoint( point );
452  }
453 
454  evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
455  QPointF evaluatedPointLayoutUnits = mLayout->convertToLayoutUnits( evaluatedPoint );
456  QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size(), mReferencePoint );
457  if ( topLeftPointLayoutUnits == scenePos() && point.units() == mItemPosition.units() )
458  {
459  //TODO - add test for second condition
460  return;
461  }
462 
463  QgsLayoutPoint referencePointTargetUnits = mLayout->convertFromLayoutUnits( evaluatedPointLayoutUnits, point.units() );
464  mItemPosition = referencePointTargetUnits;
465  setScenePos( topLeftPointLayoutUnits );
466  emit sizePositionChanged();
467 }
468 
469 void QgsLayoutItem::attemptSetSceneRect( const QRectF &rect, bool includesFrame )
470 {
471  QPointF newPos = rect.topLeft();
472 
473  blockSignals( true );
474  // translate new size to current item units
475  QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( rect.size(), mItemSize.units() );
476  attemptResize( newSize, includesFrame );
477 
478  // translate new position to current item units
479  QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, mItemPosition.units() );
480  attemptMove( itemPos, false, includesFrame );
481  blockSignals( false );
482  emit sizePositionChanged();
483 }
484 
485 void QgsLayoutItem::attemptMoveBy( double deltaX, double deltaY )
486 {
487  if ( !mLayout )
488  {
489  moveBy( deltaX, deltaY );
490  return;
491  }
492 
493  QgsLayoutPoint itemPos = positionWithUnits();
494  QgsLayoutPoint deltaPos = mLayout->convertFromLayoutUnits( QPointF( deltaX, deltaY ), itemPos.units() );
495  itemPos.setX( itemPos.x() + deltaPos.x() );
496  itemPos.setY( itemPos.y() + deltaPos.y() );
497  attemptMove( itemPos );
498 }
499 
501 {
502  if ( !mLayout )
503  return -1;
504 
505  return mLayout->pageCollection()->pageNumberForPoint( pos() );
506 }
507 
508 QPointF QgsLayoutItem::pagePos() const
509 {
510  QPointF p = positionAtReferencePoint( mReferencePoint );
511 
512  if ( !mLayout )
513  return p;
514 
515  // try to get page
516  QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page() );
517  if ( !pageItem )
518  return p;
519 
520  p.ry() -= pageItem->pos().y();
521  return p;
522 }
523 
525 {
526  QPointF p = pagePos();
527  if ( !mLayout )
528  return QgsLayoutPoint( p );
529 
530  return mLayout->convertFromLayoutUnits( p, mItemPosition.units() );
531 }
532 
533 void QgsLayoutItem::setScenePos( const QPointF destinationPos )
534 {
535  //since setPos does not account for item rotation, use difference between
536  //current scenePos (which DOES account for rotation) and destination pos
537  //to calculate how much the item needs to move
538  if ( parentItem() )
539  setPos( pos() + ( destinationPos - scenePos() ) + parentItem()->scenePos() );
540  else
541  setPos( pos() + ( destinationPos - scenePos() ) );
542 }
543 
544 bool QgsLayoutItem::shouldBlockUndoCommands() const
545 {
546  return !mLayout || mLayout != scene() || mBlockUndoCommands;
547 }
548 
550 {
551  if ( !mLayout || mLayout->renderContext().isPreviewRender() )
552  {
553  //preview mode so OK to draw item
554  return true;
555  }
556 
557  //exporting layout, so check if item is excluded from exports
558  return !mEvaluatedExcludeFromExports;
559 }
560 
562 {
563  return mItemRotation;
564 }
565 
566 bool QgsLayoutItem::writeXml( QDomElement &parentElement, QDomDocument &doc, const QgsReadWriteContext &context ) const
567 {
568  QDomElement element = doc.createElement( QStringLiteral( "LayoutItem" ) );
569  element.setAttribute( QStringLiteral( "type" ), QString::number( type() ) );
570 
571  element.setAttribute( QStringLiteral( "uuid" ), mUuid );
572  element.setAttribute( QStringLiteral( "templateUuid" ), mUuid );
573  element.setAttribute( QStringLiteral( "id" ), mId );
574  element.setAttribute( QStringLiteral( "referencePoint" ), QString::number( static_cast< int >( mReferencePoint ) ) );
575  element.setAttribute( QStringLiteral( "position" ), mItemPosition.encodePoint() );
576  element.setAttribute( QStringLiteral( "positionOnPage" ), pagePositionWithUnits().encodePoint() );
577  element.setAttribute( QStringLiteral( "size" ), mItemSize.encodeSize() );
578  element.setAttribute( QStringLiteral( "itemRotation" ), QString::number( mItemRotation ) );
579  element.setAttribute( QStringLiteral( "groupUuid" ), mParentGroupUuid );
580 
581  element.setAttribute( QStringLiteral( "zValue" ), QString::number( zValue() ) );
582  element.setAttribute( QStringLiteral( "visibility" ), isVisible() );
583  //position lock for mouse moves/resizes
584  if ( mIsLocked )
585  {
586  element.setAttribute( QStringLiteral( "positionLock" ), QStringLiteral( "true" ) );
587  }
588  else
589  {
590  element.setAttribute( QStringLiteral( "positionLock" ), QStringLiteral( "false" ) );
591  }
592 
593  //frame
594  if ( mFrame )
595  {
596  element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "true" ) );
597  }
598  else
599  {
600  element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "false" ) );
601  }
602 
603  //background
604  if ( mBackground )
605  {
606  element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "true" ) );
607  }
608  else
609  {
610  element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "false" ) );
611  }
612 
613  //frame color
614  QDomElement frameColorElem = doc.createElement( QStringLiteral( "FrameColor" ) );
615  frameColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFrameColor.red() ) );
616  frameColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFrameColor.green() ) );
617  frameColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFrameColor.blue() ) );
618  frameColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFrameColor.alpha() ) );
619  element.appendChild( frameColorElem );
620  element.setAttribute( QStringLiteral( "outlineWidthM" ), mFrameWidth.encodeMeasurement() );
621  element.setAttribute( QStringLiteral( "frameJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );
622 
623  //background color
624  QDomElement bgColorElem = doc.createElement( QStringLiteral( "BackgroundColor" ) );
625  bgColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mBackgroundColor.red() ) );
626  bgColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mBackgroundColor.green() ) );
627  bgColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mBackgroundColor.blue() ) );
628  bgColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mBackgroundColor.alpha() ) );
629  element.appendChild( bgColorElem );
630 
631  //blend mode
632  element.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
633 
634  //opacity
635  element.setAttribute( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
636 
637  element.setAttribute( QStringLiteral( "excludeFromExports" ), mExcludeFromExports );
638 
639  writeObjectPropertiesToElement( element, doc, context );
640 
641  writePropertiesToElement( element, doc, context );
642  parentElement.appendChild( element );
643 
644  return true;
645 }
646 
647 bool QgsLayoutItem::readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
648 {
649  if ( element.nodeName() != QStringLiteral( "LayoutItem" ) )
650  {
651  return false;
652  }
653 
654  readObjectPropertiesFromElement( element, doc, context );
655 
656  mBlockUndoCommands = true;
657  mUuid = element.attribute( QStringLiteral( "uuid" ), QUuid::createUuid().toString() );
658  setId( element.attribute( QStringLiteral( "id" ) ) );
659  mReferencePoint = static_cast< ReferencePoint >( element.attribute( QStringLiteral( "referencePoint" ) ).toInt() );
660  setItemRotation( element.attribute( QStringLiteral( "itemRotation" ), QStringLiteral( "0" ) ).toDouble() );
661  attemptMove( QgsLayoutPoint::decodePoint( element.attribute( QStringLiteral( "position" ) ) ) );
662  attemptResize( QgsLayoutSize::decodeSize( element.attribute( QStringLiteral( "size" ) ) ) );
663 
664  mParentGroupUuid = element.attribute( QStringLiteral( "groupUuid" ) );
665  if ( !mParentGroupUuid.isEmpty() )
666  {
667  if ( QgsLayoutItemGroup *group = parentGroup() )
668  {
669  group->addItem( this );
670  }
671  }
672  mTemplateUuid = element.attribute( QStringLiteral( "templateUuid" ) );
673 
674  //position lock for mouse moves/resizes
675  QString positionLock = element.attribute( QStringLiteral( "positionLock" ) );
676  if ( positionLock.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
677  {
678  setLocked( true );
679  }
680  else
681  {
682  setLocked( false );
683  }
684  //visibility
685  setVisibility( element.attribute( QStringLiteral( "visibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
686  setZValue( element.attribute( QStringLiteral( "zValue" ) ).toDouble() );
687 
688  //frame
689  QString frame = element.attribute( QStringLiteral( "frame" ) );
690  if ( frame.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
691  {
692  mFrame = true;
693  }
694  else
695  {
696  mFrame = false;
697  }
698 
699  //frame
700  QString background = element.attribute( QStringLiteral( "background" ) );
701  if ( background.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
702  {
703  mBackground = true;
704  }
705  else
706  {
707  mBackground = false;
708  }
709 
710  //pen
711  mFrameWidth = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "outlineWidthM" ) ) );
712  mFrameJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( element.attribute( QStringLiteral( "frameJoinStyle" ), QStringLiteral( "miter" ) ) );
713  QDomNodeList frameColorList = element.elementsByTagName( QStringLiteral( "FrameColor" ) );
714  if ( !frameColorList.isEmpty() )
715  {
716  QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
717  bool redOk = false;
718  bool greenOk = false;
719  bool blueOk = false;
720  bool alphaOk = false;
721  int penRed, penGreen, penBlue, penAlpha;
722 
723  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
724  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
725  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
726  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
727 
728  if ( redOk && greenOk && blueOk && alphaOk )
729  {
730  mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
731  }
732  }
733  refreshFrame( false );
734 
735  //brush
736  QDomNodeList bgColorList = element.elementsByTagName( QStringLiteral( "BackgroundColor" ) );
737  if ( !bgColorList.isEmpty() )
738  {
739  QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
740  bool redOk, greenOk, blueOk, alphaOk;
741  int bgRed, bgGreen, bgBlue, bgAlpha;
742  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
743  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
744  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
745  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
746  if ( redOk && greenOk && blueOk && alphaOk )
747  {
748  mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
749  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
750  }
751  //apply any data defined settings
752  refreshBackgroundColor( false );
753  }
754 
755  //blend mode
756  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( element.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
757 
758  //opacity
759  if ( element.hasAttribute( QStringLiteral( "opacity" ) ) )
760  {
761  setItemOpacity( element.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) ).toDouble() );
762  }
763  else
764  {
765  setItemOpacity( 1.0 - element.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 100.0 );
766  }
767 
768  mExcludeFromExports = element.attribute( QStringLiteral( "excludeFromExports" ), QStringLiteral( "0" ) ).toInt();
769  mEvaluatedExcludeFromExports = mExcludeFromExports;
770 
771  bool result = readPropertiesFromElement( element, doc, context );
772 
773  mBlockUndoCommands = false;
774 
775  emit changed();
776  update();
777  return result;
778 }
779 
781 {
782 }
783 
784 QgsAbstractLayoutUndoCommand *QgsLayoutItem::createCommand( const QString &text, int id, QUndoCommand *parent )
785 {
786  return new QgsLayoutItemUndoCommand( this, text, id, parent );
787 }
788 
790 {
791  if ( drawFrame == mFrame )
792  {
793  //no change
794  return;
795  }
796 
797  mFrame = drawFrame;
798  refreshFrame( true );
799  emit frameChanged();
800 }
801 
802 void QgsLayoutItem::setFrameStrokeColor( const QColor &color )
803 {
804  if ( mFrameColor == color )
805  {
806  //no change
807  return;
808  }
809  mFrameColor = color;
810  // apply any datadefined overrides
811  refreshFrame( true );
812  emit frameChanged();
813 }
814 
816 {
817  if ( mFrameWidth == width )
818  {
819  //no change
820  return;
821  }
822  mFrameWidth = width;
823  refreshFrame();
824  emit frameChanged();
825 }
826 
827 void QgsLayoutItem::setFrameJoinStyle( const Qt::PenJoinStyle style )
828 {
829  if ( mFrameJoinStyle == style )
830  {
831  //no change
832  return;
833  }
834  mFrameJoinStyle = style;
835 
836  QPen itemPen = pen();
837  itemPen.setJoinStyle( mFrameJoinStyle );
838  setPen( itemPen );
839  emit frameChanged();
840 }
841 
843 {
844  mBackground = drawBackground;
845  update();
846 }
847 
848 void QgsLayoutItem::setBackgroundColor( const QColor &color )
849 {
850  mBackgroundColor = color;
851  // apply any datadefined overrides
852  refreshBackgroundColor( true );
853 }
854 
855 void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
856 {
857  mBlendMode = mode;
858  // Update the item effect to use the new blend mode
860 }
861 
862 void QgsLayoutItem::setItemOpacity( double opacity )
863 {
864  mOpacity = opacity;
865  refreshOpacity( mItemCachedImage.isNull() );
866  if ( !mItemCachedImage.isNull() )
867  invalidateCache();
868 }
869 
871 {
872  return mExcludeFromExports;
873 }
874 
876 {
877  mExcludeFromExports = exclude;
879 }
880 
882 {
883  return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
884 }
885 
887 {
888  return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) ||
889  blendMode() != QPainter::CompositionMode_SourceOver;
890 }
891 
893 {
894  if ( !frameEnabled() )
895  {
896  return 0;
897  }
898 
899  return pen().widthF() / 2.0;
900 }
901 
903 {
904  double frameBleed = estimatedFrameBleed();
905  return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
906 }
907 
908 void QgsLayoutItem::moveContent( double, double )
909 {
910 
911 }
912 
914 {
915 
916 }
917 
918 void QgsLayoutItem::zoomContent( double, QPointF )
919 {
920 
921 }
922 
923 void QgsLayoutItem::beginCommand( const QString &commandText, UndoCommand command )
924 {
925  if ( !mLayout )
926  return;
927 
928  mLayout->undoStack()->beginCommand( this, commandText, command );
929 }
930 
932 {
933  if ( mLayout )
934  mLayout->undoStack()->endCommand();
935 }
936 
938 {
939  if ( mLayout )
940  mLayout->undoStack()->cancelCommand();
941 }
942 
943 QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
944 {
945  if ( !mLayout )
946  {
947  return position;
948  }
949 
951  double evaluatedX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PositionX, context, position.x() );
952  double evaluatedY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PositionY, context, position.y() );
953  return QgsLayoutPoint( evaluatedX, evaluatedY, position.units() );
954 }
955 
956 void QgsLayoutItem::applyDataDefinedOrientation( double &width, double &height, const QgsExpressionContext &context )
957 {
958  bool ok = false;
959  QString orientationString = mDataDefinedProperties.valueAsString( QgsLayoutObject::PaperOrientation, context, QString(), &ok );
960  if ( ok && !orientationString.isEmpty() )
961  {
962  QgsLayoutItemPage::Orientation orientation = QgsLayoutUtils::decodePaperOrientation( orientationString, ok );
963  if ( ok )
964  {
965  double heightD = 0.0, widthD = 0.0;
966  switch ( orientation )
967  {
969  {
970  heightD = std::max( height, width );
971  widthD = std::min( height, width );
972  break;
973  }
975  {
976  heightD = std::min( height, width );
977  widthD = std::max( height, width );
978  break;
979  }
980  }
981  width = widthD;
982  height = heightD;
983  }
984  }
985 }
986 
988 {
989  if ( !mLayout )
990  {
991  return size;
992  }
993 
998  return size;
999 
1000 
1002 
1003  // lowest priority is page size
1005  QgsPageSize matchedSize;
1006  double evaluatedWidth = size.width();
1007  double evaluatedHeight = size.height();
1008  if ( QgsApplication::pageSizeRegistry()->decodePageSize( pageSize, matchedSize ) )
1009  {
1010  QgsLayoutSize convertedSize = mLayout->renderContext().measurementConverter().convert( matchedSize.size, size.units() );
1011  evaluatedWidth = convertedSize.width();
1012  evaluatedHeight = convertedSize.height();
1013  }
1014 
1015  // highest priority is dd width/height
1016  evaluatedWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemWidth, context, evaluatedWidth );
1017  evaluatedHeight = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemHeight, context, evaluatedHeight );
1018 
1019  //which is finally overwritten by data defined orientation
1020  applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1021 
1022  return QgsLayoutSize( evaluatedWidth, evaluatedHeight, size.units() );
1023 }
1024 
1025 double QgsLayoutItem::applyDataDefinedRotation( const double rotation )
1026 {
1027  if ( !mLayout )
1028  {
1029  return rotation;
1030  }
1031 
1033  double evaluatedRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemRotation, context, rotation );
1034  return evaluatedRotation;
1035 }
1036 
1038 {
1039  //update data defined properties and update item to match
1040 
1041  //evaluate width and height first, since they may affect position if non-top-left reference point set
1042  if ( property == QgsLayoutObject::ItemWidth || property == QgsLayoutObject::ItemHeight ||
1043  property == QgsLayoutObject::AllProperties )
1044  {
1045  refreshItemSize();
1046  }
1047  if ( property == QgsLayoutObject::PositionX || property == QgsLayoutObject::PositionY ||
1048  property == QgsLayoutObject::AllProperties )
1049  {
1051  }
1052  if ( property == QgsLayoutObject::ItemRotation || property == QgsLayoutObject::AllProperties )
1053  {
1055  }
1056  if ( property == QgsLayoutObject::Opacity || property == QgsLayoutObject::AllProperties )
1057  {
1058  refreshOpacity( false );
1059  }
1060  if ( property == QgsLayoutObject::FrameColor || property == QgsLayoutObject::AllProperties )
1061  {
1062  refreshFrame( false );
1063  }
1064  if ( property == QgsLayoutObject::BackgroundColor || property == QgsLayoutObject::AllProperties )
1065  {
1066  refreshBackgroundColor( false );
1067  }
1068  if ( property == QgsLayoutObject::BlendMode || property == QgsLayoutObject::AllProperties )
1069  {
1070  refreshBlendMode();
1071  }
1073  {
1074  bool exclude = mExcludeFromExports;
1075  //data defined exclude from exports set?
1077  }
1078 
1079  update();
1080 }
1081 
1082 void QgsLayoutItem::setItemRotation( double angle, const bool adjustPosition )
1083 {
1084  if ( angle >= 360.0 || angle <= -360.0 )
1085  {
1086  angle = std::fmod( angle, 360.0 );
1087  }
1088 
1089  QPointF point = adjustPosition ? positionAtReferencePoint( QgsLayoutItem::Middle )
1090  : pos();
1091  double rotationRequired = angle - rotation();
1092  rotateItem( rotationRequired, point );
1093 
1094  mItemRotation = angle;
1095 }
1096 
1097 void QgsLayoutItem::updateStoredItemPosition()
1098 {
1099  QPointF layoutPosReferencePoint = positionAtReferencePoint( mReferencePoint );
1100  mItemPosition = mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.units() );
1101 }
1102 
1103 void QgsLayoutItem::rotateItem( const double angle, const QPointF transformOrigin )
1104 {
1105  double evaluatedAngle = angle + rotation();
1106  evaluatedAngle = QgsLayoutUtils::normalizedAngle( evaluatedAngle, true );
1107  mItemRotation = evaluatedAngle;
1108 
1109  QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1110 
1111  refreshItemRotation( &itemTransformOrigin );
1112 }
1113 
1115 {
1118  return context;
1119 }
1120 
1121 
1123 {
1125  refreshItemSize();
1126 
1128 }
1129 
1131 {
1132  if ( !mItemCachedImage.isNull() )
1133  {
1134  mItemCachedImage = QImage();
1135  mItemCacheDpi = -1;
1136  update();
1137  }
1138 }
1139 
1141 {
1142  update();
1143 }
1144 
1145 void QgsLayoutItem::drawDebugRect( QPainter *painter )
1146 {
1147  if ( !painter )
1148  {
1149  return;
1150  }
1151 
1152  painter->save();
1153  painter->setRenderHint( QPainter::Antialiasing, false );
1154  painter->setPen( Qt::NoPen );
1155  painter->setBrush( QColor( 100, 255, 100, 200 ) );
1156  painter->drawRect( rect() );
1157  painter->restore();
1158 }
1159 
1161 {
1162  if ( !mFrame || !context.painter() )
1163  return;
1164 
1165  QPainter *p = context.painter();
1166  p->save();
1167  p->setPen( pen() );
1168  p->setBrush( Qt::NoBrush );
1169  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1170  p->restore();
1171 }
1172 
1174 {
1175  if ( !mBackground || !context.painter() )
1176  return;
1177 
1178  QPainter *p = context.painter();
1179  p->save();
1180  p->setBrush( brush() );
1181  p->setPen( Qt::NoPen );
1182  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1183  p->restore();
1184 }
1185 
1187 {
1188  mFixedSize = size;
1189  refreshItemSize();
1190 }
1191 
1193 {
1194  mMinimumSize = size;
1195  refreshItemSize();
1196 }
1197 
1198 QSizeF QgsLayoutItem::applyItemSizeConstraint( const QSizeF targetSize )
1199 {
1200  return targetSize;
1201 }
1202 
1204 {
1205  attemptResize( mItemSize );
1206 }
1207 
1209 {
1210  attemptMove( mItemPosition );
1211 }
1212 
1213 QPointF QgsLayoutItem::itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF size ) const
1214 {
1215  switch ( reference )
1216  {
1217  case UpperMiddle:
1218  return QPointF( size.width() / 2.0, 0 );
1219  case UpperRight:
1220  return QPointF( size.width(), 0 );
1221  case MiddleLeft:
1222  return QPointF( 0, size.height() / 2.0 );
1223  case Middle:
1224  return QPointF( size.width() / 2.0, size.height() / 2.0 );
1225  case MiddleRight:
1226  return QPointF( size.width(), size.height() / 2.0 );
1227  case LowerLeft:
1228  return QPointF( 0, size.height() );
1229  case LowerMiddle:
1230  return QPointF( size.width() / 2.0, size.height() );
1231  case LowerRight:
1232  return QPointF( size.width(), size.height() );
1233  case UpperLeft:
1234  return QPointF( 0, 0 );
1235  }
1236  // no warnings
1237  return QPointF( 0, 0 );
1238 }
1239 
1240 QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF position, const QSizeF size, const ReferencePoint reference ) const
1241 {
1242  QPointF itemPosition = mapFromScene( position ); //need to map from scene to handle item rotation
1243  QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1244  return mapToScene( adjustedPointInsideItem );
1245 }
1246 
1248 {
1249  QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1250  return mapToScene( pointWithinItem );
1251 }
1252 
1254 {
1255  QPointF topLeft = mLayout->convertToLayoutUnits( point );
1256  QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1257  return mLayout->convertFromLayoutUnits( refPoint, point.units() );
1258 }
1259 
1260 bool QgsLayoutItem::writePropertiesToElement( QDomElement &, QDomDocument &, const QgsReadWriteContext & ) const
1261 {
1262  return true;
1263 }
1264 
1265 bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &, const QDomDocument &, const QgsReadWriteContext & )
1266 {
1267 
1268  return true;
1269 }
1270 
1271 void QgsLayoutItem::initConnectionsToLayout()
1272 {
1273  if ( !mLayout )
1274  return;
1275 
1276 }
1277 
1278 void QgsLayoutItem::preparePainter( QPainter *painter )
1279 {
1280  if ( !painter || !painter->device() )
1281  {
1282  return;
1283  }
1284 
1285  painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1286 }
1287 
1288 bool QgsLayoutItem::shouldDrawAntialiased() const
1289 {
1290  if ( !mLayout )
1291  {
1292  return true;
1293  }
1294  return mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagAntialiasing ) && !mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagDebug );
1295 }
1296 
1297 bool QgsLayoutItem::shouldDrawDebugRect() const
1298 {
1299  return mLayout && mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagDebug );
1300 }
1301 
1302 QSizeF QgsLayoutItem::applyMinimumSize( const QSizeF targetSize )
1303 {
1304  if ( !mLayout || minimumSize().isEmpty() )
1305  {
1306  return targetSize;
1307  }
1308  QSizeF minimumSizeLayoutUnits = mLayout->convertToLayoutUnits( minimumSize() );
1309  return targetSize.expandedTo( minimumSizeLayoutUnits );
1310 }
1311 
1312 QSizeF QgsLayoutItem::applyFixedSize( const QSizeF targetSize )
1313 {
1314  if ( !mLayout || fixedSize().isEmpty() )
1315  {
1316  return targetSize;
1317  }
1318 
1319  QSizeF size = targetSize;
1320  QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
1321  if ( fixedSizeLayoutUnits.width() > 0 )
1322  size.setWidth( fixedSizeLayoutUnits.width() );
1323  if ( fixedSizeLayoutUnits.height() > 0 )
1324  size.setHeight( fixedSizeLayoutUnits.height() );
1325 
1326  return size;
1327 }
1328 
1329 void QgsLayoutItem::refreshItemRotation( QPointF *origin )
1330 {
1331  double r = mItemRotation;
1332 
1333  //data defined rotation set?
1335 
1336  if ( qgsDoubleNear( r, rotation() ) && !origin )
1337  {
1338  return;
1339  }
1340 
1341  QPointF transformPoint = origin ? *origin : mapFromScene( positionAtReferencePoint( QgsLayoutItem::Middle ) );
1342 
1343  if ( !transformPoint.isNull() )
1344  {
1345  //adjustPosition set, so shift the position of the item so that rotation occurs around item center
1346  //create a line from the transform point to the item's origin, in scene coordinates
1347  QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1348  //rotate this line by the current rotation angle
1349  refLine.setAngle( refLine.angle() - r + rotation() );
1350  //get new end point of line - this is the new item position
1351  QPointF rotatedReferencePoint = refLine.p2();
1352  setPos( rotatedReferencePoint );
1353  }
1354 
1355  setTransformOriginPoint( 0, 0 );
1356  QGraphicsItem::setRotation( r );
1357 
1358  //adjust stored position of item to match scene pos of reference point
1359  updateStoredItemPosition();
1360  emit sizePositionChanged();
1361 
1362  emit rotationChanged( r );
1363 
1364  //update bounds of scene, since rotation may affect this
1365  mLayout->updateBounds();
1366 }
1367 
1368 void QgsLayoutItem::refreshOpacity( bool updateItem )
1369 {
1370  //data defined opacity set?
1372 
1373  // Set the QGraphicItem's opacity
1374  mEvaluatedOpacity = opacity / 100.0;
1375 
1377  {
1378  // item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
1379  // we have to rely on QGraphicsItem opacity to handle this
1380  setOpacity( mEvaluatedOpacity );
1381  }
1382 
1383  if ( updateItem )
1384  {
1385  update();
1386  }
1387 }
1388 
1389 void QgsLayoutItem::refreshFrame( bool updateItem )
1390 {
1391  if ( !mFrame )
1392  {
1393  setPen( Qt::NoPen );
1394  return;
1395  }
1396 
1397  //data defined stroke color set?
1398  bool ok = false;
1399  QColor frameColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::FrameColor, createExpressionContext(), mFrameColor, &ok );
1400  QPen itemPen;
1401  if ( ok )
1402  {
1403  itemPen = QPen( frameColor );
1404  }
1405  else
1406  {
1407  itemPen = QPen( mFrameColor );
1408  }
1409  itemPen.setJoinStyle( mFrameJoinStyle );
1410 
1411  if ( mLayout )
1412  itemPen.setWidthF( mLayout->convertToLayoutUnits( mFrameWidth ) );
1413  else
1414  itemPen.setWidthF( mFrameWidth.length() );
1415 
1416  setPen( itemPen );
1417 
1418  if ( updateItem )
1419  {
1420  update();
1421  }
1422 }
1423 
1425 {
1426  //data defined fill color set?
1427  bool ok = false;
1429  if ( ok )
1430  {
1431  setBrush( QBrush( backgroundColor, Qt::SolidPattern ) );
1432  }
1433  else
1434  {
1435  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1436  }
1437  if ( updateItem )
1438  {
1439  update();
1440  }
1441 }
1442 
1444 {
1445  QPainter::CompositionMode blendMode = mBlendMode;
1446 
1447  //data defined blend mode set?
1448  bool ok = false;
1450  if ( ok && !blendStr.isEmpty() )
1451  {
1452  QString blendstr = blendStr.trimmed();
1453  QPainter::CompositionMode blendModeD = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1454  blendMode = blendModeD;
1455  }
1456 
1457  // Update the item effect to use the new blend mode
1458  mEffect->setCompositionMode( blendMode );
1459 }
1460 
static QgsLayoutSize decodeSize(const QString &string)
Decodes a size from a string.
The class is used as a container of context for various read/write operations on other objects...
QgsLayoutPoint positionWithUnits() const
Returns the item&#39;s current position, including units.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
static double scaleFactorFromItemStyle(const QStyleOptionGraphicsItem *style)
Extracts the scale factor from an item style.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
static void multiplyOpacity(QImage &image, double factor)
Multiplies opacity of image pixel values by a factor.
Portrait orientation.
QString encodePoint() const
Encodes the layout point to a string.
QgsLayoutItemRenderContext(QgsRenderContext &context, double viewScaleFactor=1.0)
Constructor for QgsLayoutItemRenderContext.
QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects&#39; current state.
void beginCommand(const QString &commandText, UndoCommand command=UndoNone)
Starts new undo command for this item.
QgsLayoutSize applyDataDefinedSize(const QgsLayoutSize &size)
Applies any present data defined size overrides to the specified layout size.
int type() const override
Returns a unique graphics item type identifier.
virtual bool containsAdvancedEffects() const
Returns true if the item contains contents with blend modes or transparency effects which can only be...
Lower left corner of item.
bool readObjectPropertiesFromElement(const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context)
Sets object properties from a DOM element.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void refreshBackgroundColor(bool updateItem=true)
Refresh item&#39;s background color, considering data defined colors.
Base class for commands to undo/redo layout and layout object changes.
Landscape orientation.
void setY(const double y)
Sets y coordinate of point.
virtual QRectF rectWithFrame() const
Returns the item&#39;s rectangular bounds, including any bleed caused by the item&#39;s frame.
UndoCommand
Layout item undo commands, used for collapsing undo commands.
virtual double estimatedFrameBleed() const
Returns the estimated amount the item&#39;s frame bleeds outside the item&#39;s actual rectangle.
void refreshItemPosition()
Refreshes an item&#39;s position by rechecking it against any possible overrides such as data defined pos...
QgsUnitTypes::LayoutUnit units() const
Returns the native units for the layout.
Definition: qgslayout.h:328
virtual void setMinimumSize(const QgsLayoutSize &size)
Sets the minimum allowed size for the layout item.
Exclude item from exports.
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
QPointF positionAtReferencePoint(ReferencePoint reference) const
Returns the current position (in layout units) of a reference point for the item. ...
bool isGroupMember() const
Returns true if the item is part of a QgsLayoutItemGroup group.
Upper center of item.
bool frameEnabled() const
Returns true if the item includes a frame.
Y position on page.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
A container for grouping several QgsLayoutItems.
void flagsChanged(QgsLayoutRenderContext::Flags flags)
Emitted whenever the context&#39;s flags change.
virtual void setSelected(bool selected)
Sets whether the item should be selected.
virtual void setFrameStrokeWidth(QgsLayoutMeasurement width)
Sets the frame stroke width.
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
QgsLayoutPoint pagePositionWithUnits() const
Returns the item&#39;s position (in item units) relative to the top left corner of its current page...
virtual QgsLayoutSize fixedSize() const
Returns the fixed size of the item, if applicable, or an empty size if item can be freely resized...
void attemptMoveBy(double deltaX, double deltaY)
Attempts to shift the item&#39;s position by a specified deltaX and deltaY, in layout units...
void setFrameStrokeColor(const QColor &color)
Sets the frame stroke color.
Stores metadata about one layout item class.
Lower right corner of item.
QPointF adjustPointForReferencePosition(QPointF point, QSizeF size, ReferencePoint reference) const
Adjusts the specified point at which a reference position of the item sits and returns the top left c...
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void cancelCommand()
Cancels the current item command and discards it.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
This class provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
ReferencePoint
Fixed position reference point.
static QgsExpressionContextScope * layoutItemScope(const QgsLayoutItem *item)
Creates a new scope which contains variables and functions relating to a QgsLayoutItem.
void refreshItemSize()
Refreshes an item&#39;s size by rechecking it against any possible item fixed or minimum sizes...
void frameChanged()
Emitted if the item&#39;s frame style changes.
void sizePositionChanged()
Emitted when the item&#39;s size or position changes.
bool writeXml(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores the item state in a DOM element.
void setX(const double x)
Sets the x coordinate of point.
virtual void drawFrame(QgsRenderContext &context)
Draws the frame around the item.
X position on page.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the point.
A QGraphicsEffect subclass used for rendering layout items onto a scene with custom composition modes...
virtual void invalidateCache()
Forces a deferred update of any cached image the item uses.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application&#39;s layout item registry, used for layout item types.
virtual void setId(const QString &id)
Set the item&#39;s id name.
void setExcludeFromExports(bool exclude)
Sets whether the item should be excluded from layout exports and prints.
QgsPropertyCollection mDataDefinedProperties
virtual QgsLayoutSize minimumSize() const
Returns the minimum allowed size of the item, if applicable, or an empty size if item can be freely r...
void setLocked(bool locked)
Sets whether the item is locked, preventing mouse interactions with the item.
virtual void setFixedSize(const QgsLayoutSize &size)
Sets a fixed size for the layout item, which prevents it from being freely resized.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
virtual bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context)
Sets item state from a DOM element.
Upper right corner of item.
void endCommand()
Completes the current item command and push it onto the layout&#39;s undo stack.
void setFrameJoinStyle(Qt::PenJoinStyle style)
Sets the join style used when drawing the item&#39;s frame.
virtual void drawBackground(QgsRenderContext &context)
Draws the background for the item.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Handles preparing a paint surface for the layout item and painting the item&#39;s content.
double itemRotation() const
Returns the current rotation for the item, in degrees clockwise.
Lower center of item.
void setBlendMode(QPainter::CompositionMode mode)
Sets the item&#39;s composition blending mode.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item&#39;s position and size to match the passed rect in layout coordinates...
Middle right of item.
QColor backgroundColor() const
Returns the background color for this item.
double y() const
Returns y coordinate of point.
virtual void rotateItem(double angle, QPointF transformOrigin)
Rotates the item by a specified angle in degrees clockwise around a specified reference point...
virtual void cleanup()
Called just before a batch of items are deleted, allowing them to run cleanup tasks.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
QPointer< QgsLayout > mLayout
virtual void attemptResize(const QgsLayoutSize &size, bool includesFrame=false)
Attempts to resize the item to a specified target size.
Minimum z value for items.
Definition: qgslayout.h:59
virtual void setItemRotation(double rotation, bool adjustPosition=true)
Sets the layout item&#39;s rotation, in degrees clockwise.
double x() const
Returns x coordinate of point.
int page() const
Returns the page the item is currently on, with the first page returning 0.
double itemOpacity() const
Returns the item&#39;s opacity.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the size.
QString id() const
Returns the item&#39;s ID name.
static QPainter::CompositionMode decodeBlendMode(const QString &s)
QgsLayoutPoint topLeftToReferencePoint(const QgsLayoutPoint &point) const
Returns the position for the reference point of the item, if the top-left of the item was placed at t...
Upper left corner of item.
QPainter::CompositionMode blendMode() const
Returns the item&#39;s composition blending mode.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
virtual bool requiresRasterization() const
Returns true if the item is drawn in such a way that forces the whole layout to be rasterized when ex...
void setHeight(const double height)
Sets the height for the size.
Definition: qgslayoutsize.h:97
virtual Flags itemFlags() const
Returns the item&#39;s flags, which indicate how the item behaves.
virtual void setVisibility(bool visible)
Sets whether the item is visible.
QPointF toQPointF() const
Converts the layout point to a QPointF.
void setParentGroup(QgsLayoutItemGroup *group)
Sets the item&#39;s parent group.
virtual void drawDebugRect(QPainter *painter)
Draws a debugging rectangle of the item&#39;s current bounds within the specified painter.
virtual void redraw()
Triggers a redraw (update) of the item.
Use antialiasing when drawing items.
bool readXml(const QDomElement &itemElement, const QDomDocument &document, const QgsReadWriteContext &context)
Sets the item state from a DOM element.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
Middle left of item.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:43
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
QPointF pagePos() const
Returns the item&#39;s position (in layout units) relative to the top left corner of its current page...
virtual QString displayName() const
Gets item display name.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
bool writeObjectPropertiesToElement(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores object properties within an XML DOM element.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
void lockChanged()
Emitted if the item&#39;s lock status changes.
Contains information about the context of a rendering operation.
Force output in vector format where possible, even if items require rasterization to keep their corre...
double length() const
Returns the length of the measurement.
QPainter * painter()
Returns the destination QPainter for the render operation.
static QgsPageSizeRegistry * pageSizeRegistry()
Returns the application&#39;s page size registry, used for managing layout page sizes.
~QgsLayoutItem() override
A named page size for layouts.
Center of item.
Enable advanced effects such as blend modes.
virtual void setMoveContentPreviewOffset(double dx, double dy)
Sets temporary offset for the item, by a specified dx and dy in layout units.
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void moveContent(double dx, double dy)
Moves the content of the item, by a specified dx and dy in layout units.
Preset paper size for composition.
virtual void attemptMove(const QgsLayoutPoint &point, bool useReferencePoint=true, bool includesFrame=false, int page=-1)
Attempts to move the item to a specified point.
void setReferencePoint(ReferencePoint point)
Sets the reference point for positioning of the layout item.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
virtual QString uuid() const
Returns the item identification string.
Orientation
Page orientiation.
void refreshFrame(bool updateItem=true)
Refresh item&#39;s frame, considering data defined colors and frame size.
virtual QSizeF applyItemSizeConstraint(QSizeF targetSize)
Applies any item-specific size constraint handling to a given targetSize in layout units...
void refreshOpacity(bool updateItem=true)
Refresh item&#39;s opacity, considering data defined opacity.
virtual void zoomContent(double factor, QPointF point)
Zooms content of item.
virtual bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores item state within an XML DOM element.
bool excludeFromExports() const
Returns whether the item should be excluded from layout exports and prints.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
#define CACHE_SIZE_LIMIT
A base class for objects which belong to a layout.
Debug/testing mode, items are drawn as solid rectangles.
LayoutUnit
Layout measurement units.
Definition: qgsunittypes.h:124
virtual void draw(QgsLayoutItemRenderContext &context)=0
Draws the item&#39;s contents using the specified item render context.
Item overrides the default layout item painting method.
void refreshBlendMode()
Refresh item&#39;s blend mode, considering data defined blend mode.
static double normalizedAngle(double angle, bool allowNegative=false)
Ensures that an angle (in degrees) is in the range 0 <= angle < 360.
virtual void refresh()
Refreshes the object, causing a recalculation of any property overrides.
QString encodeMeasurement() const
Encodes the layout measurement to a string.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
void refreshItemRotation(QPointF *origin=nullptr)
Refreshes an item&#39;s rotation by rechecking it against any possible overrides such as data defined rot...
void rotationChanged(double newRotation)
Emitted on item rotation change.
void changed()
Emitted when the object&#39;s properties change.
QgsLayoutSize size
Page size.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
QgsLayoutItemGroup * parentGroup() const
Returns the item&#39;s parent group, if the item is part of a QgsLayoutItemGroup group.
DataDefinedProperty
Data defined properties for different item types.
static QgsLayoutItemPage::Orientation decodePaperOrientation(const QString &string, bool &ok)
Decodes a string representing a paper orientation and returns the decoded orientation.
void refresh() override
Refreshes the item, causing a recalculation of any property overrides and recalculation of its positi...
QString encodeSize() const
Encodes the layout size to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void setItemOpacity(double opacity)
Sets the item&#39;s opacity.
static QgsLayoutPoint decodePoint(const QString &string)
Decodes a point from a string.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsAbstractLayoutUndoCommand * createCommand(const QString &text, int id, QUndoCommand *parent=nullptr) override
Creates a new layout undo command with the specified text and parent.
void setWidth(const double width)
Sets the width for the size.
Definition: qgslayoutsize.h:83
void setBackgroundColor(const QColor &color)
Sets the background color for this item.
Item representing the paper in a layout.
All properties for item.
double width() const
Returns the width of the size.
Definition: qgslayoutsize.h:76