QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  bool previewRender = !mLayout || mLayout->renderContext().isPreviewRender();
301  double destinationDpi = previewRender ? QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter ) * 25.4 : mLayout->renderContext().dpi();
302  bool useImageCache = false;
303  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  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  QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
345  QgsScopedQPainterState painterState( painter );
346  preparePainter( painter );
347  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  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  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  QgsScopedQPainterState painterState( painter );
398  preparePainter( painter );
399  QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( mLayout, painter, destinationDpi );
401  drawBackground( context );
402 
403  // scale painter from mm to dots
404  painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
405  double viewScale = QgsLayoutUtils::scaleFactorFromItemStyle( itemStyle, painter );
406  QgsLayoutItemRenderContext itemRenderContext( context, viewScale );
407  draw( itemRenderContext );
408 
409  painter->scale( context.scaleFactor(), context.scaleFactor() );
410  drawFrame( context );
411  }
412 }
413 
415 {
416  if ( point == mReferencePoint )
417  {
418  return;
419  }
420 
421  mReferencePoint = point;
422 
423  //also need to adjust stored position
424  updateStoredItemPosition();
426 }
427 
428 void QgsLayoutItem::attemptResize( const QgsLayoutSize &s, bool includesFrame )
429 {
430  if ( !mLayout )
431  {
432  mItemSize = s;
433  setRect( 0, 0, s.width(), s.height() );
434  return;
435  }
436 
437  QgsLayoutSize size = s;
438 
439  if ( includesFrame )
440  {
441  //adjust position to account for frame size
442  double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), size.units() ).length();
443  size.setWidth( size.width() - 2 * bleed );
444  size.setHeight( size.height() - 2 * bleed );
445  }
446 
447  QgsLayoutSize evaluatedSize = applyDataDefinedSize( size );
448  QSizeF targetSizeLayoutUnits = mLayout->convertToLayoutUnits( evaluatedSize );
449  QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
450  actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
451  actualSizeLayoutUnits = applyItemSizeConstraint( actualSizeLayoutUnits );
452 
453  if ( actualSizeLayoutUnits == rect().size() )
454  {
455  return;
456  }
457 
458  QgsLayoutSize actualSizeTargetUnits = mLayout->convertFromLayoutUnits( actualSizeLayoutUnits, size.units() );
459  mItemSize = actualSizeTargetUnits;
460 
461  setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
463  emit sizePositionChanged();
464 }
465 
466 void QgsLayoutItem::attemptMove( const QgsLayoutPoint &p, bool useReferencePoint, bool includesFrame, int page )
467 {
468  if ( !mLayout )
469  {
470  mItemPosition = p;
471  setPos( p.toQPointF() );
472  return;
473  }
474 
475  QgsLayoutPoint point = p;
476  if ( page >= 0 )
477  {
478  point = mLayout->pageCollection()->pagePositionToAbsolute( page, p );
479  }
480 
481  if ( includesFrame )
482  {
483  //adjust position to account for frame size
484  double bleed = mLayout->convertFromLayoutUnits( estimatedFrameBleed(), point.units() ).length();
485  point.setX( point.x() + bleed );
486  point.setY( point.y() + bleed );
487  }
488 
489  QgsLayoutPoint evaluatedPoint = point;
490  if ( !useReferencePoint )
491  {
492  evaluatedPoint = topLeftToReferencePoint( point );
493  }
494 
495  evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
496  QPointF evaluatedPointLayoutUnits = mLayout->convertToLayoutUnits( evaluatedPoint );
497  QPointF topLeftPointLayoutUnits = adjustPointForReferencePosition( evaluatedPointLayoutUnits, rect().size(), mReferencePoint );
498  if ( topLeftPointLayoutUnits == scenePos() && point.units() == mItemPosition.units() )
499  {
500  //TODO - add test for second condition
501  return;
502  }
503 
504  QgsLayoutPoint referencePointTargetUnits = mLayout->convertFromLayoutUnits( evaluatedPointLayoutUnits, point.units() );
505  mItemPosition = referencePointTargetUnits;
506  setScenePos( topLeftPointLayoutUnits );
507  emit sizePositionChanged();
508 }
509 
510 void QgsLayoutItem::attemptSetSceneRect( const QRectF &rect, bool includesFrame )
511 {
512  QPointF newPos = rect.topLeft();
513 
514  blockSignals( true );
515  // translate new size to current item units
516  QgsLayoutSize newSize = mLayout->convertFromLayoutUnits( rect.size(), mItemSize.units() );
517  attemptResize( newSize, includesFrame );
518 
519  // translate new position to current item units
520  QgsLayoutPoint itemPos = mLayout->convertFromLayoutUnits( newPos, mItemPosition.units() );
521  attemptMove( itemPos, false, includesFrame );
522  blockSignals( false );
523  emit sizePositionChanged();
524 }
525 
526 void QgsLayoutItem::attemptMoveBy( double deltaX, double deltaY )
527 {
528  if ( !mLayout )
529  {
530  moveBy( deltaX, deltaY );
531  return;
532  }
533 
534  QgsLayoutPoint itemPos = positionWithUnits();
535  QgsLayoutPoint deltaPos = mLayout->convertFromLayoutUnits( QPointF( deltaX, deltaY ), itemPos.units() );
536  itemPos.setX( itemPos.x() + deltaPos.x() );
537  itemPos.setY( itemPos.y() + deltaPos.y() );
538  attemptMove( itemPos );
539 }
540 
542 {
543  if ( !mLayout )
544  return -1;
545 
546  return mLayout->pageCollection()->pageNumberForPoint( pos() );
547 }
548 
549 QPointF QgsLayoutItem::pagePos() const
550 {
551  QPointF p = positionAtReferencePoint( mReferencePoint );
552 
553  if ( !mLayout )
554  return p;
555 
556  // try to get page
557  QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page() );
558  if ( !pageItem )
559  return p;
560 
561  p.ry() -= pageItem->pos().y();
562  return p;
563 }
564 
566 {
567  QPointF p = pagePos();
568  if ( !mLayout )
569  return QgsLayoutPoint( p );
570 
571  return mLayout->convertFromLayoutUnits( p, mItemPosition.units() );
572 }
573 
574 void QgsLayoutItem::setScenePos( const QPointF destinationPos )
575 {
576  //since setPos does not account for item rotation, use difference between
577  //current scenePos (which DOES account for rotation) and destination pos
578  //to calculate how much the item needs to move
579  if ( auto *lParentItem = parentItem() )
580  setPos( pos() + ( destinationPos - scenePos() ) + lParentItem->scenePos() );
581  else
582  setPos( pos() + ( destinationPos - scenePos() ) );
583 }
584 
585 bool QgsLayoutItem::shouldBlockUndoCommands() const
586 {
587  return !mLayout || mLayout != scene() || mBlockUndoCommands;
588 }
589 
591 {
593  return false;
594 
595  if ( !mLayout || mLayout->renderContext().isPreviewRender() )
596  {
597  //preview mode so OK to draw item
598  return true;
599  }
600 
601  //exporting layout, so check if item is excluded from exports
602  return !mEvaluatedExcludeFromExports;
603 }
604 
606 {
607  return mItemRotation;
608 }
609 
610 bool QgsLayoutItem::writeXml( QDomElement &parentElement, QDomDocument &doc, const QgsReadWriteContext &context ) const
611 {
612  QDomElement element = doc.createElement( QStringLiteral( "LayoutItem" ) );
613  element.setAttribute( QStringLiteral( "type" ), QString::number( type() ) );
614 
615  element.setAttribute( QStringLiteral( "uuid" ), mUuid );
616  element.setAttribute( QStringLiteral( "templateUuid" ), mUuid );
617  element.setAttribute( QStringLiteral( "id" ), mId );
618  element.setAttribute( QStringLiteral( "referencePoint" ), QString::number( static_cast< int >( mReferencePoint ) ) );
619  element.setAttribute( QStringLiteral( "position" ), mItemPosition.encodePoint() );
620  element.setAttribute( QStringLiteral( "positionOnPage" ), pagePositionWithUnits().encodePoint() );
621  element.setAttribute( QStringLiteral( "size" ), mItemSize.encodeSize() );
622  element.setAttribute( QStringLiteral( "itemRotation" ), QString::number( mItemRotation ) );
623  element.setAttribute( QStringLiteral( "groupUuid" ), mParentGroupUuid );
624 
625  element.setAttribute( QStringLiteral( "zValue" ), QString::number( zValue() ) );
626  element.setAttribute( QStringLiteral( "visibility" ), isVisible() );
627  //position lock for mouse moves/resizes
628  if ( mIsLocked )
629  {
630  element.setAttribute( QStringLiteral( "positionLock" ), QStringLiteral( "true" ) );
631  }
632  else
633  {
634  element.setAttribute( QStringLiteral( "positionLock" ), QStringLiteral( "false" ) );
635  }
636 
637  //frame
638  if ( mFrame )
639  {
640  element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "true" ) );
641  }
642  else
643  {
644  element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "false" ) );
645  }
646 
647  //background
648  if ( mBackground )
649  {
650  element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "true" ) );
651  }
652  else
653  {
654  element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "false" ) );
655  }
656 
657  //frame color
658  QDomElement frameColorElem = doc.createElement( QStringLiteral( "FrameColor" ) );
659  frameColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFrameColor.red() ) );
660  frameColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFrameColor.green() ) );
661  frameColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFrameColor.blue() ) );
662  frameColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFrameColor.alpha() ) );
663  element.appendChild( frameColorElem );
664  element.setAttribute( QStringLiteral( "outlineWidthM" ), mFrameWidth.encodeMeasurement() );
665  element.setAttribute( QStringLiteral( "frameJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );
666 
667  //background color
668  QDomElement bgColorElem = doc.createElement( QStringLiteral( "BackgroundColor" ) );
669  bgColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mBackgroundColor.red() ) );
670  bgColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mBackgroundColor.green() ) );
671  bgColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mBackgroundColor.blue() ) );
672  bgColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mBackgroundColor.alpha() ) );
673  element.appendChild( bgColorElem );
674 
675  //blend mode
676  element.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
677 
678  //opacity
679  element.setAttribute( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
680 
681  element.setAttribute( QStringLiteral( "excludeFromExports" ), mExcludeFromExports );
682 
683  writeObjectPropertiesToElement( element, doc, context );
684 
685  writePropertiesToElement( element, doc, context );
686  parentElement.appendChild( element );
687 
688  return true;
689 }
690 
691 bool QgsLayoutItem::readXml( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
692 {
693  if ( element.nodeName() != QLatin1String( "LayoutItem" ) )
694  {
695  return false;
696  }
697 
698  readObjectPropertiesFromElement( element, doc, context );
699 
700  mBlockUndoCommands = true;
701  mUuid = element.attribute( QStringLiteral( "uuid" ), QUuid::createUuid().toString() );
702  setId( element.attribute( QStringLiteral( "id" ) ) );
703  mReferencePoint = static_cast< ReferencePoint >( element.attribute( QStringLiteral( "referencePoint" ) ).toInt() );
704  setItemRotation( element.attribute( QStringLiteral( "itemRotation" ), QStringLiteral( "0" ) ).toDouble() );
705  attemptMove( QgsLayoutPoint::decodePoint( element.attribute( QStringLiteral( "position" ) ) ) );
706  attemptResize( QgsLayoutSize::decodeSize( element.attribute( QStringLiteral( "size" ) ) ) );
707 
708  mParentGroupUuid = element.attribute( QStringLiteral( "groupUuid" ) );
709  if ( !mParentGroupUuid.isEmpty() )
710  {
711  if ( QgsLayoutItemGroup *group = parentGroup() )
712  {
713  group->addItem( this );
714  }
715  }
716  mTemplateUuid = element.attribute( QStringLiteral( "templateUuid" ) );
717 
718  //position lock for mouse moves/resizes
719  QString positionLock = element.attribute( QStringLiteral( "positionLock" ) );
720  if ( positionLock.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
721  {
722  setLocked( true );
723  }
724  else
725  {
726  setLocked( false );
727  }
728  //visibility
729  setVisibility( element.attribute( QStringLiteral( "visibility" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
730  setZValue( element.attribute( QStringLiteral( "zValue" ) ).toDouble() );
731 
732  //frame
733  QString frame = element.attribute( QStringLiteral( "frame" ) );
734  if ( frame.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
735  {
736  mFrame = true;
737  }
738  else
739  {
740  mFrame = false;
741  }
742 
743  //frame
744  QString background = element.attribute( QStringLiteral( "background" ) );
745  if ( background.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
746  {
747  mBackground = true;
748  }
749  else
750  {
751  mBackground = false;
752  }
753 
754  //pen
755  mFrameWidth = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "outlineWidthM" ) ) );
756  mFrameJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( element.attribute( QStringLiteral( "frameJoinStyle" ), QStringLiteral( "miter" ) ) );
757  QDomNodeList frameColorList = element.elementsByTagName( QStringLiteral( "FrameColor" ) );
758  if ( !frameColorList.isEmpty() )
759  {
760  QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
761  bool redOk = false;
762  bool greenOk = false;
763  bool blueOk = false;
764  bool alphaOk = false;
765  int penRed, penGreen, penBlue, penAlpha;
766 
767  penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
768  penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
769  penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
770  penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
771 
772  if ( redOk && greenOk && blueOk && alphaOk )
773  {
774  mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
775  }
776  }
777  refreshFrame( false );
778 
779  //brush
780  QDomNodeList bgColorList = element.elementsByTagName( QStringLiteral( "BackgroundColor" ) );
781  if ( !bgColorList.isEmpty() )
782  {
783  QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
784  bool redOk, greenOk, blueOk, alphaOk;
785  int bgRed, bgGreen, bgBlue, bgAlpha;
786  bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
787  bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
788  bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
789  bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
790  if ( redOk && greenOk && blueOk && alphaOk )
791  {
792  mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
793  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
794  }
795  //apply any data defined settings
796  refreshBackgroundColor( false );
797  }
798 
799  //blend mode
800  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( element.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) ) );
801 
802  //opacity
803  if ( element.hasAttribute( QStringLiteral( "opacity" ) ) )
804  {
805  setItemOpacity( element.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) ).toDouble() );
806  }
807  else
808  {
809  setItemOpacity( 1.0 - element.attribute( QStringLiteral( "transparency" ), QStringLiteral( "0" ) ).toInt() / 100.0 );
810  }
811 
812  mExcludeFromExports = element.attribute( QStringLiteral( "excludeFromExports" ), QStringLiteral( "0" ) ).toInt();
813  mEvaluatedExcludeFromExports = mExcludeFromExports;
814 
815  bool result = readPropertiesFromElement( element, doc, context );
816 
817  mBlockUndoCommands = false;
818 
819  emit changed();
820  update();
821  return result;
822 }
823 
825 {
826 }
827 
828 QgsAbstractLayoutUndoCommand *QgsLayoutItem::createCommand( const QString &text, int id, QUndoCommand *parent )
829 {
830  return new QgsLayoutItemUndoCommand( this, text, id, parent );
831 }
832 
833 void QgsLayoutItem::setFrameEnabled( bool drawFrame )
834 {
835  if ( drawFrame == mFrame )
836  {
837  //no change
838  return;
839  }
840 
841  mFrame = drawFrame;
842  refreshFrame( true );
843  emit frameChanged();
844 }
845 
846 void QgsLayoutItem::setFrameStrokeColor( const QColor &color )
847 {
848  if ( mFrameColor == color )
849  {
850  //no change
851  return;
852  }
853  mFrameColor = color;
854  // apply any datadefined overrides
855  refreshFrame( true );
856  emit frameChanged();
857 }
858 
860 {
861  if ( mFrameWidth == width )
862  {
863  //no change
864  return;
865  }
866  mFrameWidth = width;
867  refreshFrame();
868  emit frameChanged();
869 }
870 
871 void QgsLayoutItem::setFrameJoinStyle( const Qt::PenJoinStyle style )
872 {
873  if ( mFrameJoinStyle == style )
874  {
875  //no change
876  return;
877  }
878  mFrameJoinStyle = style;
879 
880  QPen itemPen = pen();
881  itemPen.setJoinStyle( mFrameJoinStyle );
882  setPen( itemPen );
883  emit frameChanged();
884 }
885 
886 void QgsLayoutItem::setBackgroundEnabled( bool drawBackground )
887 {
888  mBackground = drawBackground;
889  update();
890 }
891 
892 void QgsLayoutItem::setBackgroundColor( const QColor &color )
893 {
894  mBackgroundColor = color;
895  // apply any datadefined overrides
896  refreshBackgroundColor( true );
897 }
898 
899 void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
900 {
901  mBlendMode = mode;
902  // Update the item effect to use the new blend mode
904 }
905 
906 void QgsLayoutItem::setItemOpacity( double opacity )
907 {
908  mOpacity = opacity;
909  refreshOpacity( mItemCachedImage.isNull() );
910  if ( !mItemCachedImage.isNull() )
911  invalidateCache();
912 }
913 
915 {
916  return mExcludeFromExports;
917 }
918 
920 {
921  mExcludeFromExports = exclude;
923 }
924 
926 {
927  return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
928 }
929 
931 {
932  return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) ||
933  blendMode() != QPainter::CompositionMode_SourceOver;
934 }
935 
937 {
938  if ( !frameEnabled() )
939  {
940  return 0;
941  }
942 
943  return pen().widthF() / 2.0;
944 }
945 
947 {
948  double frameBleed = estimatedFrameBleed();
949  return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
950 }
951 
952 void QgsLayoutItem::moveContent( double, double )
953 {
954 
955 }
956 
958 {
959 
960 }
961 
962 void QgsLayoutItem::zoomContent( double, QPointF )
963 {
964 
965 }
966 
967 void QgsLayoutItem::beginCommand( const QString &commandText, UndoCommand command )
968 {
969  if ( !mLayout )
970  return;
971 
972  mLayout->undoStack()->beginCommand( this, commandText, command );
973 }
974 
976 {
977  if ( mLayout )
978  mLayout->undoStack()->endCommand();
979 }
980 
982 {
983  if ( mLayout )
984  mLayout->undoStack()->cancelCommand();
985 }
986 
987 QgsLayoutPoint QgsLayoutItem::applyDataDefinedPosition( const QgsLayoutPoint &position )
988 {
989  if ( !mLayout )
990  {
991  return position;
992  }
993 
995  double evaluatedX = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PositionX, context, position.x() );
996  double evaluatedY = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PositionY, context, position.y() );
997  return QgsLayoutPoint( evaluatedX, evaluatedY, position.units() );
998 }
999 
1000 void QgsLayoutItem::applyDataDefinedOrientation( double &width, double &height, const QgsExpressionContext &context )
1001 {
1002  bool ok = false;
1003  QString orientationString = mDataDefinedProperties.valueAsString( QgsLayoutObject::PaperOrientation, context, QString(), &ok );
1004  if ( ok && !orientationString.isEmpty() )
1005  {
1006  QgsLayoutItemPage::Orientation orientation = QgsLayoutUtils::decodePaperOrientation( orientationString, ok );
1007  if ( ok )
1008  {
1009  double heightD = 0.0, widthD = 0.0;
1010  switch ( orientation )
1011  {
1013  {
1014  heightD = std::max( height, width );
1015  widthD = std::min( height, width );
1016  break;
1017  }
1019  {
1020  heightD = std::min( height, width );
1021  widthD = std::max( height, width );
1022  break;
1023  }
1024  }
1025  width = widthD;
1026  height = heightD;
1027  }
1028  }
1029 }
1030 
1032 {
1033  if ( !mLayout )
1034  {
1035  return size;
1036  }
1037 
1042  return size;
1043 
1044 
1046 
1047  // lowest priority is page size
1049  QgsPageSize matchedSize;
1050  double evaluatedWidth = size.width();
1051  double evaluatedHeight = size.height();
1052  if ( QgsApplication::pageSizeRegistry()->decodePageSize( pageSize, matchedSize ) )
1053  {
1054  QgsLayoutSize convertedSize = mLayout->renderContext().measurementConverter().convert( matchedSize.size, size.units() );
1055  evaluatedWidth = convertedSize.width();
1056  evaluatedHeight = convertedSize.height();
1057  }
1058 
1059  // highest priority is dd width/height
1060  evaluatedWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemWidth, context, evaluatedWidth );
1061  evaluatedHeight = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemHeight, context, evaluatedHeight );
1062 
1063  //which is finally overwritten by data defined orientation
1064  applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1065 
1066  return QgsLayoutSize( evaluatedWidth, evaluatedHeight, size.units() );
1067 }
1068 
1069 double QgsLayoutItem::applyDataDefinedRotation( const double rotation )
1070 {
1071  if ( !mLayout )
1072  {
1073  return rotation;
1074  }
1075 
1077  double evaluatedRotation = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::ItemRotation, context, rotation );
1078  return evaluatedRotation;
1079 }
1080 
1082 {
1083  //update data defined properties and update item to match
1084 
1085  //evaluate width and height first, since they may affect position if non-top-left reference point set
1086  if ( property == QgsLayoutObject::ItemWidth || property == QgsLayoutObject::ItemHeight ||
1087  property == QgsLayoutObject::AllProperties )
1088  {
1089  refreshItemSize();
1090  }
1091  if ( property == QgsLayoutObject::PositionX || property == QgsLayoutObject::PositionY ||
1092  property == QgsLayoutObject::AllProperties )
1093  {
1095  }
1096  if ( property == QgsLayoutObject::ItemRotation || property == QgsLayoutObject::AllProperties )
1097  {
1099  }
1100  if ( property == QgsLayoutObject::Opacity || property == QgsLayoutObject::AllProperties )
1101  {
1102  refreshOpacity( false );
1103  }
1104  if ( property == QgsLayoutObject::FrameColor || property == QgsLayoutObject::AllProperties )
1105  {
1106  refreshFrame( false );
1107  }
1108  if ( property == QgsLayoutObject::BackgroundColor || property == QgsLayoutObject::AllProperties )
1109  {
1110  refreshBackgroundColor( false );
1111  }
1112  if ( property == QgsLayoutObject::BlendMode || property == QgsLayoutObject::AllProperties )
1113  {
1114  refreshBlendMode();
1115  }
1117  {
1118  bool exclude = mExcludeFromExports;
1119  //data defined exclude from exports set?
1121  }
1122 
1123  update();
1124 }
1125 
1126 void QgsLayoutItem::setItemRotation( double angle, const bool adjustPosition )
1127 {
1128  if ( angle >= 360.0 || angle <= -360.0 )
1129  {
1130  angle = std::fmod( angle, 360.0 );
1131  }
1132 
1133  QPointF point = adjustPosition ? positionAtReferencePoint( QgsLayoutItem::Middle )
1134  : pos();
1135  double rotationRequired = angle - rotation();
1136  rotateItem( rotationRequired, point );
1137 
1138  mItemRotation = angle;
1139 }
1140 
1141 void QgsLayoutItem::updateStoredItemPosition()
1142 {
1143  QPointF layoutPosReferencePoint = positionAtReferencePoint( mReferencePoint );
1144  mItemPosition = mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.units() );
1145 }
1146 
1147 void QgsLayoutItem::rotateItem( const double angle, const QPointF transformOrigin )
1148 {
1149  double evaluatedAngle = angle + rotation();
1150  evaluatedAngle = QgsLayoutUtils::normalizedAngle( evaluatedAngle, true );
1151  mItemRotation = evaluatedAngle;
1152 
1153  QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1154 
1155  refreshItemRotation( &itemTransformOrigin );
1156 }
1157 
1159 {
1162  return context;
1163 }
1164 
1166 {
1167  Q_UNUSED( visitor );
1168  return true;
1169 }
1170 
1172 {
1173  return QgsGeometry();
1174 }
1175 
1177 {
1179  refreshItemSize();
1180 
1182 }
1183 
1185 {
1186  if ( !mItemCachedImage.isNull() )
1187  {
1188  mItemCachedImage = QImage();
1189  mItemCacheDpi = -1;
1190  update();
1191  }
1192 }
1193 
1195 {
1196  update();
1197 }
1198 
1199 void QgsLayoutItem::drawDebugRect( QPainter *painter )
1200 {
1201  if ( !painter )
1202  {
1203  return;
1204  }
1205 
1206  QgsScopedQPainterState painterState( painter );
1207  painter->setRenderHint( QPainter::Antialiasing, false );
1208  painter->setPen( Qt::NoPen );
1209  painter->setBrush( QColor( 100, 255, 100, 200 ) );
1210  painter->drawRect( rect() );
1211 }
1212 
1213 QPainterPath QgsLayoutItem::framePath() const
1214 {
1215  QPainterPath path;
1216  path.addRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1217  return path;
1218 }
1219 
1221 {
1222  if ( !mFrame || !context.painter() )
1223  return;
1224 
1225  QPainter *p = context.painter();
1226 
1227  QgsScopedQPainterState painterState( p );
1228 
1229  p->setPen( pen() );
1230  p->setBrush( Qt::NoBrush );
1231  context.setPainterFlagsUsingContext( p );
1232 
1233  p->drawPath( framePath() );
1234 }
1235 
1237 {
1238  if ( !mBackground || !context.painter() )
1239  return;
1240 
1241  QgsScopedQPainterState painterState( context.painter() );
1242 
1243  QPainter *p = context.painter();
1244  p->setBrush( brush() );
1245  p->setPen( Qt::NoPen );
1246  context.setPainterFlagsUsingContext( p );
1247 
1248  p->drawPath( framePath() );
1249 }
1250 
1252 {
1253  mFixedSize = size;
1254  refreshItemSize();
1255 }
1256 
1258 {
1259  mMinimumSize = size;
1260  refreshItemSize();
1261 }
1262 
1263 QSizeF QgsLayoutItem::applyItemSizeConstraint( const QSizeF targetSize )
1264 {
1265  return targetSize;
1266 }
1267 
1269 {
1270  attemptResize( mItemSize );
1271 }
1272 
1274 {
1275  attemptMove( mItemPosition );
1276 }
1277 
1278 QPointF QgsLayoutItem::itemPositionAtReferencePoint( const ReferencePoint reference, const QSizeF size ) const
1279 {
1280  switch ( reference )
1281  {
1282  case UpperMiddle:
1283  return QPointF( size.width() / 2.0, 0 );
1284  case UpperRight:
1285  return QPointF( size.width(), 0 );
1286  case MiddleLeft:
1287  return QPointF( 0, size.height() / 2.0 );
1288  case Middle:
1289  return QPointF( size.width() / 2.0, size.height() / 2.0 );
1290  case MiddleRight:
1291  return QPointF( size.width(), size.height() / 2.0 );
1292  case LowerLeft:
1293  return QPointF( 0, size.height() );
1294  case LowerMiddle:
1295  return QPointF( size.width() / 2.0, size.height() );
1296  case LowerRight:
1297  return QPointF( size.width(), size.height() );
1298  case UpperLeft:
1299  return QPointF( 0, 0 );
1300  }
1301  // no warnings
1302  return QPointF( 0, 0 );
1303 }
1304 
1305 QPointF QgsLayoutItem::adjustPointForReferencePosition( const QPointF position, const QSizeF size, const ReferencePoint reference ) const
1306 {
1307  QPointF itemPosition = mapFromScene( position ); //need to map from scene to handle item rotation
1308  QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1309  return mapToScene( adjustedPointInsideItem );
1310 }
1311 
1313 {
1314  QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1315  return mapToScene( pointWithinItem );
1316 }
1317 
1319 {
1320  QPointF topLeft = mLayout->convertToLayoutUnits( point );
1321  QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1322  return mLayout->convertFromLayoutUnits( refPoint, point.units() );
1323 }
1324 
1325 bool QgsLayoutItem::writePropertiesToElement( QDomElement &, QDomDocument &, const QgsReadWriteContext & ) const
1326 {
1327  return true;
1328 }
1329 
1330 bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &, const QDomDocument &, const QgsReadWriteContext & )
1331 {
1332 
1333  return true;
1334 }
1335 
1336 void QgsLayoutItem::initConnectionsToLayout()
1337 {
1338  if ( !mLayout )
1339  return;
1340 
1341 }
1342 
1343 void QgsLayoutItem::preparePainter( QPainter *painter )
1344 {
1345  if ( !painter || !painter->device() )
1346  {
1347  return;
1348  }
1349 
1350  painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1351 
1352 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
1353  painter->setRenderHint( QPainter::LosslessImageRendering, mLayout && mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagLosslessImageRendering ) );
1354 #endif
1355 }
1356 
1357 bool QgsLayoutItem::shouldDrawAntialiased() const
1358 {
1359  if ( !mLayout )
1360  {
1361  return true;
1362  }
1363  return mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagAntialiasing ) && !mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagDebug );
1364 }
1365 
1366 bool QgsLayoutItem::shouldDrawDebugRect() const
1367 {
1368  return mLayout && mLayout->renderContext().testFlag( QgsLayoutRenderContext::FlagDebug );
1369 }
1370 
1371 QSizeF QgsLayoutItem::applyMinimumSize( const QSizeF targetSize )
1372 {
1373  if ( !mLayout || minimumSize().isEmpty() )
1374  {
1375  return targetSize;
1376  }
1377  QSizeF minimumSizeLayoutUnits = mLayout->convertToLayoutUnits( minimumSize() );
1378  return targetSize.expandedTo( minimumSizeLayoutUnits );
1379 }
1380 
1381 QSizeF QgsLayoutItem::applyFixedSize( const QSizeF targetSize )
1382 {
1383  if ( !mLayout || fixedSize().isEmpty() )
1384  {
1385  return targetSize;
1386  }
1387 
1388  QSizeF size = targetSize;
1389  QSizeF fixedSizeLayoutUnits = mLayout->convertToLayoutUnits( fixedSize() );
1390  if ( fixedSizeLayoutUnits.width() > 0 )
1391  size.setWidth( fixedSizeLayoutUnits.width() );
1392  if ( fixedSizeLayoutUnits.height() > 0 )
1393  size.setHeight( fixedSizeLayoutUnits.height() );
1394 
1395  return size;
1396 }
1397 
1398 void QgsLayoutItem::refreshItemRotation( QPointF *origin )
1399 {
1400  double r = mItemRotation;
1401 
1402  //data defined rotation set?
1404 
1405  if ( qgsDoubleNear( r, rotation() ) && !origin )
1406  {
1407  return;
1408  }
1409 
1410  QPointF transformPoint = origin ? *origin : mapFromScene( positionAtReferencePoint( QgsLayoutItem::Middle ) );
1411 
1412  if ( !transformPoint.isNull() )
1413  {
1414  //adjustPosition set, so shift the position of the item so that rotation occurs around item center
1415  //create a line from the transform point to the item's origin, in scene coordinates
1416  QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1417  //rotate this line by the current rotation angle
1418  refLine.setAngle( refLine.angle() - r + rotation() );
1419  //get new end point of line - this is the new item position
1420  QPointF rotatedReferencePoint = refLine.p2();
1421  setPos( rotatedReferencePoint );
1422  }
1423 
1424  setTransformOriginPoint( 0, 0 );
1425  QGraphicsItem::setRotation( r );
1426 
1427  //adjust stored position of item to match scene pos of reference point
1428  updateStoredItemPosition();
1429  emit sizePositionChanged();
1430 
1431  emit rotationChanged( r );
1432 
1433  //update bounds of scene, since rotation may affect this
1434  mLayout->updateBounds();
1435 }
1436 
1437 void QgsLayoutItem::refreshOpacity( bool updateItem )
1438 {
1439  //data defined opacity set?
1441 
1442  // Set the QGraphicItem's opacity
1443  mEvaluatedOpacity = opacity / 100.0;
1444 
1446  {
1447  // item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
1448  // we have to rely on QGraphicsItem opacity to handle this
1449  setOpacity( mEvaluatedOpacity );
1450  }
1451 
1452  if ( updateItem )
1453  {
1454  update();
1455  }
1456 }
1457 
1458 void QgsLayoutItem::refreshFrame( bool updateItem )
1459 {
1460  if ( !mFrame )
1461  {
1462  setPen( Qt::NoPen );
1463  return;
1464  }
1465 
1466  //data defined stroke color set?
1467  bool ok = false;
1468  QColor frameColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::FrameColor, createExpressionContext(), mFrameColor, &ok );
1469  QPen itemPen;
1470  if ( ok )
1471  {
1472  itemPen = QPen( frameColor );
1473  }
1474  else
1475  {
1476  itemPen = QPen( mFrameColor );
1477  }
1478  itemPen.setJoinStyle( mFrameJoinStyle );
1479 
1480  if ( mLayout )
1481  itemPen.setWidthF( mLayout->convertToLayoutUnits( mFrameWidth ) );
1482  else
1483  itemPen.setWidthF( mFrameWidth.length() );
1484 
1485  setPen( itemPen );
1486 
1487  if ( updateItem )
1488  {
1489  update();
1490  }
1491 }
1492 
1494 {
1495  //data defined fill color set?
1496  bool ok = false;
1498  if ( ok )
1499  {
1500  setBrush( QBrush( backgroundColor, Qt::SolidPattern ) );
1501  }
1502  else
1503  {
1504  setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1505  }
1506  if ( updateItem )
1507  {
1508  update();
1509  }
1510 }
1511 
1513 {
1514  QPainter::CompositionMode blendMode = mBlendMode;
1515 
1516  //data defined blend mode set?
1517  bool ok = false;
1519  if ( ok && !blendStr.isEmpty() )
1520  {
1521  QString blendstr = blendStr.trimmed();
1522  QPainter::CompositionMode blendModeD = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1523  blendMode = blendModeD;
1524  }
1525 
1526  // Update the item effect to use the new blend mode
1527  mEffect->setCompositionMode( blendMode );
1528 }
1529 
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:124
static void multiplyOpacity(QImage &image, double factor)
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:1080
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1079
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:598
#define CACHE_SIZE_LIMIT
Contains details of a particular export layer relating to a layout item.