Skip to content
Permalink
Browse files

[composer] Fix resizing of item groups with rotated items

  • Loading branch information
nyalldawson committed Dec 30, 2013
1 parent 41365d8 commit 3b6a303eb441e71f2265b88cc2cc0f14ebd7ba07
@@ -17,6 +17,7 @@

#include "qgscomposeritemgroup.h"
#include "qgscomposition.h"
#include "qgslogger.h"

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

//update extent (which is in scene coordinates)
double minXItem = item->pos().x();
double minYItem = item->pos().y();
double maxXItem = minXItem + item->rect().width();
double maxYItem = minYItem + item->rect().height();

if ( mSceneBoundingRectangle.isEmpty() ) //we add the first item
//update extent
if ( mBoundingRectangle.isEmpty() ) //we add the first item
{
mSceneBoundingRectangle.setLeft( minXItem );
mSceneBoundingRectangle.setTop( minYItem );
mSceneBoundingRectangle.setRight( maxXItem );
mSceneBoundingRectangle.setBottom( maxYItem );
mBoundingRectangle = QRectF( 0, 0, item->rect().width(), item->rect().height() );
//call method of superclass to avoid repositioning of items
QgsComposerItem::setSceneRect( QRectF( item->pos().x(), item->pos().y(), item->rect().width(), item->rect().height() ) );

if ( item->itemRotation() != 0 )
{
setItemRotation( item->itemRotation() );
}
}
else
{
if ( minXItem < mSceneBoundingRectangle.left() )
{
mSceneBoundingRectangle.setLeft( minXItem );
}
if ( minYItem < mSceneBoundingRectangle.top() )
if ( item->itemRotation() != itemRotation() )
{
mSceneBoundingRectangle.setTop( minYItem );
//items have mixed rotation, so reset rotation of group
mBoundingRectangle = mapRectToScene( mBoundingRectangle );
setItemRotation( 0 );
mBoundingRectangle = mBoundingRectangle.united( item->mapRectToScene( item->rect() ) );
//call method of superclass to avoid repositioning of items
QgsComposerItem::setSceneRect( mBoundingRectangle );
}
if ( maxXItem > mSceneBoundingRectangle.right() )
else
{
mSceneBoundingRectangle.setRight( maxXItem );
}
if ( maxYItem > mSceneBoundingRectangle.bottom() )
{
mSceneBoundingRectangle.setBottom( maxYItem );
//items have same rotation, so keep rotation of group
mBoundingRectangle = mBoundingRectangle.united( mapRectFromItem( item, item->rect() ) );
QPointF newPos = mapToScene( mBoundingRectangle.topLeft().x(), mBoundingRectangle.topLeft().y() );
mBoundingRectangle = QRectF( 0, 0, mBoundingRectangle.width(), mBoundingRectangle.height() );
QgsComposerItem::setSceneRect( QRectF( newPos.x(), newPos.y(), mBoundingRectangle.width(), mBoundingRectangle.height() ) );
}
}

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

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

void QgsComposerItemGroup::setSceneRect( const QRectF& rectangle )
{
//calculate values between 0 and 1 for boundaries of all contained items, depending on their positions in the item group rectangle.
//then position the item boundaries in the new item group rect such that these values are the same
double xLeftCurrent = pos().x();
double xRightCurrent = xLeftCurrent + rect().width();
double yTopCurrent = pos().y();
double yBottomCurrent = yTopCurrent + rect().height();

double xItemLeft, xItemRight, yItemTop, yItemBottom;
double xItemLeftNew, xItemRightNew, yItemTopNew, yItemBottomNew;
double xParamLeft, xParamRight, yParamTop, yParamBottom;

//resize all items in this group
//first calculate new group rectangle in current group coordsys
QPointF newOrigin = mapFromScene( rectangle.topLeft() );
QRectF newRect = QRectF( newOrigin.x(), newOrigin.y(), rectangle.width(), rectangle.height() );

QSet<QgsComposerItem*>::iterator item_it = mItems.begin();
for ( ; item_it != mItems.end(); ++item_it )
{
xItemLeft = ( *item_it )->pos().x();
xItemRight = xItemLeft + ( *item_it )->rect().width();
yItemTop = ( *item_it )->pos().y();
yItemBottom = yItemTop + ( *item_it )->rect().height();

xParamLeft = ( xItemLeft - xLeftCurrent ) / ( xRightCurrent - xLeftCurrent );
xParamRight = ( xItemRight - xLeftCurrent ) / ( xRightCurrent - xLeftCurrent );
yParamTop = ( yItemTop - yTopCurrent ) / ( yBottomCurrent - yTopCurrent );
yParamBottom = ( yItemBottom - yTopCurrent ) / ( yBottomCurrent - yTopCurrent );

xItemLeftNew = xParamLeft * rectangle.right() + ( 1 - xParamLeft ) * rectangle.left();
xItemRightNew = xParamRight * rectangle.right() + ( 1 - xParamRight ) * rectangle.left();
yItemTopNew = yParamTop * rectangle.bottom() + ( 1 - yParamTop ) * rectangle.top();
yItemBottomNew = yParamBottom * rectangle.bottom() + ( 1 - yParamBottom ) * rectangle.top();

( *item_it )->setSceneRect( QRectF( xItemLeftNew, yItemTopNew, xItemRightNew - xItemLeftNew, yItemBottomNew - yItemTopNew ) );
//each item needs to be scaled relatively to the final size of the group
QRectF itemRect = mapRectFromItem(( *item_it ), ( *item_it )->rect() );
QgsComposition::relativeResizeRect( itemRect, rect(), newRect );

QPointF newPos = mapToScene( itemRect.topLeft() );
( *item_it )->setSceneRect( QRectF( newPos.x(), newPos.y(), itemRect.width(), itemRect.height() ) );
}
//lastly, set new rect for group
QgsComposerItem::setSceneRect( rectangle );
}

