Skip to content

Commit 353d697

Browse files
committedJan 18, 2013
Merge branch 'composer_inference_lines'
2 parents 7809cbd + 1910621 commit 353d697

File tree

8 files changed

+448
-77
lines changed

8 files changed

+448
-77
lines changed
 

‎python/core/composer/qgscomposition.sip

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ class QgsComposition : QGraphicsScene
6565
void setGridStyle( GridStyle s );
6666
GridStyle gridStyle() const;
6767

68+
void setAlignmentSnap( bool s );
69+
bool alignmentSnap() const;
70+
71+
void setAlignmentSnapTolerance( double t );
72+
double alignmentSnapTolerance() const;
73+
6874
/**Returns pointer to undo/redo command storage*/
6975
QUndoStack* undoStack();
7076

‎src/app/composer/qgscompositionwidget.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ QgsCompositionWidget::QgsCompositionWidget( QWidget* parent, QgsComposition* c )
5656
mPrintAsRasterCheckBox->setCheckState( Qt::Unchecked );
5757
}
5858

59+
mAlignmentSnapCheckBox->setCheckState( mComposition->alignmentSnap() ? Qt::Checked : Qt::Unchecked );
60+
mAlignmentToleranceSpinBox->setValue( mComposition->alignmentSnapTolerance() );
61+
5962
//snap grid
6063
if ( mComposition->snapToGridEnabled() )
6164
{
@@ -524,6 +527,22 @@ void QgsCompositionWidget::on_mSelectionToleranceSpinBox_valueChanged( double d
524527
}
525528
}
526529

530+
void QgsCompositionWidget::on_mAlignmentSnapCheckBox_stateChanged( int state )
531+
{
532+
if ( mComposition )
533+
{
534+
mComposition->setAlignmentSnap( state == Qt::Checked ? true : false );
535+
}
536+
}
537+
538+
void QgsCompositionWidget::on_mAlignmentToleranceSpinBox_valueChanged( double d )
539+
{
540+
if ( mComposition )
541+
{
542+
mComposition->setAlignmentSnapTolerance( d );
543+
}
544+
}
545+
527546
void QgsCompositionWidget::blockSignals( bool block )
528547
{
529548
mPaperSizeComboBox->blockSignals( block );
@@ -542,4 +561,6 @@ void QgsCompositionWidget::blockSignals( bool block )
542561
mGridColorButton->blockSignals( block );
543562
mGridStyleComboBox->blockSignals( block );
544563
mSelectionToleranceSpinBox->blockSignals( block );
564+
mAlignmentSnapCheckBox->blockSignals( block );
565+
mAlignmentToleranceSpinBox->blockSignals( block );
545566
}

‎src/app/composer/qgscompositionwidget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class QgsCompositionWidget: public QWidget, private Ui::QgsCompositionWidgetBase
5757
void on_mGridStyleComboBox_currentIndexChanged( const QString& text );
5858
void on_mPenWidthSpinBox_valueChanged( double d );
5959
void on_mSelectionToleranceSpinBox_valueChanged( double d );
60+
void on_mAlignmentSnapCheckBox_stateChanged( int state );
61+
void on_mAlignmentToleranceSpinBox_valueChanged( double d );
6062

6163
/**Sets GUI elements to width/height from composition*/
6264
void displayCompositionWidthHeight();

‎src/core/composer/qgscomposeritem.cpp

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <QWidget>
1818
#include <QDomNode>
1919
#include <QFile>
20+
#include <QGraphicsLineItem>
2021
#include <QGraphicsScene>
2122
#include <QGraphicsSceneMouseEvent>
2223
#include <QGraphicsView>
@@ -42,6 +43,8 @@ QgsComposerItem::QgsComposerItem( QgsComposition* composition, bool manageZValue
4243
, QGraphicsRectItem( 0 )
4344
, mComposition( composition )
4445
, mBoundingResizeRectangle( 0 )
46+
, mHAlignSnapItem( 0 )
47+
, mVAlignSnapItem( 0 )
4548
, mFrame( false )
4649
, mItemPositionLocked( false )
4750
, mLastValidViewScaleFactor( -1 )
@@ -55,6 +58,8 @@ QgsComposerItem::QgsComposerItem( qreal x, qreal y, qreal width, qreal height, Q
5558
, QGraphicsRectItem( 0, 0, width, height, 0 )
5659
, mComposition( composition )
5760
, mBoundingResizeRectangle( 0 )
61+
, mHAlignSnapItem( 0 )
62+
, mVAlignSnapItem( 0 )
5863
, mFrame( false )
5964
, mItemPositionLocked( false )
6065
, mLastValidViewScaleFactor( -1 )
@@ -90,6 +95,7 @@ QgsComposerItem::~QgsComposerItem()
9095
}
9196

9297
delete mBoundingResizeRectangle;
98+
deleteAlignItems();
9399
}
94100

