Skip to content

Commit

Permalink
Move layout manager model from app to core and add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 11, 2019
1 parent 0b270f9 commit cba2277
Show file tree
Hide file tree
Showing 8 changed files with 729 additions and 198 deletions.
3 changes: 3 additions & 0 deletions python/core/auto_additions/qgslayoutmanager.py
@@ -0,0 +1,3 @@
# The following has been generated automatically from src/core/layout/qgslayoutmanager.h
QgsLayoutManagerProxyModel.Filters.baseClass = QgsLayoutManagerProxyModel
Filters = QgsLayoutManagerProxyModel # dirty hack since SIP seems to introduce the flags in module
117 changes: 116 additions & 1 deletion python/core/auto_generated/layout/qgslayoutmanager.sip.in
Expand Up @@ -9,7 +9,6 @@




class QgsLayoutManager : QObject
{
%Docstring
Expand Down Expand Up @@ -145,6 +144,122 @@ Emitted when a layout is renamed

};


class QgsLayoutManagerModel : QAbstractListModel
{
%Docstring

List model representing the print layouts and reports available in a
layout manager.

.. versionadded:: 3.8
%End

%TypeHeaderCode
#include "qgslayoutmanager.h"
%End
public:

enum Role
{
LayoutRole,
};

explicit QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLayoutManagerModel, showing the layouts from the specified ``manager``.
%End

virtual int rowCount( const QModelIndex &parent ) const;

virtual QVariant data( const QModelIndex &index, int role ) const;

virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );

virtual Qt::ItemFlags flags( const QModelIndex &index ) const;


QgsMasterLayoutInterface *layoutFromIndex( const QModelIndex &index ) const;
%Docstring
Returns the layout at the corresponding ``index``.

.. seealso:: :py:func:`indexFromLayout`
%End

QModelIndex indexFromLayout( QgsMasterLayoutInterface *layout ) const;
%Docstring
Returns the model index corresponding to a ``layout``.

.. seealso:: :py:func:`layoutFromIndex`
%End

void setAllowEmptyLayout( bool allowEmpty );
%Docstring
Sets whether an optional empty layout ("not set") option is present in the model.

.. seealso:: :py:func:`allowEmptyLayout`
%End

bool allowEmptyLayout() const;
%Docstring
Returns ``True`` if the model allows the empty layout ("not set") choice.

.. seealso:: :py:func:`setAllowEmptyLayout`
%End

};


class QgsLayoutManagerProxyModel : QSortFilterProxyModel
{
%Docstring

QSortFilterProxyModel subclass for QgsLayoutManagerModel

.. versionadded:: 3.8
%End

%TypeHeaderCode
#include "qgslayoutmanager.h"
%End
public:

enum Filter
{
FilterPrintLayouts,
FilterReports,
};
typedef QFlags<QgsLayoutManagerProxyModel::Filter> Filters;


explicit QgsLayoutManagerProxyModel( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLayoutManagerProxyModel.
%End
virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;

virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;


QgsLayoutManagerProxyModel::Filters filters() const;
%Docstring
Returns the current filters used for filtering available layouts.

.. seealso:: :py:func:`setFilters`
%End

void setFilters( QgsLayoutManagerProxyModel::Filters filters );
%Docstring
Sets the current ``filters`` used for filtering available layouts.

.. seealso:: :py:func:`filters`
%End

};

QFlags<QgsLayoutManagerProxyModel::Filter> operator|(QgsLayoutManagerProxyModel::Filter f1, QFlags<QgsLayoutManagerProxyModel::Filter> f2);


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
157 changes: 0 additions & 157 deletions src/app/layout/qgslayoutmanagerdialog.cpp
Expand Up @@ -488,161 +488,4 @@ void QgsLayoutManagerDialog::showHelp()
QgsHelp::openHelp( QStringLiteral( "print_composer/overview_composer.html#the-layout-manager" ) );
}

//
// QgsLayoutManagerModel
//

QgsLayoutManagerModel::QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent )
: QAbstractListModel( parent )
, mLayoutManager( manager )
{
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeAdded, this, &QgsLayoutManagerModel::layoutAboutToBeAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAdded, this, &QgsLayoutManagerModel::layoutAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeRemoved, this, &QgsLayoutManagerModel::layoutAboutToBeRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRemoved, this, &QgsLayoutManagerModel::layoutRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRenamed, this, &QgsLayoutManagerModel::layoutRenamed );
}

int QgsLayoutManagerModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return mLayoutManager->layouts().count();
}

QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const
{
if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
return QVariant();

switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
case Qt::EditRole:
return mLayoutManager->layouts().at( index.row() )->name();

case LayoutRole:
{
if ( QgsLayout *l = dynamic_cast< QgsLayout * >( mLayoutManager->layouts().at( index.row() ) ) )
return QVariant::fromValue( l );
else if ( QgsReport *r = dynamic_cast< QgsReport * >( mLayoutManager->layouts().at( index.row() ) ) )
return QVariant::fromValue( r );
else
return QVariant();
}

case Qt::DecorationRole:
{
return mLayoutManager->layouts().at( index.row() )->icon();
}

default:
return QVariant();
}
}

bool QgsLayoutManagerModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
{
return false;
}
if ( index.row() >= mLayoutManager->layouts().count() )
{
return false;
}

if ( value.toString().isEmpty() )
return false;

QgsMasterLayoutInterface *layout = layoutFromIndex( index );
if ( !layout )
return false;

//has name changed?
bool changed = layout->name() != value.toString();
if ( !changed )
return true;

//check if name already exists
QStringList layoutNames;
const QList< QgsMasterLayoutInterface * > layouts = QgsProject::instance()->layoutManager()->layouts();
for ( QgsMasterLayoutInterface *l : layouts )
{
layoutNames << l->name();
}
if ( layoutNames.contains( value.toString() ) )
{
//name exists!
QMessageBox::warning( nullptr, tr( "Rename Layout" ), tr( "There is already a layout named “%1”." ).arg( value.toString() ) );
return false;
}

layout->setName( value.toString() );
return true;
}

Qt::ItemFlags QgsLayoutManagerModel::flags( const QModelIndex &index ) const
{
Qt::ItemFlags flags = QAbstractListModel::flags( index );
#if 0 // double-click is now used for opening the layout
if ( index.isValid() )
{
return flags | Qt::ItemIsEditable;
}
else
{
return flags;
}
#endif
return flags;
}

QgsMasterLayoutInterface *QgsLayoutManagerModel::layoutFromIndex( const QModelIndex &index ) const
{
if ( QgsPrintLayout *l = qobject_cast< QgsPrintLayout * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return l;
else if ( QgsReport *r = qobject_cast< QgsReport * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return r;
else
return nullptr;
}

void QgsLayoutManagerModel::layoutAboutToBeAdded( const QString & )
{
int row = mLayoutManager->layouts().count();
beginInsertRows( QModelIndex(), row, row );
}

void QgsLayoutManagerModel::layoutAboutToBeRemoved( const QString &name )
{
QgsMasterLayoutInterface *l = mLayoutManager->layoutByName( name );
int row = mLayoutManager->layouts().indexOf( l );
if ( row >= 0 )
beginRemoveRows( QModelIndex(), row, row );
}

void QgsLayoutManagerModel::layoutAdded( const QString & )
{
endInsertRows();
}

void QgsLayoutManagerModel::layoutRemoved( const QString & )
{
endRemoveRows();
}

void QgsLayoutManagerModel::layoutRenamed( QgsMasterLayoutInterface *layout, const QString & )
{
int row = mLayoutManager->layouts().indexOf( layout );
QModelIndex index = createIndex( row, 0 );
emit dataChanged( index, index, QVector<int>() << Qt::DisplayRole );
}

QgsLayoutManagerProxyModel::QgsLayoutManagerProxyModel( QObject *parent )
: QSortFilterProxyModel( parent )
{
setDynamicSortFilter( true );
sort( 0 );
setSortCaseSensitivity( Qt::CaseInsensitive );
}
41 changes: 2 additions & 39 deletions src/app/layout/qgslayoutmanagerdialog.h
Expand Up @@ -26,45 +26,8 @@ class QListWidgetItem;
class QgsLayoutDesignerDialog;
class QgsMasterLayoutInterface;
class QgsLayoutManager;

class QgsLayoutManagerModel : public QAbstractListModel
{
Q_OBJECT

public:

enum Role
{
LayoutRole = Qt::UserRole + 1,
};

explicit QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent = nullptr );

int rowCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;
QgsMasterLayoutInterface *layoutFromIndex( const QModelIndex &index ) const;

private slots:
void layoutAboutToBeAdded( const QString &name );
void layoutAboutToBeRemoved( const QString &name );
void layoutAdded( const QString &name );
void layoutRemoved( const QString &name );
void layoutRenamed( QgsMasterLayoutInterface *layout, const QString &newName );
private:
QgsLayoutManager *mLayoutManager = nullptr;
};

class QgsLayoutManagerProxyModel : public QSortFilterProxyModel
{
Q_OBJECT

public:

explicit QgsLayoutManagerProxyModel( QObject *parent );

};
class QgsLayoutManagerModel;
class QgsLayoutManagerProxyModel;

/**
* A dialog that allows management of layouts within a project.
Expand Down

0 comments on commit cba2277

Please sign in to comment.