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