Skip to content

Commit 3b6a303

Browse files
committed
[composer] Fix resizing of item groups with rotated items
1 parent 41365d8 commit 3b6a303

6 files changed

+68
-84
lines changed

src/core/composer/qgscomposeritemgroup.cpp

+35-51
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "qgscomposeritemgroup.h"
1919
#include "qgscomposition.h"
20+
#include "qgslogger.h"
2021

2122
#include <QPen>
2223
#include <QPainter>
@@ -59,40 +60,39 @@ void QgsComposerItemGroup::addItem( QgsComposerItem* item )
5960
item->setSelected( false );
6061
item->setFlag( QGraphicsItem::ItemIsSelectable, false ); //item in groups cannot be selected
6162

62-
//update extent (which is in scene coordinates)
63-
double minXItem = item->pos().x();
64-
double minYItem = item->pos().y();
65-
double maxXItem = minXItem + item->rect().width();
66-
double maxYItem = minYItem + item->rect().height();
67-
68-
if ( mSceneBoundingRectangle.isEmpty() ) //we add the first item
63+
//update extent
64+
if ( mBoundingRectangle.isEmpty() ) //we add the first item
6965
{
70-
mSceneBoundingRectangle.setLeft( minXItem );
71-
mSceneBoundingRectangle.setTop( minYItem );
72-
mSceneBoundingRectangle.setRight( maxXItem );
73-
mSceneBoundingRectangle.setBottom( maxYItem );
66+
mBoundingRectangle = QRectF( 0, 0, item->rect().width(), item->rect().height() );
67+
//call method of superclass to avoid repositioning of items
68+
QgsComposerItem::setSceneRect( QRectF( item->pos().x(), item->pos().y(), item->rect().width(), item->rect().height() ) );
69+
70+
if ( item->itemRotation() != 0 )
71+
{
72+
setItemRotation( item->itemRotation() );
73+
}
7474
}
7575
else
7676
{
77-
if ( minXItem < mSceneBoundingRectangle.left() )
78-
{
79-
mSceneBoundingRectangle.setLeft( minXItem );
80-
}
81-
if ( minYItem < mSceneBoundingRectangle.top() )
77+
if ( item->itemRotation() != itemRotation() )
8278
{
83-
mSceneBoundingRectangle.setTop( minYItem );
79+
//items have mixed rotation, so reset rotation of group
80+
mBoundingRectangle = mapRectToScene( mBoundingRectangle );
81+
setItemRotation( 0 );
82+
mBoundingRectangle = mBoundingRectangle.united( item->mapRectToScene( item->rect() ) );
83+
//call method of superclass to avoid repositioning of items
84+
QgsComposerItem::setSceneRect( mBoundingRectangle );
8485
}
85-
if ( maxXItem > mSceneBoundingRectangle.right() )
86+
else
8687
{
87-
mSceneBoundingRectangle.setRight( maxXItem );
88-
}
89-
if ( maxYItem > mSceneBoundingRectangle.bottom() )
90-
{
91-
mSceneBoundingRectangle.setBottom( maxYItem );
88+
//items have same rotation, so keep rotation of group
89+
mBoundingRectangle = mBoundingRectangle.united( mapRectFromItem( item, item->rect() ) );
90+
QPointF newPos = mapToScene( mBoundingRectangle.topLeft().x(), mBoundingRectangle.topLeft().y() );
91+
mBoundingRectangle = QRectF( 0, 0, mBoundingRectangle.width(), mBoundingRectangle.height() );
92+
QgsComposerItem::setSceneRect( QRectF( newPos.x(), newPos.y(), mBoundingRectangle.width(), mBoundingRectangle.height() ) );
9293
}
9394
}
9495

95-
QgsComposerItem::setSceneRect( mSceneBoundingRectangle ); //call method of superclass to avoid repositioning of items
9696
}
9797

