412 changes: 1 addition & 411 deletions src/core/composer/qgscomposeritem.cpp

Large diffs are not rendered by default.

29 changes: 1 addition & 28 deletions src/core/composer/qgscomposeritem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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*/
Expand Down Expand Up @@ -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 );

Expand Down
893 changes: 893 additions & 0 deletions src/core/composer/qgscomposermousehandles.cpp

Large diffs are not rendered by default.

174 changes: 174 additions & 0 deletions src/core/composer/qgscomposermousehandles.h
Original file line number Diff line number Diff line change
@@ -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
192 changes: 8 additions & 184 deletions src/core/composer/qgscomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -64,13 +65,19 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer )
, mSnapGridOffsetY( 0.0 )
, mAlignmentSnap( true )
, mAlignmentSnapTolerance( 2 )
, mSelectionHandles( 0 )
, mActiveItemCommand( 0 )
, mActiveMultiFrameCommand( 0 )
, mAtlasComposition( this )
{
setBackgroundBrush( Qt::gray );
addPaperItem();

mSelectionHandles = new QgsComposerMouseHandles( this );
addItem( mSelectionHandles );
mSelectionHandles->setRect( 30, 30, 100, 100 );
mSelectionHandles->setZValue( 200 );

mPrintResolution = 300; //hardcoded default
loadSettings();
}
Expand All @@ -93,6 +100,7 @@ QgsComposition::QgsComposition()
mSnapGridOffsetY( 0.0 ),
mAlignmentSnap( true ),
mAlignmentSnapTolerance( 2 ),
mSelectionHandles( 0 ),
mActiveItemCommand( 0 ),
mActiveMultiFrameCommand( 0 ),
mAtlasComposition( this )
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
{
//
Expand Down
35 changes: 8 additions & 27 deletions src/core/composer/qgscomposition.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class QGraphicsRectItem;
class QgsMapRenderer;
class QDomElement;
class QgsComposerArrow;
class QgsComposerMouseHandles;
class QgsComposerHtml;
class QgsComposerItem;
class QgsComposerLabel;
Expand Down Expand Up @@ -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 );

/**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 snap lines collection*/
QList< QGraphicsLineItem* >* snapLines() {return &mSnapLines;};

/**Returns pointer to selection handles*/
QgsComposerMouseHandles* selectionHandles() {return mSelectionHandles;};

/**Add a custom snap line (can be horizontal or vertical)*/
QGraphicsLineItem* addSnapLine();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
61 changes: 46 additions & 15 deletions src/gui/qgscomposerview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 )
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand Down