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 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) 
   60   double guideMarkerSize = mRulerFontMetrics->width( QStringLiteral( 
"*" ) );
 
   62   double guideMarkerSize = mRulerFontMetrics->horizontalAdvance( 
'*' );
 
   64   mDragGuideTolerance = guideMarkerSize;
 
   65   switch ( mOrientation )
 
   68       mGuideMarker << QPoint( -guideMarkerSize / 2, mRulerMinSize - guideMarkerSize ) << QPoint( 0, mRulerMinSize ) <<
 
   69                    QPoint( guideMarkerSize / 2, mRulerMinSize - guideMarkerSize );
 
   73       mGuideMarker << QPoint( mRulerMinSize - guideMarkerSize, -guideMarkerSize / 2 ) << QPoint( mRulerMinSize, 0 ) <<
 
   74                    QPoint( mRulerMinSize - guideMarkerSize, guideMarkerSize / 2 );
 
   81   return QSize( mRulerMinSize, mRulerMinSize );
 
   95   drawGuideMarkers( &p, layout );
 
   97   QTransform t = mTransform.inverted();
 
   98   p.setFont( mRulerFont );
 
  100   QBrush brush = p.brush();
 
  101   QColor color = brush.color();
 
  102   color.setAlphaF( 0.7 );
 
  103   brush.setColor( color );
 
  107   color.setAlphaF( 0.7 );
 
  108   pen.setColor( color );
 
  114   int mmDisplay = optimumScale( mScaleMinPixelsWidth, magnitude, multiple );
 
  117   int numSmallDivisions = optimumNumberDivisions( mmDisplay, multiple );
 
  119   switch ( mOrientation )
 
  129       double startX = t.map( QPointF( 0, 0 ) ).x();
 
  130       double endX = t.map( QPointF( width(), 0 ) ).x();
 
  133       double markerPos = ( std::floor( startX / mmDisplay ) + 1 ) * mmDisplay;
 
  136       drawSmallDivisions( &p, markerPos, numSmallDivisions, -mmDisplay );
 
  138       while ( markerPos <= endX )
 
  140         double pixelCoord = mTransform.map( QPointF( markerPos, 0 ) ).x();
 
  143         p.drawLine( pixelCoord, 0, pixelCoord, mRulerMinSize );
 
  144         p.drawText( QPointF( pixelCoord + mPixelsBetweenLineAndText, mTextBaseline ), QString::number( markerPos ) );
 
  147         drawSmallDivisions( &p, markerPos, numSmallDivisions, mmDisplay, endX );
 
  149         markerPos += mmDisplay;
 
  160       double startY = t.map( QPointF( 0, 0 ) ).y(); 
 
  161       double endY = t.map( QPointF( 0, height() ) ).y(); 
 
  167       double currentPageY = 0;
 
  168       for ( 
int page = 0; page < layout->
pageCollection()->pageCount(); ++page )
 
  170         if ( currentY < startY )
 
  173           currentPageY = currentY;
 
  178         if ( currentY > endY )
 
  184         double beforePageCoord = -mmDisplay;
 
  185         double firstPageY = mTransform.map( QPointF( 0, 0 ) ).y();
 
  188         while ( beforePageCoord > startY )
 
  190           double pixelCoord = mTransform.map( QPointF( 0, beforePageCoord ) ).y();
 
  191           p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
 
  193           QString label = QString::number( beforePageCoord );
 
  194           int labelSize = mRulerFontMetrics->boundingRect( label ).width();
 
  197           if ( pixelCoord + labelSize + 8 < firstPageY )
 
  199             drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
 
  203           drawSmallDivisions( &p, beforePageCoord, numSmallDivisions, mmDisplay );
 
  205           beforePageCoord -= mmDisplay;
 
  209         drawSmallDivisions( &p, beforePageCoord + mmDisplay, numSmallDivisions, -mmDisplay, startY );
 
  212       double nextPageStartPos = 0;
 
  213       int nextPageStartPixel = 0;
 
  215       for ( 
int i = startPage; i <= endPage; ++i )
 
  217         double pageCoord = 0; 
 
  220         double totalCoord = currentPageY;
 
  227           nextPageStartPixel = mTransform.map( QPointF( 0, nextPageStartPos ) ).y();
 
  232           nextPageStartPos = 0;
 
  233           nextPageStartPixel = 0;
 
  236         while ( ( totalCoord < nextPageStartPos ) || ( ( nextPageStartPos == 0 ) && ( totalCoord <= endY ) ) )
 
  238           double pixelCoord = mTransform.map( QPointF( 0, totalCoord ) ).y();
 
  239           p.drawLine( 0, pixelCoord, mRulerMinSize, pixelCoord );
 
  241           QString label = QString::number( pageCoord );
 
  242           int labelSize = mRulerFontMetrics->boundingRect( label ).width();
 
  245           if ( ( pixelCoord + labelSize + 8 < nextPageStartPixel )
 
  246                || ( nextPageStartPixel == 0 ) )
 
  248             drawRotatedText( &p, QPointF( mTextBaseline, pixelCoord + mMinSpacingVerticalLabels + labelSize ), label );
 
  252           drawSmallDivisions( &p, totalCoord, numSmallDivisions, mmDisplay, nextPageStartPos );
 
  254           pageCoord += mmDisplay;
 
  255           totalCoord += mmDisplay;
 
  268 void QgsLayoutRuler::drawMarkerPos( QPainter *painter )
 
  271   painter->setPen( QColor( Qt::red ) );
 
  272   switch ( mOrientation )
 
  276       painter->drawLine( mMarkerPos.x(), 0, mMarkerPos.x(), mRulerMinSize );
 
  281       painter->drawLine( 0, mMarkerPos.y(), mRulerMinSize, mMarkerPos.y() );
 
  287 void QgsLayoutRuler::drawGuideMarkers( QPainter *p, 
QgsLayout *layout )
 
  289   QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
 
  290   QList< QgsLayoutGuide * > guides = layout->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
 
  292   p->setRenderHint( QPainter::Antialiasing, 
true );
 
  293   p->setPen( Qt::NoPen );
 
  294   const auto constGuides = guides;
 
  297     if ( visiblePages.contains( guide->page() ) )
 
  299       if ( guide == mHoverGuide )
 
  301         p->setBrush( QBrush( QColor( 255, 0, 0, 225 ) ) );
 
  305         p->setBrush( QBrush( QColor( 255, 0, 0, 150 ) ) );
 
  308       switch ( mOrientation )
 
  311           point = QPointF( guide->layoutPosition(), 0 );
 
  315           point = QPointF( 0, guide->layoutPosition() );
 
  318       drawGuideAtPos( p, convertLayoutPointToLocal( point ) );
 
  323 void QgsLayoutRuler::drawGuideAtPos( QPainter *painter, QPoint pos )
 
  325   switch ( mOrientation )
 
  329       painter->translate( pos.x(), 0 );
 
  330       painter->drawPolygon( mGuideMarker );
 
  331       painter->translate( -pos.x(), 0 );
 
  336       painter->translate( 0, pos.y() );
 
  337       painter->drawPolygon( mGuideMarker );
 
  338       painter->translate( 0, -pos.y() );
 
  344 void QgsLayoutRuler::createTemporaryGuideItem()
 
  350   mGuideItem = 