9898
void QgsComposerItemGroup::removeItems()
@@ -124,38 +124,22 @@ void QgsComposerItemGroup::paint( QPainter * painter, const QStyleOptionGraphics
124124

125125
void QgsComposerItemGroup::setSceneRect( const QRectF& rectangle )
126126
{
127-
//calculate values between 0 and 1 for boundaries of all contained items, depending on their positions in the item group rectangle.
128-
//then position the item boundaries in the new item group rect such that these values are the same
129-
double xLeftCurrent = pos().x();
130-
double xRightCurrent = xLeftCurrent + rect().width();
131-
double yTopCurrent = pos().y();
132-
double yBottomCurrent = yTopCurrent + rect().height();
133-
134-
double xItemLeft, xItemRight, yItemTop, yItemBottom;
135-
double xItemLeftNew, xItemRightNew, yItemTopNew, yItemBottomNew;
136-
double xParamLeft, xParamRight, yParamTop, yParamBottom;
137-
127+
//resize all items in this group
128+
//first calculate new group rectangle in current group coordsys
129+
QPointF newOrigin = mapFromScene( rectangle.topLeft() );
130+
QRectF newRect = QRectF( newOrigin.x(), newOrigin.y(), rectangle.width(), rectangle.height() );
138131

139132
QSet<QgsComposerItem*>::iterator item_it = mItems.begin();
140133
for ( ; item_it != mItems.end(); ++item_it )
141134
{
142-
xItemLeft = ( *item_it )->pos().x();
143-
xItemRight = xItemLeft + ( *item_it )->rect().width();
144-
yItemTop = ( *item_it )->pos().y();
145-
yItemBottom = yItemTop + ( *item_it )->rect().height();
146-
147-
xParamLeft = ( xItemLeft - xLeftCurrent ) / ( xRightCurrent - xLeftCurrent );
148-
xParamRight = ( xItemRight - xLeftCurrent ) / ( xRightCurrent - xLeftCurrent );
149-
yParamTop = ( yItemTop - yTopCurrent ) / ( yBottomCurrent - yTopCurrent );
150-
yParamBottom = ( yItemBottom - yTopCurrent ) / ( yBottomCurrent - yTopCurrent );
151-
152-
xItemLeftNew = xParamLeft * rectangle.right() + ( 1 - xParamLeft ) * rectangle.left();
153-
xItemRightNew = xParamRight * rectangle.right() + ( 1 - xParamRight ) * rectangle.left();
154-
yItemTopNew = yParamTop * rectangle.bottom() + ( 1 - yParamTop ) * rectangle.top();
155-
yItemBottomNew = yParamBottom * rectangle.bottom() + ( 1 - yParamBottom ) * rectangle.top();
156-
157-
( *item_it )->setSceneRect( QRectF( xItemLeftNew, yItemTopNew, xItemRightNew - xItemLeftNew, yItemBottomNew - yItemTopNew ) );
135+
//each item needs to be scaled relatively to the final size of the group
136+
QRectF itemRect = mapRectFromItem(( *item_it ), ( *item_it )->rect() );
137+
QgsComposition::relativeResizeRect( itemRect, rect(), newRect );
138+
139+
QPointF newPos = mapToScene( itemRect.topLeft() );
140+
( *item_it )->setSceneRect( QRectF( newPos.x(), newPos.y(), itemRect.width(), itemRect.height() ) );
158141
}
142+
//lastly, set new rect for group
159143
QgsComposerItem::setSceneRect( rectangle );
160144
}
161145

src/core/composer/qgscomposeritemgroup.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,5 @@ class CORE_EXPORT QgsComposerItemGroup: public QgsComposerItem
6767

6868
private:
6969
QSet<QgsComposerItem*> mItems;
70-
QRectF mSceneBoundingRectangle;
70+
QRectF mBoundingRectangle;
7171
};

src/core/composer/qgscomposermousehandles.cpp

