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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgslayoutmodel.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                 :            :  *                                                                         *
      11                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      12                 :            :  *   it under the terms of the GNU General Public License as published by  *
      13                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      14                 :            :  *   (at your option) any later version.                                   *
      15                 :            :  *                                                                         *
      16                 :            :  ***************************************************************************/
      17                 :            : 
      18                 :            : #include "qgslayoutmodel.h"
      19                 :            : #include "qgslayout.h"
      20                 :            : #include "qgsapplication.h"
      21                 :            : #include "qgslogger.h"
      22                 :            : #include "qgslayoutitemgroup.h"
      23                 :            : #include <QApplication>
      24                 :            : #include <QGraphicsItem>
      25                 :            : #include <QDomDocument>
      26                 :            : #include <QDomElement>
      27                 :            : #include <QMimeData>
      28                 :            : #include <QSettings>
      29                 :            : #include <QMessageBox>
      30                 :            : #include <QIcon>
      31                 :            : #include <QIODevice>
      32                 :            : 
      33                 :          0 : QgsLayoutModel::QgsLayoutModel( QgsLayout *layout, QObject *parent )
      34                 :          0 :   : QAbstractItemModel( parent )
      35                 :          0 :   , mLayout( layout )
      36                 :          0 : {
      37                 :            : 
      38                 :          0 : }
      39                 :            : 
      40                 :          0 : QgsLayoutItem *QgsLayoutModel::itemFromIndex( const QModelIndex &index ) const
      41                 :            : {
      42                 :            :   //try to return the QgsLayoutItem corresponding to a QModelIndex
      43                 :          0 :   if ( !index.isValid() || index.row() == 0 )
      44                 :            :   {
      45                 :          0 :     return nullptr;
      46                 :            :   }
      47                 :            : 
      48                 :          0 :   QgsLayoutItem *item = static_cast<QgsLayoutItem *>( index.internalPointer() );
      49                 :          0 :   return item;
      50                 :          0 : }
      51                 :            : 
      52                 :          0 : QModelIndex QgsLayoutModel::index( int row, int column,
      53                 :            :                                    const QModelIndex &parent ) const
      54                 :            : {
      55                 :          0 :   if ( column < 0 || column >= columnCount() )
      56                 :            :   {
      57                 :            :     //column out of bounds
      58                 :          0 :     return QModelIndex();
      59                 :            :   }
      60                 :            : 
      61                 :          0 :   if ( !parent.isValid() && row == 0 )
      62                 :            :   {
      63                 :          0 :     return createIndex( row, column, nullptr );
      64                 :            :   }
      65                 :          0 :   else if ( !parent.isValid() && row >= 1 && row < mItemsInScene.size() + 1 )
      66                 :            :   {
      67                 :            :     //return an index for the layout item at this position
      68                 :          0 :     return createIndex( row, column, mItemsInScene.at( row - 1 ) );
      69                 :            :   }
      70                 :            : 
      71                 :            :   //only top level supported for now
      72                 :          0 :   return QModelIndex();
      73                 :          0 : }
      74                 :            : 
      75                 :          0 : void QgsLayoutModel::refreshItemsInScene()
      76                 :            : {
      77                 :          0 :   mItemsInScene.clear();
      78                 :            : 
      79                 :          0 :   const QList< QGraphicsItem * > items = mLayout->items();
      80                 :            :   //filter paper items from list
      81                 :            :   //TODO - correctly handle grouped item z order placement
      82                 :          0 :   for ( QgsLayoutItem *item : std::as_const( mItemZList ) )
      83                 :            :   {
      84                 :          0 :     if ( item->type() != QgsLayoutItemRegistry::LayoutPage && items.contains( item ) )
      85                 :            :     {
      86                 :          0 :       mItemsInScene.push_back( item );
      87                 :          0 :     }
      88                 :            :   }
      89                 :          0 : }
      90                 :            : 
      91                 :          0 : QModelIndex QgsLayoutModel::parent( const QModelIndex &index ) const
      92                 :            : {
      93                 :          0 :   Q_UNUSED( index )
      94                 :            : 
      95                 :            :   //all items are top level for now
      96                 :          0 :   return QModelIndex();
      97                 :            : }
      98                 :            : 
      99                 :          0 : int QgsLayoutModel::rowCount( const QModelIndex &parent ) const
     100                 :            : {
     101                 :          0 :   if ( !parent.isValid() )
     102                 :            :   {
     103                 :          0 :     return mItemsInScene.size() + 1;
     104                 :            :   }
     105                 :            : 
     106                 :            : #if 0
     107                 :            :   QGraphicsItem *parentItem = itemFromIndex( parent );
     108                 :            : 
     109                 :            :   if ( parentItem )
     110                 :            :   {
     111                 :            :     // return child count for item
     112                 :            :     return 0;
     113                 :            :   }
     114                 :            : #endif
     115                 :            : 
     116                 :            :   //no children for now
     117                 :          0 :   return 0;
     118                 :          0 : }
     119                 :            : 
     120                 :          0 : int QgsLayoutModel::columnCount( const QModelIndex &parent ) const
     121                 :            : {
     122                 :          0 :   Q_UNUSED( parent )
     123                 :          0 :   return 3;
     124                 :            : }
     125                 :            : 
     126                 :          0 : QVariant QgsLayoutModel::data( const QModelIndex &index, int role ) const
     127                 :            : {
     128                 :          0 :   if ( !index.isValid() )
     129                 :          0 :     return QVariant();
     130                 :            : 
     131                 :          0 :   QgsLayoutItem *item = itemFromIndex( index );
     132                 :          0 :   if ( !item )
     133                 :            :   {
     134                 :          0 :     return QVariant();
     135                 :            :   }
     136                 :            : 
     137                 :          0 :   switch ( role )
     138                 :            :   {
     139                 :            :     case Qt::DisplayRole:
     140                 :          0 :       if ( index.column() == ItemId )
     141                 :            :       {
     142                 :          0 :         return item->displayName();
     143                 :            :       }
     144                 :            :       else
     145                 :            :       {
     146                 :          0 :         return QVariant();
     147                 :            :       }
     148                 :            : 
     149                 :            :     case Qt::DecorationRole:
     150                 :          0 :       if ( index.column() == ItemId )
     151                 :            :       {
     152                 :          0 :         return item->icon();
     153                 :            :       }
     154                 :            :       else
     155                 :            :       {
     156                 :          0 :         return QVariant();
     157                 :            :       }
     158                 :            : 
     159                 :            :     case Qt::EditRole:
     160                 :          0 :       if ( index.column() == ItemId )
     161                 :            :       {
     162                 :          0 :         return item->id();
     163                 :            :       }
     164                 :            :       else
     165                 :            :       {
     166                 :          0 :         return QVariant();
     167                 :            :       }
     168                 :            : 
     169                 :            :     case Qt::UserRole:
     170                 :            :       //store item uuid in userrole so we can later get the QModelIndex for a specific item
     171                 :          0 :       return item->uuid();
     172                 :            :     case Qt::UserRole+1:
     173                 :            :       //user role stores reference in column object
     174                 :          0 :       return QVariant::fromValue( qobject_cast<QObject *>( item ) );
     175                 :            : 
     176                 :            :     case Qt::TextAlignmentRole:
     177                 :          0 :       return Qt::AlignLeft & Qt::AlignVCenter;
     178                 :            : 
     179                 :            :     case Qt::CheckStateRole:
     180                 :          0 :       switch ( index.column() )
     181                 :            :       {
     182                 :            :         case Visibility:
     183                 :            :           //column 0 is visibility of item
     184                 :          0 :           return item->isVisible() ? Qt::Checked : Qt::Unchecked;
     185                 :            :         case LockStatus:
     186                 :            :           //column 1 is locked state of item
     187                 :          0 :           return item->isLocked() ? Qt::Checked : Qt::Unchecked;
     188                 :            :         default:
     189                 :          0 :           return QVariant();
     190                 :            :       }
     191                 :            : 
     192                 :            :     default:
     193                 :          0 :       return QVariant();
     194                 :            :   }
     195                 :          0 : }
     196                 :            : 
     197                 :          0 : bool QgsLayoutModel::setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
     198                 :            : {
     199                 :            :   Q_UNUSED( role )
     200                 :            : 
     201                 :          0 :   if ( !index.isValid() )
     202                 :          0 :     return false;
     203                 :            : 
     204                 :          0 :   QgsLayoutItem *item = itemFromIndex( index );
     205                 :          0 :   if ( !item )
     206                 :            :   {
     207                 :          0 :     return false;
     208                 :            :   }
     209                 :            : 
     210                 :          0 :   switch ( index.column() )
     211                 :            :   {
     212                 :            :     case Visibility:
     213                 :            :       //first column is item visibility
     214                 :          0 :       item->setVisibility( value.toBool() );
     215                 :          0 :       return true;
     216                 :            : 
     217                 :            :     case LockStatus:
     218                 :            :       //second column is item lock state
     219                 :          0 :       item->setLocked( value.toBool() );
     220                 :          0 :       return true;
     221                 :            : 
     222                 :            :     case ItemId:
     223                 :            :       //last column is item id
     224                 :          0 :       item->setId( value.toString() );
     225                 :          0 :       return true;
     226                 :            :   }
     227                 :            : 
     228                 :          0 :   return false;
     229                 :          0 : }
     230                 :            : 
     231                 :          0 : QVariant QgsLayoutModel::headerData( int section, Qt::Orientation orientation, int role ) const
     232                 :            : {
     233                 :          0 :   switch ( role )
     234                 :            :   {
     235                 :            :     case Qt::DisplayRole:
     236                 :            :     {
     237                 :          0 :       if ( section == ItemId )
     238                 :            :       {
     239                 :          0 :         return tr( "Item" );
     240                 :            :       }
     241                 :          0 :       return QVariant();
     242                 :            :     }
     243                 :            : 
     244                 :            :     case Qt::DecorationRole:
     245                 :            :     {
     246                 :          0 :       if ( section == Visibility )
     247                 :            :       {
     248                 :          0 :         return QVariant::fromValue( QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowAllLayersGray.svg" ) ) );
     249                 :            :       }
     250                 :          0 :       else if ( section == LockStatus )
     251                 :            :       {
     252                 :          0 :         return QVariant::fromValue( QgsApplication::getThemeIcon( QStringLiteral( "/lockedGray.svg" ) ) );
     253                 :            :       }
     254                 :            : 
     255                 :          0 :       return QVariant();
     256                 :            :     }
     257                 :            : 
     258                 :            :     case Qt::TextAlignmentRole:
     259                 :          0 :       return Qt::AlignLeft & Qt::AlignVCenter;
     260                 :            : 
     261                 :            :     default:
     262                 :          0 :       return QAbstractItemModel::headerData( section, orientation, role );
     263                 :            :   }
     264                 :            : 
     265                 :          0 : }
     266                 :            : 
     267                 :          0 : Qt::DropActions QgsLayoutModel::supportedDropActions() const
     268                 :            : {
     269                 :          0 :   return Qt::MoveAction;
     270                 :            : }
     271                 :            : 
     272                 :          0 : QStringList QgsLayoutModel::mimeTypes() const
     273                 :            : {
     274                 :          0 :   QStringList types;
     275                 :          0 :   types << QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" );
     276                 :          0 :   return types;
     277                 :          0 : }
     278                 :            : 
     279                 :          0 : QMimeData *QgsLayoutModel::mimeData( const QModelIndexList &indexes ) const
     280                 :            : {
     281                 :          0 :   QMimeData *mimeData = new QMimeData();
     282                 :          0 :   QByteArray encodedData;
     283                 :            : 
     284                 :          0 :   QDataStream stream( &encodedData, QIODevice::WriteOnly );
     285                 :            : 
     286                 :          0 :   for ( const QModelIndex &index : indexes )
     287                 :            :   {
     288                 :          0 :     if ( index.isValid() && index.column() == ItemId )
     289                 :            :     {
     290                 :          0 :       QgsLayoutItem *item = itemFromIndex( index );
     291                 :          0 :       if ( !item )
     292                 :            :       {
     293                 :          0 :         continue;
     294                 :            :       }
     295                 :          0 :       QString text = item->uuid();
     296                 :          0 :       stream << text;
     297                 :          0 :     }
     298                 :            :   }
     299                 :            : 
     300                 :          0 :   mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ), encodedData );
     301                 :          0 :   return mimeData;
     302                 :          0 : }
     303                 :            : 
     304                 :          0 : bool zOrderDescending( QgsLayoutItem *item1, QgsLayoutItem *item2 )
     305                 :            : {
     306                 :          0 :   return item1->zValue() > item2->zValue();
     307                 :            : }
     308                 :            : 
     309                 :          0 : bool QgsLayoutModel::dropMimeData( const QMimeData *data,
     310                 :            :                                    Qt::DropAction action, int row, int column, const QModelIndex &parent )
     311                 :            : {
     312                 :          0 :   if ( column != ItemId && column != -1 )
     313                 :            :   {
     314                 :          0 :     return false;
     315                 :            :   }
     316                 :            : 
     317                 :          0 :   if ( action == Qt::IgnoreAction )
     318                 :            :   {
     319                 :          0 :     return true;
     320                 :            :   }
     321                 :            : 
     322                 :          0 :   if ( !data->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) ) )
     323                 :            :   {
     324                 :          0 :     return false;
     325                 :            :   }
     326                 :            : 
     327                 :          0 :   if ( parent.isValid() )
     328                 :            :   {
     329                 :          0 :     return false;
     330                 :            :   }
     331                 :            : 
     332                 :          0 :   int beginRow = row != -1 ? row : rowCount( QModelIndex() );
     333                 :            : 
     334                 :          0 :   QByteArray encodedData = data->data( QStringLiteral( "application/x-vnd.qgis.qgis.composeritemid" ) );
     335                 :          0 :   QDataStream stream( &encodedData, QIODevice::ReadOnly );
     336                 :          0 :   QList<QgsLayoutItem *> droppedItems;
     337                 :          0 :   int rows = 0;
     338                 :            : 
     339                 :          0 :   while ( !stream.atEnd() )
     340                 :            :   {
     341                 :          0 :     QString text;
     342                 :          0 :     stream >> text;
     343                 :          0 :     QgsLayoutItem *item = mLayout->itemByUuid( text );
     344                 :          0 :     if ( item )
     345                 :            :     {
     346                 :          0 :       droppedItems << item;
     347                 :          0 :       ++rows;
     348                 :          0 :     }
     349                 :          0 :   }
     350                 :            : 
     351                 :          0 :   if ( droppedItems.empty() )
     352                 :            :   {
     353                 :            :     //no dropped items
     354                 :          0 :     return false;
     355                 :            :   }
     356                 :            : 
     357                 :            :   //move dropped items
     358                 :            : 
     359                 :            :   //first sort them by z-order
     360                 :          0 :   std::sort( droppedItems.begin(), droppedItems.end(), zOrderDescending );
     361                 :            : 
     362                 :            :   //calculate position in z order list to drop items at
     363                 :          0 :   int destPos = 0;
     364                 :          0 :   if ( beginRow < rowCount() )
     365                 :            :   {
     366                 :          0 :     QgsLayoutItem *itemBefore = mItemsInScene.at( beginRow - 1 );
     367                 :          0 :     destPos = mItemZList.indexOf( itemBefore );
     368                 :          0 :   }
     369                 :            :   else
     370                 :            :   {
     371                 :            :     //place items at end
     372                 :          0 :     destPos = mItemZList.size();
     373                 :            :   }
     374                 :            : 
     375                 :            :   //calculate position to insert moved rows to
     376                 :          0 :   int insertPos = destPos;
     377                 :          0 :   for ( QgsLayoutItem *item : std::as_const( droppedItems ) )
     378                 :            :   {
     379                 :          0 :     int listPos = mItemZList.indexOf( item );
     380                 :          0 :     if ( listPos == -1 )
     381                 :            :     {
     382                 :            :       //should be impossible
     383                 :          0 :       continue;
     384                 :            :     }
     385                 :            : 
     386                 :          0 :     if ( listPos < destPos )
     387                 :            :     {
     388                 :          0 :       insertPos--;
     389                 :          0 :     }
     390                 :            :   }
     391                 :            : 
     392                 :            :   //remove rows from list
     393                 :          0 :   auto itemIt = droppedItems.begin();
     394                 :          0 :   for ( ; itemIt != droppedItems.end(); ++itemIt )
     395                 :            :   {
     396                 :          0 :     mItemZList.removeOne( *itemIt );
     397                 :          0 :   }
     398                 :            : 
     399                 :            :   //insert items
     400                 :          0 :   itemIt = droppedItems.begin();
     401                 :          0 :   for ( ; itemIt != droppedItems.end(); ++itemIt )
     402                 :            :   {
     403                 :          0 :     mItemZList.insert( insertPos, *itemIt );
     404                 :          0 :     insertPos++;
     405                 :          0 :   }
     406                 :            : 
     407                 :          0 :   rebuildSceneItemList();
     408                 :            : 
     409                 :          0 :   mLayout->updateZValues( true );
     410                 :            : 
     411                 :          0 :   return true;
     412                 :          0 : }
     413                 :            : 
     414                 :          0 : bool QgsLayoutModel::removeRows( int row, int count, const QModelIndex &parent )
     415                 :            : {
     416                 :            :   Q_UNUSED( count )
     417                 :          0 :   if ( parent.isValid() )
     418                 :            :   {
     419                 :          0 :     return false;
     420                 :            :   }
     421                 :            : 
     422                 :          0 :   if ( row >= rowCount() )
     423                 :            :   {
     424                 :          0 :     return false;
     425                 :            :   }
     426                 :            : 
     427                 :            :   //do nothing - moves are handled by the dropMimeData method
     428                 :          0 :   return true;
     429                 :          0 : }
     430                 :            : 
     431                 :            : ///@cond PRIVATE
     432                 :          0 : void QgsLayoutModel::clear()
     433                 :            : {
     434                 :            :   //totally reset model
     435                 :          0 :   beginResetModel();
     436                 :          0 :   mItemZList.clear();
     437                 :          0 :   refreshItemsInScene();
     438                 :          0 :   endResetModel();
     439                 :          0 : }
     440                 :            : 
     441                 :          0 : int QgsLayoutModel::zOrderListSize() const
     442                 :            : {
     443                 :          0 :   return mItemZList.size();
     444                 :            : }
     445                 :            : 
     446                 :          0 : void QgsLayoutModel::rebuildZList()
     447                 :            : {
     448                 :          0 :   QList<QgsLayoutItem *> sortedList;
     449                 :            :   //rebuild the item z order list based on the current zValues of items in the scene
     450                 :            : 
     451                 :            :   //get items in descending zValue order
     452                 :          0 :   const QList<QGraphicsItem *> itemList = mLayout->items( Qt::DescendingOrder );
     453                 :          0 :   for ( QGraphicsItem *item : itemList )
     454                 :            :   {
     455                 :          0 :     if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
     456                 :            :     {
     457                 :          0 :       if ( layoutItem->type() != QgsLayoutItemRegistry::LayoutPage )
     458                 :            :       {
     459                 :          0 :         sortedList.append( layoutItem );
     460                 :          0 :       }
     461                 :          0 :     }
     462                 :            :   }
     463                 :            : 
     464                 :          0 :   mItemZList = sortedList;
     465                 :          0 :   rebuildSceneItemList();
     466                 :          0 : }
     467                 :            : ///@endcond
     468                 :            : 
     469                 :          0 : void QgsLayoutModel::rebuildSceneItemList()
     470                 :            : {
     471                 :            :   //step through the z list and rebuild the items in scene list,
     472                 :            :   //emitting signals as required
     473                 :          0 :   int row = 0;
     474                 :          0 :   const QList< QGraphicsItem * > items = mLayout->items();
     475                 :          0 :   for ( QgsLayoutItem *item : std::as_const( mItemZList ) )
     476                 :            :   {
     477                 :          0 :     if ( item->type() == QgsLayoutItemRegistry::LayoutPage || !items.contains( item ) )
     478                 :            :     {
     479                 :            :       //item not in scene, skip it
     480                 :          0 :       continue;
     481                 :            :     }
     482                 :            : 
     483                 :          0 :     int sceneListPos = mItemsInScene.indexOf( item );
     484                 :          0 :     if ( sceneListPos == row )
     485                 :            :     {
     486                 :            :       //already in list in correct position, nothing to do
     487                 :            : 
     488                 :          0 :     }
     489                 :          0 :     else if ( sceneListPos != -1 )
     490                 :            :     {
     491                 :            :       //in list, but in wrong spot
     492                 :          0 :       beginMoveRows( QModelIndex(), sceneListPos + 1, sceneListPos + 1, QModelIndex(), row + 1 );
     493                 :          0 :       mItemsInScene.removeAt( sceneListPos );
     494                 :          0 :       mItemsInScene.insert( row, item );
     495                 :          0 :       endMoveRows();
     496                 :          0 :     }
     497                 :            :     else
     498                 :            :     {
     499                 :            :       //needs to be inserted into list
     500                 :          0 :       beginInsertRows( QModelIndex(), row + 1, row + 1 );
     501                 :          0 :       mItemsInScene.insert( row, item );
     502                 :          0 :       endInsertRows();
     503                 :            :     }
     504                 :          0 :     row++;
     505                 :            :   }
     506                 :          0 : }
     507                 :            : ///@cond PRIVATE
     508                 :          0 : void QgsLayoutModel::addItemAtTop( QgsLayoutItem *item )
     509                 :            : {
     510                 :          0 :   mItemZList.push_front( item );
     511                 :          0 :   refreshItemsInScene();
     512                 :          0 :   item->setZValue( mItemZList.size() );
     513                 :          0 : }
     514                 :            : 
     515                 :          0 : void QgsLayoutModel::removeItem( QgsLayoutItem *item )
     516                 :            : {
     517                 :          0 :   if ( !item )
     518                 :            :   {
     519                 :            :     //nothing to do
     520                 :          0 :     return;
     521                 :            :   }
     522                 :            : 
     523                 :          0 :   int pos = mItemZList.indexOf( item );
     524                 :          0 :   if ( pos == -1 )
     525                 :            :   {
     526                 :            :     //item not in z list, nothing to do
     527                 :          0 :     return;
     528                 :            :   }
     529                 :            : 
     530                 :            :   //need to get QModelIndex of item
     531                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     532                 :          0 :   if ( !itemIndex.isValid() )
     533                 :            :   {
     534                 :            :     //removing an item not in the scene (e.g., deleted item)
     535                 :            :     //we need to remove it from the list, but don't need to call
     536                 :            :     //beginRemoveRows or endRemoveRows since the item was not used by the model
     537                 :          0 :     mItemZList.removeAt( pos );
     538                 :          0 :     refreshItemsInScene();
     539                 :          0 :     return;
     540                 :            :   }
     541                 :            : 
     542                 :            :   //remove item from model
     543                 :          0 :   int row = itemIndex.row();
     544                 :          0 :   beginRemoveRows( QModelIndex(), row, row );
     545                 :          0 :   mItemZList.removeAt( pos );
     546                 :          0 :   refreshItemsInScene();
     547                 :          0 :   endRemoveRows();
     548                 :          0 : }
     549                 :            : 
     550                 :          0 : void QgsLayoutModel::setItemRemoved( QgsLayoutItem *item )
     551                 :            : {
     552                 :          0 :   if ( !item )
     553                 :            :   {
     554                 :            :     //nothing to do
     555                 :          0 :     return;
     556                 :            :   }
     557                 :            : 
     558                 :          0 :   int pos = mItemZList.indexOf( item );
     559                 :          0 :   if ( pos == -1 )
     560                 :            :   {
     561                 :            :     //item not in z list, nothing to do
     562                 :          0 :     return;
     563                 :            :   }
     564                 :            : 
     565                 :            :   //need to get QModelIndex of item
     566                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     567                 :          0 :   if ( !itemIndex.isValid() )
     568                 :            :   {
     569                 :          0 :     return;
     570                 :            :   }
     571                 :            : 
     572                 :            :   //removing item
     573                 :          0 :   int row = itemIndex.row();
     574                 :          0 :   beginRemoveRows( QModelIndex(), row, row );
     575                 :          0 :   mLayout->removeItem( item );
     576                 :          0 :   refreshItemsInScene();
     577                 :          0 :   endRemoveRows();
     578                 :          0 : }
     579                 :            : 
     580                 :          0 : void QgsLayoutModel::updateItemDisplayName( QgsLayoutItem *item )
     581                 :            : {
     582                 :          0 :   if ( !item )
     583                 :            :   {
     584                 :            :     //nothing to do
     585                 :          0 :     return;
     586                 :            :   }
     587                 :            : 
     588                 :            :   //need to get QModelIndex of item
     589                 :          0 :   QModelIndex itemIndex = indexForItem( item, ItemId );
     590                 :          0 :   if ( !itemIndex.isValid() )
     591                 :            :   {
     592                 :          0 :     return;
     593                 :            :   }
     594                 :            : 
     595                 :            :   //emit signal for item id change
     596                 :          0 :   emit dataChanged( itemIndex, itemIndex );
     597                 :          0 : }
     598                 :            : 
     599                 :          0 : void QgsLayoutModel::updateItemLockStatus( QgsLayoutItem *item )
     600                 :            : {
     601                 :          0 :   if ( !item )
     602                 :            :   {
     603                 :            :     //nothing to do
     604                 :          0 :     return;
     605                 :            :   }
     606                 :            : 
     607                 :            :   //need to get QModelIndex of item
     608                 :          0 :   QModelIndex itemIndex = indexForItem( item, LockStatus );
     609                 :          0 :   if ( !itemIndex.isValid() )
     610                 :            :   {
     611                 :          0 :     return;
     612                 :            :   }
     613                 :            : 
     614                 :            :   //emit signal for item lock status change
     615                 :          0 :   emit dataChanged( itemIndex, itemIndex );
     616                 :          0 : }
     617                 :            : 
     618                 :          0 : void QgsLayoutModel::updateItemVisibility( QgsLayoutItem *item )
     619                 :            : {
     620                 :          0 :   if ( !item )
     621                 :            :   {
     622                 :            :     //nothing to do
     623                 :          0 :     return;
     624                 :            :   }
     625                 :            : 
     626                 :            :   //need to get QModelIndex of item
     627                 :          0 :   QModelIndex itemIndex = indexForItem( item, Visibility );
     628                 :          0 :   if ( !itemIndex.isValid() )
     629                 :            :   {
     630                 :          0 :     return;
     631                 :            :   }
     632                 :            : 
     633                 :            :   //emit signal for item visibility change
     634                 :          0 :   emit dataChanged( itemIndex, itemIndex );
     635                 :          0 : }
     636                 :            : 
     637                 :          0 : void QgsLayoutModel::updateItemSelectStatus( QgsLayoutItem *item )
     638                 :            : {
     639                 :          0 :   if ( !item )
     640                 :            :   {
     641                 :            :     //nothing to do
     642                 :          0 :     return;
     643                 :            :   }
     644                 :            : 
     645                 :            :   //need to get QModelIndex of item
     646                 :          0 :   QModelIndex itemIndex = indexForItem( item, ItemId );
     647                 :          0 :   if ( !itemIndex.isValid() )
     648                 :            :   {
     649                 :          0 :     return;
     650                 :            :   }
     651                 :            : 
     652                 :            :   //emit signal for item visibility change
     653                 :          0 :   emit dataChanged( itemIndex, itemIndex );
     654                 :          0 : }
     655                 :            : 
     656                 :          0 : bool QgsLayoutModel::reorderItemUp( QgsLayoutItem *item )
     657                 :            : {
     658                 :          0 :   if ( !item )
     659                 :            :   {
     660                 :          0 :     return false;
     661                 :            :   }
     662                 :            : 
     663                 :          0 :   if ( mItemsInScene.at( 0 ) == item )
     664                 :            :   {
     665                 :            :     //item is already topmost item present in scene, nothing to do
     666                 :          0 :     return false;
     667                 :            :   }
     668                 :            : 
     669                 :            :   //move item in z list
     670                 :          0 :   QMutableListIterator<QgsLayoutItem *> it( mItemZList );
     671                 :          0 :   if ( ! it.findNext( item ) )
     672                 :            :   {
     673                 :            :     //can't find item in z list, nothing to do
     674                 :          0 :     return false;
     675                 :            :   }
     676                 :            : 
     677                 :          0 :   const QList< QGraphicsItem * > sceneItems = mLayout->items();
     678                 :            : 
     679                 :          0 :   it.remove();
     680                 :          0 :   while ( it.hasPrevious() )
     681                 :            :   {
     682                 :            :     //search through item z list to find previous item which is present in the scene
     683                 :          0 :     it.previous();
     684                 :          0 :     if ( it.value() && sceneItems.contains( it.value() ) )
     685                 :            :     {
     686                 :          0 :       break;
     687                 :            :     }
     688                 :            :   }
     689                 :          0 :   it.insert( item );
     690                 :            : 
     691                 :            :   //also move item in scene items z list and notify of model changes
     692                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     693                 :          0 :   if ( !itemIndex.isValid() )
     694                 :            :   {
     695                 :          0 :     return true;
     696                 :            :   }
     697                 :            : 
     698                 :            :   //move item up in scene list
     699                 :          0 :   int row = itemIndex.row();
     700                 :          0 :   beginMoveRows( QModelIndex(), row, row, QModelIndex(), row - 1 );
     701                 :          0 :   refreshItemsInScene();
     702                 :          0 :   endMoveRows();
     703                 :          0 :   return true;
     704                 :          0 : }
     705                 :            : 
     706                 :          0 : bool QgsLayoutModel::reorderItemDown( QgsLayoutItem *item )
     707                 :            : {
     708                 :          0 :   if ( !item )
     709                 :            :   {
     710                 :          0 :     return false;
     711                 :            :   }
     712                 :            : 
     713                 :          0 :   if ( mItemsInScene.last() == item )
     714                 :            :   {
     715                 :            :     //item is already lowest item present in scene, nothing to do
     716                 :          0 :     return false;
     717                 :            :   }
     718                 :            : 
     719                 :            :   //move item in z list
     720                 :          0 :   QMutableListIterator<QgsLayoutItem *> it( mItemZList );
     721                 :          0 :   if ( ! it.findNext( item ) )
     722                 :            :   {
     723                 :            :     //can't find item in z list, nothing to do
     724                 :          0 :     return false;
     725                 :            :   }
     726                 :            : 
     727                 :          0 :   const QList< QGraphicsItem * > sceneItems = mLayout->items();
     728                 :          0 :   it.remove();
     729                 :          0 :   while ( it.hasNext() )
     730                 :            :   {
     731                 :            :     //search through item z list to find next item which is present in the scene
     732                 :            :     //(deleted items still exist in the z list so that they can be restored to their correct stacking order,
     733                 :            :     //but since they are not in the scene they should be ignored here)
     734                 :          0 :     it.next();
     735                 :          0 :     if ( it.value() && sceneItems.contains( it.value() ) )
     736                 :            :     {
     737                 :          0 :       break;
     738                 :            :     }
     739                 :            :   }
     740                 :          0 :   it.insert( item );
     741                 :            : 
     742                 :            :   //also move item in scene items z list and notify of model changes
     743                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     744                 :          0 :   if ( !itemIndex.isValid() )
     745                 :            :   {
     746                 :          0 :     return true;
     747                 :            :   }
     748                 :            : 
     749                 :            :   //move item down in scene list
     750                 :          0 :   int row = itemIndex.row();
     751                 :          0 :   beginMoveRows( QModelIndex(), row, row, QModelIndex(), row + 2 );
     752                 :          0 :   refreshItemsInScene();
     753                 :          0 :   endMoveRows();
     754                 :          0 :   return true;
     755                 :          0 : }
     756                 :            : 
     757                 :          0 : bool QgsLayoutModel::reorderItemToTop( QgsLayoutItem *item )
     758                 :            : {
     759                 :          0 :   if ( !item || !mItemsInScene.contains( item ) )
     760                 :            :   {
     761                 :          0 :     return false;
     762                 :            :   }
     763                 :            : 
     764                 :          0 :   if ( mItemsInScene.at( 0 ) == item )
     765                 :            :   {
     766                 :            :     //item is already topmost item present in scene, nothing to do
     767                 :          0 :     return false;
     768                 :            :   }
     769                 :            : 
     770                 :            :   //move item in z list
     771                 :          0 :   QMutableListIterator<QgsLayoutItem *> it( mItemZList );
     772                 :          0 :   if ( it.findNext( item ) )
     773                 :            :   {
     774                 :          0 :     it.remove();
     775                 :          0 :   }
     776                 :          0 :   mItemZList.push_front( item );
     777                 :            : 
     778                 :            :   //also move item in scene items z list and notify of model changes
     779                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     780                 :          0 :   if ( !itemIndex.isValid() )
     781                 :            :   {
     782                 :          0 :     return true;
     783                 :            :   }
     784                 :            : 
     785                 :            :   //move item to top
     786                 :          0 :   int row = itemIndex.row();
     787                 :          0 :   beginMoveRows( QModelIndex(), row, row, QModelIndex(), 1 );
     788                 :          0 :   refreshItemsInScene();
     789                 :          0 :   endMoveRows();
     790                 :          0 :   return true;
     791                 :          0 : }
     792                 :            : 
     793                 :          0 : bool QgsLayoutModel::reorderItemToBottom( QgsLayoutItem *item )
     794                 :            : {
     795                 :          0 :   if ( !item || !mItemsInScene.contains( item ) )
     796                 :            :   {
     797                 :          0 :     return false;
     798                 :            :   }
     799                 :            : 
     800                 :          0 :   if ( mItemsInScene.last() == item )
     801                 :            :   {
     802                 :            :     //item is already lowest item present in scene, nothing to do
     803                 :          0 :     return false;
     804                 :            :   }
     805                 :            : 
     806                 :            :   //move item in z list
     807                 :          0 :   QMutableListIterator<QgsLayoutItem *> it( mItemZList );
     808                 :          0 :   if ( it.findNext( item ) )
     809                 :            :   {
     810                 :          0 :     it.remove();
     811                 :          0 :   }
     812                 :          0 :   mItemZList.push_back( item );
     813                 :            : 
     814                 :            :   //also move item in scene items z list and notify of model changes
     815                 :          0 :   QModelIndex itemIndex = indexForItem( item );
     816                 :          0 :   if ( !itemIndex.isValid() )
     817                 :            :   {
     818                 :          0 :     return true;
     819                 :            :   }
     820                 :            : 
     821                 :            :   //move item to bottom
     822                 :          0 :   int row = itemIndex.row();
     823                 :          0 :   beginMoveRows( QModelIndex(), row, row, QModelIndex(), rowCount() );
     824                 :          0 :   refreshItemsInScene();
     825                 :          0 :   endMoveRows();
     826                 :          0 :   return true;
     827                 :          0 : }
     828                 :            : 
     829                 :          0 : QgsLayoutItem *QgsLayoutModel::findItemAbove( QgsLayoutItem *item ) const
     830                 :            : {
     831                 :            :   //search item z list for selected item
     832                 :          0 :   QListIterator<QgsLayoutItem *> it( mItemZList );
     833                 :          0 :   it.toBack();
     834                 :          0 :   if ( it.findPrevious( item ) )
     835                 :            :   {
     836                 :            :     //move position to before selected item
     837                 :          0 :     while ( it.hasPrevious() )
     838                 :            :     {
     839                 :            :       //now find previous item, since list is sorted from lowest->highest items
     840                 :          0 :       if ( it.hasPrevious() && !it.peekPrevious()->isGroupMember() )
     841                 :            :       {
     842                 :          0 :         return it.previous();
     843                 :            :       }
     844                 :          0 :       it.previous();
     845                 :            :     }
     846                 :          0 :   }
     847                 :          0 :   return nullptr;
     848                 :          0 : }
     849                 :            : 
     850                 :          0 : QgsLayoutItem *QgsLayoutModel::findItemBelow( QgsLayoutItem *item ) const
     851                 :            : {
     852                 :            :   //search item z list for selected item
     853                 :          0 :   QListIterator<QgsLayoutItem *> it( mItemZList );
     854                 :          0 :   if ( it.findNext( item ) )
     855                 :            :   {
     856                 :            :     //return next item (list is sorted from lowest->highest items)
     857                 :          0 :     while ( it.hasNext() )
     858                 :            :     {
     859                 :          0 :       if ( !it.peekNext()->isGroupMember() )
     860                 :            :       {
     861                 :          0 :         return it.next();
     862                 :            :       }
     863                 :          0 :       it.next();
     864                 :            :     }
     865                 :          0 :   }
     866                 :          0 :   return nullptr;
     867                 :          0 : }
     868                 :            : 
     869                 :          0 : QList<QgsLayoutItem *> &QgsLayoutModel::zOrderList()
     870                 :            : {
     871                 :          0 :   return mItemZList;
     872                 :            : }
     873                 :            : 
     874                 :            : ///@endcond
     875                 :            : 
     876                 :          0 : Qt::ItemFlags QgsLayoutModel::flags( const QModelIndex &index ) const
     877                 :            : {
     878                 :          0 :   Qt::ItemFlags flags = QAbstractItemModel::flags( index );
     879                 :            : 
     880                 :          0 :   if ( ! index.isValid() )
     881                 :            :   {
     882                 :          0 :     return flags | Qt::ItemIsDropEnabled;
     883                 :            :   }
     884                 :            : 
     885                 :          0 :   if ( index.row() == 0 )
     886                 :            :   {
     887                 :          0 :     return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
     888                 :            :   }
     889                 :            :   else
     890                 :            :   {
     891                 :          0 :     switch ( index.column() )
     892                 :            :     {
     893                 :            :       case Visibility:
     894                 :            :       case LockStatus:
     895                 :          0 :         return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
     896                 :            :       case ItemId:
     897                 :          0 :         return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
     898                 :            :       default:
     899                 :          0 :         return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
     900                 :            :     }
     901                 :            :   }
     902                 :          0 : }
     903                 :            : 
     904                 :          0 : QModelIndex QgsLayoutModel::indexForItem( QgsLayoutItem *item, const int column )
     905                 :            : {
     906                 :          0 :   if ( !item )
     907                 :            :   {
     908                 :          0 :     return QModelIndex();
     909                 :            :   }
     910                 :            : 
     911                 :          0 :   int row = mItemsInScene.indexOf( item );
     912                 :          0 :   if ( row == -1 )
     913                 :            :   {
     914                 :            :     //not found
     915                 :          0 :     return QModelIndex();
     916                 :            :   }
     917                 :            : 
     918                 :          0 :   return index( row + 1, column );
     919                 :          0 : }
     920                 :            : 
     921                 :            : ///@cond PRIVATE
     922                 :          0 : void QgsLayoutModel::setSelected( const QModelIndex &index )
     923                 :            : {
     924                 :          0 :   QgsLayoutItem *item = itemFromIndex( index );
     925                 :          0 :   if ( !item )
     926                 :            :   {
     927                 :          0 :     return;
     928                 :            :   }
     929                 :            : 
     930                 :            :   // find top level group this item is contained within, and mark the group as selected
     931                 :          0 :   QgsLayoutItemGroup *group = item->parentGroup();
     932                 :          0 :   while ( group && group->parentGroup() )
     933                 :            :   {
     934                 :          0 :     group = group->parentGroup();
     935                 :            :   }
     936                 :            : 
     937                 :            :   // but the actual main selected item is the item itself (allows editing of item properties)
     938                 :          0 :   mLayout->setSelectedItem( item );
     939                 :            : 
     940                 :          0 :   if ( group && group != item )
     941                 :          0 :     group->setSelected( true );
     942                 :          0 : }
     943                 :            : ///@endcond
     944                 :            : 
     945                 :            : //
     946                 :            : // QgsLayoutProxyModel
     947                 :            : //
     948                 :            : 
     949                 :          0 : QgsLayoutProxyModel::QgsLayoutProxyModel( QgsLayout *layout, QObject *parent )
     950                 :          0 :   : QSortFilterProxyModel( parent )
     951                 :          0 :   , mLayout( layout )
     952                 :          0 :   , mItemTypeFilter( QgsLayoutItemRegistry::LayoutItem )
     953                 :          0 : {
     954                 :          0 :   if ( mLayout )
     955                 :          0 :     setSourceModel( mLayout->itemsModel() );
     956                 :            : 
     957                 :          0 :   setDynamicSortFilter( true );
     958                 :          0 :   setSortLocaleAware( true );
     959                 :          0 :   sort( QgsLayoutModel::ItemId );
     960                 :          0 : }
     961                 :            : 
     962                 :          0 : bool QgsLayoutProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
     963                 :            : {
     964                 :          0 :   const QString leftText = sourceModel()->data( left, Qt::DisplayRole ).toString();
     965                 :          0 :   const QString rightText = sourceModel()->data( right, Qt::DisplayRole ).toString();
     966                 :          0 :   if ( leftText.isEmpty() )
     967                 :          0 :     return true;
     968                 :          0 :   if ( rightText.isEmpty() )
     969                 :          0 :     return false;
     970                 :            : 
     971                 :            :   //sort by item id
     972                 :          0 :   const QgsLayoutItem *item1 = itemFromSourceIndex( left );
     973                 :          0 :   const QgsLayoutItem *item2 = itemFromSourceIndex( right );
     974                 :          0 :   if ( !item1 )
     975                 :          0 :     return false;
     976                 :            : 
     977                 :          0 :   if ( !item2 )
     978                 :          0 :     return true;
     979                 :            : 
     980                 :          0 :   return QString::localeAwareCompare( item1->displayName(), item2->displayName() ) < 0;
     981                 :          0 : }
     982                 :            : 
     983                 :          0 : QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sourceIndex ) const
     984                 :            : {
     985                 :          0 :   if ( !mLayout )
     986                 :          0 :     return nullptr;
     987                 :            : 
     988                 :            :   //get column corresponding to an index from the source model
     989                 :          0 :   QVariant itemAsVariant = sourceModel()->data( sourceIndex, Qt::UserRole + 1 );
     990                 :          0 :   return qobject_cast<QgsLayoutItem *>( itemAsVariant.value<QObject *>() );
     991                 :          0 : }
     992                 :            : 
     993                 :          0 : void QgsLayoutProxyModel::setAllowEmptyItem( bool allowEmpty )
     994                 :            : {
     995                 :          0 :   mAllowEmpty = allowEmpty;
     996                 :          0 :   invalidateFilter();
     997                 :          0 : }
     998                 :            : 
     999                 :          0 : bool QgsLayoutProxyModel::allowEmptyItem() const
    1000                 :            : {
    1001                 :          0 :   return mAllowEmpty;
    1002                 :            : }
    1003                 :            : 
    1004                 :          0 : void QgsLayoutProxyModel::setItemFlags( QgsLayoutItem::Flags flags )
    1005                 :            : {
    1006                 :          0 :   mItemFlags = flags;
    1007                 :          0 :   invalidateFilter();
    1008                 :          0 : }
    1009                 :            : 
    1010                 :          0 : QgsLayoutItem::Flags QgsLayoutProxyModel::itemFlags() const
    1011                 :            : {
    1012                 :          0 :   return mItemFlags;
    1013                 :            : }
    1014                 :            : 
    1015                 :          0 : void QgsLayoutProxyModel::setFilterType( QgsLayoutItemRegistry::ItemType filter )
    1016                 :            : {
    1017                 :          0 :   mItemTypeFilter = filter;
    1018                 :          0 :   invalidate();
    1019                 :          0 : }
    1020                 :            : 
    1021                 :          0 : void QgsLayoutProxyModel::setExceptedItemList( const QList< QgsLayoutItem *> &items )
    1022                 :            : {
    1023                 :          0 :   if ( mExceptedList == items )
    1024                 :          0 :     return;
    1025                 :            : 
    1026                 :          0 :   mExceptedList = items;
    1027                 :          0 :   invalidateFilter();
    1028                 :          0 : }
    1029                 :            : 
    1030                 :          0 : bool QgsLayoutProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
    1031                 :            : {
    1032                 :            :   //get QgsComposerItem corresponding to row
    1033                 :          0 :   QModelIndex index = sourceModel()->index( sourceRow, 0, sourceParent );
    1034                 :          0 :   QgsLayoutItem *item = itemFromSourceIndex( index );
    1035                 :            : 
    1036                 :          0 :   if ( !item )
    1037                 :          0 :     return mAllowEmpty;
    1038                 :            : 
    1039                 :            :   // specific exceptions
    1040                 :          0 :   if ( mExceptedList.contains( item ) )
    1041                 :          0 :     return false;
    1042                 :            : 
    1043                 :            :   // filter by type
    1044                 :          0 :   if ( mItemTypeFilter != QgsLayoutItemRegistry::LayoutItem && item->type() != mItemTypeFilter )
    1045                 :          0 :     return false;
    1046                 :            : 
    1047                 :          0 :   if ( mItemFlags && !( item->itemFlags() & mItemFlags ) )
    1048                 :            :   {
    1049                 :          0 :     return false;
    1050                 :            :   }
    1051                 :            : 
    1052                 :          0 :   return true;
    1053                 :          0 : }

Generated by: LCOV version 1.14