34 #include <QStyleOptionGraphicsItem>
37 #define CACHE_SIZE_LIMIT 5000
40 : mRenderContext( context )
41 , mViewScaleFactor( viewScaleFactor )
49 , QGraphicsRectItem( nullptr )
50 , mUuid( QUuid::createUuid().toString() )
55 setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption | QGraphicsItem::ItemIsSelectable );
57 setCacheMode( QGraphicsItem::DeviceCoordinateCache );
61 mItemPosition =
QgsLayoutPoint( scenePos().x(), scenePos().y(), initialUnits );
62 mItemSize =
QgsLayoutSize( rect().width(), rect().height(), initialUnits );
68 initConnectionsToLayout();
73 mLayoutManagesZValue =
true;
74 mLayout->itemsModel()->addItemAtTop(
this );
78 mLayoutManagesZValue =
false;
88 mEffect->setEnabled( flags & QgsLayoutRenderContext::FlagUseAdvancedEffects );
91 setGraphicsEffect( mEffect.get() );
101 if (
mLayout && mLayoutManagesZValue )
103 mLayout->itemsModel()->removeItem(
this );
110 if ( !
id().isEmpty() )
118 return tr(
"<%1>" ).arg( metadata->visibleName() );
121 return tr(
"<item>" );
131 return QgsLayoutItem::Flags();
141 if ( !shouldBlockUndoCommands() )
142 mLayout->undoStack()->beginCommand(
this, tr(
"Change Item ID" ) );
146 if ( !shouldBlockUndoCommands() )
147 mLayout->undoStack()->endCommand();
154 mLayout->itemsModel()->updateItemDisplayName(
this );
162 QGraphicsRectItem::setSelected( selected );
166 mLayout->itemsModel()->updateItemSelectStatus(
this );
172 if ( visible == isVisible() )
178 std::unique_ptr< QgsAbstractLayoutUndoCommand > command;
179 if ( !shouldBlockUndoCommands() )
181 command.reset(
createCommand( visible ? tr(
"Show Item" ) : tr(
"Hide Item" ), 0 ) );
182 command->saveBeforeState();
185 QGraphicsItem::setVisible( visible );
189 command->saveAfterState();
190 mLayout->undoStack()->push( command.release() );
196 mLayout->itemsModel()->updateItemVisibility(
this );
202 if (
locked == mIsLocked )
207 if ( !shouldBlockUndoCommands() )
208 mLayout->undoStack()->beginCommand(
this,
locked ? tr(
"Lock Item" ) : tr(
"Unlock Item" ) );
212 if ( !shouldBlockUndoCommands() )
213 mLayout->undoStack()->endCommand();
218 mLayout->itemsModel()->updateItemLockStatus(
this );
227 return !mParentGroupUuid.isEmpty() &&
mLayout &&
static_cast< bool >(
mLayout->itemByUuid( mParentGroupUuid ) );
232 if ( !
mLayout || mParentGroupUuid.isEmpty() )
235 return qobject_cast< QgsLayoutItemGroup * >(
mLayout->itemByUuid( mParentGroupUuid ) );
241 mParentGroupUuid.clear();
243 mParentGroupUuid = group->
uuid();
244 setFlag( QGraphicsItem::ItemIsSelectable, !
static_cast< bool>( group ) );
270 if ( !
mLayout ||
mLayout->renderContext().currentExportLayer() == -1 )
276 return mLayout->renderContext().currentExportLayer() < layers;
294 if ( shouldDrawDebugRect() )
300 const bool previewRender = !
mLayout ||
mLayout->renderContext().isPreviewRender();
302 const bool useImageCache =
false;
305 if ( useImageCache || forceRasterOutput )
307 double widthInPixels = 0;
308 double heightInPixels = 0;
318 widthInPixels = boundingRect().width() * layoutUnitsToPixels;
319 heightInPixels = boundingRect().height() * layoutUnitsToPixels;
326 if ( widthInPixels > heightInPixels )
330 heightInPixels /= scale;
336 widthInPixels /= scale;
338 destinationDpi = destinationDpi / scale;
341 if ( previewRender && !mItemCachedImage.isNull() &&
qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
346 preparePainter( painter );
347 const double cacheScale = destinationDpi / mItemCacheDpi;
349 painter->drawImage( boundingRect().x() * context.
scaleFactor() / cacheScale,
350 boundingRect().y() * context.
scaleFactor() / cacheScale, mItemCachedImage );
355 QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
356 image.fill( Qt::transparent );
357 image.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
358 image.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
359 QPainter p( &image );
361 preparePainter( &p );
373 draw( itemRenderContext );
384 painter->drawImage( boundingRect().x() * context.
scaleFactor(),
385 boundingRect().y() * context.
scaleFactor(), image );
389 mItemCacheDpi = destinationDpi;
390 mItemCachedImage = image;
398 preparePainter( painter );
408 draw( itemRenderContext );
417 if ( point == mReferencePoint )
422 mReferencePoint = point;
425 updateStoredItemPosition();
449 const QSizeF targetSizeLayoutUnits =
mLayout->convertToLayoutUnits( evaluatedSize );
450 QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
451 actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
454 if ( actualSizeLayoutUnits == rect().size() )
460 mItemSize = actualSizeTargetUnits;
462 setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
479 point =
mLayout->pageCollection()->pagePositionToAbsolute(
page, p );
486 point.
setX( point.
x() + bleed );
487 point.
setY( point.
y() + bleed );
491 if ( !useReferencePoint )
496 evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
497 const QPointF evaluatedPointLayoutUnits =
mLayout->convertToLayoutUnits( evaluatedPoint );
499 if ( topLeftPointLayoutUnits == scenePos() && point.
units() == mItemPosition.
units() )
506 mItemPosition = referencePointTargetUnits;
507 setScenePos( topLeftPointLayoutUnits );
513 const QPointF newPos = rect.topLeft();
515 blockSignals(
true );
523 blockSignals(
false );
531 moveBy( deltaX, deltaY );
537 itemPos.
setX( itemPos.
x() + deltaPos.
x() );
538 itemPos.
setY( itemPos.
y() + deltaPos.
y() );
547 return mLayout->pageCollection()->pageNumberForPoint( pos() );
562 p.ry() -= pageItem->pos().y();
572 return mLayout->convertFromLayoutUnits( p, mItemPosition.
units() );
575 void QgsLayoutItem::setScenePos(
const QPointF destinationPos )
580 if (
auto *lParentItem = parentItem() )
581 setPos( pos() + ( destinationPos - scenePos() ) + lParentItem->scenePos() );
583 setPos( pos() + ( destinationPos - scenePos() ) );
586 bool QgsLayoutItem::shouldBlockUndoCommands()
const
603 return !mEvaluatedExcludeFromExports;
608 return mItemRotation;
613 QDomElement element = doc.createElement( QStringLiteral(
"LayoutItem" ) );
614 element.setAttribute( QStringLiteral(
"type" ), QString::number(
type() ) );
616 element.setAttribute( QStringLiteral(
"uuid" ), mUuid );
617 element.setAttribute( QStringLiteral(
"templateUuid" ), mUuid );
618 element.setAttribute( QStringLiteral(
"id" ), mId );
619 element.setAttribute( QStringLiteral(
"referencePoint" ), QString::number(
static_cast< int >( mReferencePoint ) ) );
620 element.setAttribute( QStringLiteral(
"position" ), mItemPosition.
encodePoint() );
622 element.setAttribute( QStringLiteral(
"size" ), mItemSize.
encodeSize() );
623 element.setAttribute( QStringLiteral(
"itemRotation" ), QString::number( mItemRotation ) );
624 element.setAttribute( QStringLiteral(
"groupUuid" ), mParentGroupUuid );
626 element.setAttribute( QStringLiteral(
"zValue" ), QString::number( zValue() ) );
627 element.setAttribute( QStringLiteral(
"visibility" ), isVisible() );
631 element.setAttribute( QStringLiteral(
"positionLock" ), QStringLiteral(
"true" ) );
635 element.setAttribute( QStringLiteral(
"positionLock" ), QStringLiteral(
"false" ) );
641 element.setAttribute( QStringLiteral(
"frame" ), QStringLiteral(
"true" ) );
645 element.setAttribute( QStringLiteral(
"frame" ), QStringLiteral(
"false" ) );
651 element.setAttribute( QStringLiteral(
"background" ), QStringLiteral(
"true" ) );
655 element.setAttribute( QStringLiteral(
"background" ), QStringLiteral(
"false" ) );
659 QDomElement frameColorElem = doc.createElement( QStringLiteral(
"FrameColor" ) );
660 frameColorElem.setAttribute( QStringLiteral(
"red" ), QString::number( mFrameColor.red() ) );
661 frameColorElem.setAttribute( QStringLiteral(
"green" ), QString::number( mFrameColor.green() ) );
662 frameColorElem.setAttribute( QStringLiteral(
"blue" ), QString::number( mFrameColor.blue() ) );
663 frameColorElem.setAttribute( QStringLiteral(
"alpha" ), QString::number( mFrameColor.alpha() ) );
664 element.appendChild( frameColorElem );
665 element.setAttribute( QStringLiteral(
"outlineWidthM" ), mFrameWidth.
encodeMeasurement() );
669 QDomElement bgColorElem = doc.createElement( QStringLiteral(
"BackgroundColor" ) );
670 bgColorElem.setAttribute( QStringLiteral(
"red" ), QString::number( mBackgroundColor.red() ) );
671 bgColorElem.setAttribute( QStringLiteral(
"green" ), QString::number( mBackgroundColor.green() ) );
672 bgColorElem.setAttribute( QStringLiteral(
"blue" ), QString::number( mBackgroundColor.blue() ) );
673 bgColorElem.setAttribute( QStringLiteral(
"alpha" ), QString::number( mBackgroundColor.alpha() ) );
674 element.appendChild( bgColorElem );
680 element.setAttribute( QStringLiteral(
"opacity" ), QString::number( mOpacity ) );
682 element.setAttribute( QStringLiteral(
"excludeFromExports" ), mExcludeFromExports );
687 parentElement.appendChild( element );
694 if ( element.nodeName() != QLatin1String(
"LayoutItem" ) )
701 mBlockUndoCommands =
true;
702 mUuid = element.attribute( QStringLiteral(
"uuid" ), QUuid::createUuid().toString() );
703 setId( element.attribute( QStringLiteral(
"id" ) ) );
704 mReferencePoint =
static_cast< ReferencePoint >( element.attribute( QStringLiteral(
"referencePoint" ) ).toInt() );
705 setItemRotation( element.attribute( QStringLiteral(
"itemRotation" ), QStringLiteral(
"0" ) ).toDouble() );
709 mParentGroupUuid = element.attribute( QStringLiteral(
"groupUuid" ) );
710 if ( !mParentGroupUuid.isEmpty() )
714 group->addItem(
this );
717 mTemplateUuid = element.attribute( QStringLiteral(
"templateUuid" ) );
720 const QString positionLock = element.attribute( QStringLiteral(
"positionLock" ) );
721 if ( positionLock.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
730 setVisibility( element.attribute( QStringLiteral(
"visibility" ), QStringLiteral(
"1" ) ) != QLatin1String(
"0" ) );
731 setZValue( element.attribute( QStringLiteral(
"zValue" ) ).toDouble() );
734 const QString frame = element.attribute( QStringLiteral(
"frame" ) );
735 if ( frame.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
745 const QString background = element.attribute( QStringLiteral(
"background" ) );
746 if ( background.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
758 const QDomNodeList frameColorList = element.elementsByTagName( QStringLiteral(
"FrameColor" ) );
759 if ( !frameColorList.isEmpty() )
761 const QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
763 bool greenOk =
false;
765 bool alphaOk =
false;
766 int penRed, penGreen, penBlue, penAlpha;
768 penRed = frameColorElem.attribute( QStringLiteral(
"red" ) ).toDouble( &redOk );
769 penGreen = frameColorElem.attribute( QStringLiteral(
"green" ) ).toDouble( &greenOk );
770 penBlue = frameColorElem.attribute( QStringLiteral(
"blue" ) ).toDouble( &blueOk );
771 penAlpha = frameColorElem.attribute( QStringLiteral(
"alpha" ) ).toDouble( &alphaOk );
773 if ( redOk && greenOk && blueOk && alphaOk )
775 mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
781 const QDomNodeList bgColorList = element.elementsByTagName( QStringLiteral(
"BackgroundColor" ) );
782 if ( !bgColorList.isEmpty() )
784 const QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
785 bool redOk, greenOk, blueOk, alphaOk;
786 int bgRed, bgGreen, bgBlue, bgAlpha;
787 bgRed = bgColorElem.attribute( QStringLiteral(
"red" ) ).toDouble( &redOk );
788 bgGreen = bgColorElem.attribute( QStringLiteral(
"green" ) ).toDouble( &greenOk );
789 bgBlue = bgColorElem.attribute( QStringLiteral(
"blue" ) ).toDouble( &blueOk );
790 bgAlpha = bgColorElem.attribute( QStringLiteral(
"alpha" ) ).toDouble( &alphaOk );
791 if ( redOk && greenOk && blueOk && alphaOk )
793 mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
794 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
804 if ( element.hasAttribute( QStringLiteral(
"opacity" ) ) )
806 setItemOpacity( element.attribute( QStringLiteral(
"opacity" ), QStringLiteral(
"1" ) ).toDouble() );
810 setItemOpacity( 1.0 - element.attribute( QStringLiteral(
"transparency" ), QStringLiteral(
"0" ) ).toInt() / 100.0 );
813 mExcludeFromExports = element.attribute( QStringLiteral(
"excludeFromExports" ), QStringLiteral(
"0" ) ).toInt();
814 mEvaluatedExcludeFromExports = mExcludeFromExports;
818 mBlockUndoCommands =
false;
831 return new QgsLayoutItemUndoCommand(
this, text,
id, parent );
849 if ( mFrameColor == color )
862 if ( mFrameWidth == width )
874 if ( mFrameJoinStyle == style )
879 mFrameJoinStyle = style;
881 QPen itemPen = pen();
882 itemPen.setJoinStyle( mFrameJoinStyle );
895 mBackgroundColor = color;
911 if ( !mItemCachedImage.isNull() )
917 return mExcludeFromExports;
922 mExcludeFromExports = exclude;
928 return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
934 blendMode() != QPainter::CompositionMode_SourceOver;
944 return pen().widthF() / 2.0;
950 return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
973 mLayout->undoStack()->beginCommand(
this, commandText, command );
979 mLayout->undoStack()->endCommand();
985 mLayout->undoStack()->cancelCommand();
1001 void QgsLayoutItem::applyDataDefinedOrientation(
double &width,
double &height,
const QgsExpressionContext &context )
1005 if ( ok && !orientationString.isEmpty() )
1010 double heightD = 0.0, widthD = 0.0;
1011 switch ( orientation )
1015 heightD = std::max( height, width );
1016 widthD = std::min( height, width );
1021 heightD = std::min( height, width );
1022 widthD = std::max( height, width );
1051 double evaluatedWidth = size.
width();
1052 double evaluatedHeight = size.
height();
1056 evaluatedWidth = convertedSize.
width();
1057 evaluatedHeight = convertedSize.
height();
1065 applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1070 double QgsLayoutItem::applyDataDefinedRotation(
const double rotation )
1079 return evaluatedRotation;
1119 const bool exclude = mExcludeFromExports;
1136 const double rotationRequired =
angle - rotation();
1139 mItemRotation =
angle;
1142 void QgsLayoutItem::updateStoredItemPosition()
1145 mItemPosition =
mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.
units() );
1150 double evaluatedAngle =
angle + rotation();
1152 mItemRotation = evaluatedAngle;
1154 QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1168 Q_UNUSED( visitor );
1187 if ( !mItemCachedImage.isNull() )
1189 mItemCachedImage = QImage();
1208 painter->setRenderHint( QPainter::Antialiasing,
false );
1209 painter->setPen( Qt::NoPen );
1210 painter->setBrush( QColor( 100, 255, 100, 200 ) );
1211 painter->drawRect( rect() );
1217 path.addRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1223 if ( !mFrame || !context.
painter() )
1226 QPainter *p = context.
painter();
1231 p->setBrush( Qt::NoBrush );
1239 if ( !mBackground || !context.
painter() )
1244 QPainter *p = context.
painter();
1245 p->setBrush( brush() );
1246 p->setPen( Qt::NoPen );
1260 mMinimumSize = size;
1279 QPointF QgsLayoutItem::itemPositionAtReferencePoint(
const ReferencePoint reference,
const QSizeF size )
const
1281 switch ( reference )
1284 return QPointF( size.width() / 2.0, 0 );
1286 return QPointF( size.width(), 0 );
1288 return QPointF( 0, size.height() / 2.0 );
1290 return QPointF( size.width() / 2.0, size.height() / 2.0 );
1292 return QPointF( size.width(), size.height() / 2.0 );
1294 return QPointF( 0, size.height() );
1296 return QPointF( size.width() / 2.0, size.height() );
1298 return QPointF( size.width(), size.height() );
1300 return QPointF( 0, 0 );
1303 return QPointF( 0, 0 );
1308 const QPointF itemPosition = mapFromScene( position );
1309 const QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1310 return mapToScene( adjustedPointInsideItem );
1315 const QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1316 return mapToScene( pointWithinItem );
1321 const QPointF topLeft =
mLayout->convertToLayoutUnits( point );
1322 const QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1323 return mLayout->convertFromLayoutUnits( refPoint, point.
units() );
1337 void QgsLayoutItem::initConnectionsToLayout()
1344 void QgsLayoutItem::preparePainter( QPainter *painter )
1346 if ( !painter || !painter->device() )
1351 painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1353 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
1358 bool QgsLayoutItem::shouldDrawAntialiased()
const
1367 bool QgsLayoutItem::shouldDrawDebugRect()
const
1372 QSizeF QgsLayoutItem::applyMinimumSize(
const QSizeF targetSize )
1378 const QSizeF minimumSizeLayoutUnits =
mLayout->convertToLayoutUnits(
minimumSize() );
1379 return targetSize.expandedTo( minimumSizeLayoutUnits );
1382 QSizeF QgsLayoutItem::applyFixedSize(
const QSizeF targetSize )
1389 QSizeF size = targetSize;
1390 const QSizeF fixedSizeLayoutUnits =
mLayout->convertToLayoutUnits(
fixedSize() );
1391 if ( fixedSizeLayoutUnits.width() > 0 )
1392 size.setWidth( fixedSizeLayoutUnits.width() );
1393 if ( fixedSizeLayoutUnits.height() > 0 )
1394 size.setHeight( fixedSizeLayoutUnits.height() );
1401 double r = mItemRotation;
1413 if ( !transformPoint.isNull() )
1417 QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1419 refLine.setAngle( refLine.angle() - r + rotation() );
1421 const QPointF rotatedReferencePoint = refLine.p2();
1422 setPos( rotatedReferencePoint );
1425 setTransformOriginPoint( 0, 0 );
1426 QGraphicsItem::setRotation( r );
1429 updateStoredItemPosition();
1444 mEvaluatedOpacity = opacity / 100.0;
1450 setOpacity( mEvaluatedOpacity );
1463 setPen( Qt::NoPen );
1473 itemPen = QPen( frameColor );
1477 itemPen = QPen( mFrameColor );
1479 itemPen.setJoinStyle( mFrameJoinStyle );
1482 itemPen.setWidthF(
mLayout->convertToLayoutUnits( mFrameWidth ) );
1484 itemPen.setWidthF( mFrameWidth.
length() );
1505 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1515 QPainter::CompositionMode
blendMode = mBlendMode;
1520 if ( ok && !blendStr.isEmpty() )
1522 const QString blendstr = blendStr.trimmed();
1528 mEffect->setCompositionMode(
blendMode );