new QGraphicsLineItem();
 
  353   QPen linePen( Qt::DotLine );
 
  354   linePen.setColor( QColor( 255, 0, 0, 150 ) );
 
  355   linePen.setWidthF( 0 );
 
  356   mGuideItem->setPen( linePen );
 
  361 QPointF QgsLayoutRuler::convertLocalPointToLayout( QPoint localPoint )
 const 
  363   QPoint viewPoint = mView->mapFromGlobal( mapToGlobal( localPoint ) );
 
  364   return  mView->mapToScene( viewPoint );
 
  367 QPoint QgsLayoutRuler::convertLayoutPointToLocal( QPointF layoutPoint )
 const 
  369   QPoint viewPoint = mView->mapFromScene( layoutPoint );
 
  370   return mapFromGlobal( mView->mapToGlobal( viewPoint ) );
 
  373 QgsLayoutGuide *QgsLayoutRuler::guideAtPoint( QPoint localPoint )
 const 
  378   QPointF layoutPoint = convertLocalPointToLayout( localPoint );
 
  379   QList< QgsLayoutItemPage * > visiblePages = mView->
visiblePages();
 
  380   QList< QgsLayoutGuide * > guides = mView->
currentLayout()->
guides().
guides( mOrientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal );
 
  382   double minDelta = std::numeric_limits<double>::max();
 
  383   const auto constGuides = guides;
 
  386     if ( visiblePages.contains( guide->page() ) )
 
  388       double currentDelta = 0;
 
  389       switch ( mOrientation )
 
  392           currentDelta = std::fabs( layoutPoint.x() - guide->layoutPosition() );
 
  396           currentDelta = std::fabs( layoutPoint.y() - guide->layoutPosition() );
 
  399       if ( currentDelta < minDelta )
 
  401         minDelta = currentDelta;
 
  402         closestGuide = guide;
 
  407   if ( minDelta * mView->transform().m11() <= mDragGuideTolerance )
 
  417 void QgsLayoutRuler::drawRotatedText( QPainter *painter, QPointF pos, 
const QString &text )
 
  420   painter->translate( pos.x(), pos.y() );
 
  421   painter->rotate( 270 );
 
  422   painter->drawText( 0, 0, text );
 
  425 void QgsLayoutRuler::drawSmallDivisions( QPainter *painter, 
double startPos, 
int numDivisions, 
double rulerScale, 
double maxPos )
 
  427   if ( numDivisions == 0 )
 
  431   double smallMarkerPos = startPos;
 
  432   double smallDivisionSpacing = rulerScale / numDivisions;
 
  434   double pixelCoord = 0.0;
 
  437   for ( 
int i = 0; i < numDivisions; ++i )
 
  439     smallMarkerPos += smallDivisionSpacing;
 
  441     if ( maxPos > 0 && smallMarkerPos > maxPos )
 
  448     switch ( mOrientation )
 
  452         pixelCoord = mTransform.map( QPointF( smallMarkerPos, 0 ) ).x();
 
  457         pixelCoord = mTransform.map( QPointF( 0, smallMarkerPos ) ).y();
 
  464     if ( ( numDivisions == 10 && i == 4 ) || ( numDivisions == 4 && i == 1 ) )
 
  467       lineSize = mRulerMinSize / 1.5;
 
  471       lineSize = mRulerMinSize / 1.25;
 
  475     switch ( mOrientation )
 
  479         painter->drawLine( pixelCoord, lineSize, pixelCoord, mRulerMinSize );
 
  484         painter->drawLine( lineSize, pixelCoord, mRulerMinSize, pixelCoord );
 
  491 int QgsLayoutRuler::optimumScale( 
double minPixelDiff, 
int &magnitude, 
int &multiple )
 
  496   for ( 
unsigned int magnitudeCandidate = 0; magnitudeCandidate < 
COUNT_VALID_MAGNITUDES; ++magnitudeCandidate )
 
  498     for ( 
unsigned int multipleCandidate = 0; multipleCandidate < 
COUNT_VALID_MULTIPLES; ++multipleCandidate )
 
  500       int candidateScale = VALID_SCALE_MULTIPLES[multipleCandidate] * VALID_SCALE_MAGNITUDES[magnitudeCandidate];
 
  502       double pixelDiff = mTransform.map( QPointF( candidateScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
 
  503       if ( pixelDiff > minPixelDiff )
 
  506         magnitude = VALID_SCALE_MAGNITUDES[magnitudeCandidate];
 
  507         multiple = VALID_SCALE_MULTIPLES[multipleCandidate];
 
  508         return candidateScale;
 
  516 int QgsLayoutRuler::optimumNumberDivisions( 
double rulerScale, 
int scaleMultiple )
 
  519   double largeDivisionSize = mTransform.map( QPointF( rulerScale, 0 ) ).x() - mTransform.map( QPointF( 0, 0 ) ).x();
 
  522   QList<int> validSmallDivisions;
 
  523   switch ( scaleMultiple )
 
  528       validSmallDivisions << 10 << 5 << 2;
 
  533       validSmallDivisions << 10 << 4 << 2;
 
  538       validSmallDivisions << 10 << 5;
 
  543   QList<int>::iterator divisions_it;
 
  544   for ( divisions_it = validSmallDivisions.begin(); divisions_it != validSmallDivisions.end(); ++divisions_it )
 
  547     double candidateSize = largeDivisionSize / ( *divisions_it );
 
  549     if ( candidateSize >= mMinPixelsPerDivision )
 
  552       return ( *divisions_it );
 
  563   mTransform = transform;
 
  580   mMarkerPos = mView->mapFromScene( position );
 
  586   mMarkerPos = 
event->pos();
 
  593   if ( mCreatingGuide || mDraggingGuide )
 
  596     displayPos = convertLocalPointToLayout( event->pos() );
 
  598     if ( mCreatingGuide )
 
  606       QPen linePen = mGuideItem->pen();
 
  610         linePen.setColor( QColor( 255, 0, 0, 150 ) );
 
  614         linePen.setColor( QColor( 255, 0, 0, 225 ) );
 
  616       mGuideItem->setPen( linePen );
 
  617       switch ( mOrientation )
 
  622           mGuideItem->setLine( page->scenePos().x(), displayPos.y(), page->scenePos().x() + page->rect().width(), displayPos.y() );
 
  623           displayPos.setX( 0 );
 
  629           mGuideItem->setLine( displayPos.x(), page->scenePos().y(), displayPos.x(), page->scenePos().y() + page->rect().height() );
 
  630           displayPos.setY( 0 );
 
  638       switch ( mOrientation )
 
  643           displayPos.setY( 0 );
 
  649           displayPos.setX( 0 );
 
  658     mHoverGuide = guideAtPoint( event->pos() );
 
  661       setCursor( mOrientation == Qt::Vertical ? Qt::SplitVCursor : Qt::SplitHCursor );
 
  665       setCursor( Qt::ArrowCursor );
 
  669     displayPos = mTransform.inverted().map( event->pos() );
 
  670     switch ( mOrientation )
 
  675         displayPos.setY( 0 );
 
  681         displayPos.setX( 0 );
 
  694   if ( event->button() == Qt::LeftButton )
 
  696     mDraggingGuide = guideAtPoint( event->pos() );
 
  697     if ( !mDraggingGuide )
 
  702         mCreatingGuide = 
true;
 
  703         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       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       QPointF scenePos = convertLocalPointToLayout( event->pos() );
 
  787       std::unique_ptr< QgsLayoutGuide > guide;
 
  788       switch ( mOrientation )
 
  807   else if ( event->button() == Qt::RightButton )
 
  810       mMenu->popup( event->globalPos() );