Skip to content

Commit

Permalink
Port duplicate layout functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 6, 2017
1 parent a64a675 commit 3a0b751
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 2 deletions.
8 changes: 8 additions & 0 deletions python/core/composer/qgslayoutmanager.sip
Expand Up @@ -109,6 +109,14 @@ class QgsLayoutManager : QObject
:rtype: QgsComposition
%End

QgsLayout *duplicateLayout( const QgsLayout *layout, const QString &newName );
%Docstring
Duplicates an existing ``layout`` from the manager. The new
layout will automatically be stored in the manager.
Returns new the layout if duplication was successful.
:rtype: QgsLayout
%End

QString generateUniqueTitle() const;
%Docstring
Generates a unique title for a new composition, which does not
Expand Down
28 changes: 28 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Expand Up @@ -46,6 +46,7 @@
#include "qgslayoutmodel.h"
#include "qgslayoutitemslistview.h"
#include "qgsproject.h"
#include "qgsbusyindicatordialog.h"
#include <QShortcut>
#include <QComboBox>
#include <QLineEdit>
Expand Down Expand Up @@ -311,6 +312,7 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla

connect( mActionSaveAsTemplate, &QAction::triggered, this, &QgsLayoutDesignerDialog::saveAsTemplate );
connect( mActionLoadFromTemplate, &QAction::triggered, this, &QgsLayoutDesignerDialog::addItemsFromTemplate );
connect( mActionDuplicateLayout, &QAction::triggered, this, &QgsLayoutDesignerDialog::duplicate );

connect( mActionZoomIn, &QAction::triggered, mView, &QgsLayoutView::zoomIn );
connect( mActionZoomOut, &QAction::triggered, mView, &QgsLayoutView::zoomOut );
Expand Down Expand Up @@ -1319,6 +1321,32 @@ void QgsLayoutDesignerDialog::addItemsFromTemplate()
}
}

void QgsLayoutDesignerDialog::duplicate()
{
QString newTitle;
if ( !QgisApp::instance()->uniqueLayoutTitle( this, newTitle, false, tr( "%1 copy" ).arg( currentLayout()->name() ) ) )
{
return;
}

// provide feedback, since loading of template into duplicate layout will be hidden
QDialog *dlg = new QgsBusyIndicatorDialog( tr( "Duplicating layout…" ) );
dlg->setStyleSheet( QgisApp::instance()->styleSheet() );
dlg->show();

QgsLayoutDesignerDialog *newDialog = QgisApp::instance()->duplicateLayout( currentLayout(), newTitle );

dlg->close();
delete dlg;
dlg = nullptr;

if ( !newDialog )
{
QMessageBox::warning( this, tr( "Duplicate layout" ),
tr( "Layout duplication failed." ) );
}
}

