LCOV - code coverage report
Current view: top level - core/layout - qgslayoutaligner.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 230 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                              qgslayoutaligner.cpp
       3                 :            :                              --------------------
       4                 :            :     begin                : October 2017
       5                 :            :     copyright            : (C) 2017 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************/
       8                 :            : /***************************************************************************
       9                 :            :  *                                                                         *
      10                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      11                 :            :  *   it under the terms of the GNU General Public License as published by  *
      12                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      13                 :            :  *   (at your option) any later version.                                   *
      14                 :            :  *                                                                         *
      15                 :            :  ***************************************************************************/
      16                 :            : 
      17                 :            : #include "qgslayoutaligner.h"
      18                 :            : #include "qgslayoutitem.h"
      19                 :            : #include "qgslayout.h"
      20                 :            : #include "qgslayoutundostack.h"
      21                 :            : 
      22                 :          0 : void QgsLayoutAligner::alignItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Alignment alignment )
      23                 :            : {
      24                 :          0 :   if ( !layout || items.size() < 2 )
      25                 :            :   {
      26                 :          0 :     return;
      27                 :            :   }
      28                 :            : 
      29                 :          0 :   QRectF itemBBox = boundingRectOfItems( items );
      30                 :          0 :   if ( !itemBBox.isValid() )
      31                 :            :   {
      32                 :          0 :     return;
      33                 :            :   }
      34                 :            : 
      35                 :          0 :   double refCoord = 0;
      36                 :          0 :   switch ( alignment )
      37                 :            :   {
      38                 :            :     case AlignLeft:
      39                 :          0 :       refCoord = itemBBox.left();
      40                 :          0 :       break;
      41                 :            :     case AlignHCenter:
      42                 :          0 :       refCoord = itemBBox.center().x();
      43                 :          0 :       break;
      44                 :            :     case AlignRight:
      45                 :          0 :       refCoord = itemBBox.right();
      46                 :          0 :       break;
      47                 :            :     case AlignTop:
      48                 :          0 :       refCoord = itemBBox.top();
      49                 :          0 :       break;
      50                 :            :     case AlignVCenter:
      51                 :          0 :       refCoord = itemBBox.center().y();
      52                 :          0 :       break;
      53                 :            :     case AlignBottom:
      54                 :          0 :       refCoord = itemBBox.bottom();
      55                 :          0 :       break;
      56                 :            :   }
      57                 :            : 
      58                 :          0 :   layout->undoStack()->beginMacro( undoText( alignment ) );
      59                 :          0 :   for ( QgsLayoutItem *item : items )
      60                 :            :   {
      61                 :          0 :     layout->undoStack()->beginCommand( item, QString() );
      62                 :            : 
      63                 :          0 :     QPointF shifted = item->pos();
      64                 :          0 :     switch ( alignment )
      65                 :            :     {
      66                 :            :       case AlignLeft:
      67                 :          0 :         shifted.setX( refCoord );
      68                 :          0 :         break;
      69                 :            :       case AlignHCenter:
      70                 :          0 :         shifted.setX( refCoord - item->rect().width() / 2.0 );
      71                 :          0 :         break;
      72                 :            :       case AlignRight:
      73                 :          0 :         shifted.setX( refCoord - item->rect().width() );
      74                 :          0 :         break;
      75                 :            :       case AlignTop:
      76                 :          0 :         shifted.setY( refCoord );
      77                 :          0 :         break;
      78                 :            :       case AlignVCenter:
      79                 :          0 :         shifted.setY( refCoord - item->rect().height() / 2.0 );
      80                 :          0 :         break;
      81                 :            :       case AlignBottom:
      82                 :          0 :         shifted.setY( refCoord - item->rect().height() );
      83                 :          0 :         break;
      84                 :            :     }
      85                 :            : 
      86                 :            :     // need to keep item units
      87                 :          0 :     QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
      88                 :          0 :     item->attemptMove( newPos, false );
      89                 :            : 
      90                 :          0 :     layout->undoStack()->endCommand();
      91                 :            :   }
      92                 :          0 :   layout->undoStack()->endMacro();
      93                 :          0 : }
      94                 :            : 
      95                 :          0 : void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Distribution distribution )
      96                 :            : {
      97                 :          0 :   if ( items.size() < 2 )
      98                 :          0 :     return;
      99                 :            : 
     100                 :            :   // equispaced distribution doesn't follow the same approach of the other distribution types
     101                 :          0 :   if ( distribution == DistributeHSpace || distribution == DistributeVSpace )
     102                 :            :   {
     103                 :          0 :     distributeEquispacedItems( layout, items, distribution );
     104                 :          0 :     return;
     105                 :            :   }
     106                 :            : 
     107                 :          0 :   auto collectReferenceCoord = [distribution]( QgsLayoutItem * item )->double
     108                 :            :   {
     109                 :          0 :     QRectF itemBBox = item->sceneBoundingRect();
     110                 :          0 :     switch ( distribution )
     111                 :            :     {
     112                 :            :       case DistributeLeft:
     113                 :          0 :         return itemBBox.left();
     114                 :            :       case DistributeHCenter:
     115                 :          0 :         return itemBBox.center().x();
     116                 :            :       case DistributeRight:
     117                 :          0 :         return itemBBox.right();
     118                 :            :       case DistributeTop:
     119                 :          0 :         return itemBBox.top();
     120                 :            :       case DistributeVCenter:
     121                 :          0 :         return itemBBox.center().y();
     122                 :            :       case DistributeBottom:
     123                 :          0 :         return itemBBox.bottom();
     124                 :            :       case DistributeHSpace:
     125                 :            :       case DistributeVSpace:
     126                 :            :         // not reachable branch, just to avoid compilation warning
     127                 :          0 :         return std::numeric_limits<double>::quiet_NaN();
     128                 :            :     }
     129                 :            :     // no warnings
     130                 :          0 :     return itemBBox.left();
     131                 :          0 :   };
     132                 :            : 
     133                 :            : 
     134                 :          0 :   double minCoord = std::numeric_limits<double>::max();
     135                 :          0 :   double maxCoord = std::numeric_limits<double>::lowest();
     136                 :          0 :   QMap< double, QgsLayoutItem * > itemCoords;
     137                 :          0 :   for ( QgsLayoutItem *item : items )
     138                 :            :   {
     139                 :          0 :     double refCoord = collectReferenceCoord( item );
     140                 :          0 :     minCoord = std::min( minCoord, refCoord );
     141                 :          0 :     maxCoord = std::max( maxCoord, refCoord );
     142                 :          0 :     itemCoords.insert( refCoord, item );
     143                 :            :   }
     144                 :            : 
     145                 :          0 :   const double step = ( maxCoord - minCoord ) / ( items.size() - 1 );
     146                 :            : 
     147                 :          0 :   auto distributeItemToCoord = [layout, distribution]( QgsLayoutItem * item, double refCoord )
     148                 :            :   {
     149                 :          0 :     QPointF shifted = item->pos();
     150                 :          0 :     switch ( distribution )
     151                 :            :     {
     152                 :            :       case DistributeLeft:
     153                 :          0 :         shifted.setX( refCoord );
     154                 :          0 :         break;
     155                 :            :       case DistributeHCenter:
     156                 :          0 :         shifted.setX( refCoord - item->rect().width() / 2.0 );
     157                 :          0 :         break;
     158                 :            :       case DistributeRight:
     159                 :          0 :         shifted.setX( refCoord - item->rect().width() );
     160                 :          0 :         break;
     161                 :            :       case DistributeTop:
     162                 :          0 :         shifted.setY( refCoord );
     163                 :          0 :         break;
     164                 :            :       case DistributeVCenter:
     165                 :          0 :         shifted.setY( refCoord - item->rect().height() / 2.0 );
     166                 :          0 :         break;
     167                 :            :       case DistributeBottom:
     168                 :          0 :         shifted.setY( refCoord - item->rect().height() );
     169                 :          0 :         break;
     170                 :            :       case DistributeHSpace:
     171                 :            :       case DistributeVSpace:
     172                 :            :         // not reachable branch, just to avoid compilation warning
     173                 :          0 :         break;
     174                 :            :     }
     175                 :            : 
     176                 :            :     // need to keep item units
     177                 :          0 :     QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
     178                 :          0 :     item->attemptMove( newPos, false );
     179                 :          0 :   };
     180                 :            : 
     181                 :            : 
     182                 :          0 :   layout->undoStack()->beginMacro( undoText( distribution ) );
     183                 :          0 :   double currentVal = minCoord;
     184                 :          0 :   for ( auto itemIt = itemCoords.constBegin(); itemIt != itemCoords.constEnd(); ++itemIt )
     185                 :            :   {
     186                 :          0 :     layout->undoStack()->beginCommand( itemIt.value(), QString() );
     187                 :          0 :     distributeItemToCoord( itemIt.value(), currentVal );
     188                 :          0 :     layout->undoStack()->endCommand();
     189                 :            : 
     190                 :          0 :     currentVal += step;
     191                 :          0 :   }
     192                 :          0 :   layout->undoStack()->endMacro();
     193                 :          0 : }
     194                 :            : 
     195                 :          0 : void QgsLayoutAligner::resizeItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Resize resize )
     196                 :            : {
     197                 :          0 :   if ( !( items.size() >= 2 || ( items.size() == 1 && resize == ResizeToSquare ) ) )
     198                 :          0 :     return;
     199                 :            : 
     200                 :          0 :   auto collectSize = [resize]( QgsLayoutItem * item )->double
     201                 :            :   {
     202                 :          0 :     QRectF itemBBox = item->sceneBoundingRect();
     203                 :          0 :     switch ( resize )
     204                 :            :     {
     205                 :            :       case ResizeNarrowest:
     206                 :            :       case ResizeWidest:
     207                 :            :       case ResizeToSquare:
     208                 :          0 :         return itemBBox.width();
     209                 :            :       case ResizeShortest:
     210                 :            :       case ResizeTallest:
     211                 :          0 :         return itemBBox.height();
     212                 :            :     }
     213                 :            :     // no warnings
     214                 :          0 :     return itemBBox.width();
     215                 :          0 :   };
     216                 :            : 
     217                 :          0 :   double newSize = collectSize( items.at( 0 ) );
     218                 :          0 :   for ( QgsLayoutItem *item : items )
     219                 :            :   {
     220                 :          0 :     double size = collectSize( item );
     221                 :          0 :     switch ( resize )
     222                 :            :     {
     223                 :            :       case ResizeNarrowest:
     224                 :            :       case ResizeShortest:
     225                 :          0 :         newSize = std::min( size, newSize );
     226                 :          0 :         break;
     227                 :            :       case ResizeTallest:
     228                 :            :       case ResizeWidest:
     229                 :          0 :         newSize = std::max( size, newSize );
     230                 :          0 :         break;
     231                 :            :       case ResizeToSquare:
     232                 :          0 :         break;
     233                 :            :     }
     234                 :            :   }
     235                 :            : 
     236                 :          0 :   auto resizeItemToSize = [layout, resize]( QgsLayoutItem * item, double size )
     237                 :            :   {
     238                 :          0 :     QSizeF newSize = item->rect().size();
     239                 :          0 :     switch ( resize )
     240                 :            :     {
     241                 :            :       case ResizeNarrowest:
     242                 :            :       case ResizeWidest:
     243                 :          0 :         newSize.setWidth( size );
     244                 :          0 :         break;
     245                 :            :       case ResizeTallest:
     246                 :            :       case ResizeShortest:
     247                 :          0 :         newSize.setHeight( size );
     248                 :          0 :         break;
     249                 :            :       case ResizeToSquare:
     250                 :            :       {
     251                 :          0 :         if ( newSize.width() > newSize.height() )
     252                 :          0 :           newSize.setHeight( newSize.width() );
     253                 :            :         else
     254                 :          0 :           newSize.setWidth( newSize.height() );
     255                 :          0 :         break;
     256                 :            :       }
     257                 :            :     }
     258                 :            : 
     259                 :            :     // need to keep item units
     260                 :          0 :     QgsLayoutSize newSizeWithUnits = layout->convertFromLayoutUnits( newSize, item->sizeWithUnits().units() );
     261                 :          0 :     item->attemptResize( newSizeWithUnits );
     262                 :          0 :   };
     263                 :            : 
     264                 :          0 :   layout->undoStack()->beginMacro( undoText( resize ) );
     265                 :          0 :   for ( QgsLayoutItem *item : items )
     266                 :            :   {
     267                 :          0 :     layout->undoStack()->beginCommand( item, QString() );
     268                 :          0 :     resizeItemToSize( item, newSize );
     269                 :          0 :     layout->undoStack()->endCommand();
     270                 :            :   }
     271                 :          0 :   layout->undoStack()->endMacro();
     272                 :          0 : }
     273                 :            : 
     274                 :          0 : QRectF QgsLayoutAligner::boundingRectOfItems( const QList<QgsLayoutItem *> &items )
     275                 :            : {
     276                 :          0 :   if ( items.empty() )
     277                 :            :   {
     278                 :          0 :     return QRectF();
     279                 :            :   }
     280                 :            : 
     281                 :          0 :   auto it = items.constBegin();
     282                 :            :   //set the box to the first item
     283                 :          0 :   QgsLayoutItem *currentItem = *it;
     284                 :          0 :   it++;
     285                 :          0 :   double minX = currentItem->pos().x();
     286                 :          0 :   double minY = currentItem->pos().y();
     287                 :          0 :   double maxX = minX + currentItem->rect().width();
     288                 :          0 :   double maxY = minY + currentItem->rect().height();
     289                 :            : 
     290                 :            :   double currentMinX, currentMinY, currentMaxX, currentMaxY;
     291                 :            : 
     292                 :          0 :   for ( ; it != items.constEnd(); ++it )
     293                 :            :   {
     294                 :          0 :     currentItem = *it;
     295                 :          0 :     currentMinX = currentItem->pos().x();
     296                 :          0 :     currentMinY = currentItem->pos().y();
     297                 :          0 :     currentMaxX = currentMinX + currentItem->rect().width();
     298                 :          0 :     currentMaxY = currentMinY + currentItem->rect().height();
     299                 :            : 
     300                 :          0 :     if ( currentMinX < minX )
     301                 :          0 :       minX = currentMinX;
     302                 :          0 :     if ( currentMaxX > maxX )
     303                 :          0 :       maxX = currentMaxX;
     304                 :          0 :     if ( currentMinY < minY )
     305                 :          0 :       minY = currentMinY;
     306                 :          0 :     if ( currentMaxY > maxY )
     307                 :          0 :       maxY = currentMaxY;
     308                 :          0 :   }
     309                 :            : 
     310                 :          0 :   return QRectF( QPointF( minX, minY ), QPointF( maxX, maxY ) );
     311                 :          0 : }
     312                 :            : 
     313                 :          0 : QString QgsLayoutAligner::undoText( Distribution distribution )
     314                 :            : {
     315                 :          0 :   switch ( distribution )
     316                 :            :   {
     317                 :            :     case DistributeLeft:
     318                 :          0 :       return QObject::tr( "Distribute Items by Left" );
     319                 :            :     case DistributeHCenter:
     320                 :          0 :       return QObject::tr( "Distribute Items by Horizontal Center" );
     321                 :            :     case DistributeHSpace:
     322                 :          0 :       return QObject::tr( "Distribute Horizontal Spacing Equally" );
     323                 :            :     case DistributeRight:
     324                 :          0 :       return QObject::tr( "Distribute Items by Right" );
     325                 :            :     case DistributeTop:
     326                 :          0 :       return QObject::tr( "Distribute Items by Top" );
     327                 :            :     case DistributeVCenter:
     328                 :          0 :       return QObject::tr( "Distribute Items by Vertical Center" );
     329                 :            :     case DistributeVSpace:
     330                 :          0 :       return QObject::tr( "Distribute Vertical Spacing Equally" );
     331                 :            :     case DistributeBottom:
     332                 :          0 :       return QObject::tr( "Distribute Items by Bottom" );
     333                 :            :   }
     334                 :          0 :   return QString(); //no warnings
     335                 :          0 : }
     336                 :            : 
     337                 :          0 : QString QgsLayoutAligner::undoText( QgsLayoutAligner::Resize resize )
     338                 :            : {
     339                 :          0 :   switch ( resize )
     340                 :            :   {
     341                 :            :     case ResizeNarrowest:
     342                 :          0 :       return QObject::tr( "Resize Items to Narrowest" );
     343                 :            :     case ResizeWidest:
     344                 :          0 :       return QObject::tr( "Resize Items to Widest" );
     345                 :            :     case ResizeShortest:
     346                 :          0 :       return QObject::tr( "Resize Items to Shortest" );
     347                 :            :     case ResizeTallest:
     348                 :          0 :       return QObject::tr( "Resize Items to Tallest" );
     349                 :            :     case ResizeToSquare:
     350                 :          0 :       return QObject::tr( "Resize Items to Square" );
     351                 :            :   }
     352                 :          0 :   return QString(); //no warnings
     353                 :          0 : }
     354                 :            : 
     355                 :          0 : QString QgsLayoutAligner::undoText( Alignment alignment )
     356                 :            : {
     357                 :          0 :   switch ( alignment )
     358                 :            :   {
     359                 :            :     case AlignLeft:
     360                 :          0 :       return QObject::tr( "Align Items to Left" );
     361                 :            :     case AlignHCenter:
     362                 :          0 :       return QObject::tr( "Align Items to Center" );
     363                 :            :     case AlignRight:
     364                 :          0 :       return QObject::tr( "Align Items to Right" );
     365                 :            :     case AlignTop:
     366                 :          0 :       return QObject::tr( "Align Items to Top" );
     367                 :            :     case AlignVCenter:
     368                 :          0 :       return QObject::tr( "Align Items to Vertical Center" );
     369                 :            :     case AlignBottom:
     370                 :          0 :       return QObject::tr( "Align Items to Bottom" );
     371                 :            :   }
     372                 :          0 :   return QString(); //no warnings
     373                 :          0 : }
     374                 :            : 
     375                 :          0 : void QgsLayoutAligner::distributeEquispacedItems( QgsLayout *layout, const QList<QgsLayoutItem *> &items, QgsLayoutAligner::Distribution distribution )
     376                 :            : {
     377                 :          0 :   double length = 0.0;
     378                 :          0 :   double minCoord = std::numeric_limits<double>::max();
     379                 :          0 :   double maxCoord = std::numeric_limits<double>::lowest();
     380                 :          0 :   QMap< double, QgsLayoutItem * > itemCoords;
     381                 :            : 
     382                 :          0 :   for ( QgsLayoutItem *item : items )
     383                 :            :   {
     384                 :          0 :     QRectF itemBBox = item->sceneBoundingRect();
     385                 :            : 
     386                 :          0 :     double item_min = ( distribution == DistributeHSpace ? itemBBox.left() :
     387                 :          0 :                         itemBBox.top() );
     388                 :          0 :     double item_max = ( distribution == DistributeHSpace ? itemBBox.right() :
     389                 :          0 :                         itemBBox.bottom() );
     390                 :            : 
     391                 :          0 :     minCoord = std::min( minCoord, item_min );
     392                 :          0 :     maxCoord = std::max( maxCoord, item_max );
     393                 :          0 :     length += ( item_max - item_min );
     394                 :          0 :     itemCoords.insert( item_min, item );
     395                 :            :   }
     396                 :          0 :   const double step = ( maxCoord - minCoord - length ) / ( items.size() - 1 );
     397                 :            : 
     398                 :          0 :   double currentVal = minCoord;
     399                 :          0 :   layout->undoStack()->beginMacro( undoText( distribution ) );
     400                 :          0 :   for ( auto itemIt = itemCoords.constBegin(); itemIt != itemCoords.constEnd(); ++itemIt )
     401                 :            :   {
     402                 :          0 :     QgsLayoutItem *item = itemIt.value();
     403                 :          0 :     QPointF shifted = item->pos();
     404                 :            : 
     405                 :          0 :     layout->undoStack()->beginCommand( itemIt.value(), QString() );
     406                 :            : 
     407                 :          0 :     if ( distribution == DistributeHSpace )
     408                 :            :     {
     409                 :          0 :       shifted.setX( currentVal );
     410                 :          0 :     }
     411                 :            :     else
     412                 :            :     {
     413                 :          0 :       shifted.setY( currentVal );
     414                 :            :     }
     415                 :            : 
     416                 :          0 :     QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
     417                 :          0 :     item->attemptMove( newPos, false );
     418                 :            : 
     419                 :          0 :     layout->undoStack()->endCommand();
     420                 :            : 
     421                 :          0 :     currentVal += ( distribution == DistributeHSpace ? item->rect().width() :
     422                 :          0 :                     item->rect().height() ) + step;
     423                 :          0 :   }
     424                 :          0 :   layout->undoStack()->endMacro();
     425                 :            :   return;
     426                 :          0 : }

Generated by: LCOV version 1.14