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