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 bool previewRender = !
mLayout ||
mLayout->renderContext().isPreviewRender();
302 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 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 );
407 draw( itemRenderContext );
416 if ( point == mReferencePoint )
421 mReferencePoint = point;
424 updateStoredItemPosition();
448 QSizeF targetSizeLayoutUnits =
mLayout->convertToLayoutUnits( evaluatedSize );
449 QSizeF actualSizeLayoutUnits = applyMinimumSize( targetSizeLayoutUnits );
450 actualSizeLayoutUnits = applyFixedSize( actualSizeLayoutUnits );
453 if ( actualSizeLayoutUnits == rect().size() )
459 mItemSize = actualSizeTargetUnits;
461 setRect( 0, 0, actualSizeLayoutUnits.width(), actualSizeLayoutUnits.height() );
478 point =
mLayout->pageCollection()->pagePositionToAbsolute(
page, p );
485 point.
setX( point.
x() + bleed );
486 point.
setY( point.
y() + bleed );
490 if ( !useReferencePoint )
495 evaluatedPoint = applyDataDefinedPosition( evaluatedPoint );
496 QPointF evaluatedPointLayoutUnits =
mLayout->convertToLayoutUnits( evaluatedPoint );
498 if ( topLeftPointLayoutUnits == scenePos() && point.
units() == mItemPosition.
units() )
505 mItemPosition = referencePointTargetUnits;
506 setScenePos( topLeftPointLayoutUnits );
512 QPointF newPos = rect.topLeft();
514 blockSignals(
true );
522 blockSignals(
false );
530 moveBy( deltaX, deltaY );
536 itemPos.
setX( itemPos.
x() + deltaPos.
x() );
537 itemPos.
setY( itemPos.
y() + deltaPos.
y() );
546 return mLayout->pageCollection()->pageNumberForPoint( pos() );
561 p.ry() -= pageItem->pos().y();
571 return mLayout->convertFromLayoutUnits( p, mItemPosition.
units() );
574 void QgsLayoutItem::setScenePos(
const QPointF destinationPos )
579 if (
auto *lParentItem = parentItem() )
580 setPos( pos() + ( destinationPos - scenePos() ) + lParentItem->scenePos() );
582 setPos( pos() + ( destinationPos - scenePos() ) );
585 bool QgsLayoutItem::shouldBlockUndoCommands()
const
602 return !mEvaluatedExcludeFromExports;
607 return mItemRotation;
612 QDomElement element = doc.createElement( QStringLiteral(
"LayoutItem" ) );
613 element.setAttribute( QStringLiteral(
"type" ), QString::number(
type() ) );
615 element.setAttribute( QStringLiteral(
"uuid" ), mUuid );
616 element.setAttribute( QStringLiteral(
"templateUuid" ), mUuid );
617 element.setAttribute( QStringLiteral(
"id" ), mId );
618 element.setAttribute( QStringLiteral(
"referencePoint" ), QString::number(
static_cast< int >( mReferencePoint ) ) );
619 element.setAttribute( QStringLiteral(
"position" ), mItemPosition.
encodePoint() );
621 element.setAttribute( QStringLiteral(
"size" ), mItemSize.
encodeSize() );
622 element.setAttribute( QStringLiteral(
"itemRotation" ), QString::number( mItemRotation ) );
623 element.setAttribute( QStringLiteral(
"groupUuid" ), mParentGroupUuid );
625 element.setAttribute( QStringLiteral(
"zValue" ), QString::number( zValue() ) );
626 element.setAttribute( QStringLiteral(
"visibility" ), isVisible() );
630 element.setAttribute( QStringLiteral(
"positionLock" ), QStringLiteral(
"true" ) );
634 element.setAttribute( QStringLiteral(
"positionLock" ), QStringLiteral(
"false" ) );
640 element.setAttribute( QStringLiteral(
"frame" ), QStringLiteral(
"true" ) );
644 element.setAttribute( QStringLiteral(
"frame" ), QStringLiteral(
"false" ) );
650 element.setAttribute( QStringLiteral(
"background" ), QStringLiteral(
"true" ) );
654 element.setAttribute( QStringLiteral(
"background" ), QStringLiteral(
"false" ) );
658 QDomElement frameColorElem = doc.createElement( QStringLiteral(
"FrameColor" ) );
659 frameColorElem.setAttribute( QStringLiteral(
"red" ), QString::number( mFrameColor.red() ) );
660 frameColorElem.setAttribute( QStringLiteral(
"green" ), QString::number( mFrameColor.green() ) );
661 frameColorElem.setAttribute( QStringLiteral(
"blue" ), QString::number( mFrameColor.blue() ) );
662 frameColorElem.setAttribute( QStringLiteral(
"alpha" ), QString::number( mFrameColor.alpha() ) );
663 element.appendChild( frameColorElem );
664 element.setAttribute( QStringLiteral(
"outlineWidthM" ), mFrameWidth.
encodeMeasurement() );
668 QDomElement bgColorElem = doc.createElement( QStringLiteral(
"BackgroundColor" ) );
669 bgColorElem.setAttribute( QStringLiteral(
"red" ), QString::number( mBackgroundColor.red() ) );
670 bgColorElem.setAttribute( QStringLiteral(
"green" ), QString::number( mBackgroundColor.green() ) );
671 bgColorElem.setAttribute( QStringLiteral(
"blue" ), QString::number( mBackgroundColor.blue() ) );
672 bgColorElem.setAttribute( QStringLiteral(
"alpha" ), QString::number( mBackgroundColor.alpha() ) );
673 element.appendChild( bgColorElem );
679 element.setAttribute( QStringLiteral(
"opacity" ), QString::number( mOpacity ) );
681 element.setAttribute( QStringLiteral(
"excludeFromExports" ), mExcludeFromExports );
686 parentElement.appendChild( element );
693 if ( element.nodeName() != QLatin1String(
"LayoutItem" ) )
700 mBlockUndoCommands =
true;
701 mUuid = element.attribute( QStringLiteral(
"uuid" ), QUuid::createUuid().toString() );
702 setId( element.attribute( QStringLiteral(
"id" ) ) );
703 mReferencePoint =
static_cast< ReferencePoint >( element.attribute( QStringLiteral(
"referencePoint" ) ).toInt() );
704 setItemRotation( element.attribute( QStringLiteral(
"itemRotation" ), QStringLiteral(
"0" ) ).toDouble() );
708 mParentGroupUuid = element.attribute( QStringLiteral(
"groupUuid" ) );
709 if ( !mParentGroupUuid.isEmpty() )
713 group->addItem(
this );
716 mTemplateUuid = element.attribute( QStringLiteral(
"templateUuid" ) );
719 QString positionLock = element.attribute( QStringLiteral(
"positionLock" ) );
720 if ( positionLock.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
729 setVisibility( element.attribute( QStringLiteral(
"visibility" ), QStringLiteral(
"1" ) ) != QLatin1String(
"0" ) );
730 setZValue( element.attribute( QStringLiteral(
"zValue" ) ).toDouble() );
733 QString frame = element.attribute( QStringLiteral(
"frame" ) );
734 if ( frame.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
744 QString background = element.attribute( QStringLiteral(
"background" ) );
745 if ( background.compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0 )
757 QDomNodeList frameColorList = element.elementsByTagName( QStringLiteral(
"FrameColor" ) );
758 if ( !frameColorList.isEmpty() )
760 QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
762 bool greenOk =
false;
764 bool alphaOk =
false;
765 int penRed, penGreen, penBlue, penAlpha;
767 penRed = frameColorElem.attribute( QStringLiteral(
"red" ) ).toDouble( &redOk );
768 penGreen = frameColorElem.attribute( QStringLiteral(
"green" ) ).toDouble( &greenOk );
769 penBlue = frameColorElem.attribute( QStringLiteral(
"blue" ) ).toDouble( &blueOk );
770 penAlpha = frameColorElem.attribute( QStringLiteral(
"alpha" ) ).toDouble( &alphaOk );
772 if ( redOk && greenOk && blueOk && alphaOk )
774 mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
780 QDomNodeList bgColorList = element.elementsByTagName( QStringLiteral(
"BackgroundColor" ) );
781 if ( !bgColorList.isEmpty() )
783 QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
784 bool redOk, greenOk, blueOk, alphaOk;
785 int bgRed, bgGreen, bgBlue, bgAlpha;
786 bgRed = bgColorElem.attribute( QStringLiteral(
"red" ) ).toDouble( &redOk );
787 bgGreen = bgColorElem.attribute( QStringLiteral(
"green" ) ).toDouble( &greenOk );
788 bgBlue = bgColorElem.attribute( QStringLiteral(
"blue" ) ).toDouble( &blueOk );
789 bgAlpha = bgColorElem.attribute( QStringLiteral(
"alpha" ) ).toDouble( &alphaOk );
790 if ( redOk && greenOk && blueOk && alphaOk )
792 mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
793 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
803 if ( element.hasAttribute( QStringLiteral(
"opacity" ) ) )
805 setItemOpacity( element.attribute( QStringLiteral(
"opacity" ), QStringLiteral(
"1" ) ).toDouble() );
809 setItemOpacity( 1.0 - element.attribute( QStringLiteral(
"transparency" ), QStringLiteral(
"0" ) ).toInt() / 100.0 );
812 mExcludeFromExports = element.attribute( QStringLiteral(
"excludeFromExports" ), QStringLiteral(
"0" ) ).toInt();
813 mEvaluatedExcludeFromExports = mExcludeFromExports;
817 mBlockUndoCommands =
false;
830 return new QgsLayoutItemUndoCommand(
this, text,
id, parent );
848 if ( mFrameColor == color )
861 if ( mFrameWidth == width )
873 if ( mFrameJoinStyle == style )
878 mFrameJoinStyle = style;
880 QPen itemPen = pen();
881 itemPen.setJoinStyle( mFrameJoinStyle );
894 mBackgroundColor = color;
910 if ( !mItemCachedImage.isNull() )
916 return mExcludeFromExports;
921 mExcludeFromExports = exclude;
927 return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
933 blendMode() != QPainter::CompositionMode_SourceOver;
943 return pen().widthF() / 2.0;
949 return rect().adjusted( -frameBleed, -frameBleed, frameBleed, frameBleed );
972 mLayout->undoStack()->beginCommand(
this, commandText, command );
978 mLayout->undoStack()->endCommand();
984 mLayout->undoStack()->cancelCommand();
1000 void QgsLayoutItem::applyDataDefinedOrientation(
double &width,
double &height,
const QgsExpressionContext &context )
1004 if ( ok && !orientationString.isEmpty() )
1009 double heightD = 0.0, widthD = 0.0;
1010 switch ( orientation )
1014 heightD = std::max( height, width );
1015 widthD = std::min( height, width );
1020 heightD = std::min( height, width );
1021 widthD = std::max( height, width );
1050 double evaluatedWidth = size.
width();
1051 double evaluatedHeight = size.
height();
1055 evaluatedWidth = convertedSize.
width();
1056 evaluatedHeight = convertedSize.
height();
1064 applyDataDefinedOrientation( evaluatedWidth, evaluatedHeight, context );
1069 double QgsLayoutItem::applyDataDefinedRotation(
const double rotation )
1078 return evaluatedRotation;
1118 bool exclude = mExcludeFromExports;
1135 double rotationRequired =
angle - rotation();
1138 mItemRotation =
angle;
1141 void QgsLayoutItem::updateStoredItemPosition()
1144 mItemPosition =
mLayout->convertFromLayoutUnits( layoutPosReferencePoint, mItemPosition.
units() );
1149 double evaluatedAngle =
angle + rotation();
1151 mItemRotation = evaluatedAngle;
1153 QPointF itemTransformOrigin = mapFromScene( transformOrigin );
1167 Q_UNUSED( visitor );
1186 if ( !mItemCachedImage.isNull() )
1188 mItemCachedImage = QImage();
1207 painter->setRenderHint( QPainter::Antialiasing,
false );
1208 painter->setPen( Qt::NoPen );
1209 painter->setBrush( QColor( 100, 255, 100, 200 ) );
1210 painter->drawRect( rect() );
1216 path.addRect( QRectF( 0, 0, rect().width(), rect().height() ) );
1222 if ( !mFrame || !context.
painter() )
1225 QPainter *p = context.
painter();
1230 p->setBrush( Qt::NoBrush );
1238 if ( !mBackground || !context.
painter() )
1243 QPainter *p = context.
painter();
1244 p->setBrush( brush() );
1245 p->setPen( Qt::NoPen );
1259 mMinimumSize = size;
1278 QPointF QgsLayoutItem::itemPositionAtReferencePoint(
const ReferencePoint reference,
const QSizeF size )
const
1280 switch ( reference )
1283 return QPointF( size.width() / 2.0, 0 );
1285 return QPointF( size.width(), 0 );
1287 return QPointF( 0, size.height() / 2.0 );
1289 return QPointF( size.width() / 2.0, size.height() / 2.0 );
1291 return QPointF( size.width(), size.height() / 2.0 );
1293 return QPointF( 0, size.height() );
1295 return QPointF( size.width() / 2.0, size.height() );
1297 return QPointF( size.width(), size.height() );
1299 return QPointF( 0, 0 );
1302 return QPointF( 0, 0 );
1307 QPointF itemPosition = mapFromScene( position );
1308 QPointF adjustedPointInsideItem = itemPosition - itemPositionAtReferencePoint( reference, size );
1309 return mapToScene( adjustedPointInsideItem );
1314 QPointF pointWithinItem = itemPositionAtReferencePoint( reference, rect().size() );
1315 return mapToScene( pointWithinItem );
1320 QPointF topLeft =
mLayout->convertToLayoutUnits( point );
1321 QPointF refPoint = topLeft + itemPositionAtReferencePoint( mReferencePoint, rect().size() );
1322 return mLayout->convertFromLayoutUnits( refPoint, point.
units() );
1336 void QgsLayoutItem::initConnectionsToLayout()
1343 void QgsLayoutItem::preparePainter( QPainter *painter )
1345 if ( !painter || !painter->device() )
1350 painter->setRenderHint( QPainter::Antialiasing, shouldDrawAntialiased() );
1352 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
1357 bool QgsLayoutItem::shouldDrawAntialiased()
const
1366 bool QgsLayoutItem::shouldDrawDebugRect()
const
1371 QSizeF QgsLayoutItem::applyMinimumSize(
const QSizeF targetSize )
1378 return targetSize.expandedTo( minimumSizeLayoutUnits );
1381 QSizeF QgsLayoutItem::applyFixedSize(
const QSizeF targetSize )
1388 QSizeF size = targetSize;
1389 QSizeF fixedSizeLayoutUnits =
mLayout->convertToLayoutUnits(
fixedSize() );
1390 if ( fixedSizeLayoutUnits.width() > 0 )
1391 size.setWidth( fixedSizeLayoutUnits.width() );
1392 if ( fixedSizeLayoutUnits.height() > 0 )
1393 size.setHeight( fixedSizeLayoutUnits.height() );
1400 double r = mItemRotation;
1412 if ( !transformPoint.isNull() )
1416 QLineF refLine = QLineF( mapToScene( transformPoint ), mapToScene( QPointF( 0, 0 ) ) );
1418 refLine.setAngle( refLine.angle() - r + rotation() );
1420 QPointF rotatedReferencePoint = refLine.p2();
1421 setPos( rotatedReferencePoint );
1424 setTransformOriginPoint( 0, 0 );
1425 QGraphicsItem::setRotation( r );
1428 updateStoredItemPosition();
1443 mEvaluatedOpacity = opacity / 100.0;
1449 setOpacity( mEvaluatedOpacity );
1462 setPen( Qt::NoPen );
1472 itemPen = QPen( frameColor );
1476 itemPen = QPen( mFrameColor );
1478 itemPen.setJoinStyle( mFrameJoinStyle );
1481 itemPen.setWidthF(
mLayout->convertToLayoutUnits( mFrameWidth ) );
1483 itemPen.setWidthF( mFrameWidth.
length() );
1504 setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
1514 QPainter::CompositionMode
blendMode = mBlendMode;
1519 if ( ok && !blendStr.isEmpty() )
1521 QString blendstr = blendStr.trimmed();
1527 mEffect->setCompositionMode(
blendMode );