95101
void QgsComposerItem::setSelected( bool s )
@@ -336,6 +342,8 @@ void QgsComposerItem::mousePressEvent( QGraphicsSceneMouseEvent * event )
336342
delete mBoundingResizeRectangle;
337343
mBoundingResizeRectangle = 0;
338344
}
345+
deleteAlignItems();
346+
339347
//create and show bounding rectangle
340348
mBoundingResizeRectangle = new QGraphicsRectItem( 0 );
341349
scene()->addItem( mBoundingResizeRectangle );
@@ -385,6 +393,8 @@ void QgsComposerItem::mouseReleaseEvent( QGraphicsSceneMouseEvent * event )
385393
changeItemRectangle( mouseMoveStopPoint, mMouseMoveStartPos, this, diffX, diffY, this );
386394
endItemCommand();
387395

396+
deleteAlignItems();
397+
388398
//reset default action
389399
mCurrentMouseMoveAction = QgsComposerItem::MoveItem;
390400
setCursor( Qt::ArrowCursor );
@@ -503,8 +513,36 @@ void QgsComposerItem::changeItemRectangle( const QPointF& currentPosition,
503513

504514
double mx = 0.0, my = 0.0, rx = 0.0, ry = 0.0;
505515
QPointF snappedPosition = mComposition->snapPointToGrid( currentPosition );
506-
//double diffX = snappedPosition.x() - mouseMoveStartPos.x();
507-
//double diffY = snappedPosition.y() - mouseMoveStartPos.y();
516+
517+
//snap to grid and align to other items
518+
if ( mComposition->alignmentSnap() && mCurrentMouseMoveAction != QgsComposerItem::MoveItem )
519+
{
520+
double alignX = 0;
521+
double alignY = 0;
522+
snappedPosition = mComposition->alignPos( snappedPosition, dynamic_cast<const QgsComposerItem*>( originalItem ), alignX, alignY );
523+
if ( alignX != -1 )
524+
{
525+
QGraphicsLineItem* item = hAlignSnapItem();
526+
item->setLine( QLineF( alignX, 0, alignX, mComposition->paperHeight() ) );
527+
item->show();
528+
}
529+
else
530+
{
531+
deleteHAlignSnapItem();
532+
}
533+
534+
if ( alignY != -1 )
535+
{
536+
QGraphicsLineItem* item = vAlignSnapItem();
537+
item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
538+
item->show();
539+
}
540+
else
541+
{
542+
deleteVAlignSnapItem();
543+
}
544+
}
545+
508546
double diffX = 0;
509547
double diffY = 0;
510548

@@ -566,6 +604,36 @@ void QgsComposerItem::changeItemRectangle( const QPointF& currentPosition,
566604
QPointF upperLeftPoint( originalItem->transform().dx() + moveX, originalItem->transform().dy() + moveY );
567605
QPointF snappedLeftPoint = mComposition->snapPointToGrid( upperLeftPoint );
568606

607+
if ( snappedLeftPoint != upperLeftPoint ) //don't do align snap if grid snap has been done
608+
{
609+
deleteAlignItems();
610+
}
611+
else if ( mComposition->alignmentSnap() ) //align item
612+
{
613+
double alignX = 0;
614+
double alignY = 0;
615+
snappedLeftPoint = mComposition->alignItem( dynamic_cast<const QgsComposerItem*>( originalItem ), alignX, alignY, moveX, moveY );
616+
if ( alignX != -1 )
617+
{
618+
QGraphicsLineItem* item = hAlignSnapItem();
619+
item->setLine( QLineF( alignX, 0, alignX, mComposition->paperHeight() ) );
620+
item->show();
621+
}
622+
else
623+
{
624+
deleteHAlignSnapItem();
625+
}
626+
if ( alignY != -1 )
627+
{
628+
QGraphicsLineItem* item = vAlignSnapItem();
629+
item->setLine( QLineF( 0, alignY, mComposition->paperWidth(), alignY ) );
630+
item->show();
631+
}
632+
else
633+
{
634+
deleteVAlignSnapItem();
635+
}
636+
}
569637
double moveRectX = snappedLeftPoint.x() - originalItem->transform().dx();
570638
double moveRectY = snappedLeftPoint.y() - originalItem->transform().dy();
571639

@@ -1094,6 +1162,56 @@ void QgsComposerItem::rotate( double angle, double& x, double& y ) const
10941162
y = yRot;
10951163
}
10961164

1165+
QGraphicsLineItem* QgsComposerItem::hAlignSnapItem()
1166+
{
1167+
if ( !mHAlignSnapItem )
1168+
{
1169+
mHAlignSnapItem = new QGraphicsLineItem( 0 );
1170+
mHAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1171+
scene()->addItem( mHAlignSnapItem );
1172+
mHAlignSnapItem->setZValue( 90 );
1173+
}
1174+
return mHAlignSnapItem;
1175+
}
1176+
1177+
QGraphicsLineItem* QgsComposerItem::vAlignSnapItem()
1178+
{
1179+
if ( !mVAlignSnapItem )
1180+
{
1181+
mVAlignSnapItem = new QGraphicsLineItem( 0 );
1182+
mVAlignSnapItem->setPen( QPen( QColor( Qt::red ) ) );
1183+
scene()->addItem( mVAlignSnapItem );
1184+
mVAlignSnapItem->setZValue( 90 );
1185+
}
1186+
return mVAlignSnapItem;
1187+
}
1188+
1189+
void QgsComposerItem::deleteHAlignSnapItem()
1190+
{
1191+
if ( mHAlignSnapItem )
1192+
{
1193+
scene()->removeItem( mHAlignSnapItem );
1194+
delete mHAlignSnapItem;
1195+
mHAlignSnapItem = 0;
1196+
}
1197+
}
1198+
1199+
void QgsComposerItem::deleteVAlignSnapItem()
1200+
{
1201+
if ( mVAlignSnapItem )
1202+
{
1203+
scene()->removeItem( mVAlignSnapItem );
1204+
delete mVAlignSnapItem;
1205+
mVAlignSnapItem = 0;
1206+
}
1207+
}
1208+
1209+
void QgsComposerItem::deleteAlignItems()
1210+
{
1211+
deleteHAlignSnapItem();
1212+
deleteVAlignSnapItem();
1213+
}
1214+
10971215
void QgsComposerItem::repaint()
10981216
{
10991217
update();

‎src/core/composer/qgscomposeritem.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
class QWidget;
2525
class QDomDocument;
2626
class QDomElement;
27+
class QGraphicsLineItem;
2728

2829
class QqsComposition;
2930

@@ -265,6 +266,8 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
265266

266267
/**Rectangle used during move and resize actions*/
267268
QGraphicsRectItem* mBoundingResizeRectangle;
269+
QGraphicsLineItem* mHAlignSnapItem;
270+
QGraphicsLineItem* mVAlignSnapItem;
268271

269272
/**True if item fram needs to be painted*/
270273
bool mFrame;
@@ -345,6 +348,14 @@ class CORE_EXPORT QgsComposerItem: public QObject, public QGraphicsRectItem
345348
@param y in/out: y cooreinate before / after the rotation*/
346349
void rotate( double angle, double& x, double& y ) const;
347350

351+
/**Return horizontal align snap item. Creates a new graphics line if 0*/
352+
QGraphicsLineItem* hAlignSnapItem();
353+
void deleteHAlignSnapItem();
354+
/**Return vertical align snap item. Creates a new graphics line if 0*/
355+
QGraphicsLineItem* vAlignSnapItem();
356+
void deleteVAlignSnapItem();
357+
void deleteAlignItems();
358+
348359
signals:
349360
/**Is emitted on rotation change to notify north arrow pictures*/
350361
void rotationChanged( double newRotation );

‎src/core/composer/qgscomposition.cpp

Lines changed: 179 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949

5050
QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer ) :
5151
QGraphicsScene( 0 ), mMapRenderer( mapRenderer ), mPlotStyle( QgsComposition::Preview ), mPageWidth( 297 ), mPageHeight( 210 ), mSpaceBetweenPages( 10 ), mPrintAsRaster( false ), mSelectionTolerance( 0.0 ),
52-
mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mActiveItemCommand( 0 ), mActiveMultiFrameCommand( 0 ),
53-
mAtlasComposition( this )
52+
mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mAlignmentSnap( true ), mAlignmentSnapTolerance( 2 ),
53+
mActiveItemCommand( 0 ), mActiveMultiFrameCommand( 0 ), mAtlasComposition( this )
5454
{
5555
setBackgroundBrush( Qt::gray );
5656
addPaperItem();
@@ -61,8 +61,8 @@ QgsComposition::QgsComposition( QgsMapRenderer* mapRenderer ) :
6161

6262
QgsComposition::QgsComposition():
6363
QGraphicsScene( 0 ), mMapRenderer( 0 ), mPlotStyle( QgsComposition::Preview ), mPageWidth( 297 ), mPageHeight( 210 ), mSpaceBetweenPages( 10 ), mPrintAsRaster( false ),
64-
mSelectionTolerance( 0.0 ), mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mActiveItemCommand( 0 ), mActiveMultiFrameCommand( 0 ),
65-
mAtlasComposition( this )
64+
mSelectionTolerance( 0.0 ), mSnapToGrid( false ), mSnapGridResolution( 0.0 ), mSnapGridOffsetX( 0.0 ), mSnapGridOffsetY( 0.0 ), mAlignmentSnap( true ),
65+
mAlignmentSnapTolerance( 2 ), mActiveItemCommand( 0 ), mActiveMultiFrameCommand( 0 ), mAtlasComposition( this )
6666
{
6767
loadSettings();
6868
}
@@ -303,6 +303,9 @@ bool QgsComposition::writeXML( QDomElement& composerElem, QDomDocument& doc )
303303
compositionElem.setAttribute( "printResolution", mPrintResolution );
304304
compositionElem.setAttribute( "printAsRaster", mPrintAsRaster );
305305

306+
compositionElem.setAttribute( "alignmentSnap", mAlignmentSnap ? 1 : 0 );
307+
compositionElem.setAttribute( "alignmentSnapTolerance", mAlignmentSnapTolerance );
308+
306309
//save items except paper items and frame items (they are saved with the corresponding multiframe)
307310
QList<QGraphicsItem*> itemList = items();
308311
QList<QGraphicsItem*>::const_iterator itemIt = itemList.constBegin();
@@ -365,8 +368,11 @@ bool QgsComposition::readXML( const QDomElement& compositionElem, const QDomDocu
365368
mSnapGridResolution = compositionElem.attribute( "snapGridResolution" ).toDouble();
366369
mSnapGridOffsetX = compositionElem.attribute( "snapGridOffsetX" ).toDouble();
367370
mSnapGridOffsetY = compositionElem.attribute( "snapGridOffsetY" ).toDouble();
368-
mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
369371

372+
mAlignmentSnap = compositionElem.attribute( "alignmentSnap", "1" ).toInt() == 0 ? false : true;
373+
mAlignmentSnapTolerance = compositionElem.attribute( "alignmentSnapTolerance", "2.0" ).toDouble();
374+
375+
mPrintAsRaster = compositionElem.attribute( "printAsRaster" ).toInt();
370376
mPrintResolution = compositionElem.attribute( "printResolution", "300" ).toInt();
371377

372378
updatePaperItems();
@@ -976,6 +982,88 @@ QPointF QgsComposition::snapPointToGrid( const QPointF& scenePoint ) const
976982
return QPointF( xRatio * mSnapGridResolution + mSnapGridOffsetX, yRatio * mSnapGridResolution + mSnapGridOffsetY + yOffset );
977983
}
978984

985+
QPointF QgsComposition::alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx, double dy )
986+
{
987+
if ( !item )
988+
{
989+
return QPointF();
990+
}
991+
992+
double left = item->transform().dx() + dx;
993+
double right = left + item->rect().width();
994+
double midH = ( left + right ) / 2.0;
995+
double top = item->transform().dy() + dy;
996+
double bottom = top + item->rect().height();
997+
double midV = ( top + bottom ) / 2.0;
998+
999+
QMap<double, const QgsComposerItem* > xAlignCoordinates;
1000+
QMap<double, const QgsComposerItem* > yAlignCoordinates;
1001+
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, item );
1002+
1003+
//find nearest matches x
1004+
double xItemLeft = left; //new left coordinate of the item
1005+
double xAlignCoord = 0;
1006+
double smallestDiffX = DBL_MAX;
1007+
1008+
checkNearestItem( left, xAlignCoordinates, smallestDiffX, 0, xItemLeft, xAlignCoord );
1009+
checkNearestItem( midH, xAlignCoordinates, smallestDiffX, ( left - right ) / 2.0, xItemLeft, xAlignCoord );
1010+
checkNearestItem( right, xAlignCoordinates, smallestDiffX, left - right, xItemLeft, xAlignCoord );
1011+
1012+
//find nearest matches y
1013+
double yItemTop = top; //new top coordinate of the item
1014+
double yAlignCoord = 0;
1015+
double smallestDiffY = DBL_MAX;
1016+
1017+
checkNearestItem( top, yAlignCoordinates, smallestDiffY, 0, yItemTop, yAlignCoord );
1018+
checkNearestItem( midV, yAlignCoordinates, smallestDiffY, ( top - bottom ) / 2.0, yItemTop, yAlignCoord );
1019+
checkNearestItem( bottom, yAlignCoordinates, smallestDiffY, top - bottom, yItemTop, yAlignCoord );
1020+
1021+
double xCoord = ( smallestDiffX < 5 ) ? xItemLeft : item->transform().dx() + dx;
1022+
alignX = ( smallestDiffX < 5 ) ? xAlignCoord : -1;
1023+
double yCoord = ( smallestDiffY < 5 ) ? yItemTop : item->transform().dy() + dy;
1024+
alignY = ( smallestDiffY < 5 ) ? yAlignCoord : -1;
1025+
return QPointF( xCoord, yCoord );
1026+
}
1027+
1028+
QPointF QgsComposition::alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY )
1029+
{
1030+
QMap<double, const QgsComposerItem* > xAlignCoordinates;
1031+
QMap<double, const QgsComposerItem* > yAlignCoordinates;
1032+
collectAlignCoordinates( xAlignCoordinates, yAlignCoordinates, excludeItem );
1033+
1034+
double nearestX = pos.x();
1035+
double nearestY = pos.y();
1036+
if ( !nearestItem( xAlignCoordinates, pos.x(), nearestX )
1037+
|| !nearestItem( yAlignCoordinates, pos.y(), nearestY ) )
1038+
{
1039+
alignX = -1;
1040+
alignY = -1;
1041+
return pos;
1042+
}
1043+
1044+
QPointF result( pos.x(), pos.y() );
1045+
if ( abs( nearestX - pos.x() ) < mAlignmentSnapTolerance )
1046+
{
1047+
result.setX( nearestX );
1048+
alignX = nearestX;
1049+
}
1050+
else
1051+
{
1052+
alignX = -1;
1053+
}
1054+
1055+
if ( abs( nearestY - pos.y() ) < mAlignmentSnapTolerance )
1056+
{
1057+
result.setY( nearestY );
1058+
alignY = nearestY;
1059+
}
1060+
else
1061+
{
1062+
alignY = -1;
1063+
}
1064+
return result;
1065+
}
1066+
9791067
int QgsComposition::boundingRectOfSelectedItems( QRectF& bRect )
9801068
{
9811069
QList<QgsComposerItem*> selectedItems = selectedComposerItems();
@@ -1634,3 +1722,89 @@ QString QgsComposition::encodeStringForXML( const QString& str )
16341722
return modifiedStr;
16351723
}
16361724