+5-26
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ void QgsComposerMouseHandles::drawSelectedItemBounds( QPainter* painter )
167167
//get item bounds in mouse handle item's coordinate system
168168
QRectF itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rect() );
169169
//now, resize it relative to the current resized dimensions of the mouse handles
170-
relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
170+
QgsComposition::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
171171
itemBounds = QPolygonF( itemRect );
172172
}
173173
else
@@ -592,7 +592,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
592592
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
593593
for ( ; itemIter != selectedItems.end(); ++itemIter )
594594
{
595-
if (( *itemIter )->positionLock() )
595+
if (( *itemIter )->positionLock() || ( !( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) )
596596
{
597597
//don't move locked items
598598
continue;
@@ -615,9 +615,9 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
615615
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
616616
for ( ; itemIter != selectedItems.end(); ++itemIter )
617617
{
618-
if (( *itemIter )->positionLock() )
618+
if (( *itemIter )->positionLock() || ( !( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) )
619619
{
620-
//don't resize locked items
620+
//don't resize locked items or unselectable items (eg, items which make up an item group)
621621
continue;
622622
}
623623
QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
@@ -633,7 +633,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
633633
{
634634
//multiple items selected, so each needs to be scaled relatively to the final size of the mouse handles
635635
itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rect() );
636-
relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
636+
QgsComposition::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
637637
}
638638

639639
itemRect = itemRect.normalized();
@@ -981,27 +981,6 @@ void QgsComposerMouseHandles::resizeMouseMove( const QPointF& currentPosition, b
981981
mComposition->setStatusMessage( QString( tr( "width: %1 mm height: %2 mm" ) ).arg( rect().width() ).arg( rect().height() ) );
982982
}
983983

984-
void QgsComposerMouseHandles::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
985-
{
986-
//linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
987-
double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
988-
double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
989-
double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
990-
double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
991-
992-
rectToResize.setRect( left, top, right - left, bottom - top );
993-
}
994-
995-
double QgsComposerMouseHandles::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
996-
{
997-
//calculate parameters for linear scale between before and after ranges
998-
double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
999-
double c = afterMin - ( beforeMin * m );
1000-
1001-
//return linearly scaled position
1002-
return m * position + c;
1003-
}
1004-
1005984
QPointF QgsComposerMouseHandles::snapPoint( const QPointF& point, QgsComposerMouseHandles::SnapGuideMode mode )
1006985
{
1007986
//snap to grid

src/core/composer/qgscomposermousehandles.h

-5
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,6 @@ class CORE_EXPORT QgsComposerMouseHandles: public QObject, public QGraphicsRectI
157157
/**Handles resizing of items during mouse move*/
158158
void resizeMouseMove( const QPointF& currentPosition, bool lockAspect, bool fromCenter );
159159

160-
/**Resizes a QRectF relative to the change from boundsBefore to boundsAfter*/
161-
void relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter );
162-
/**Returns a scaled position given a before and after range*/
163-
double relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax );
164-
165160
/**Return horizontal align snap item. Creates a new graphics line if 0*/
166161
QGraphicsLineItem* hAlignSnapItem();
167162
void deleteHAlignSnapItem();

src/core/composer/qgscomposition.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -2273,3 +2273,24 @@ void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c
22732273
e = r[3] * s[1] + r[4] * s[4];
22742274
f = r[3] * s[2] + r[4] * s[5] + r[5];
22752275
}
2276+
2277+
void QgsComposition::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
2278+
{
2279+
//linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
2280+
double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
2281+
double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
2282+
double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
2283+
double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
2284+
2285+
rectToResize.setRect( left, top, right - left, bottom - top );
2286+
}
2287+
2288+
double QgsComposition::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
2289+
{
2290+
//calculate parameters for linear scale between before and after ranges
2291+
double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
2292+
double c = afterMin - ( beforeMin * m );
2293+
2294+
//return linearly scaled position
2295+
return m * position + c;
2296+
}

src/core/composer/qgscomposition.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,12 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
412412
/** Compute world file parameters */
413413
void computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const;
414414

415-
QgsAtlasComposition& atlasComposition() { return mAtlasComposition; }
415+
QgsAtlasComposition& atlasComposition() { return mAtlasComposition; };
416+
417+
/**Resizes a QRectF relative to the change from boundsBefore to boundsAfter*/
418+
static void relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter );
419+
/**Returns a scaled position given a before and after range*/
420+
static double relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax );
416421

417422
public slots:
418423
/**Casts object to the proper subclass type and calls corresponding itemAdded signal*/

0 commit comments

Comments
 (0)