@@ -67,5 +67,5 @@ class CORE_EXPORT QgsComposerItemGroup: public QgsComposerItem

private:
QSet<QgsComposerItem*> mItems;
QRectF mSceneBoundingRectangle;
QRectF mBoundingRectangle;
};
@@ -167,7 +167,7 @@ void QgsComposerMouseHandles::drawSelectedItemBounds( QPainter* painter )
//get item bounds in mouse handle item's coordinate system
QRectF itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rect() );
//now, resize it relative to the current resized dimensions of the mouse handles
relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
QgsComposition::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
itemBounds = QPolygonF( itemRect );
}
else
@@ -592,7 +592,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
for ( ; itemIter != selectedItems.end(); ++itemIter )
{
if (( *itemIter )->positionLock() )
if (( *itemIter )->positionLock() || ( !( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) )
{
//don't move locked items
continue;
@@ -615,9 +615,9 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
for ( ; itemIter != selectedItems.end(); ++itemIter )
{
if (( *itemIter )->positionLock() )
if (( *itemIter )->positionLock() || ( !( *itemIter )->flags() & QGraphicsItem::ItemIsSelectable ) )
{
//don't resize locked items
//don't resize locked items or unselectable items (eg, items which make up an item group)
continue;
}
QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
@@ -633,7 +633,7 @@ void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event
{
//multiple items selected, so each needs to be scaled relatively to the final size of the mouse handles
itemRect = mapRectFromItem(( *itemIter ), ( *itemIter )->rect() );
relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
QgsComposition::relativeResizeRect( itemRect, QRectF( -mResizeMoveX, -mResizeMoveY, mBeginHandleWidth, mBeginHandleHeight ), mResizeRect );
}

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

void QgsComposerMouseHandles::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
{
//linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );

rectToResize.setRect( left, top, right - left, bottom - top );
}

double QgsComposerMouseHandles::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
{
//calculate parameters for linear scale between before and after ranges
double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
double c = afterMin - ( beforeMin * m );

//return linearly scaled position
return m * position + c;
}

QPointF QgsComposerMouseHandles::snapPoint( const QPointF& point, QgsComposerMouseHandles::SnapGuideMode mode )
{
//snap to grid
@@ -157,11 +157,6 @@ class CORE_EXPORT QgsComposerMouseHandles: public QObject, public QGraphicsRectI
/**Handles resizing of items during mouse move*/
void resizeMouseMove( const QPointF& currentPosition, bool lockAspect, bool fromCenter );

/**Resizes a QRectF relative to the change from boundsBefore to boundsAfter*/
void relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter );
/**Returns a scaled position given a before and after range*/
double relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax );

/**Return horizontal align snap item. Creates a new graphics line if 0*/
QGraphicsLineItem* hAlignSnapItem();
void deleteHAlignSnapItem();
@@ -2273,3 +2273,24 @@ void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c
e = r[3] * s[1] + r[4] * s[4];
f = r[3] * s[2] + r[4] * s[5] + r[5];
}

void QgsComposition::relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter )
{
//linearly scale rectToResize relative to the scaling from boundsBefore to boundsAfter
double left = relativePosition( rectToResize.left(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double right = relativePosition( rectToResize.right(), boundsBefore.left(), boundsBefore.right(), boundsAfter.left(), boundsAfter.right() );
double top = relativePosition( rectToResize.top(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );
double bottom = relativePosition( rectToResize.bottom(), boundsBefore.top(), boundsBefore.bottom(), boundsAfter.top(), boundsAfter.bottom() );

rectToResize.setRect( left, top, right - left, bottom - top );
}

double QgsComposition::relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax )
{
//calculate parameters for linear scale between before and after ranges
double m = ( afterMax - afterMin ) / ( beforeMax - beforeMin );
double c = afterMin - ( beforeMin * m );

//return linearly scaled position
return m * position + c;
}
@@ -412,7 +412,12 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
/** Compute world file parameters */
void computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const;

QgsAtlasComposition& atlasComposition() { return mAtlasComposition; }
QgsAtlasComposition& atlasComposition() { return mAtlasComposition; };

/**Resizes a QRectF relative to the change from boundsBefore to boundsAfter*/
static void relativeResizeRect( QRectF& rectToResize, const QRectF& boundsBefore, const QRectF& boundsAfter );
/**Returns a scaled position given a before and after range*/
static double relativePosition( double position, double beforeMin, double beforeMax, double afterMin, double afterMax );

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

0 comments on commit 3b6a303

Please sign in to comment.
You can’t perform that action at this time.