1725+
void QgsComposition::collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX, QMap< double, const QgsComposerItem* >& alignCoordsY,
1726+
const QgsComposerItem* excludeItem )
1727+
{
1728+
alignCoordsX.clear();
1729+
alignCoordsY.clear();
1730+
1731+
QList<QGraphicsItem *> itemList = items();
1732+
QList<QGraphicsItem *>::iterator itemIt = itemList.begin();
1733+
for ( ; itemIt != itemList.end(); ++itemIt )
1734+
{
1735+
const QgsComposerItem* currentItem = dynamic_cast<const QgsComposerItem *>( *itemIt );
1736+
if ( excludeItem )
1737+
{
1738+
if ( !currentItem || currentItem == excludeItem || currentItem->type() == QgsComposerItem::ComposerPaper )
1739+
{
1740+
continue;
1741+
}
1742+
alignCoordsX.insert( currentItem->transform().dx(), currentItem );
1743+
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().width(), currentItem );
1744+
alignCoordsX.insert( currentItem->transform().dx() + currentItem->rect().center().x(), currentItem );
1745+
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().top(), currentItem );
1746+
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().center().y(), currentItem );
1747+
alignCoordsY.insert( currentItem->transform().dy() + currentItem->rect().bottom(), currentItem );
1748+
}
1749+
}
1750+
}
1751+
1752+
void QgsComposition::checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
1753+
double itemCoordOffset, double& itemCoord, double& alignCoord ) const
1754+
{
1755+
double currentCoord = 0;
1756+
if ( !nearestItem( alignCoords, checkCoord, currentCoord ) )
1757+
{
1758+
return;
1759+
}
1760+
1761+
double currentDiff = abs( checkCoord - currentCoord );
1762+
if ( currentDiff < mAlignmentSnapTolerance )
1763+
{
1764+
itemCoord = currentCoord + itemCoordOffset;
1765+
alignCoord = currentCoord;
1766+
smallestDiff = currentDiff;
1767+
}
1768+
}
1769+
1770+
bool QgsComposition::nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue )
1771+
{
1772+
if ( coords.size() < 1 )
1773+
{
1774+
return false;
1775+
}
1776+
1777+
QMap< double, const QgsComposerItem* >::const_iterator it = coords.lowerBound( value );
1778+
if ( it == coords.constBegin() ) //value smaller than first map value
1779+
{
1780+
nearestValue = it.key();
1781+
return true;
1782+
}
1783+
else if ( it == coords.constEnd() ) //value larger than last map value
1784+
{
1785+
--it;
1786+
nearestValue = it.key();
1787+
return true;
1788+
}
1789+
else
1790+
{
1791+
//get smaller value and larger value and return the closer one
1792+
double upperVal = it.key();
1793+
--it;
1794+
double lowerVal = it.key();
1795+
1796+
double lowerDiff = value - lowerVal;
1797+
double upperDiff = upperVal - value;
1798+
if ( lowerDiff < upperDiff )
1799+
{
1800+
nearestValue = lowerVal;
1801+
return true;
1802+
}
1803+
else
1804+
{
1805+
nearestValue = upperVal;
1806+
return true;
1807+
}
1808+
}
1809+
}
1810+

