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