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