‎src/core/composer/qgscomposition.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
115115
void setGridStyle( GridStyle s );
116116
GridStyle gridStyle() const {return mGridStyle;}
117117

118+
void setAlignmentSnap( bool s ) { mAlignmentSnap = s; }
119+
bool alignmentSnap() const { return mAlignmentSnap; }
120+
121+
void setAlignmentSnapTolerance( double t ) { mAlignmentSnapTolerance = t; }
122+
double alignmentSnapTolerance() const { return mAlignmentSnapTolerance; }
123+
118124
/**Returns pointer to undo/redo command storage*/
119125
QUndoStack* undoStack() { return &mUndoStack; }
120126

@@ -236,6 +242,22 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
236242
/**Snaps a scene coordinate point to grid*/
237243
QPointF snapPointToGrid( const QPointF& scenePoint ) const;
238244

245+
/**Snaps item position to align with other items (left / middle / right or top / middle / bottom
246+
@param itemRectangle current item rectangle
247+
@param alignX x-coordinate of align or -1 if not aligned to x
248+
@param alignY y-coordinate of align or -1 if not aligned to y
249+
@param dx item shift in x direction
250+
@param dy item shift in y direction
251+
@return new upper left point after the align*/
252+
QPointF alignItem( const QgsComposerItem* item, double& alignX, double& alignY, double dx = 0, double dy = 0 );
253+
254+
/**Snaps position to align with the boundaries of other items
255+
@param pos position to snap
256+
@param alignX snapped x coordinate or -1 if not snapped
257+
@param alignY snapped y coordinate or -1 if not snapped
258+
@return snapped position or original position if no snap*/
259+
QPointF alignPos( const QPointF& pos, const QgsComposerItem* excludeItem, double& alignX, double& alignY );
260+
239261
/**Allocates new item command and saves initial state in it
240262
@param item target item
241263
@param commandText descriptive command text
@@ -342,6 +364,10 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
342364
QPen mGridPen;
343365
GridStyle mGridStyle;
344366

367+
/**Parameters for alignment snap*/
368+
bool mAlignmentSnap;
369+
double mAlignmentSnapTolerance;
370+
345371
QUndoStack mUndoStack;
346372

