mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-19 00:07:15 -04:00
[FEATURE] Allow multi item drag and resize in composer (fix #7918)
[FEATURE] Always draw selection handles on top of composition, add dashed border for selected items (fix #7793) Move responsibility for drawing selection mouse handles and mouse interaction with selection to new class
This commit is contained in:
parent
7bc72d4f6d
commit
24d110606c
@ -332,10 +332,6 @@ class QgsComposerItem: QObject, QGraphicsRectItem
|
|||||||
@note this method was added in version 1.2*/
|
@note this method was added in version 1.2*/
|
||||||
bool positionLock() const;
|
bool positionLock() const;
|
||||||
|
|
||||||
/**Update mouse cursor at (item) position
|
|
||||||
@note this method was added in version 1.2*/
|
|
||||||
void updateCursor( const QPointF& itemPos );
|
|
||||||
|
|
||||||
double rotation() const;
|
double rotation() const;
|
||||||
|
|
||||||
/**Updates item, with the possibility to do custom update for subclasses*/
|
/**Updates item, with the possibility to do custom update for subclasses*/
|
||||||
@ -367,22 +363,6 @@ class QgsComposerItem: QObject, QGraphicsRectItem
|
|||||||
|
|
||||||
virtual void hoverMoveEvent( QGraphicsSceneHoverEvent * event );
|
virtual void hoverMoveEvent( QGraphicsSceneHoverEvent * event );
|
||||||
|
|
||||||
/**Finds out the appropriate cursor for the current mouse position in the widget (e.g. move in the middle, resize at border)*/
|
|
||||||
Qt::CursorShape cursorForPosition( const QPointF& itemCoordPos );
|
|
||||||
|
|
||||||
/**Finds out which mouse move action to choose depending on the cursor position inside the widget*/
|
|
||||||
QgsComposerItem::MouseMoveAction mouseMoveActionForPosition( const QPointF& itemCoordPos );
|
|
||||||
|
|
||||||
/**Changes the rectangle of an item depending on current mouse action (resize or move)
|
|
||||||
@param currentPosition current position of mouse cursor
|
|
||||||
@param mouseMoveStartPos cursor position at the start of the current mouse action
|
|
||||||
@param originalItem Item position at the start of the mouse action
|
|
||||||
@param dx x-Change of mouse cursor
|
|
||||||
@param dy y-Change of mouse cursor
|
|
||||||
@param changeItem Item to change size (can be the same as originalItem or a differen one)
|
|
||||||
*/
|
|
||||||
void changeItemRectangle( const QPointF& currentPosition, const QPointF& mouseMoveStartPos, const QGraphicsRectItem* originalItem, double dx, double dy, QGraphicsRectItem* changeItem );
|
|
||||||
|
|
||||||
/**Draw selection boxes around item*/
|
/**Draw selection boxes around item*/
|
||||||
virtual void drawSelectionBoxes( QPainter* p );
|
virtual void drawSelectionBoxes( QPainter* p );
|
||||||
|
|
||||||
|
@ -213,23 +213,6 @@ class QgsComposition : QGraphicsScene
|
|||||||
/**Snaps a scene coordinate point to grid*/
|
/**Snaps a scene coordinate point to grid*/
|
||||||
QPointF snapPointToGrid( const QPointF& scenePoint ) const;
|
QPointF snapPointToGrid( const QPointF& scenePoint ) const;
|
||||||
|
|
||||||
/**Snaps item position to align with other items (left / middle / right or top / middle / bottom
|
|
||||||
@param item current item
|
|
||||||
@param alignX x-coordinate of align or -1 if not aligned to x
|
|
||||||
@param alignY y-coordinate of align or -1 if not aligned to y
|
|
||||||
@param dx item shift in x direction
|
|
||||||
@param dy item shift in y direction
|
|
||||||
@return new upper left point after the align*/
|
|
||||||
QPointF alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx = 0, double dy = 0 );
|
|
||||||
|
|
||||||
/**Snaps position to align with the boundaries of other items
|
|
||||||
@param pos position to snap
|
|
||||||
@param excludeItem item to exclude
|
|
||||||
@param alignX snapped x coordinate or -1 if not snapped
|
|
||||||
@param alignY snapped y coordinate or -1 if not snapped
|
|
||||||
@return snapped position or original position if no snap*/
|
|
||||||
QPointF alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY );
|
|
||||||
|
|
||||||
/**Add a custom snap line (can be horizontal or vertical)*/
|
/**Add a custom snap line (can be horizontal or vertical)*/
|
||||||
QGraphicsLineItem* addSnapLine();
|
QGraphicsLineItem* addSnapLine();
|
||||||
/**Remove custom snap line (and delete the object)*/
|
/**Remove custom snap line (and delete the object)*/
|
||||||
|
@ -133,6 +133,7 @@ SET(QGIS_CORE_SRCS
|
|||||||
composer/qgscomposermultiframecommand.cpp
|
composer/qgscomposermultiframecommand.cpp
|
||||||
composer/qgscomposerarrow.cpp
|
composer/qgscomposerarrow.cpp
|
||||||
composer/qgscomposerframe.cpp
|
composer/qgscomposerframe.cpp
|
||||||
|
composer/qgscomposermousehandles.cpp
|
||||||
composer/qgscomposeritem.cpp
|
composer/qgscomposeritem.cpp
|
||||||
composer/qgscomposeritemcommand.cpp
|
composer/qgscomposeritemcommand.cpp
|
||||||
composer/qgscomposeritemgroup.cpp
|
composer/qgscomposeritemgroup.cpp
|
||||||
@ -324,6 +325,7 @@ SET(QGIS_CORE_MOC_HDRS
|
|||||||
composer/qgscomposerscalebar.h
|
composer/qgscomposerscalebar.h
|
||||||
composer/qgscomposeritem.h
|
composer/qgscomposeritem.h
|
||||||
composer/qgscomposeritemgroup.h
|
composer/qgscomposeritemgroup.h
|
||||||
|
composer/qgscomposermousehandles.h
|
||||||
composer/qgscomposerlabel.h
|
composer/qgscomposerlabel.h
|
||||||
composer/qgscomposershape.h
|
composer/qgscomposershape.h
|
||||||
composer/qgscomposerattributetable.h
|
composer/qgscomposerattributetable.h
|
||||||
|
@ -94,7 +94,6 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
|
|||||||
void QgsComposerItem::init( bool manageZValue )
|
void QgsComposerItem::init( bool manageZValue )
|
||||||
{
|
{
|
||||||
setFlag( QGraphicsItem::ItemIsSelectable, true );
|
setFlag( QGraphicsItem::ItemIsSelectable, true );
|
||||||
setAcceptsHoverEvents( true );
|
|
||||||
//set default pen and brush
|
//set default pen and brush
|
||||||
setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
|
setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
|
||||||
QPen defaultPen( QColor( 0, 0, 0 ) );
|
QPen defaultPen( QColor( 0, 0, 0 ) );
|
||||||
@ -369,390 +368,9 @@ void QgsComposerItem::cancelCommand()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsComposerItem::mouseMoveEvent( QGraphicsSceneMouseEvent * event )
|
|
||||||
{
|
|
||||||
if ( mItemPositionLocked )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !isSelected() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( mBoundingResizeRectangle )
|
|
||||||
{
|
|
||||||
double diffX = event->lastScenePos().x() - mLastMouseEventPos.x();
|
|
||||||
double diffY = event->lastScenePos().y() - mLastMouseEventPos.y();
|
|
||||||
|
|
||||||
changeItemRectangle( event->lastScenePos(), mMouseMoveStartPos, this, diffX, diffY, mBoundingResizeRectangle );
|
|
||||||
}
|
|
||||||
mLastMouseEventPos = event->lastScenePos();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::mousePressEvent( QGraphicsSceneMouseEvent * event )
|
|
||||||
{
|
|
||||||
if ( mItemPositionLocked )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !isSelected() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//set current position and type of mouse move action
|
|
||||||
mMouseMoveStartPos = event->lastScenePos();
|
|
||||||
mLastMouseEventPos = event->lastScenePos();
|
|
||||||
mCurrentMouseMoveAction = mouseMoveActionForPosition( event->pos() );
|
|
||||||
|
|
||||||
//remove the old rubber band item if it is still there
|
|
||||||
if ( mBoundingResizeRectangle )
|
|
||||||
{
|
|
||||||
scene()->removeItem( mBoundingResizeRectangle );
|
|
||||||
delete mBoundingResizeRectangle;
|
|
||||||
mBoundingResizeRectangle = 0;
|
|
||||||
}
|
|
||||||
deleteAlignItems();
|
|
||||||
|
|
||||||
//create and show bounding rectangle
|
|
||||||
mBoundingResizeRectangle = new QGraphicsRectItem( 0 );
|
|
||||||
scene()->addItem( mBoundingResizeRectangle );
|
|
||||||
mBoundingResizeRectangle->setRect( QRectF( 0, 0, rect().width(), rect().height() ) );
|
|
||||||
QTransform resizeTransform;
|
|
||||||
resizeTransform.translate( transform().dx(), transform().dy() );
|
|
||||||
mBoundingResizeRectangle->setTransform( resizeTransform );
|
|
||||||
|
|
||||||
mBoundingResizeRectangle->setBrush( Qt::NoBrush );
|
|
||||||
mBoundingResizeRectangle->setPen( QPen( QColor( 0, 0, 0 ), 0 ) );
|
|
||||||
mBoundingResizeRectangle->setZValue( 90 );
|
|
||||||
mBoundingResizeRectangle->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * event )
|
|
||||||
{
|
|
||||||
|
|
||||||
if ( mItemPositionLocked )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !isSelected() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//delete frame rectangle
|
|
||||||
if ( mBoundingResizeRectangle )
|
|
||||||
{
|
|
||||||
scene()->removeItem( mBoundingResizeRectangle );
|
|
||||||
delete mBoundingResizeRectangle;
|
|
||||||
mBoundingResizeRectangle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF mouseMoveStopPoint = event->lastScenePos();
|
|
||||||
double diffX = mouseMoveStopPoint.x() - mMouseMoveStartPos.x();
|
|
||||||
double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y();
|
|
||||||
|
|
||||||
//it was only a click
|
|
||||||
if ( qAbs( diffX ) < std::numeric_limits<double>::min() && qAbs( diffY ) < std::numeric_limits<double>::min() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
beginItemCommand( tr( "Change item position" ) );
|
|
||||||
changeItemRectangle( mouseMoveStopPoint, mMouseMoveStartPos, this, diffX, diffY, this );
|
|
||||||
endItemCommand();
|
|
||||||
|
|
||||||
deleteAlignItems();
|
|
||||||
|
|
||||||
//reset default action
|
|
||||||
mCurrentMouseMoveAction = QgsComposerItem::MoveItem;
|
|
||||||
setCursor( Qt::ArrowCursor );
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::CursorShape QgsComposerItem::cursorForPosition( const QPointF& itemCoordPos )
|
|
||||||
{
|
|
||||||
QgsComposerItem::MouseMoveAction mouseAction = mouseMoveActionForPosition( itemCoordPos );
|
|
||||||
switch ( mouseAction )
|
|
||||||
{
|
|
||||||
case NoAction:
|
|
||||||
return Qt::ForbiddenCursor;
|
|
||||||
case MoveItem:
|
|
||||||
return Qt::SizeAllCursor;
|
|
||||||
case ResizeUp:
|
|
||||||
case ResizeDown:
|
|
||||||
return Qt::SizeVerCursor;
|
|
||||||
case ResizeLeft:
|
|
||||||
case ResizeRight:
|
|
||||||
return Qt::SizeHorCursor;
|
|
||||||
case ResizeLeftUp:
|
|
||||||
case ResizeRightDown:
|
|
||||||
return Qt::SizeFDiagCursor;
|
|
||||||
case ResizeRightUp:
|
|
||||||
case ResizeLeftDown:
|
|
||||||
return Qt::SizeBDiagCursor;
|
|
||||||
default:
|
|
||||||
return Qt::ArrowCursor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QgsComposerItem::MouseMoveAction QgsComposerItem::mouseMoveActionForPosition( const QPointF& itemCoordPos )
|
|
||||||
{
|
|
||||||
|
|
||||||
//no action at all if item position is locked for mouse
|
|
||||||
if ( mItemPositionLocked )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::NoAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool nearLeftBorder = false;
|
|
||||||
bool nearRightBorder = false;
|
|
||||||
bool nearLowerBorder = false;
|
|
||||||
bool nearUpperBorder = false;
|
|
||||||
|
|
||||||
double borderTolerance = rectHandlerBorderTolerance();
|
|
||||||
|
|
||||||
if ( itemCoordPos.x() < borderTolerance )
|
|
||||||
{
|
|
||||||
nearLeftBorder = true;
|
|
||||||
}
|
|
||||||
if ( itemCoordPos.y() < borderTolerance )
|
|
||||||
{
|
|
||||||
nearUpperBorder = true;
|
|
||||||
}
|
|
||||||
if ( itemCoordPos.x() > ( rect().width() - borderTolerance ) )
|
|
||||||
{
|
|
||||||
nearRightBorder = true;
|
|
||||||
}
|
|
||||||
if ( itemCoordPos.y() > ( rect().height() - borderTolerance ) )
|
|
||||||
{
|
|
||||||
nearLowerBorder = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( nearLeftBorder && nearUpperBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeLeftUp;
|
|
||||||
}
|
|
||||||
else if ( nearLeftBorder && nearLowerBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeLeftDown;
|
|
||||||
}
|
|
||||||
else if ( nearRightBorder && nearUpperBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeRightUp;
|
|
||||||
}
|
|
||||||
else if ( nearRightBorder && nearLowerBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeRightDown;
|
|
||||||
}
|
|
||||||
else if ( nearLeftBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeLeft;
|
|
||||||
}
|
|
||||||
else if ( nearRightBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeRight;
|
|
||||||
}
|
|
||||||
else if ( nearUpperBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeUp;
|
|
||||||
}
|
|
||||||
else if ( nearLowerBorder )
|
|
||||||
{
|
|
||||||
return QgsComposerItem::ResizeDown;
|
|
||||||
}
|
|
||||||
|
|
||||||
return QgsComposerItem::MoveItem; //default
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::changeItemRectangle( const QPointF& currentPosition,
|
|
||||||
const QPointF& mouseMoveStartPos,
|
|
||||||
const QGraphicsRectItem* originalItem,
|
|
||||||
double dx, double dy,
|
|
||||||
QGraphicsRectItem* changeItem )
|
|
||||||
{
|
|
||||||
Q_UNUSED( dx );
|
|
||||||
Q_UNUSED( dy );
|
|
||||||
if ( !changeItem || !originalItem || !mComposition )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//test if change item is a composer item. If so, prefer call to setSceneRect() instead of setTransform() and setRect()
|
|
||||||
QgsComposerItem* changeComposerItem = dynamic_cast<QgsComposerItem *>( changeItem );
|
|
||||||
|
|
||||||
double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
|
|
||||||
QPointF snappedPosition = mComposition->snapPointToGrid( currentPosition );
|
|
||||||
|
|
||||||
//snap to grid and align to other items
|
|
||||||
if ( mComposition->alignmentSnap() && mCurrentMouseMoveAction != QgsComposerItem::MoveItem )
|
|
||||||
{
|
|
||||||
double alignX = 0;
|
|
||||||
double alignY = 0;
|
|
||||||
snappedPosition = mComposition->alignPos( snappedPosition, dynamic_cast<const QgsComposerItem*>( originalItem ), alignX, alignY );
|
|
||||||
if ( alignX != -1 )
|
|
||||||
{
|
|
||||||
QGraphicsLineItem* item = hAlignSnapItem();
|
|
||||||
item->setLine( QLineF( alignX, 0, alignX, mComposition->paperHeight() ) );
|
|
||||||
item->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteHAlignSnapItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( alignY != -1 )
|
|
||||||
{
|
|
||||||
QGraphicsLineItem* item = vAlignSnapItem();
|
|
||||||
item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
|
|
||||||
item->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteVAlignSnapItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double diffX = 0;
|
|
||||||
double diffY = 0;
|
|
||||||
|
|
||||||
switch ( mCurrentMouseMoveAction )
|
|
||||||
{
|
|
||||||
//vertical resize
|
|
||||||
case QgsComposerItem::ResizeUp:
|
|
||||||
diffY = snappedPosition.y() - originalItem->transform().dy();
|
|
||||||
mx = 0; my = diffY; rx = 0; ry = -diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::ResizeDown:
|
|
||||||
diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
|
|
||||||
mx = 0; my = 0; rx = 0; ry = diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
//horizontal resize
|
|
||||||
case QgsComposerItem::ResizeLeft:
|
|
||||||
diffX = snappedPosition.x() - originalItem->transform().dx();
|
|
||||||
mx = diffX, my = 0; rx = -diffX; ry = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::ResizeRight:
|
|
||||||
diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
|
|
||||||
mx = 0; my = 0; rx = diffX, ry = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
//diagonal resize
|
|
||||||
case QgsComposerItem::ResizeLeftUp:
|
|
||||||
diffX = snappedPosition.x() - originalItem->transform().dx();
|
|
||||||
diffY = snappedPosition.y() - originalItem->transform().dy();
|
|
||||||
mx = diffX, my = diffY; rx = -diffX; ry = -diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::ResizeRightDown:
|
|
||||||
diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
|
|
||||||
diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
|
|
||||||
mx = 0; my = 0; rx = diffX, ry = diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::ResizeRightUp:
|
|
||||||
diffX = snappedPosition.x() - ( originalItem->transform().dx() + originalItem->rect().width() );
|
|
||||||
diffY = snappedPosition.y() - originalItem->transform().dy();
|
|
||||||
mx = 0; my = diffY, rx = diffX, ry = -diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::ResizeLeftDown:
|
|
||||||
diffX = snappedPosition.x() - originalItem->transform().dx();
|
|
||||||
diffY = snappedPosition.y() - ( originalItem->transform().dy() + originalItem->rect().height() );
|
|
||||||
mx = diffX, my = 0; rx = -diffX; ry = diffY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QgsComposerItem::MoveItem:
|
|
||||||
{
|
|
||||||
//calculate total move difference
|
|
||||||
double moveX = currentPosition.x() - mouseMoveStartPos.x();
|
|
||||||
double moveY = currentPosition.y() - mouseMoveStartPos.y();
|
|
||||||
|
|
||||||
QPointF upperLeftPoint( originalItem->transform().dx() + moveX, originalItem->transform().dy() + moveY );
|
|
||||||
QPointF snappedLeftPoint = mComposition->snapPointToGrid( upperLeftPoint );
|
|
||||||
|
|
||||||
if ( snappedLeftPoint != upperLeftPoint ) //don't do align snap if grid snap has been done
|
|
||||||
{
|
|
||||||
deleteAlignItems();
|
|
||||||
}
|
|
||||||
else if ( mComposition->alignmentSnap() ) //align item
|
|
||||||
{
|
|
||||||
double alignX = 0;
|
|
||||||
double alignY = 0;
|
|
||||||
snappedLeftPoint = mComposition->alignItem( dynamic_cast<const QgsComposerItem*>( originalItem ), alignX, alignY, moveX, moveY );
|
|
||||||
if ( alignX != -1 )
|
|
||||||
{
|
|
||||||
QGraphicsLineItem* item = hAlignSnapItem();
|
|
||||||
int numPages = mComposition->numPages();
|
|
||||||
double yLineCoord = 300; //default in case there is no single page
|
|
||||||
if ( numPages > 0 )
|
|
||||||
{
|
|
||||||
yLineCoord = mComposition->paperHeight() * numPages + mComposition->spaceBetweenPages() * ( numPages - 1 );
|
|
||||||
}
|
|
||||||
item->setLine( QLineF( alignX, 0, alignX, yLineCoord ) );
|
|
||||||
item->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteHAlignSnapItem();
|
|
||||||
}
|
|
||||||
if ( alignY != -1 )
|
|
||||||
{
|
|
||||||
QGraphicsLineItem* item = vAlignSnapItem();
|
|
||||||
item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
|
|
||||||
item->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteVAlignSnapItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double moveRectX = snappedLeftPoint.x() - originalItem->transform().dx();
|
|
||||||
double moveRectY = snappedLeftPoint.y() - originalItem->transform().dy();
|
|
||||||
|
|
||||||
if ( !changeComposerItem )
|
|
||||||
{
|
|
||||||
QTransform moveTransform;
|
|
||||||
moveTransform.translate( originalItem->transform().dx() + moveRectX, originalItem->transform().dy() + moveRectY );
|
|
||||||
changeItem->setTransform( moveTransform );
|
|
||||||
}
|
|
||||||
else //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
|
|
||||||
{
|
|
||||||
changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + moveRectX,
|
|
||||||
originalItem->transform().dy() + moveRectY,
|
|
||||||
originalItem->rect().width(), originalItem->rect().height() ) );
|
|
||||||
changeComposerItem->updateItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case QgsComposerItem::NoAction:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !changeComposerItem )
|
|
||||||
{
|
|
||||||
QTransform itemTransform;
|
|
||||||
itemTransform.translate( originalItem->transform().dx() + mx, originalItem->transform().dy() + my );
|
|
||||||
changeItem->setTransform( itemTransform );
|
|
||||||
QRectF itemRect( 0, 0, originalItem->rect().width() + rx, originalItem->rect().height() + ry );
|
|
||||||
changeItem->setRect( itemRect );
|
|
||||||
}
|
|
||||||
else //for composer items, we prefer setSceneRect as subclasses can implement custom behaviour (e.g. item group)
|
|
||||||
{
|
|
||||||
changeComposerItem->setSceneRect( QRectF( originalItem->transform().dx() + mx, originalItem->transform().dy() + my,
|
|
||||||
originalItem->rect().width() + rx, originalItem->rect().height() + ry ) );
|
|
||||||
changeComposerItem->updateItem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::drawSelectionBoxes( QPainter* p )
|
void QgsComposerItem::drawSelectionBoxes( QPainter* p )
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( !mComposition )
|
if ( !mComposition )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -760,8 +378,6 @@ void QgsComposerItem::drawSelectionBoxes( QPainter* p )
|
|||||||
|
|
||||||
if ( mComposition->plotStyle() == QgsComposition::Preview )
|
if ( mComposition->plotStyle() == QgsComposition::Preview )
|
||||||
{
|
{
|
||||||
//size of symbol boxes depends on zoom level in composer view
|
|
||||||
double rectHandlerSize = rectHandlerBorderTolerance();
|
|
||||||
double sizeLockSymbol = lockSymbolSize();
|
double sizeLockSymbol = lockSymbolSize();
|
||||||
|
|
||||||
if ( mItemPositionLocked )
|
if ( mItemPositionLocked )
|
||||||
@ -779,15 +395,6 @@ void QgsComposerItem::drawSelectionBoxes( QPainter* p )
|
|||||||
p->drawImage( QRectF( 0, 0, sizeLockSymbol, sizeLockSymbol ), lockImage, QRectF( 0, 0, lockImage.width(), lockImage.height() ) );
|
p->drawImage( QRectF( 0, 0, sizeLockSymbol, sizeLockSymbol ), lockImage, QRectF( 0, 0, lockImage.width(), lockImage.height() ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else //draw blue squares
|
|
||||||
{
|
|
||||||
p->setPen( QColor( 50, 100, 120, 200 ) );
|
|
||||||
p->setBrush( QColor( 200, 200, 210, 120 ) );
|
|
||||||
p->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
|
|
||||||
p->drawRect( QRectF( rect().width() - rectHandlerSize, 0, rectHandlerSize, rectHandlerSize ) );
|
|
||||||
p->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
|
|
||||||
p->drawRect( QRectF( 0, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,18 +529,6 @@ void QgsComposerItem::setEffectsEnabled( bool effectsEnabled )
|
|||||||
mEffect->setEnabled( effectsEnabled );
|
mEffect->setEnabled( effectsEnabled );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsComposerItem::hoverMoveEvent( QGraphicsSceneHoverEvent * event )
|
|
||||||
{
|
|
||||||
if ( isSelected() )
|
|
||||||
{
|
|
||||||
setCursor( cursorForPosition( event->pos() ) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setCursor( Qt::ArrowCursor );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font ) const
|
void QgsComposerItem::drawText( QPainter* p, double x, double y, const QString& text, const QFont& font ) const
|
||||||
{
|
{
|
||||||
QFont textFont = scaledFontPixelSize( font );
|
QFont textFont = scaledFontPixelSize( font );
|
||||||
@ -1108,11 +703,6 @@ double QgsComposerItem::lockSymbolSize() const
|
|||||||
return lockSymbolSize;
|
return lockSymbolSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsComposerItem::updateCursor( const QPointF& itemPos )
|
|
||||||
{
|
|
||||||
setCursor( cursorForPosition( itemPos ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposerItem::setRotation( double r )
|
void QgsComposerItem::setRotation( double r )
|
||||||
{
|
{
|
||||||
if ( r > 360 )
|
if ( r > 360 )
|
||||||
|
@ -108,7 +108,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
|
|||||||
virtual void setSelected( bool s );
|
virtual void setSelected( bool s );
|
||||||
|
|
||||||
/** \brief Is selected */
|
/** \brief Is selected */
|
||||||
virtual bool selected() {return QGraphicsRectItem::isSelected();}
|
virtual bool selected() const {return QGraphicsRectItem::isSelected();};
|
||||||
|
|
||||||
/** stores state in project */
|
/** stores state in project */
|
||||||
virtual bool writeSettings();
|
virtual bool writeSettings();
|
||||||
@ -287,10 +287,6 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
|
|||||||
@note this method was added in version 1.2*/
|
@note this method was added in version 1.2*/
|
||||||
bool positionLock() const {return mItemPositionLocked;}
|
bool positionLock() const {return mItemPositionLocked;}
|
||||||
|
|
||||||
/**Update mouse cursor at (item) position
|
|
||||||
@note this method was added in version 1.2*/
|
|
||||||
void updateCursor( const QPointF& itemPos );
|
|
||||||
|
|
||||||
double rotation() const {return mRotation;}
|
double rotation() const {return mRotation;}
|
||||||
|
|
||||||
/**Updates item, with the possibility to do custom update for subclasses*/
|
/**Updates item, with the possibility to do custom update for subclasses*/
|
||||||
@ -357,29 +353,6 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
|
|||||||
@note: this member was added in version 2.0*/
|
@note: this member was added in version 2.0*/
|
||||||
ItemPositionMode mLastUsedPositionMode;
|
ItemPositionMode mLastUsedPositionMode;
|
||||||
|
|
||||||
//event handlers
|
|
||||||
virtual void mouseMoveEvent( QGraphicsSceneMouseEvent * event );
|
|
||||||
virtual void mousePressEvent( QGraphicsSceneMouseEvent * event );
|
|
||||||
virtual void mouseReleaseEvent( QGraphicsSceneMouseEvent * event );
|
|
||||||
|
|
||||||
virtual void hoverMoveEvent( QGraphicsSceneHoverEvent * event );
|
|
||||||
|
|
||||||
/**Finds out the appropriate cursor for the current mouse position in the widget (e.g. move in the middle, resize at border)*/
|
|
||||||
Qt::CursorShape cursorForPosition( const QPointF& itemCoordPos );
|
|
||||||
|
|
||||||
/**Finds out which mouse move action to choose depending on the cursor position inside the widget*/
|
|
||||||
QgsComposerItem::MouseMoveAction mouseMoveActionForPosition( const QPointF& itemCoordPos );
|
|
||||||
|
|
||||||
/**Changes the rectangle of an item depending on current mouse action (resize or move)
|
|
||||||
@param currentPosition current position of mouse cursor
|
|
||||||
@param mouseMoveStartPos cursor position at the start of the current mouse action
|
|
||||||
@param originalItem Item position at the start of the mouse action
|
|
||||||
@param dx x-Change of mouse cursor
|
|
||||||
@param dy y-Change of mouse cursor
|
|
||||||
@param changeItem Item to change size (can be the same as originalItem or a differen one)
|
|
||||||
*/
|
|
||||||
void changeItemRectangle( const QPointF& currentPosition, const QPointF& mouseMoveStartPos, const QGraphicsRectItem* originalItem, double dx, double dy, QGraphicsRectItem* changeItem );
|
|
||||||
|
|
||||||
/**Draw selection boxes around item*/
|
/**Draw selection boxes around item*/
|
||||||
virtual void drawSelectionBoxes( QPainter* p );
|
virtual void drawSelectionBoxes( QPainter* p );
|
||||||
|
|
||||||
|
893
src/core/composer/qgscomposermousehandles.cpp
Normal file
893
src/core/composer/qgscomposermousehandles.cpp
Normal file
@ -0,0 +1,893 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgscomposermousehandles.cpp
|
||||||
|
-------------------
|
||||||
|
begin : September 2013
|
||||||
|
copyright : (C) 2013 by Nyall Dawson, Radim Blazek
|
||||||
|
email : nyall.dawson@gmail.com
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "qgscomposermousehandles.h"
|
||||||
|
#include "qgscomposeritem.h"
|
||||||
|
#include "qgscomposition.h"
|
||||||
|
#include "qgis.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
|
||||||
|
QgsComposerMouseHandles::QgsComposerMouseHandles( QgsComposition *composition ) : QObject( 0 ),
|
||||||
|
QGraphicsRectItem( 0 ),
|
||||||
|
mComposition( composition ),
|
||||||
|
mBeginHandleWidth( 0 ),
|
||||||
|
mBeginHandleHeight( 0 ),
|
||||||
|
mIsDragging( false ),
|
||||||
|
mIsResizing( false ),
|
||||||
|
mHAlignSnapItem( 0 ),
|
||||||
|
mVAlignSnapItem( 0 )
|
||||||
|
{
|
||||||
|
//listen for selection changes, and update handles accordingly
|
||||||
|
QObject::connect( mComposition, SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
|
||||||
|
|
||||||
|
//accept hover events, required for changing cursor to resize cursors
|
||||||
|
setAcceptsHoverEvents( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsComposerMouseHandles::~QgsComposerMouseHandles()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
|
||||||
|
{
|
||||||
|
Q_UNUSED( itemStyle );
|
||||||
|
Q_UNUSED( pWidget );
|
||||||
|
|
||||||
|
//draw resize handles around bounds of entire selection
|
||||||
|
double rectHandlerSize = rectHandlerBorderTolerance();
|
||||||
|
drawHandles( painter, rectHandlerSize );
|
||||||
|
|
||||||
|
//draw dotted boxes around selected items
|
||||||
|
drawSelectedItemBounds( painter );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::drawHandles( QPainter* painter, double rectHandlerSize )
|
||||||
|
{
|
||||||
|
//blue, zero width cosmetic pen for outline
|
||||||
|
QPen handlePen = QPen( QColor( 55, 140, 195, 255 ) );
|
||||||
|
handlePen.setWidth( 0 );
|
||||||
|
painter->setPen( handlePen );
|
||||||
|
|
||||||
|
//draw box around entire selection bounds
|
||||||
|
painter->setBrush( Qt::NoBrush );
|
||||||
|
painter->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
|
||||||
|
|
||||||
|
//draw resize handles, using a filled white box
|
||||||
|
painter->setBrush( QColor( 255, 255, 255, 255 ) );
|
||||||
|
//top left
|
||||||
|
painter->drawRect( QRectF( 0, 0, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//mid top
|
||||||
|
painter->drawRect( QRectF(( rect().width() - rectHandlerSize ) / 2, 0, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//top right
|
||||||
|
painter->drawRect( QRectF( rect().width() - rectHandlerSize, 0, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//mid left
|
||||||
|
painter->drawRect( QRectF( 0, ( rect().height() - rectHandlerSize ) / 2, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//mid right
|
||||||
|
painter->drawRect( QRectF( rect().width() - rectHandlerSize, ( rect().height() - rectHandlerSize ) / 2, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//bottom left
|
||||||
|
painter->drawRect( QRectF( 0, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//mid bottom
|
||||||
|
painter->drawRect( QRectF(( rect().width() - rectHandlerSize ) / 2, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
//bottom right
|
||||||
|
painter->drawRect( QRectF( rect().width() - rectHandlerSize, rect().height() - rectHandlerSize, rectHandlerSize, rectHandlerSize ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::drawSelectedItemBounds( QPainter* painter )
|
||||||
|
{
|
||||||
|
//draw dotted border around selected items to give visual feedback which items are selected
|
||||||
|
QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems();
|
||||||
|
if ( selectedItems.size() == 0 )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//use difference mode so that they are visible regardless of item colors
|
||||||
|
painter->save();
|
||||||
|
painter->setCompositionMode( QPainter::CompositionMode_Difference );
|
||||||
|
|
||||||
|
// use a grey dashed pen - in difference mode this should always be visible
|
||||||
|
QPen selectedItemPen = QPen( QColor( 144, 144, 144, 255 ) );
|
||||||
|
selectedItemPen.setStyle( Qt::DashLine );
|
||||||
|
selectedItemPen.setWidth( 0 );
|
||||||
|
painter->setPen( selectedItemPen );
|
||||||
|
painter->setBrush( Qt::NoBrush );
|
||||||
|
|
||||||
|
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
|
||||||
|
for ( ; itemIter != selectedItems.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
//scene bounds of selected item
|
||||||
|
QRectF itemSceneBounds = ( *itemIter )->sceneBoundingRect();
|
||||||
|
//convert scene bounds to handle item bounds
|
||||||
|
QRectF itemBounds;
|
||||||
|
if ( mIsDragging )
|
||||||
|
{
|
||||||
|
//if currently dragging, draw selected item bounds relative to current mouse position
|
||||||
|
itemBounds = mapRectFromScene( itemSceneBounds );
|
||||||
|
itemBounds.translate( transform().dx(), transform().dy() );
|
||||||
|
}
|
||||||
|
else if ( mIsResizing )
|
||||||
|
{
|
||||||
|
//if currently resizing, calculate relative resize of this item
|
||||||
|
itemBounds = itemSceneBounds;
|
||||||
|
relativeResizeRect( itemBounds, QRectF( mBeginHandlePos.x(), mBeginHandlePos.y(), mBeginHandleWidth, mBeginHandleHeight ), sceneBoundingRect() );
|
||||||
|
itemBounds = mapRectFromScene( itemBounds );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//not resizing or moving, so just map from scene bounds
|
||||||
|
itemBounds = mapRectFromScene( itemSceneBounds );
|
||||||
|
}
|
||||||
|
painter->drawRect( itemBounds );
|
||||||
|
}
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::selectionChanged()
|
||||||
|
{
|
||||||
|
//listen out for selected items' sizeChanged signal
|
||||||
|
QList<QGraphicsItem *> itemList = composition()->items();
|
||||||
|
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
|
||||||
|
for ( ; itemIt != itemList.end(); ++itemIt )
|
||||||
|
{
|
||||||
|
QgsComposerItem* item = dynamic_cast<QgsComposerItem *>( *itemIt );
|
||||||
|
if ( item )
|
||||||
|
{
|
||||||
|
if ( item->selected() )
|
||||||
|
{
|
||||||
|
QObject::connect( item, SIGNAL( sizeChanged() ), this, SLOT( selectedItemSizeChanged() ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QObject::disconnect( item, SIGNAL( sizeChanged() ), this, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::selectedItemSizeChanged()
|
||||||
|
{
|
||||||
|
if ( !mIsDragging && !mIsResizing )
|
||||||
|
{
|
||||||
|
//only required for non-mouse initiated size changes
|
||||||
|
updateHandles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::updateHandles()
|
||||||
|
{
|
||||||
|
//recalculate size and position of handle item
|
||||||
|
|
||||||
|
//first check to see if any items are selected
|
||||||
|
QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems();
|
||||||
|
if ( selectedItems.size() > 0 )
|
||||||
|
{
|
||||||
|
//one or more items are selected, get bounds of all selected items
|
||||||
|
QRectF newHandleBounds = selectionBounds();
|
||||||
|
//update size and position of handle object
|
||||||
|
setRect( 0, 0, newHandleBounds.width(), newHandleBounds.height() );
|
||||||
|
setPos( newHandleBounds.topLeft() );
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//no items selected, hide handles
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
//force redraw
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF QgsComposerMouseHandles::selectionBounds() const
|
||||||
|
{
|
||||||
|
//calculate scene bounds of all currently selected items
|
||||||
|
QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems();
|
||||||
|
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
|
||||||
|
|
||||||
|
//start with scene bounds of first selected item
|
||||||
|
QRectF bounds = ( *itemIter )->sceneBoundingRect();
|
||||||
|
|
||||||
|
//iterate through remaining items, expanding the bounds as required
|
||||||
|
for ( ++itemIter; itemIter != selectedItems.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
bounds = bounds.united(( *itemIter )->sceneBoundingRect() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
double QgsComposerMouseHandles::rectHandlerBorderTolerance() const
|
||||||
|
{
|
||||||
|
//calculate size for resize handles
|
||||||
|
//get view scale factor
|
||||||
|
QList<QGraphicsView*> viewList = mComposition->views();
|
||||||
|
QGraphicsView* currentView = viewList.at( 0 );
|
||||||
|
double viewScaleFactor = currentView->transform().m11();
|
||||||
|
|
||||||
|
//size of handle boxes depends on zoom level in composer view
|
||||||
|
double rectHandlerSize = 10.0 / viewScaleFactor;
|
||||||
|
|
||||||
|
//make sure the boxes don't get too large
|
||||||
|
if ( rectHandlerSize > ( rect().width() / 3 ) )
|
||||||
|
{
|
||||||
|
rectHandlerSize = rect().width() / 3;
|
||||||
|
}
|
||||||
|
if ( rectHandlerSize > ( rect().height() / 3 ) )
|
||||||
|
{
|
||||||
|
rectHandlerSize = rect().height() / 3;
|
||||||
|
}
|
||||||
|
return rectHandlerSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::CursorShape QgsComposerMouseHandles::cursorForPosition( const QPointF& itemCoordPos )
|
||||||
|
{
|
||||||
|
QgsComposerMouseHandles::MouseAction mouseAction = mouseActionForPosition( itemCoordPos );
|
||||||
|
switch ( mouseAction )
|
||||||
|
{
|
||||||
|
case NoAction:
|
||||||
|
return Qt::ForbiddenCursor;
|
||||||
|
case MoveItem:
|
||||||
|
return Qt::SizeAllCursor;
|
||||||
|
case ResizeUp:
|
||||||
|
case ResizeDown:
|
||||||
|
return Qt::SizeVerCursor;
|
||||||
|
case ResizeLeft:
|
||||||
|
case ResizeRight:
|
||||||
|
return Qt::SizeHorCursor;
|
||||||
|
case ResizeLeftUp:
|
||||||
|
case ResizeRightDown:
|
||||||
|
return Qt::SizeFDiagCursor;
|
||||||
|
case ResizeRightUp:
|
||||||
|
case ResizeLeftDown:
|
||||||
|
return Qt::SizeBDiagCursor;
|
||||||
|
case SelectItem:
|
||||||
|
default:
|
||||||
|
return Qt::ArrowCursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsComposerMouseHandles::MouseAction QgsComposerMouseHandles::mouseActionForPosition( const QPointF& itemCoordPos )
|
||||||
|
{
|
||||||
|
bool nearLeftBorder = false;
|
||||||
|
bool nearRightBorder = false;
|
||||||
|
bool nearLowerBorder = false;
|
||||||
|
bool nearUpperBorder = false;
|
||||||
|
|
||||||
|
double borderTolerance = rectHandlerBorderTolerance();
|
||||||
|
|
||||||
|
if ( itemCoordPos.x() >= 0 && itemCoordPos.x() < borderTolerance )
|
||||||
|
{
|
||||||
|
nearLeftBorder = true;
|
||||||
|
}
|
||||||
|
if ( itemCoordPos.y() >= 0 && itemCoordPos.y() < borderTolerance )
|
||||||
|
{
|
||||||
|
nearUpperBorder = true;
|
||||||
|
}
|
||||||
|
if ( itemCoordPos.x() <= rect().width() && itemCoordPos.x() > ( rect().width() - borderTolerance ) )
|
||||||
|
{
|
||||||
|
nearRightBorder = true;
|
||||||
|
}
|
||||||
|
if ( itemCoordPos.y() <= rect().height() && itemCoordPos.y() > ( rect().height() - borderTolerance ) )
|
||||||
|
{
|
||||||
|
nearLowerBorder = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( nearLeftBorder && nearUpperBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeLeftUp;
|
||||||
|
}
|
||||||
|
else if ( nearLeftBorder && nearLowerBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeLeftDown;
|
||||||
|
}
|
||||||
|
else if ( nearRightBorder && nearUpperBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeRightUp;
|
||||||
|
}
|
||||||
|
else if ( nearRightBorder && nearLowerBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeRightDown;
|
||||||
|
}
|
||||||
|
else if ( nearLeftBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeLeft;
|
||||||
|
}
|
||||||
|
else if ( nearRightBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeRight;
|
||||||
|
}
|
||||||
|
else if ( nearUpperBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeUp;
|
||||||
|
}
|
||||||
|
else if ( nearLowerBorder )
|
||||||
|
{
|
||||||
|
return QgsComposerMouseHandles::ResizeDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find out if cursor position is over a selected item
|
||||||
|
QPointF scenePoint = mapToScene( itemCoordPos );
|
||||||
|
QList<QGraphicsItem *> itemsAtCursorPos = mComposition->items( scenePoint );
|
||||||
|
if ( itemsAtCursorPos.size() == 0 )
|
||||||
|
{
|
||||||
|
//no items at cursor position
|
||||||
|
return QgsComposerMouseHandles::SelectItem;
|
||||||
|
}
|
||||||
|
QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
|
||||||
|
for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
|
||||||
|
if ( item && item->selected() )
|
||||||
|
{
|
||||||
|
//cursor is over a selected composer item
|
||||||
|
return QgsComposerMouseHandles::MoveItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//default
|
||||||
|
return QgsComposerMouseHandles::SelectItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsComposerMouseHandles::MouseAction QgsComposerMouseHandles::mouseActionForScenePos( const QPointF& sceneCoordPos )
|
||||||
|
{
|
||||||
|
// convert sceneCoordPos to item coordinates
|
||||||
|
QPointF itemPos = mapFromScene( sceneCoordPos );
|
||||||
|
return mouseActionForPosition( itemPos );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::hoverMoveEvent( QGraphicsSceneHoverEvent * event )
|
||||||
|
{
|
||||||
|
setCursor( cursorForPosition( event->pos() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
|
||||||
|
{
|
||||||
|
if ( mIsDragging )
|
||||||
|
{
|
||||||
|
//currently dragging a selection
|
||||||
|
dragMouseMove( event->lastScenePos() );
|
||||||
|
}
|
||||||
|
else if ( mIsResizing )
|
||||||
|
{
|
||||||
|
//currently resizing a selection
|
||||||
|
resizeMouseMove( event->lastScenePos() );
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastMouseEventPos = event->lastScenePos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
|
||||||
|
{
|
||||||
|
QPointF mouseMoveStopPoint = event->lastScenePos();
|
||||||
|
double diffX = mouseMoveStopPoint.x() - mMouseMoveStartPos.x();
|
||||||
|
double diffY = mouseMoveStopPoint.y() - mMouseMoveStartPos.y();
|
||||||
|
|
||||||
|
//it was only a click
|
||||||
|
if ( qAbs( diffX ) < std::numeric_limits<double>::min() && qAbs( diffY ) < std::numeric_limits<double>::min() )
|
||||||
|
{
|
||||||
|
mIsDragging = false;
|
||||||
|
mIsResizing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( mCurrentMouseMoveAction == QgsComposerMouseHandles::MoveItem )
|
||||||
|
{
|
||||||
|
//move selected items
|
||||||
|
QUndoCommand* parentCommand = new QUndoCommand( tr( "Change item position" ) );
|
||||||
|
|
||||||
|
QPointF mEndHandleMovePos = scenePos();
|
||||||
|
|
||||||
|
//move all selected items
|
||||||
|
QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems();
|
||||||
|
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
|
||||||
|
for ( ; itemIter != selectedItems.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
|
||||||
|
subcommand->savePreviousState();
|
||||||
|
( *itemIter )->move( mEndHandleMovePos.x() - mBeginHandlePos.x(), mEndHandleMovePos.y() - mBeginHandlePos.y() );
|
||||||
|
subcommand->saveAfterState();
|
||||||
|
}
|
||||||
|
mComposition->undoStack()->push( parentCommand );
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( mCurrentMouseMoveAction != QgsComposerMouseHandles::NoAction )
|
||||||
|
{
|
||||||
|
//resize selected items
|
||||||
|
QUndoCommand* parentCommand = new QUndoCommand( tr( "Change item size" ) );
|
||||||
|
|
||||||
|
//resize all selected items
|
||||||
|
QList<QgsComposerItem*> selectedItems = mComposition->selectedComposerItems();
|
||||||
|
QList<QgsComposerItem*>::iterator itemIter = selectedItems.begin();
|
||||||
|
for ( ; itemIter != selectedItems.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
QgsComposerItemCommand* subcommand = new QgsComposerItemCommand( *itemIter, "", parentCommand );
|
||||||
|
subcommand->savePreviousState();
|
||||||
|
QRectF itemBounds = ( *itemIter )->sceneBoundingRect();
|
||||||
|
relativeResizeRect( itemBounds, QRectF( mBeginHandlePos.x(), mBeginHandlePos.y(), mBeginHandleWidth, mBeginHandleHeight ), sceneBoundingRect() );
|
||||||
|
( *itemIter )->setSceneRect( itemBounds );
|
||||||
|
subcommand->saveAfterState();
|
||||||
|
}
|
||||||
|
mComposition->undoStack()->push( parentCommand );
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAlignItems();
|
||||||
|
|
||||||
|
if ( mIsDragging )
|
||||||
|
{
|
||||||
|
mIsDragging = false;
|
||||||
|
}
|
||||||
|
if ( mIsResizing )
|
||||||
|
{
|
||||||
|
mIsResizing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset default action
|
||||||
|
mCurrentMouseMoveAction = QgsComposerMouseHandles::MoveItem;
|
||||||
|
setCursor( Qt::ArrowCursor );
|
||||||
|
//redraw handles
|
||||||
|
resetTransform();
|
||||||
|
updateHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::mousePressEvent( QGraphicsSceneMouseEvent* event )
|
||||||
|
{
|
||||||
|
//save current cursor position
|
||||||
|
mMouseMoveStartPos = event->lastScenePos();
|
||||||
|
mLastMouseEventPos = event->lastScenePos();
|
||||||
|
//save current item geometry
|
||||||
|
mBeginMouseEventPos = event->lastScenePos();
|
||||||
|
mBeginHandlePos = scenePos();
|
||||||
|
mBeginHandleWidth = rect().width();
|
||||||
|
mBeginHandleHeight = rect().height();
|
||||||
|
//type of mouse move action
|
||||||
|
mCurrentMouseMoveAction = mouseActionForPosition( event->pos() );
|
||||||
|
|
||||||
|
deleteAlignItems();
|
||||||
|
|
||||||
|
if ( mCurrentMouseMoveAction == QgsComposerMouseHandles::MoveItem )
|
||||||
|
{
|
||||||
|
mIsDragging = true;
|
||||||
|
}
|
||||||
|
else if ( mCurrentMouseMoveAction != QgsComposerMouseHandles::SelectItem &&
|
||||||
|
mCurrentMouseMoveAction != QgsComposerMouseHandles::NoAction )
|
||||||
|
{
|
||||||
|
mIsResizing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::dragMouseMove( const QPointF& currentPosition )
|
||||||
|
{
|
||||||
|
if ( !mComposition )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//calculate total amount of mouse movement since drag began
|
||||||
|
double moveX = currentPosition.x() - mBeginMouseEventPos.x();
|
||||||
|
double moveY = currentPosition.y() - mBeginMouseEventPos.y();
|
||||||
|
|
||||||
|
//find target position before snapping (in scene coordinates)
|
||||||
|
QPointF upperLeftPoint( mBeginHandlePos.x() + moveX, mBeginHandlePos.y() + moveY );
|
||||||
|
|
||||||
|
//snap to grid and guides
|
||||||
|
QPointF snappedLeftPoint = snapPoint( upperLeftPoint, QgsComposerMouseHandles::Item );
|
||||||
|
|
||||||
|
//TODO: shift moving should lock to horizontal/vertical movement
|
||||||
|
|
||||||
|
//calculate total shift for item from beginning of drag operation to current position
|
||||||
|
double moveRectX = snappedLeftPoint.x() - mBeginHandlePos.x();
|
||||||
|
double moveRectY = snappedLeftPoint.y() - mBeginHandlePos.y();
|
||||||
|
|
||||||
|
//shift handle item to new position
|
||||||
|
QTransform moveTransform;
|
||||||
|
moveTransform.translate( moveRectX, moveRectY );
|
||||||
|
setTransform( moveTransform );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::resizeMouseMove( const QPointF& currentPosition )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( !mComposition )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
|
||||||
|
QPointF snappedPosition = snapPoint( currentPosition, QgsComposerMouseHandles::Point );
|
||||||
|
|
||||||
|
double diffX = 0;
|
||||||
|
double diffY = 0;
|
||||||
|
|
||||||
|
//TODO: shift-resizing should lock aspect ratio
|
||||||
|
|
||||||
|
//TODO: resizing eg from top handle to below bottom handle
|
||||||
|
switch ( mCurrentMouseMoveAction )
|
||||||
|
{
|
||||||
|
//vertical resize
|
||||||
|
case QgsComposerMouseHandles::ResizeUp:
|
||||||
|
diffY = snappedPosition.y() - mBeginHandlePos.y();
|
||||||
|
mx = 0; my = diffY; rx = 0; ry = -diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::ResizeDown:
|
||||||
|
diffY = snappedPosition.y() - ( mBeginHandlePos.y() + mBeginHandleHeight );
|
||||||
|
mx = 0; my = 0; rx = 0; ry = diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//horizontal resize
|
||||||
|
case QgsComposerMouseHandles::ResizeLeft:
|
||||||
|
diffX = snappedPosition.x() - mBeginHandlePos.x();
|
||||||
|
mx = diffX, my = 0; rx = -diffX; ry = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::ResizeRight:
|
||||||
|
diffX = snappedPosition.x() - ( mBeginHandlePos.x() + mBeginHandleWidth );
|
||||||
|
mx = 0; my = 0; rx = diffX, ry = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//diagonal resize
|
||||||
|
case QgsComposerMouseHandles::ResizeLeftUp:
|
||||||
|
diffX = snappedPosition.x() - mBeginHandlePos.x();
|
||||||
|
diffY = snappedPosition.y() - mBeginHandlePos.y();
|
||||||
|
mx = diffX, my = diffY; rx = -diffX; ry = -diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::ResizeRightDown:
|
||||||
|
diffX = snappedPosition.x() - ( mBeginHandlePos.x() + mBeginHandleWidth );
|
||||||
|
diffY = snappedPosition.y() - ( mBeginHandlePos.y() + mBeginHandleHeight );
|
||||||
|
mx = 0; my = 0; rx = diffX, ry = diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::ResizeRightUp:
|
||||||
|
diffX = snappedPosition.x() - ( mBeginHandlePos.x() + mBeginHandleWidth );
|
||||||
|
diffY = snappedPosition.y() - mBeginHandlePos.y();
|
||||||
|
mx = 0; my = diffY, rx = diffX, ry = -diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::ResizeLeftDown:
|
||||||
|
diffX = snappedPosition.x() - mBeginHandlePos.x();
|
||||||
|
diffY = snappedPosition.y() - ( mBeginHandlePos.y() + mBeginHandleHeight );
|
||||||
|
mx = diffX, my = 0; rx = -diffX; ry = diffY;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsComposerMouseHandles::MoveItem:
|
||||||
|
case QgsComposerMouseHandles::SelectItem:
|
||||||
|
case QgsComposerMouseHandles::NoAction:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//update selection handle rectangle
|
||||||
|
QTransform itemTransform;
|
||||||
|
itemTransform.translate( mx, my );
|
||||||
|
setTransform( itemTransform );
|
||||||
|
QRectF itemRect( 0, 0, mBeginHandleWidth + rx, mBeginHandleHeight + ry );
|
||||||
|
setRect( itemRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
QPointF snappedPoint = mComposition->snapPointToGrid( point );
|
||||||
|
|
||||||
|
if ( snappedPoint != point ) //don't do align snap if grid snap has been done
|
||||||
|
{
|
||||||
|
deleteAlignItems();
|
||||||
|
return snappedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
//align item
|
||||||
|
if ( !mComposition->alignmentSnap() )
|
||||||
|
{
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
double alignX = 0;
|
||||||
|
double alignY = 0;
|
||||||
|
|
||||||
|
//depending on the mode, we either snap just the single point, or all the bounds of the selection
|
||||||
|
switch ( mode )
|
||||||
|
{
|
||||||
|
case QgsComposerMouseHandles::Item:
|
||||||
|
snappedPoint = alignItem( alignX, alignY, point.x(), point.y() );
|
||||||
|
break;
|
||||||
|
case QgsComposerMouseHandles::Point:
|
||||||
|
snappedPoint = alignPos( point, alignX, alignY );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( alignX != -1 )
|
||||||
|
{
|
||||||
|
QGraphicsLineItem* item = hAlignSnapItem();
|
||||||
|
int numPages = mComposition->numPages();
|
||||||
|
double yLineCoord = 300; //default in case there is no single page
|
||||||
|
if ( numPages > 0 )
|
||||||
|
{
|
||||||
|
yLineCoord = mComposition->paperHeight() * numPages + mComposition->spaceBetweenPages() * ( numPages - 1 );
|
||||||
|
}
|
||||||
|
item->setLine( QLineF( alignX, 0, alignX, yLineCoord ) );
|
||||||
|
item->show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deleteHAlignSnapItem();
|
||||||
|
}
|
||||||
|
if ( alignY != -1 )
|
||||||
|
{
|
||||||
|
QGraphicsLineItem* item = vAlignSnapItem();
|
||||||
|
item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
|
||||||
|
item->show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deleteVAlignSnapItem();
|
||||||
|
}
|
||||||
|
return snappedPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGraphicsLineItem* QgsComposerMouseHandles::hAlignSnapItem()
|
||||||
|
{
|
||||||
|
if ( !mHAlignSnapItem )
|
||||||
|
{
|
||||||
|
mHAlignSnapItem = new QGraphicsLineItem( 0 );
|
||||||
|
mHAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
|
||||||
|
scene()->addItem( mHAlignSnapItem );
|
||||||
|
mHAlignSnapItem->setZValue( 90 );
|
||||||
|
}
|
||||||
|
return mHAlignSnapItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGraphicsLineItem* QgsComposerMouseHandles::vAlignSnapItem()
|
||||||
|
{
|
||||||
|
if ( !mVAlignSnapItem )
|
||||||
|
{
|
||||||
|
mVAlignSnapItem = new QGraphicsLineItem( 0 );
|
||||||
|
mVAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
|
||||||
|
scene()->addItem( mVAlignSnapItem );
|
||||||
|
mVAlignSnapItem->setZValue( 90 );
|
||||||
|
}
|
||||||
|
return mVAlignSnapItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::deleteHAlignSnapItem()
|
||||||
|
{
|
||||||
|
if ( mHAlignSnapItem )
|
||||||
|
{
|
||||||
|
scene()->removeItem( mHAlignSnapItem );
|
||||||
|
delete mHAlignSnapItem;
|
||||||
|
mHAlignSnapItem = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::deleteVAlignSnapItem()
|
||||||
|
{
|
||||||
|
if ( mVAlignSnapItem )
|
||||||
|
{
|
||||||
|
scene()->removeItem( mVAlignSnapItem );
|
||||||
|
delete mVAlignSnapItem;
|
||||||
|
mVAlignSnapItem = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::deleteAlignItems()
|
||||||
|
{
|
||||||
|
deleteHAlignSnapItem();
|
||||||
|
deleteVAlignSnapItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF QgsComposerMouseHandles::alignItem( double& alignX, double& alignY, double unalignedX, double unalignedY )
|
||||||
|
{
|
||||||
|
double left = unalignedX;
|
||||||
|
double right = left + rect().width();
|
||||||
|
double midH = ( left + right ) / 2.0;
|
||||||
|
double top = unalignedY;
|
||||||
|
double bottom = top + rect().height();
|
||||||
|
double midV = ( top + bottom ) / 2.0;
|
||||||
|
|
||||||
|
QMap<double, const QgsComposerItem* > xAlignCoordinates;
|
||||||
|
QMap<double, const QgsComposerItem* > yAlignCoordinates;
|
||||||
|
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates );
|
||||||
|
|
||||||
|
//find nearest matches x
|
||||||
|
double xItemLeft = left; //new left coordinate of the item
|
||||||
|
double xAlignCoord = 0;
|
||||||
|
double smallestDiffX = DBL_MAX;
|
||||||
|
|
||||||
|
checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
|
||||||
|
checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
|
||||||
|
checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
|
||||||
|
|
||||||
|
//find nearest matches y
|
||||||
|
double yItemTop = top; //new top coordinate of the item
|
||||||
|
double yAlignCoord = 0;
|
||||||
|
double smallestDiffY = DBL_MAX;
|
||||||
|
|
||||||
|
checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
|
||||||
|
checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
|
||||||
|
checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
|
||||||
|
|
||||||
|
double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : unalignedX;
|
||||||
|
alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
|
||||||
|
double yCoord = ( smallestDiffY < 5 ) ? yItemTop : unalignedY;
|
||||||
|
alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
|
||||||
|
return QPointF( xCoord, yCoord );
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF QgsComposerMouseHandles::alignPos( const QPointF& pos, double& alignX, double& alignY )
|
||||||
|
{
|
||||||
|
QMap<double, const QgsComposerItem* > xAlignCoordinates;
|
||||||
|
QMap<double, const QgsComposerItem* > yAlignCoordinates;
|
||||||
|
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates );
|
||||||
|
|
||||||
|
double nearestX = pos.x();
|
||||||
|
double nearestY = pos.y();
|
||||||
|
if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
|
||||||
|
|| !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
|
||||||
|
{
|
||||||
|
alignX = -1;
|
||||||
|
alignY = -1;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF result( pos.x(), pos.y() );
|
||||||
|
if ( abs( nearestX - pos.x() ) < mComposition->alignmentSnapTolerance() )
|
||||||
|
{
|
||||||
|
result.setX( nearestX );
|
||||||
|
alignX = nearestX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alignX = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( abs( nearestY - pos.y() ) < mComposition->alignmentSnapTolerance() )
|
||||||
|
{
|
||||||
|
result.setY( nearestY );
|
||||||
|
alignY = nearestY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alignY = -1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY )
|
||||||
|
{
|
||||||
|
alignCoordsX.clear();
|
||||||
|
alignCoordsY.clear();
|
||||||
|
|
||||||
|
QList<QGraphicsItem *> itemList = mComposition->items();
|
||||||
|
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
|
||||||
|
for ( ; itemIt != itemList.end(); ++itemIt )
|
||||||
|
{
|
||||||
|
const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
|
||||||
|
//don't snap to selected items, since they're the ones that will be snapping to something else
|
||||||
|
if ( !currentItem || currentItem->selected() )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
alignCoordsX.insert( currentItem->transform().dx(), currentItem );
|
||||||
|
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().width(), currentItem );
|
||||||
|
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().center().x(), currentItem );
|
||||||
|
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().top(), currentItem );
|
||||||
|
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().center().y(), currentItem );
|
||||||
|
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().bottom(), currentItem );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//arbitrary snap lines
|
||||||
|
QList< QGraphicsLineItem* >::const_iterator sIt = mComposition->snapLines()->constBegin();
|
||||||
|
for ( ; sIt != mComposition->snapLines()->constEnd(); ++sIt )
|
||||||
|
{
|
||||||
|
double x = ( *sIt )->line().x1();
|
||||||
|
double y = ( *sIt )->line().y1();
|
||||||
|
if ( qgsDoubleNear( y, 0.0 ) )
|
||||||
|
{
|
||||||
|
alignCoordsX.insert( x, 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alignCoordsY.insert( y, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsComposerMouseHandles::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff, double itemCoordOffset, double& itemCoord, double& alignCoord ) const
|
||||||
|
{
|
||||||
|
double currentCoord = 0;
|
||||||
|
if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double currentDiff = abs( checkCoord - currentCoord );
|
||||||
|
if ( currentDiff < mComposition->alignmentSnapTolerance() )
|
||||||
|
{
|
||||||
|
itemCoord = currentCoord + itemCoordOffset;
|
||||||
|
alignCoord = currentCoord;
|
||||||
|
smallestDiff = currentDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsComposerMouseHandles::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue ) const
|
||||||
|
{
|
||||||
|
if ( coords.size() < 1 )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap< double, const QgsComposerItem* >::const_iterator it = coords.lowerBound( value );
|
||||||
|
if ( it == coords.constBegin() ) //value smaller than first map value
|
||||||
|
{
|
||||||
|
nearestValue = it.key();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( it == coords.constEnd() ) //value larger than last map value
|
||||||
|
{
|
||||||
|
--it;
|
||||||
|
nearestValue = it.key();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//get smaller value and larger value and return the closer one
|
||||||
|
double upperVal = it.key();
|
||||||
|
--it;
|
||||||
|
double lowerVal = it.key();
|
||||||
|
|
||||||
|
double lowerDiff = value - lowerVal;
|
||||||
|
double upperDiff = upperVal - value;
|
||||||
|
if ( lowerDiff < upperDiff )
|
||||||
|
{
|
||||||
|
nearestValue = lowerVal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nearestValue = upperVal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
174
src/core/composer/qgscomposermousehandles.h
Normal file
174
src/core/composer/qgscomposermousehandles.h
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgscomposermousehandles.h
|
||||||
|
-------------------
|
||||||
|
begin : September 2013
|
||||||
|
copyright : (C) 2013 by Nyall Dawson, Radim Blazek
|
||||||
|
email : nyall.dawson@gmail.com
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSCOMPOSERMOUSEHANDLES_H
|
||||||
|
#define QGSCOMPOSERMOUSEHANDLES_H
|
||||||
|
|
||||||
|
#include <QGraphicsRectItem>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QgsComposition;
|
||||||
|
class QgsComposerItem;
|
||||||
|
|
||||||
|
/** \ingroup MapComposer
|
||||||
|
* Handles drawing of selection outlines and mouse handles. Responsible for mouse
|
||||||
|
* interactions such as resizing and moving selected items.
|
||||||
|
* */
|
||||||
|
class CORE_EXPORT QgsComposerMouseHandles: public QObject, public QGraphicsRectItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**Describes the action (move or resize in different directon) to be done during mouse move*/
|
||||||
|
enum MouseAction
|
||||||
|
{
|
||||||
|
MoveItem,
|
||||||
|
ResizeUp,
|
||||||
|
ResizeDown,
|
||||||
|
ResizeLeft,
|
||||||
|
ResizeRight,
|
||||||
|
ResizeLeftUp,
|
||||||
|
ResizeRightUp,
|
||||||
|
ResizeLeftDown,
|
||||||
|
ResizeRightDown,
|
||||||
|
SelectItem,
|
||||||
|
NoAction
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ItemPositionMode
|
||||||
|
{
|
||||||
|
UpperLeft,
|
||||||
|
UpperMiddle,
|
||||||
|
UpperRight,
|
||||||
|
MiddleLeft,
|
||||||
|
Middle,
|
||||||
|
MiddleRight,
|
||||||
|
LowerLeft,
|
||||||
|
LowerMiddle,
|
||||||
|
LowerRight
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SnapGuideMode
|
||||||
|
{
|
||||||
|
Item,
|
||||||
|
Point
|
||||||
|
};
|
||||||
|
|
||||||
|
QgsComposerMouseHandles( QgsComposition *composition );
|
||||||
|
virtual ~QgsComposerMouseHandles();
|
||||||
|
|
||||||
|
void setComposition( QgsComposition* c ) { mComposition = c; }
|
||||||
|
QgsComposition* composition() { return mComposition; }
|
||||||
|
|
||||||
|
void paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget );
|
||||||
|
|
||||||
|
/**Finds out which mouse move action to choose depending on the scene cursor position*/
|
||||||
|
QgsComposerMouseHandles::MouseAction mouseActionForScenePos( const QPointF& sceneCoordPos );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void mouseMoveEvent( QGraphicsSceneMouseEvent* event );
|
||||||
|
void mouseReleaseEvent( QGraphicsSceneMouseEvent* event );
|
||||||
|
void mousePressEvent( QGraphicsSceneMouseEvent* event );
|
||||||
|
void hoverMoveEvent( QGraphicsSceneHoverEvent * event );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
/**Sets up listeners to sizeChanged signal for all selected items*/
|
||||||
|
void selectionChanged();
|
||||||
|
|
||||||
|
/**Redraws handles when selected item size changes*/
|
||||||
|
void selectedItemSizeChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QgsComposition* mComposition; //reference to composition
|
||||||
|
|
||||||
|
QgsComposerMouseHandles::MouseAction mCurrentMouseMoveAction;
|
||||||
|
/**Start point of the last mouse move action (in scene coordinates)*/
|
||||||
|
QPointF mMouseMoveStartPos;
|
||||||
|
/**Position of the last mouse move event (in scene coordinates)*/
|
||||||
|
QPointF mLastMouseEventPos;
|
||||||
|
/**Position of the mouse at beginning of move/resize (in scene coordinates)*/
|
||||||
|
QPointF mBeginMouseEventPos;
|
||||||
|
/**Position of composer handles at beginning of move/resize (in scene coordinates)*/
|
||||||
|
QPointF mBeginHandlePos;
|
||||||
|
/**Width and height of composer handles at beginning of resize*/
|
||||||
|
double mBeginHandleWidth;
|
||||||
|
double mBeginHandleHeight;
|
||||||
|
|
||||||
|
/**True if user is currently dragging items*/
|
||||||
|
bool mIsDragging;
|
||||||
|
/**True is user is currently resizing items*/
|
||||||
|
bool mIsResizing;
|
||||||
|
|
||||||
|
/**Align snap lines*/
|
||||||
|
QGraphicsLineItem* mHAlignSnapItem;
|
||||||
|
QGraphicsLineItem* mVAlignSnapItem;
|
||||||
|
|
||||||
|
/**Returns the scene bounds of current selection*/
|
||||||
|
QRectF selectionBounds() const;
|
||||||
|
|
||||||
|
/**Redraws or hides the handles based on the current selection*/
|
||||||
|
void updateHandles();
|
||||||
|
/**Draws the handles*/
|
||||||
|
void drawHandles( QPainter* painter, double rectHandlerSize );
|
||||||
|
/**Draw outlines for selected items*/
|
||||||
|
void drawSelectedItemBounds( QPainter* painter );
|
||||||
|
|
||||||
|
/**Returns the current (zoom level dependent) tolerance to decide if mouse position is close enough to the
|
||||||
|
item border for resizing*/
|
||||||
|
double rectHandlerBorderTolerance() const;
|
||||||
|
|
||||||
|
/**Finds out the appropriate cursor for the current mouse position in the widget (e.g. move in the middle, resize at border)*/
|
||||||
|
Qt::CursorShape cursorForPosition( const QPointF& itemCoordPos );
|
||||||
|
|
||||||
|
/**Finds out which mouse move action to choose depending on the cursor position inside the widget*/
|
||||||
|
QgsComposerMouseHandles::MouseAction mouseActionForPosition( const QPointF& itemCoordPos );
|
||||||
|
|
||||||
|
/**Handles dragging of items during mouse move*/
|
||||||
|
void dragMouseMove( const QPointF& currentPosition );
|
||||||
|
/**Handles resizing of items during mouse move*/
|
||||||
|
void resizeMouseMove( const QPointF& currentPosition );
|
||||||
|
|
||||||
|
/**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();
|
||||||
|
/**Return vertical align snap item. Creates a new graphics line if 0*/
|
||||||
|
QGraphicsLineItem* vAlignSnapItem();
|
||||||
|
void deleteVAlignSnapItem();
|
||||||
|
void deleteAlignItems();
|
||||||
|
|
||||||
|
/**Snaps an item or point (depending on mode) originating at originalPoint to the grid or align rulers*/
|
||||||
|
QPointF snapPoint( const QPointF& originalPoint, QgsComposerMouseHandles::SnapGuideMode mode );
|
||||||
|
/**Snaps an item originating at unalignedX, unalignedY to the grid or align rulers*/
|
||||||
|
QPointF alignItem( double& alignX, double& alignY, double unalignedX, double unalignedY );
|
||||||
|
/**Snaps a point to to the grid or align rulers*/
|
||||||
|
QPointF alignPos( const QPointF& pos, double& alignX, double& alignY );
|
||||||
|
|
||||||
|
//helper functions for item align
|
||||||
|
void collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY );
|
||||||
|
bool nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue ) const;
|
||||||
|
void checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff, double itemCoordOffset, double& itemCoord, double& alignCoord ) const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSCOMPOSERMOUSEHANDLES_H
|
@ -21,6 +21,7 @@
|
|||||||
#include "qgscomposerlabel.h"
|
#include "qgscomposerlabel.h"
|
||||||
#include "qgscomposerlegend.h"
|
#include "qgscomposerlegend.h"
|
||||||
#include "qgscomposermap.h"
|
#include "qgscomposermap.h"
|
||||||
|
#include "qgscomposermousehandles.h"
|
||||||
#include "qgscomposeritemgroup.h"
|
#include "qgscomposeritemgroup.h"
|
||||||
#include "qgscomposerpicture.h"
|
#include "qgscomposerpicture.h"
|
||||||
#include "qgscomposerscalebar.h"
|
#include "qgscomposerscalebar.h"
|
||||||
@ -64,6 +65,7 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
|
|||||||
, mSnapGridOffsetY( 0.0 )
|
, mSnapGridOffsetY( 0.0 )
|
||||||
, mAlignmentSnap( true )
|
, mAlignmentSnap( true )
|
||||||
, mAlignmentSnapTolerance( 2 )
|
, mAlignmentSnapTolerance( 2 )
|
||||||
|
, mSelectionHandles( 0 )
|
||||||
, mActiveItemCommand( 0 )
|
, mActiveItemCommand( 0 )
|
||||||
, mActiveMultiFrameCommand( 0 )
|
, mActiveMultiFrameCommand( 0 )
|
||||||
, mAtlasComposition( this )
|
, mAtlasComposition( this )
|
||||||
@ -71,6 +73,11 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
|
|||||||
setBackgroundBrush( Qt::gray );
|
setBackgroundBrush( Qt::gray );
|
||||||
addPaperItem();
|
addPaperItem();
|
||||||
|
|
||||||
|
mSelectionHandles = new QgsComposerMouseHandles( this );
|
||||||
|
addItem( mSelectionHandles );
|
||||||
|
mSelectionHandles->setRect( 30, 30, 100, 100 );
|
||||||
|
mSelectionHandles->setZValue( 200 );
|
||||||
|
|
||||||
mPrintResolution = 300; //hardcoded default
|
mPrintResolution = 300; //hardcoded default
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
}
|
||||||
@ -93,6 +100,7 @@ QgsComposition::QgsComposition()
|
|||||||
mSnapGridOffsetY( 0.0 ),
|
mSnapGridOffsetY( 0.0 ),
|
||||||
mAlignmentSnap( true ),
|
mAlignmentSnap( true ),
|
||||||
mAlignmentSnapTolerance( 2 ),
|
mAlignmentSnapTolerance( 2 ),
|
||||||
|
mSelectionHandles( 0 ),
|
||||||
mActiveItemCommand( 0 ),
|
mActiveItemCommand( 0 ),
|
||||||
mActiveMultiFrameCommand( 0 ),
|
mActiveMultiFrameCommand( 0 ),
|
||||||
mAtlasComposition( this )
|
mAtlasComposition( this )
|
||||||
@ -1350,88 +1358,6 @@ QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
|
|||||||
return QPointF( xRatio * mSnapGridResolution + mSnapGridOffsetX, yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset );
|
return QPointF( xRatio * mSnapGridResolution + mSnapGridOffsetX, yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset );
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointF QgsComposition::alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx, double dy )
|
|
||||||
{
|
|
||||||
if ( !item )
|
|
||||||
{
|
|
||||||
return QPointF();
|
|
||||||
}
|
|
||||||
|
|
||||||
double left = item->transform().dx() + dx;
|
|
||||||
double right = left + item->rect().width();
|
|
||||||
double midH = ( left + right ) / 2.0;
|
|
||||||
double top = item->transform().dy() + dy;
|
|
||||||
double bottom = top + item->rect().height();
|
|
||||||
double midV = ( top + bottom ) / 2.0;
|
|
||||||
|
|
||||||
QMap<double, const QgsComposerItem* > xAlignCoordinates;
|
|
||||||
QMap<double, const QgsComposerItem* > yAlignCoordinates;
|
|
||||||
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, item );
|
|
||||||
|
|
||||||
//find nearest matches x
|
|
||||||
double xItemLeft = left; //new left coordinate of the item
|
|
||||||
double xAlignCoord = 0;
|
|
||||||
double smallestDiffX = DBL_MAX;
|
|
||||||
|
|
||||||
checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
|
|
||||||
checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
|
|
||||||
checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
|
|
||||||
|
|
||||||
//find nearest matches y
|
|
||||||
double yItemTop = top; //new top coordinate of the item
|
|
||||||
double yAlignCoord = 0;
|
|
||||||
double smallestDiffY = DBL_MAX;
|
|
||||||
|
|
||||||
checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
|
|
||||||
checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
|
|
||||||
checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
|
|
||||||
|
|
||||||
double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : item->transform().dx() + dx;
|
|
||||||
alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
|
|
||||||
double yCoord = ( smallestDiffY < 5 ) ? yItemTop : item->transform().dy() + dy;
|
|
||||||
alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
|
|
||||||
return QPointF( xCoord, yCoord );
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF QgsComposition::alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY )
|
|
||||||
{
|
|
||||||
QMap<double, const QgsComposerItem* > xAlignCoordinates;
|
|
||||||
QMap<double, const QgsComposerItem* > yAlignCoordinates;
|
|
||||||
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, excludeItem );
|
|
||||||
|
|
||||||
double nearestX = pos.x();
|
|
||||||
double nearestY = pos.y();
|
|
||||||
if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
|
|
||||||
|| !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
|
|
||||||
{
|
|
||||||
alignX = -1;
|
|
||||||
alignY = -1;
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF result( pos.x(), pos.y() );
|
|
||||||
if ( abs( nearestX - pos.x() ) < mAlignmentSnapTolerance )
|
|
||||||
{
|
|
||||||
result.setX( nearestX );
|
|
||||||
alignX = nearestX;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alignX = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( abs( nearestY - pos.y() ) < mAlignmentSnapTolerance )
|
|
||||||
{
|
|
||||||
result.setY( nearestY );
|
|
||||||
alignY = nearestY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alignY = -1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QGraphicsLineItem* QgsComposition::addSnapLine()
|
QGraphicsLineItem* QgsComposition::addSnapLine()
|
||||||
{
|
{
|
||||||
QGraphicsLineItem* item = new QGraphicsLineItem();
|
QGraphicsLineItem* item = new QGraphicsLineItem();
|
||||||
@ -2218,108 +2144,6 @@ QString QgsComposition::encodeStringForXML( const QString& str )
|
|||||||
return modifiedStr;
|
return modifiedStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsComposition::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY,
|
|
||||||
const QgsComposerItem* excludeItem )
|
|
||||||
{
|
|
||||||
alignCoordsX.clear();
|
|
||||||
alignCoordsY.clear();
|
|
||||||
|
|
||||||
QList<QGraphicsItem *> itemList = items();
|
|
||||||
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
|
|
||||||
for ( ; itemIt != itemList.end(); ++itemIt )
|
|
||||||
{
|
|
||||||
const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
|
|
||||||
if ( excludeItem )
|
|
||||||
{
|
|
||||||
if ( !currentItem || currentItem == excludeItem )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
alignCoordsX.insert( currentItem->transform().dx(), currentItem );
|
|
||||||
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().width(), currentItem );
|
|
||||||
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().center().x(), currentItem );
|
|
||||||
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().top(), currentItem );
|
|
||||||
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().center().y(), currentItem );
|
|
||||||
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().bottom(), currentItem );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//arbitrary snap lines
|
|
||||||
QList< QGraphicsLineItem* >::const_iterator sIt = mSnapLines.constBegin();
|
|
||||||
for ( ; sIt != mSnapLines.constEnd(); ++sIt )
|
|
||||||
{
|
|
||||||
double x = ( *sIt )->line().x1();
|
|
||||||
double y = ( *sIt )->line().y1();
|
|
||||||
if ( qgsDoubleNear( y, 0.0 ) )
|
|
||||||
{
|
|
||||||
alignCoordsX.insert( x, 0 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alignCoordsY.insert( y, 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposition::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
|
|
||||||
double itemCoordOffset, double& itemCoord, double& alignCoord ) const
|
|
||||||
{
|
|
||||||
double currentCoord = 0;
|
|
||||||
if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double currentDiff = abs( checkCoord - currentCoord );
|
|
||||||
if ( currentDiff < mAlignmentSnapTolerance )
|
|
||||||
{
|
|
||||||
itemCoord = currentCoord + itemCoordOffset;
|
|
||||||
alignCoord = currentCoord;
|
|
||||||
smallestDiff = currentDiff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QgsComposition::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue )
|
|
||||||
{
|
|
||||||
if ( coords.size() < 1 )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap< double, const QgsComposerItem* >::const_iterator it = coords.lowerBound( value );
|
|
||||||
if ( it == coords.constBegin() ) //value smaller than first map value
|
|
||||||
{
|
|
||||||
nearestValue = it.key();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ( it == coords.constEnd() ) //value larger than last map value
|
|
||||||
{
|
|
||||||
--it;
|
|
||||||
nearestValue = it.key();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//get smaller value and larger value and return the closer one
|
|
||||||
double upperVal = it.key();
|
|
||||||
--it;
|
|
||||||
double lowerVal = it.key();
|
|
||||||
|
|
||||||
double lowerDiff = value - lowerVal;
|
|
||||||
double upperDiff = upperVal - value;
|
|
||||||
if ( lowerDiff < upperDiff )
|
|
||||||
{
|
|
||||||
nearestValue = lowerVal;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nearestValue = upperVal;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const
|
void QgsComposition::computeWorldFileParameters( double& a, double& b, double& c, double& d, double& e, double& f ) const
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
@ -41,6 +41,7 @@ class QGraphicsRectItem;
|
|||||||
class QgsMapRenderer;
|
class QgsMapRenderer;
|
||||||
class QDomElement;
|
class QDomElement;
|
||||||
class QgsComposerArrow;
|
class QgsComposerArrow;
|
||||||
|
class QgsComposerMouseHandles;
|
||||||
class QgsComposerHtml;
|
class QgsComposerHtml;
|
||||||
class QgsComposerItem;
|
class QgsComposerItem;
|
||||||
class QgsComposerLabel;
|
class QgsComposerLabel;
|
||||||
@ -289,22 +290,11 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
|||||||
/**Snaps a scene coordinate point to grid*/
|
/**Snaps a scene coordinate point to grid*/
|
||||||
QPointF snapPointToGrid( const QPointF& scenePoint ) const;
|
QPointF snapPointToGrid( const QPointF& scenePoint ) const;
|
||||||
|
|
||||||
/**Snaps item position to align with other items (left / middle / right or top / middle / bottom
|
/**Returns pointer to snap lines collection*/
|
||||||
@param item current item
|
QList< QGraphicsLineItem* >* snapLines() {return &mSnapLines;};
|
||||||
@param alignX x-coordinate of align or -1 if not aligned to x
|
|
||||||
@param alignY y-coordinate of align or -1 if not aligned to y
|
|
||||||
@param dx item shift in x direction
|
|
||||||
@param dy item shift in y direction
|
|
||||||
@return new upper left point after the align*/
|
|
||||||
QPointF alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx = 0, double dy = 0 );
|
|
||||||
|
|
||||||
/**Snaps position to align with the boundaries of other items
|
/**Returns pointer to selection handles*/
|
||||||
@param pos position to snap
|
QgsComposerMouseHandles* selectionHandles() {return mSelectionHandles;};
|
||||||
@param excludeItem item to exclude
|
|
||||||
@param alignX snapped x coordinate or -1 if not snapped
|
|
||||||
@param alignY snapped y coordinate or -1 if not snapped
|
|
||||||
@return snapped position or original position if no snap*/
|
|
||||||
QPointF alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY );
|
|
||||||
|
|
||||||
/**Add a custom snap line (can be horizontal or vertical)*/
|
/**Add a custom snap line (can be horizontal or vertical)*/
|
||||||
QGraphicsLineItem* addSnapLine();
|
QGraphicsLineItem* addSnapLine();
|
||||||
@ -441,6 +431,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
|||||||
/**Arbitraty snap lines (horizontal and vertical)*/
|
/**Arbitraty snap lines (horizontal and vertical)*/
|
||||||
QList< QGraphicsLineItem* > mSnapLines;
|
QList< QGraphicsLineItem* > mSnapLines;
|
||||||
|
|
||||||
|
QgsComposerMouseHandles* mSelectionHandles;
|
||||||
|
|
||||||
QUndoStack mUndoStack;
|
QUndoStack mUndoStack;
|
||||||
|
|
||||||
QgsComposerItemCommand* mActiveItemCommand;
|
QgsComposerItemCommand* mActiveItemCommand;
|
||||||
@ -470,17 +462,6 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
|||||||
|
|
||||||
static QString encodeStringForXML( const QString& str );
|
static QString encodeStringForXML( const QString& str );
|
||||||
|
|
||||||
//helper functions for item align
|
|
||||||
void collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX,
|
|
||||||
QMap< double, const QgsComposerItem* >& alignCoordsY, const QgsComposerItem* excludeItem );
|
|
||||||
|
|
||||||
void checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
|
|
||||||
double itemCoordOffset, double& itemCoord, double& alignCoord ) const;
|
|
||||||
|
|
||||||
/**Find nearest item in coordinate map to a double.
|
|
||||||
@return true if item found, false if coords is empty*/
|
|
||||||
static bool nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue );
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void paperSizeChanged();
|
void paperSizeChanged();
|
||||||
void nPagesChanged();
|
void nPagesChanged();
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "qgscomposerlabel.h"
|
#include "qgscomposerlabel.h"
|
||||||
#include "qgscomposerlegend.h"
|
#include "qgscomposerlegend.h"
|
||||||
#include "qgscomposermap.h"
|
#include "qgscomposermap.h"
|
||||||
|
#include "qgscomposermousehandles.h"
|
||||||
#include "qgscomposeritemgroup.h"
|
#include "qgscomposeritemgroup.h"
|
||||||
#include "qgscomposerpicture.h"
|
#include "qgscomposerpicture.h"
|
||||||
#include "qgscomposerruler.h"
|
#include "qgscomposerruler.h"
|
||||||
@ -77,9 +78,6 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
|||||||
bool lock = selectedItem->positionLock() ? false : true;
|
bool lock = selectedItem->positionLock() ? false : true;
|
||||||
selectedItem->setPositionLock( lock );
|
selectedItem->setPositionLock( lock );
|
||||||
selectedItem->update();
|
selectedItem->update();
|
||||||
//make sure the new cursor is correct
|
|
||||||
QPointF itemPoint = selectedItem->mapFromScene( scenePoint );
|
|
||||||
selectedItem->updateCursor( itemPoint );
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -89,7 +87,21 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
|||||||
//select/deselect items and pass mouse event further
|
//select/deselect items and pass mouse event further
|
||||||
case Select:
|
case Select:
|
||||||
{
|
{
|
||||||
QgsComposerItem* selectedItem;
|
//check if we are clicking on a selection handle
|
||||||
|
if ( composition()->selectionHandles()->isVisible() )
|
||||||
|
{
|
||||||
|
//selection handles are being shown, get mouse action for current cursor position
|
||||||
|
QgsComposerMouseHandles::MouseAction mouseAction = composition()->selectionHandles()->mouseActionForScenePos( scenePoint );
|
||||||
|
|
||||||
|
if ( mouseAction != QgsComposerMouseHandles::MoveItem && mouseAction != QgsComposerMouseHandles::NoAction && mouseAction != QgsComposerMouseHandles::SelectItem )
|
||||||
|
{
|
||||||
|
//mouse is over a resize handle, so propagate event onward
|
||||||
|
QGraphicsView::mousePressEvent( e );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsComposerItem* selectedItem = 0;
|
||||||
QgsComposerItem* previousSelectedItem = 0;
|
QgsComposerItem* previousSelectedItem = 0;
|
||||||
|
|
||||||
if ( e->modifiers() & Qt::ControlModifier )
|
if ( e->modifiers() & Qt::ControlModifier )
|
||||||
@ -103,11 +115,6 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !( e->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
|
|
||||||
{
|
|
||||||
composition()->clearSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( previousSelectedItem )
|
if ( previousSelectedItem )
|
||||||
{
|
{
|
||||||
//select highest item just below previously selected item at position of event
|
//select highest item just below previously selected item at position of event
|
||||||
@ -128,9 +135,16 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
|||||||
|
|
||||||
if ( !selectedItem )
|
if ( !selectedItem )
|
||||||
{
|
{
|
||||||
|
composition()->clearSelection();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (( !selectedItem->selected() ) && //keep selection if an already selected item pressed
|
||||||
|
!( e->modifiers() & Qt::ShiftModifier ) ) //keep selection if shift key pressed
|
||||||
|
{
|
||||||
|
composition()->clearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
if (( e->modifiers() & Qt::ShiftModifier ) && ( selectedItem->selected() ) )
|
if (( e->modifiers() & Qt::ShiftModifier ) && ( selectedItem->selected() ) )
|
||||||
{
|
{
|
||||||
//SHIFT-clicking a selected item deselects it
|
//SHIFT-clicking a selected item deselects it
|
||||||
@ -154,14 +168,31 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
|||||||
|
|
||||||
case MoveItemContent:
|
case MoveItemContent:
|
||||||
{
|
{
|
||||||
//store item as member if it is selected and cursor is over item
|
//get a list of items at clicked position
|
||||||
QgsComposerItem* item = dynamic_cast<QgsComposerItem *>( itemAt( e->pos() ) );
|
QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos() );
|
||||||
if ( item )
|
if ( itemsAtCursorPos.size() == 0 )
|
||||||
{
|
{
|
||||||
mMoveContentStartPos = scenePoint;
|
//no items at clicked position
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
mMoveContentItem = item;
|
|
||||||
break;
|
//find highest QgsComposerItem at clicked position
|
||||||
|
//(other graphics items may be higher, eg selection handles)
|
||||||
|
QList<QGraphicsItem*>::iterator itemIter = itemsAtCursorPos.begin();
|
||||||
|
for ( ; itemIter != itemsAtCursorPos.end(); ++itemIter )
|
||||||
|
{
|
||||||
|
QgsComposerItem* item = dynamic_cast<QgsComposerItem *>(( *itemIter ) );
|
||||||
|
if ( item )
|
||||||
|
{
|
||||||
|
//we've found the highest QgsComposerItem
|
||||||
|
mMoveContentStartPos = scenePoint;
|
||||||
|
mMoveContentItem = item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//no QgsComposerItem at clicked position
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AddArrow:
|
case AddArrow:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user