21 #include <QDragEnterEvent>
22 #include <QGraphicsLineItem>
30 const int QgsLayoutRuler::VALID_SCALE_MULTIPLES[] = {1, 2, 5};
31 const int QgsLayoutRuler::VALID_SCALE_MAGNITUDES[] = {1, 10, 100, 1000, 10000};
35 , mOrientation( orientation )
37 setMouseTracking(
true );
41 mRulerFontMetrics.reset(
new QFontMetrics( mRulerFont ) );
46 mScaleMinPixelsWidth = mRulerFontMetrics->boundingRect( QStringLiteral(
"000" ) ).width() * 2.5;
48 mRulerMinSize = mRulerFontMetrics->height() * 1.5;
50 mMinPixelsPerDivision = mRulerMinSize / 4;
52 if ( mMinPixelsPerDivision < 2 )
53 mMinPixelsPerDivision = 2;
55 mPixelsBetweenLineAndText = mRulerMinSize / 10;
56 mTextBaseline = mRulerMinSize / 1.667;
57 mMinSpacingVerticalLabels = mRulerMinSize / 5;
59 const double guideMarkerSize = mRulerFontMetrics->horizontalAdvance(
'*' );
60 mDragGuideTolerance = guideMarkerSize;
61 switch ( mOrientation )
64 mGuideMarker << QPoint( -guideMarkerSize / 2, mRulerMinSize - guideMarkerSize ) << QPoint( 0, mRulerMinSize ) <<
65 QPoint( guideMarkerSize / 2, mRulerMinSize - guideMarkerSize );
69 mGuideMarker << QPoint( mRulerMinSize - guideMarkerSize, -guideMarkerSize / 2 ) << QPoint( mRulerMinSize, 0 ) <<
70 QPoint( mRulerMinSize - guideMarkerSize, guideMarkerSize / 2 );
77 return QSize( mRulerMinSize, mRulerMinSize );
91 drawGuideMarkers( &p, layout );
93 const QTransform t = mTransform.inverted();
94 p.setFont( mRulerFont );
96 QBrush brush = p.brush();
97 QColor color = brush.color();
98 color.setAlphaF( 0.7 );
99 brush.setColor( color );
103 color.setAlphaF( 0.7 );
104 pen.setColor( color );
110 const int mmDisplay = optimumScale( mScaleMinPixelsWidth, magnitude, multiple );
113 const int numSmallDivisions = optimumNumberDivisions( mmDisplay, multiple );
115 switch ( mOrientation )
125 const double startX = t.map( QPointF( 0, 0 ) ).x();
126 const double endX = t.map( QPointF( width(), 0 ) ).x();
129 double markerPos = ( std::floor( startX / mmDisplay ) + 1 ) * mmDisplay;
132 drawSmallDivisions( &p, markerPos, numSmallDivisions, -mmDisplay );
134 while ( markerPos <= endX )
136 const double pixelCoord = mTransform.map( QPointF( markerPos, 0 ) ).x();
139 p.drawLine( pixelCoord, 0, pixelCoord, mRulerMinSize );
140 p.drawText( QPointF( pixelCoord + mPixelsBetweenLineAndText, mTextBaseline ), QLocale().toString( markerPos ) );
143 drawSmallDivisions( &p, markerPos, numSmallDivisions, mmDisplay, endX );
145 markerPos += mmDisplay;
156 const double startY = t.map( QPointF( 0, 0 ) ).y();
157 const double endY = t.map( QPointF( 0, height() ) ).y();
163 double currentPageY = 0;
164 for (
int page = 0; page < layout->
pageCollection()->pageCount(); ++page )
166 if ( currentY < startY )
169 currentPageY = currentY;
174 if ( currentY > endY )
180 double beforePageCoord = -mmDisplay;
181 const double firstPageY = mTransform.map( QPointF( 0, 0 ) ).y();
184 while ( beforePageCoord > startY )
186 const double pixelCoord = mTransform.map( QPointF( 0, beforePageCoord ) ).y();
187 p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
189 const QString label = QLocale().toString( beforePageCoord );
190 const int labelSize = mRulerFontMetrics->boundingRect( label ).width();
193 if ( pixelCoord + labelSize + 8 < firstPageY )
195 drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
199 drawSmallDivisions( &p, beforePageCoord, numSmallDivisions, mmDisplay );
201 beforePageCoord -= mmDisplay;
205 drawSmallDivisions( &p, beforePageCoord + mmDisplay, numSmallDivisions, -mmDisplay, startY );
208 double nextPageStartPos = 0;
209 int nextPageStartPixel = 0;
211 for (
int i = startPage; i <= endPage; ++i )
213 double pageCoord = 0;
216 double totalCoord = currentPageY;
223 nextPageStartPixel = mTransform.map( QPointF( 0, nextPageStartPos ) ).y();
228 nextPageStartPos = 0;
229 nextPageStartPixel = 0;
232 while ( ( totalCoord < nextPageStartPos ) || ( ( nextPageStartPos == 0 ) && ( totalCoord <= endY ) ) )
234 const double pixelCoord = mTransform.map( QPointF( 0, totalCoord ) ).y();
235 p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
237 const QString label = QLocale().toString( pageCoord );
238 const int labelSize = mRulerFontMetrics->boundingRect( label ).width();
241 if ( ( pixelCoord + labelSize + 8 < nextPageStartPixel )
242 || ( nextPageStartPixel == 0 ) )
244 drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
248 drawSmallDivisions( &p, totalCoord, numSmallDivisions, mmDisplay, nextPageStartPos );
250 pageCoord += mmDisplay;
251 totalCoord += mmDisplay;
264 void QgsLayoutRuler::drawMarkerPos( QPainter *painter )
267 painter->setPen( QColor( Qt::red ) );
268 switch ( mOrientation )
272 painter->drawLine( mMarkerPos.x(), 0, mMarkerPos.x(), mRulerMinSize );
277 painter->drawLine( 0, mMarkerPos.y(), mRulerMinSize, mMarkerPos.y() );
283 void QgsLayoutRuler::drawGuideMarkers( QPainter *p,
QgsLayout *layout )
285 const QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
286 const QList< QgsLayoutGuide * > guides = layout->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
288 p->setRenderHint( QPainter::Antialiasing,
true );
289 p->setPen( Qt::NoPen );
290 const auto constGuides = guides;
293 if ( visiblePages.contains( guide->page() ) )
295 if ( guide == mHoverGuide )
297 p->setBrush( QBrush( QColor( 255, 0, 0, 225 ) ) );
301 p->setBrush( QBrush( QColor( 255, 0, 0, 150 ) ) );
304 switch ( mOrientation )
307 point = QPointF( guide->layoutPosition(), 0 );
311 point = QPointF( 0, guide->layoutPosition() );
314 drawGuideAtPos( p, convertLayoutPointToLocal( point ) );
319 void QgsLayoutRuler::drawGuideAtPos( QPainter *painter, QPoint pos )
321 switch ( mOrientation )
325 painter->translate( pos.x(), 0 );
326 painter->drawPolygon( mGuideMarker );
327 painter->translate( -pos.x(), 0 );
332 painter->translate( 0, pos.y() );
333 painter->drawPolygon( mGuideMarker );
334 painter->translate( 0, -pos.y() );
340 void QgsLayoutRuler::createTemporaryGuideItem()
346 mGuideItem =
new QGraphicsLineItem();
349 QPen linePen( Qt::DotLine );
350 linePen.setColor( QColor( 255, 0, 0, 150 ) );
351 linePen.setWidthF( 0 );
352 mGuideItem->setPen( linePen );
357 QPointF QgsLayoutRuler::convertLocalPointToLayout( QPoint localPoint )
const
359 const QPoint viewPoint = mView->mapFromGlobal( mapToGlobal( localPoint ) );
360 return mView->mapToScene( viewPoint );
363 QPoint QgsLayoutRuler::convertLayoutPointToLocal( QPointF layoutPoint )
const
365 const QPoint viewPoint = mView->mapFromScene( layoutPoint );
366 return mapFromGlobal( mView->mapToGlobal( viewPoint ) );
369 QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint )
const
374 const QPointF layoutPoint = convertLocalPointToLayout( localPoint );
375 const QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
376 const QList< QgsLayoutGuide * > guides = mView->
currentLayout()->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
378 double minDelta = std::numeric_limits<double>::max();
379 const auto constGuides = guides;
382 if ( visiblePages.contains( guide->page() ) )
384 double currentDelta = 0;
385 switch ( mOrientation )
388 currentDelta = std::fabs( layoutPoint.x() - guide->layoutPosition() );
392 currentDelta = std::fabs( layoutPoint.y() - guide->layoutPosition() );
395 if ( currentDelta < minDelta )
397 minDelta = currentDelta;
398 closestGuide = guide;
403 if ( minDelta * mView->transform().m11() <= mDragGuideTolerance )
413 void QgsLayoutRuler::drawRotatedText( QPainter *painter, QPointF pos,
const QString &text )
416 painter->translate( pos.x(), pos.y() );
417 painter->rotate( 270 );
418 painter->drawText( 0, 0, text );
421 void QgsLayoutRuler::drawSmallDivisions( QPainter *painter,
double startPos,
int numDivisions,
double rulerScale,
double maxPos )
423 if ( numDivisions == 0 )
427 double smallMarkerPos = startPos;
428 const double smallDivisionSpacing = rulerScale / numDivisions;
430 double pixelCoord = 0.0;
433 for (
int i = 0; i < numDivisions; ++i )
435 smallMarkerPos += smallDivisionSpacing;
437 if ( maxPos > 0 && smallMarkerPos > maxPos )
444 switch ( mOrientation )
448 pixelCoord = mTransform.map( QPointF( smallMarkerPos, 0 ) ).x();
453 pixelCoord = mTransform.map( QPointF( 0, smallMarkerPos ) ).y();
460 if ( ( numDivisions == 10 && i == 4 ) || ( numDivisions == 4 && i == 1 ) )
463 lineSize = mRulerMinSize / 1.5;
467 lineSize = mRulerMinSize / 1.25;
471 switch ( mOrientation )
475 painter->drawLine( pixelCoord, lineSize, pixelCoord, mRulerMinSize );
480 painter->drawLine( lineSize, pixelCoord, mRulerMinSize, pixelCoord );
487 int QgsLayoutRuler::optimumScale(
double minPixelDiff,
int &magnitude,
int &multiple )
492 for (
unsigned int magnitudeCandidate = 0; magnitudeCandidate <
COUNT_VALID_MAGNITUDES; ++magnitudeCandidate )
494 for (
unsigned int multipleCandidate = 0; multipleCandidate <
COUNT_VALID_MULTIPLES; ++multipleCandidate )
496 const int candidateScale = VALID_SCALE_MULTIPLES[multipleCandidate] * VALID_SCALE_MAGNITUDES[magnitudeCandidate];
498 const double pixelDiff = mTransform.map( QPointF( candidateScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
499 if ( pixelDiff > minPixelDiff )
502 magnitude = VALID_SCALE_MAGNITUDES[magnitudeCandidate];
503 multiple = VALID_SCALE_MULTIPLES[multipleCandidate];
504 return candidateScale;
512 int QgsLayoutRuler::optimumNumberDivisions(
double rulerScale,
int scaleMultiple )
515 const double largeDivisionSize = mTransform.map( QPointF( rulerScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
518 QList<int> validSmallDivisions;
519 switch ( scaleMultiple )
524 validSmallDivisions << 10 << 5 << 2;
529 validSmallDivisions << 10 << 4 << 2;
534 validSmallDivisions << 10 << 5;
539 QList<int>::iterator divisions_it;
540 for ( divisions_it = validSmallDivisions.begin(); divisions_it != validSmallDivisions.end(); ++divisions_it )
543 const double candidateSize = largeDivisionSize / ( *divisions_it );
545 if ( candidateSize >= mMinPixelsPerDivision )
548 return ( *divisions_it );
559 mTransform = transform;
576 mMarkerPos = mView->mapFromScene( position );
582 mMarkerPos =
event->pos();
589 if ( mCreatingGuide || mDraggingGuide )
592 displayPos = convertLocalPointToLayout( event->pos() );
594 if ( mCreatingGuide )
602 QPen linePen = mGuideItem->pen();
606 linePen.setColor( QColor( 255, 0, 0, 150 ) );
610 linePen.setColor( QColor( 255, 0, 0, 225 ) );
612 mGuideItem->setPen( linePen );
613 switch ( mOrientation )
618 mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
619 displayPos.setX( 0 );
625 mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
626 displayPos.setY( 0 );
634 switch ( mOrientation )
639 displayPos.setY( 0 );
645 displayPos.setX( 0 );
654 mHoverGuide = guideAtPoint( event->pos() );
657 setCursor( mOrientation == Qt::Vertical ? Qt::SplitVCursor : Qt::SplitHCursor );
661 setCursor( Qt::ArrowCursor );
665 displayPos = mTransform.inverted().map( event->pos() );
666 switch ( mOrientation )
671 displayPos.setY( 0 );
677 displayPos.setX( 0 );
690 if ( event->button() == Qt::LeftButton )
692 mDraggingGuide = guideAtPoint( event->pos() );
693 if ( !mDraggingGuide )
698 mCreatingGuide =
true;
699 createTemporaryGuideItem();
706 switch ( mOrientation )
710 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitHCursor : Qt::SplitVCursor );
714 QApplication::setOverrideCursor( mDraggingGuide ? Qt::SplitVCursor : Qt::SplitHCursor );
725 if ( event->button() == Qt::LeftButton )
727 if ( mDraggingGuide )
729 QApplication::restoreOverrideCursor();
731 const QPointF layoutPoint = convertLocalPointToLayout( event->pos() );
735 bool deleteGuide =
false;
739 if ( layoutPoint.y() < page->scenePos().y() || layoutPoint.y() > page->scenePos().y() + page->rect().height() )
744 if ( layoutPoint.x() < page->scenePos().x() || layoutPoint.x() > page->scenePos().x() + page->rect().width() )
753 mDraggingGuide =
nullptr;
757 mCreatingGuide =
false;
758 QApplication::restoreOverrideCursor();
760 mGuideItem =
nullptr;
763 switch ( mOrientation )
767 if ( event->pos().y() <= height() )
773 if ( event->pos().x() <= width() )
782 const QPointF scenePos = convertLocalPointToLayout( event->pos() );
787 std::unique_ptr< QgsLayoutGuide > guide;
788 switch ( mOrientation )
807 else if ( event->button() == Qt::RightButton )
809 if ( mCreatingGuide || mDraggingGuide )
811 QApplication::restoreOverrideCursor();
813 mGuideItem =
nullptr;
814 mCreatingGuide =
false;
815 if ( mDraggingGuide )
819 mDraggingGuide =
nullptr;
822 mMenu->popup( event->globalPos() );