347373
QgsComposerItemCommand* mActiveItemCommand;
@@ -371,6 +397,17 @@ class CORE_EXPORT QgsComposition: public QGraphicsScene
371397

372398
static QString encodeStringForXML( const QString& str );
373399

400+
//helper functions for item align
401+
void collectAlignCoordinates( QMap< double, const QgsComposerItem* >& alignCoordsX,
402+
QMap< double, const QgsComposerItem* >& alignCoordsY, const QgsComposerItem* excludeItem );
403+
404+
void checkNearestItem( double checkCoord, const QMap< double, const QgsComposerItem* >& alignCoords, double& smallestDiff,
405+
double itemCoordOffset, double& itemCoord, double& alignCoord ) const;
406+
407+
/**Find nearest item in coordinate map to a double.
408+
@return true if item found, false if coords is empty*/
409+
static bool nearestItem( const QMap< double, const QgsComposerItem* >& coords, double value, double& nearestValue );
410+
374411
signals:
375412
void paperSizeChanged();
376413
void nPagesChanged();

‎src/ui/qgscompositionwidgetbase.ui

Lines changed: 72 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>352</width>
10-
<height>570</height>
10+
<height>511</height>
1111
</rect>
1212
</property>
1313
<property name="sizePolicy">
@@ -19,13 +19,7 @@
1919
<property name="windowTitle">
2020
<string>Composition</string>
2121
</property>
22-
<layout class="QGridLayout" name="gridLayout_4">
23-
<property name="margin">
24-
<number>0</number>
25-
</property>
26-
<property name="spacing">
27-
<number>6</number>
28-
</property>
22+
<layout class="QGridLayout" name="gridLayout_3">
2923
<item row="0" column="0">
3024
<widget class="QScrollArea" name="scrollArea">
3125
<property name="frameShape">
@@ -42,14 +36,11 @@
4236
<rect>
4337
<x>0</x>
4438
<y>0</y>
45-
<width>352</width>
46-
<height>570</height>
39+
<width>344</width>
40+
<height>503</height>
4741
</rect>
4842
</property>
49-
<layout class="QGridLayout" name="gridLayout_3">
50-
<property name="margin">
51-
<number>3</number>
52-
</property>
43+
<layout class="QGridLayout" name="gridLayout_2">
5344
<item row="0" column="0">
5445
<widget class="QGroupBox" name="groupBox">
5546
<property name="sizePolicy">
@@ -229,19 +220,6 @@
229220
</layout>
230221
</widget>
231222
</item>
232-
<item row="2" column="0">
233-
<spacer name="verticalSpacer">
234-
<property name="orientation">
235-
<enum>Qt::Vertical</enum>
236-
</property>
237-
<property name="sizeHint" stdset="0">
238-
<size>
239-
<width>20</width>
240-
<height>40</height>
241-
</size>
242-
</property>
243-
</spacer>
244-
</item>
245223
<item row="1" column="0">
246224
<widget class="QGroupBox" name="mSnapGroupBox">
247225
<property name="sizePolicy">
@@ -253,18 +231,48 @@
253231
<property name="title">
254232
<string>Snapping</string>
255233
</property>
256-
<layout class="QFormLayout" name="formLayout_2">
257-
<property name="fieldGrowthPolicy">
258-
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
259-
</property>
260-
<item row="0" column="0">
234+
<layout class="QGridLayout" name="gridLayout">
235+
<item row="0" column="0" colspan="2">
236+
<widget class="QCheckBox" name="mAlignmentSnapCheckBox">
237+
<property name="text">
238+
<string>Alignment snap</string>
239+
</property>
240+
</widget>
241+
</item>
242+
<item row="1" column="0" colspan="2">
243+
<widget class="QDoubleSpinBox" name="mAlignmentToleranceSpinBox">
244+
<property name="prefix">
245+
<string>Tolerance </string>
246+
</property>
247+
</widget>
248+
</item>
249+
<item row="2" column="0">
261250
<widget class="QCheckBox" name="mSnapToGridCheckBox">
262251
<property name="text">
263252
<string>Snap to grid</string>
264253
</property>
265254
</widget>
266255
</item>
267-
<item row="2" column="0" colspan="2">
256+
<item row="3" column="0" colspan="2">
257+
<widget class="QDoubleSpinBox" name="mGridResolutionSpinBox">
258+
<property name="sizePolicy">
259+
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
260+
<horstretch>0</horstretch>
261+
<verstretch>0</verstretch>
262+
</sizepolicy>
263+
</property>
264+
<property name="prefix">
265+
<string>Spacing </string>
266+
</property>
267+
<property name="suffix">
268+
<string/>
269+
</property>
270+
<property name="maximum">
271+
<double>9999.000000000000000</double>
272+
</property>
273+
</widget>
274+
</item>
275+
<item row="4" column="0" colspan="2">
268276
<layout class="QHBoxLayout" name="horizontalLayout_2">
269277
<item>
270278
<widget class="QDoubleSpinBox" name="mOffsetXSpinBox">
@@ -285,7 +293,20 @@
285293
</item>
286294
</layout>
287295
</item>
288-
<item row="4" column="0">
296+
<item row="5" column="0" colspan="2">
297+
<widget class="QDoubleSpinBox" name="mPenWidthSpinBox">
298+
<property name="sizePolicy">
299+
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
300+
<horstretch>0</horstretch>
301+
<verstretch>0</verstretch>
302+
</sizepolicy>
303+
</property>
304+
<property name="prefix">
305+
<string>Pen width </string>
306+
</property>
307+
</widget>
308+
</item>
309+
<item row="6" column="0">
289310
<widget class="QLabel" name="mGridColorLabel">
290311
<property name="text">
291312
<string>Grid color</string>
@@ -298,7 +319,7 @@
298319
</property>
299320
</widget>
300321
</item>
301-
<item row="4" column="1">
322+
<item row="6" column="1">
302323
<widget class="QgsColorButton" name="mGridColorButton">
303324
<property name="sizePolicy">
304325
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@@ -311,7 +332,7 @@
311332
</property>
312333
</widget>
313334
</item>
314-
<item row="5" column="0">
335+
<item row="7" column="0">
315336
<widget class="QLabel" name="mGridStyleLabel">
316337
<property name="text">
317338
<string>Grid style</string>
@@ -324,51 +345,32 @@
324345
</property>
325346
</widget>
326347
</item>
327-
<item row="5" column="1">
348+
<item row="7" column="1">
328349
<widget class="QComboBox" name="mGridStyleComboBox"/>
329350
</item>
330-
<item row="6" column="0" colspan="2">
351+
<item row="8" column="0" colspan="2">
331352
<widget class="QDoubleSpinBox" name="mSelectionToleranceSpinBox">
332353
<property name="prefix">
333354
<string>Selection tolerance (mm) </string>
334355
</property>
335356
</widget>
336357
</item>
337-
<item row="1" column="0" colspan="2">
338-
<widget class="QDoubleSpinBox" name="mGridResolutionSpinBox">
339-
<property name="sizePolicy">
340-
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
341-
<horstretch>0</horstretch>
342-
<verstretch>0</verstretch>
343-
</sizepolicy>
344-
</property>
345-
<property name="prefix">
346-
<string>Spacing </string>
347-
</property>
348-
<property name="suffix">
349-
<string/>
350-
</property>
351-
<property name="maximum">
352-
<double>9999.000000000000000</double>
353-
</property>
354-
</widget>
355-
</item>
356-
<item row="3" column="0" colspan="2">
357-
<widget class="QDoubleSpinBox" name="mPenWidthSpinBox">
358-
<property name="sizePolicy">
359-
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
360-
<horstretch>0</horstretch>
361-
<verstretch>0</verstretch>
362-
</sizepolicy>
363-
</property>
364-
<property name="prefix">
365-
<string>Pen width </string>
366-
</property>
367-
</widget>
368-
</item>
369358
</layout>
370359
</widget>
371360
</item>
361+
<item row="2" column="0">
362+
<spacer name="verticalSpacer">
363+
<property name="orientation">
364+
<enum>Qt::Vertical</enum>
365+
</property>
366+
<property name="sizeHint" stdset="0">
367+
<size>
368+
<width>20</width>
369+
<height>40</height>
370+
</size>
371+
</property>
372+
</spacer>
373+
</item>
372374
</layout>
373375
</widget>
374376
</widget>

0 commit comments

Comments
 (0)
Please sign in to comment.