void QgsLayoutDesignerDialog::paste()
{
QPointF pt = mView->mapFromGlobal( QCursor::pos() );
Expand Down
1 change: 1 addition & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Expand Up @@ -268,6 +268,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
void undoRedoOccurredForItems( const QSet< QString > itemUuids );
void saveAsTemplate();
void addItemsFromTemplate();
void duplicate();

private:

Expand Down
81 changes: 81 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -205,6 +205,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeutils.h"
#include "qgslayertreeview.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayout.h"
#include "qgslayoutcustomdrophandler.h"
#include "qgslayoutdesignerdialog.h"
#include "qgslayoutmanager.h"
Expand Down Expand Up @@ -7333,6 +7334,71 @@ bool QgisApp::uniqueComposerTitle( QWidget *parent, QString &composerTitle, bool
return true;
}

bool QgisApp::uniqueLayoutTitle( QWidget *parent, QString &title, bool acceptEmpty, const QString &currentTitle )
{
if ( !parent )
{
parent = this;
}
bool ok = false;
bool titleValid = false;
QString newTitle = QString( currentTitle );
QString chooseMsg = tr( "Create unique print layout title" );
if ( acceptEmpty )
{
chooseMsg += '\n' + tr( "(title generated if left empty)" );
}
QString titleMsg = chooseMsg;

QStringList cNames;
cNames << newTitle;
#if 0 //TODO
Q_FOREACH ( QgsComposition *c, QgsProject::instance()->layoutManager()->compositions() )
{
cNames << c->name();
}
#endif
while ( !titleValid )
{
newTitle = QInputDialog::getText( parent,
tr( "Layout title" ),
titleMsg,
QLineEdit::Normal,
newTitle,
&ok );
if ( !ok )
{
return false;
}

if ( newTitle.isEmpty() )
{
if ( !acceptEmpty )
{
titleMsg = chooseMsg + "\n\n" + tr( "Title can not be empty!" );
}
else
{
titleValid = true;
newTitle = QgsProject::instance()->layoutManager()->generateUniqueTitle();
}
}
else if ( cNames.indexOf( newTitle, 1 ) >= 0 )
{
cNames[0] = QString(); // clear non-unique name
titleMsg = chooseMsg + "\n\n" + tr( "Title already exists!" );
}
else
{
titleValid = true;
}
}

title = newTitle;

return true;
}

QgsComposer *QgisApp::createNewComposer( QString title )
{
if ( title.isEmpty() )
Expand Down Expand Up @@ -7441,6 +7507,21 @@ QgsComposer *QgisApp::duplicateComposer( QgsComposer *currentComposer, QString t
return newComposer;
}

QgsLayoutDesignerDialog *QgisApp::duplicateLayout( QgsLayout *layout, const QString &t )
{
QString title = t;
if ( title.isEmpty() )
{
// TODO: inject a bit of randomness in auto-titles?
title = tr( "%1 copy" ).arg( layout->name() );
}

QgsLayout *newLayout = QgsProject::instance()->layoutManager()->duplicateLayout( layout, title );
QgsLayoutDesignerDialog *dlg = openLayoutDesignerDialog( newLayout );
dlg->activate();
return dlg;
}

void QgisApp::deletePrintComposers()
{
QSet<QgsComposer *>::iterator it = mPrintComposers.begin();
Expand Down
23 changes: 23 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -361,6 +361,21 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
* \returns QString() if user cancels input dialog
*/
bool uniqueComposerTitle( QWidget *parent, QString &composerTitle, bool acceptEmpty, const QString &currentTitle = QString() );

/**
* Gets a unique title from user for new and duplicate layouts.
*
* The \a title argument will be filled with the new layout title.
*
* If \a acceptEmpty is true then empty titles will be acceptable (one will be generated).
*
* The \a currentTitle argument specifies a base name for initial title choice.
*
* \returns true if user did not cancel the dialog.
*/
bool uniqueLayoutTitle( QWidget *parent, QString &title, bool acceptEmpty, const QString &currentTitle = QString() );


//! Creates a new composer and returns a pointer to it
QgsComposer *createNewComposer( QString title = QString() );

Expand All @@ -383,6 +398,14 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
*/
QgsComposer *duplicateComposer( QgsComposer *currentComposer, QString title = QString() );

/**
* Duplicates a \a layout and adds it to the current project.
*
* If \a title is set, it will be used as the title for the new layout. If it is not set,
* and auto-generated title will be used instead.
*/
QgsLayoutDesignerDialog *duplicateLayout( QgsLayout *layout, const QString &title = QString() );

//! Overloaded function used to sort menu entries alphabetically
QMenu *createPopupMenu() override;

Expand Down
33 changes: 33 additions & 0 deletions src/core/composer/qgslayoutmanager.cpp
Expand Up @@ -14,6 +14,7 @@
***************************************************************************/

#include "qgslayoutmanager.h"
#include "qgslayout.h"
#include "qgsproject.h"
#include "qgslogger.h"

Expand Down Expand Up @@ -186,6 +187,38 @@ QgsComposition *QgsLayoutManager::duplicateComposition( const QString &name, con
}
}

QgsLayout *QgsLayoutManager::duplicateLayout( const QgsLayout *layout, const QString &newName )
{
QDomDocument currentDoc;

QgsReadWriteContext context;
QDomElement elem = layout->writeXml( currentDoc, context );
currentDoc.appendChild( elem );

std::unique_ptr< QgsLayout > newLayout = qgis::make_unique< QgsLayout >( mProject );
bool ok = false;
newLayout->loadFromTemplate( currentDoc, context, true, &ok );
if ( !ok )
{
return nullptr;
}

newLayout->setName( newName );
#if 0 //TODO
if ( !addComposition( newComposition ) )
{
delete newComposition;
return nullptr;
}
else
{
return newComposition;
}
#endif

return newLayout.release();
}

QString QgsLayoutManager::generateUniqueTitle() const
{
QStringList names;
Expand Down
7 changes: 7 additions & 0 deletions src/core/composer/qgslayoutmanager.h
Expand Up @@ -115,6 +115,13 @@ class CORE_EXPORT QgsLayoutManager : public QObject
*/
QgsComposition *duplicateComposition( const QString &name, const QString &newName );

/**
* Duplicates an existing \a layout from the manager. The new
* layout will automatically be stored in the manager.
* Returns new the layout if duplication was successful.
*/
QgsLayout *duplicateLayout( const QgsLayout *layout, const QString &newName );

/**
* Generates a unique title for a new composition, which does not
* clash with any already contained by the manager.
Expand Down
19 changes: 17 additions & 2 deletions src/ui/layout/qgslayoutdesignerbase.ui
Expand Up @@ -62,6 +62,7 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="mActionDuplicateLayout"/>
<addaction name="mActionLoadFromTemplate"/>
<addaction name="mActionSaveAsTemplate"/>
</widget>
Expand Down Expand Up @@ -94,6 +95,8 @@
<property name="title">
<string>&amp;Layout</string>
</property>
<addaction name="mActionDuplicateLayout"/>
<addaction name="separator"/>
<addaction name="mActionLayoutProperties"/>
<addaction name="mActionAddPages"/>
<addaction name="separator"/>
Expand Down Expand Up @@ -1068,7 +1071,7 @@
<normaloff>:/images/themes/default/mActionFileSaveAs.svg</normaloff>:/images/themes/default/mActionFileSaveAs.svg</iconset>
</property>
<property name="text">
<string>Save as &amp;Template...</string>
<string>Save as &amp;Template</string>
</property>
<property name="toolTip">
<string>Save as template</string>
Expand All @@ -1080,12 +1083,24 @@
<normaloff>:/images/themes/default/mActionFileOpen.svg</normaloff>:/images/themes/default/mActionFileOpen.svg</iconset>
</property>
<property name="text">
<string>&amp;Add Items from Template...</string>
<string>&amp;Add Items from Template</string>
</property>
<property name="toolTip">
<string>Add items from template</string>
</property>
</action>
<action name="mActionDuplicateLayout">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionDuplicateComposer.svg</normaloff>:/images/themes/default/mActionDuplicateComposer.svg</iconset>
</property>
<property name="text">
<string>&amp;Duplicate Layout…</string>
</property>
<property name="toolTip">
<string>Duplicate layout</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>
Expand Down

0 comments on commit 3a0b751

Please sign in to comment.