mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-16 00:05:45 -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*/
|
||||
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;
|
||||
|
||||
/**Updates item, with the possibility to do custom update for subclasses*/
|
||||
@ -367,22 +363,6 @@ class QgsComposerItem: QObject, QGraphicsRectItem
|
||||
|
||||
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*/
|
||||
virtual void drawSelectionBoxes( QPainter* p );
|
||||
|
||||
|
@ -213,23 +213,6 @@ class QgsComposition : QGraphicsScene
|
||||
/**Snaps a scene coordinate point to grid*/
|
||||
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)*/
|
||||
QGraphicsLineItem* addSnapLine();
|
||||
/**Remove custom snap line (and delete the object)*/
|
||||
|
@ -133,6 +133,7 @@ SET(QGIS_CORE_SRCS
|
||||
composer/qgscomposermultiframecommand.cpp
|
||||
composer/qgscomposerarrow.cpp
|
||||
composer/qgscomposerframe.cpp
|
||||
composer/qgscomposermousehandles.cpp
|
||||
composer/qgscomposeritem.cpp
|
||||
composer/qgscomposeritemcommand.cpp
|
||||
composer/qgscomposeritemgroup.cpp
|
||||
@ -324,6 +325,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
composer/qgscomposerscalebar.h
|
||||
composer/qgscomposeritem.h
|
||||
composer/qgscomposeritemgroup.h
|
||||
composer/qgscomposermousehandles.h
|
||||
composer/qgscomposerlabel.h
|
||||
composer/qgscomposershape.h
|
||||
composer/qgscomposerattributetable.h
|
||||
|
@ -94,7 +94,6 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
|
||||
void QgsComposerItem::init( bool manageZValue )
|
||||
{
|
||||
setFlag( QGraphicsItem::ItemIsSelectable, true );
|
||||
setAcceptsHoverEvents( true );
|
||||
//set default pen and brush
|
||||
setBrush( QBrush( QColor( 255, 255, 255, 255 ) ) );
|
||||
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 )
|
||||
{
|
||||
|
||||
if ( !mComposition )
|
||||
{
|
||||
return;
|
||||
@ -760,8 +378,6 @@ void QgsComposerItem::drawSelectionBoxes( QPainter* p )
|
||||
|
||||
if ( mComposition->plotStyle() == QgsComposition::Preview )
|
||||
{
|
||||
//size of symbol boxes depends on zoom level in composer view
|
||||
double rectHandlerSize = rectHandlerBorderTolerance();
|
||||
double sizeLockSymbol = lockSymbolSize();
|
||||
|
||||
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() ) );
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
QFont textFont = scaledFontPixelSize( font );
|
||||
@ -1108,11 +703,6 @@ double QgsComposerItem::lockSymbolSize() const
|
||||
return lockSymbolSize;
|
||||
}
|
||||
|
||||
void QgsComposerItem::updateCursor( const QPointF& itemPos )
|
||||
{
|
||||
setCursor( cursorForPosition( itemPos ) );
|
||||
}
|
||||
|
||||
void QgsComposerItem::setRotation( double r )
|
||||
{
|
||||
if ( r > 360 )
|
||||
|
@ -108,7 +108,7 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
|
||||
virtual void setSelected( bool s );
|
||||
|
||||
/** \brief Is selected */
|
||||
virtual bool selected() {return QGraphicsRectItem::isSelected();}
|
||||
virtual bool selected() const {return QGraphicsRectItem::isSelected();};
|
||||
|
||||
/** stores state in project */
|
||||
virtual bool writeSettings();
|
||||
@ -287,10 +287,6 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
|
||||
@note this method was added in version 1.2*/
|
||||
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;}
|
||||
|
||||
/**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*/
|
||||
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*/
|
||||
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 "qgscomposerlegend.h"
|
||||
#include "qgscomposermap.h"
|
||||
#include "qgscomposermousehandles.h"
|
||||
#include "qgscomposeritemgroup.h"
|
||||
#include "qgscomposerpicture.h"
|
||||
#include "qgscomposerscalebar.h"
|
||||
@ -64,6 +65,7 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
|
||||
, mSnapGridOffsetY( 0.0 )
|
||||
, mAlignmentSnap( true )
|
||||
, mAlignmentSnapTolerance( 2 )
|
||||
, mSelectionHandles( 0 )
|
||||
, mActiveItemCommand( 0 )
|
||||
, mActiveMultiFrameCommand( 0 )
|
||||
, mAtlasComposition( this )
|
||||
@ -71,6 +73,11 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
|
||||
setBackgroundBrush( Qt::gray );
|
||||
addPaperItem();
|
||||
|
||||
mSelectionHandles = new QgsComposerMouseHandles( this );
|
||||
addItem( mSelectionHandles );
|
||||
mSelectionHandles->setRect( 30, 30, 100, 100 );
|
||||
mSelectionHandles->setZValue( 200 );
|
||||
|
||||
mPrintResolution = 300; //hardcoded default
|
||||
loadSettings();
|
||||
}
|
||||
@ -93,6 +100,7 @@ QgsComposition::QgsComposition()
|
||||
mSnapGridOffsetY( 0.0 ),
|
||||
mAlignmentSnap( true ),
|
||||
mAlignmentSnapTolerance( 2 ),
|
||||
mSelectionHandles( 0 ),
|
||||
mActiveItemCommand( 0 ),
|
||||
mActiveMultiFrameCommand( 0 ),
|
||||
mAtlasComposition( this )
|
||||
@ -1350,88 +1358,6 @@ QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
|
||||
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* item = new QGraphicsLineItem();
|
||||
@ -2218,108 +2144,6 @@ QString QgsComposition::encodeStringForXML( const QString& str )
|
||||
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
|
||||
{
|
||||
//
|
||||
|
@ -41,6 +41,7 @@ class QGraphicsRectItem;
|
||||
class QgsMapRenderer;
|
||||
class QDomElement;
|
||||
class QgsComposerArrow;
|
||||
class QgsComposerMouseHandles;
|
||||
class QgsComposerHtml;
|
||||
class QgsComposerItem;
|
||||
class QgsComposerLabel;
|
||||
@ -289,22 +290,11 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
||||
/**Snaps a scene coordinate point to grid*/
|
||||
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 );
|
||||
/**Returns pointer to snap lines collection*/
|
||||
QList< QGraphicsLineItem* >* snapLines() {return &mSnapLines;};
|
||||
|
||||
/**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 );
|
||||
/**Returns pointer to selection handles*/
|
||||
QgsComposerMouseHandles* selectionHandles() {return mSelectionHandles;};
|
||||
|
||||
/**Add a custom snap line (can be horizontal or vertical)*/
|
||||
QGraphicsLineItem* addSnapLine();
|
||||
@ -441,6 +431,8 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
||||
/**Arbitraty snap lines (horizontal and vertical)*/
|
||||
QList< QGraphicsLineItem* > mSnapLines;
|
||||
|
||||
QgsComposerMouseHandles* mSelectionHandles;
|
||||
|
||||
QUndoStack mUndoStack;
|
||||
|
||||
QgsComposerItemCommand* mActiveItemCommand;
|
||||
@ -470,17 +462,6 @@ class CORE_EXPORT QgsComposition : public QGraphicsScene
|
||||
|
||||
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:
|
||||
void paperSizeChanged();
|
||||
void nPagesChanged();
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "qgscomposerlabel.h"
|
||||
#include "qgscomposerlegend.h"
|
||||
#include "qgscomposermap.h"
|
||||
#include "qgscomposermousehandles.h"
|
||||
#include "qgscomposeritemgroup.h"
|
||||
#include "qgscomposerpicture.h"
|
||||
#include "qgscomposerruler.h"
|
||||
@ -77,9 +78,6 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
||||
bool lock = selectedItem->positionLock() ? false : true;
|
||||
selectedItem->setPositionLock( lock );
|
||||
selectedItem->update();
|
||||
//make sure the new cursor is correct
|
||||
QPointF itemPoint = selectedItem->mapFromScene( scenePoint );
|
||||
selectedItem->updateCursor( itemPoint );
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -89,7 +87,21 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
||||
//select/deselect items and pass mouse event further
|
||||
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;
|
||||
|
||||
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 )
|
||||
{
|
||||
//select highest item just below previously selected item at position of event
|
||||
@ -128,9 +135,16 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
||||
|
||||
if ( !selectedItem )
|
||||
{
|
||||
composition()->clearSelection();
|
||||
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() ) )
|
||||
{
|
||||
//SHIFT-clicking a selected item deselects it
|
||||
@ -154,14 +168,31 @@ void QgsComposerView::mousePressEvent( QMouseEvent* e )
|
||||
|
||||
case MoveItemContent:
|
||||
{
|
||||
//store item as member if it is selected and cursor is over item
|
||||
QgsComposerItem* item = dynamic_cast<QgsComposerItem *>( itemAt( e->pos() ) );
|
||||
if ( item )
|
||||
//get a list of items at clicked position
|
||||
QList<QGraphicsItem *> itemsAtCursorPos = items( e->pos() );
|
||||
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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user