Skip to content

Commit

Permalink
[layouts] Fix multiframe items (tables, html) cannot be pasted
Browse files Browse the repository at this point in the history
Fixes #10456, #17882
  • Loading branch information
nyalldawson committed Jan 22, 2018
1 parent cbe6416 commit 7a2ab1c
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 84 deletions.
35 changes: 32 additions & 3 deletions python/core/layout/qgslayout.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,33 @@ which deferred z-order updates.
Returns the layout item with matching ``uuid`` unique identifier, or a None
if a matching item could not be found.

If ``includeTemplateUuids`` is true, then item's :py:func:`QgsLayoutItem.templateUuid()`
will also be tested when trying to match the uuid.
If ``includeTemplateUuids`` is true, then item's template UUID
will also be tested when trying to match the uuid. This may differ from the item's UUID
for items which have been added to an existing layout from a template. In this case
the template UUID returns the original item UUID at the time the template was created,
vs the item's uuid() which returns the current instance of the item's unique identifier.
Note that template UUIDs are only available while a layout is being restored from XML.

.. seealso:: :py:func:`itemByTemplateUuid`

.. seealso:: :py:func:`multiFrameByUuid`

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

QgsLayoutItem *itemByTemplateUuid( const QString &uuid ) const;
%Docstring
Returns the layout item with matching template ``uuid`` unique identifier, or a None
if a matching item could not be found. Unlike itemByUuid(), this method ONLY checks
template UUIDs for a match.

Template UUIDs are valid only for items which have been added to an existing layout from a template. In this case
the template UUID is the original item UUID at the time the template was created,
vs the item's uuid() which returns the current instance of the item's unique identifier.

Note that template UUIDs are only available while a layout is being restored from XML.

.. seealso:: :py:func:`itemByUuid`

.. seealso:: :py:func:`multiFrameByUuid`

Expand All @@ -203,7 +228,11 @@ Returns the layout multiframe with matching ``uuid`` unique identifier, or a Non
if a matching multiframe could not be found.

If ``includeTemplateUuids`` is true, then the multiframe's :py:func:`QgsLayoutMultiFrame.templateUuid()`
will also be tested when trying to match the uuid.
will also be tested when trying to match the uuid. Template UUIDs are valid only for items
which have been added to an existing layout from a template. In this case
the template UUID is the original item UUID at the time the template was created,
vs the item's uuid() which returns the current instance of the item's unique identifier.
Note that template UUIDs are only available while a layout is being restored from XML.

.. seealso:: :py:func:`itemByUuid`
%End
Expand Down
12 changes: 0 additions & 12 deletions python/core/layout/qgslayoutitem.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,6 @@ upon creation.
.. seealso:: :py:func:`id`

.. seealso:: :py:func:`setId`

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

QString templateUuid() const;
%Docstring
Returns the item's original identification string. This may differ from the item's uuid()
for items which have been added to an existing layout from a template. In this case
templateUuid() returns the original item UUID at the time the template was created,
while uuid() returns the current instance of the item's unique identifier.

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

QString id() const;
Expand Down
12 changes: 0 additions & 12 deletions python/core/layout/qgslayoutmultiframe.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,6 @@ upon creation.
.. note::

There is no corresponding setter for the uuid - it's created automatically.

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

QString templateUuid() const;
%Docstring
Returns the multiframe's original identification string. This may differ from the multiframes's uuid()
for multiframes which have been added to an existing layout from a template. In this case
templateUuid() returns the original UUID at the time the template was created,
while uuid() returns the current instance of the multiframes's unique identifier.

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

virtual QSizeF totalSize() const = 0;
Expand Down
26 changes: 24 additions & 2 deletions src/core/layout/qgslayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,20 @@ QgsLayoutItem *QgsLayout::itemByUuid( const QString &uuid, bool includeTemplateU
{
if ( item->uuid() == uuid )
return item;
else if ( includeTemplateUuids && item->templateUuid() == uuid )
else if ( includeTemplateUuids && item->mTemplateUuid == uuid )
return item;
}

return nullptr;
}

QgsLayoutItem *QgsLayout::itemByTemplateUuid( const QString &uuid ) const
{
QList<QgsLayoutItem *> itemList;
layoutItems( itemList );
for ( QgsLayoutItem *item : qgis::as_const( itemList ) )
{
if ( item->mTemplateUuid == uuid )
return item;
}

Expand All @@ -265,7 +278,7 @@ QgsLayoutMultiFrame *QgsLayout::multiFrameByUuid( const QString &uuid, bool incl
{
if ( mf->uuid() == uuid )
return mf;
else if ( includeTemplateUuids && mf->templateUuid() == uuid )
else if ( includeTemplateUuids && mf->mTemplateUuid == uuid )
return mf;
}

Expand Down Expand Up @@ -1040,6 +1053,15 @@ QList< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentEl
mf->finalizeRestoreFromXml();
}

for ( QgsLayoutItem *item : qgis::as_const( newItems ) )
{
item->mTemplateUuid.clear();
}
for ( QgsLayoutMultiFrame *mf : qgis::as_const( newMultiFrames ) )
{
mf->mTemplateUuid.clear();
}

//Since this function adds items in an order which isn't the z-order, and each item is added to end of
//z order list in turn, it will now be inconsistent with the actual order of items in the scene.
//Make sure z order list matches the actual order of items in the scene.
Expand Down
32 changes: 29 additions & 3 deletions src/core/layout/qgslayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,36 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
* Returns the layout item with matching \a uuid unique identifier, or a nullptr
* if a matching item could not be found.
*
* If \a includeTemplateUuids is true, then item's QgsLayoutItem::templateUuid()
* will also be tested when trying to match the uuid.
* If \a includeTemplateUuids is true, then item's template UUID
* will also be tested when trying to match the uuid. This may differ from the item's UUID
* for items which have been added to an existing layout from a template. In this case
* the template UUID returns the original item UUID at the time the template was created,
* vs the item's uuid() which returns the current instance of the item's unique identifier.
* Note that template UUIDs are only available while a layout is being restored from XML.
*
* \see itemByTemplateUuid()
* \see multiFrameByUuid()
* \see itemById()
*/
QgsLayoutItem *itemByUuid( const QString &uuid, bool includeTemplateUuids = false ) const;

/**
* Returns the layout item with matching template \a uuid unique identifier, or a nullptr
* if a matching item could not be found. Unlike itemByUuid(), this method ONLY checks
* template UUIDs for a match.
*
* Template UUIDs are valid only for items which have been added to an existing layout from a template. In this case
* the template UUID is the original item UUID at the time the template was created,
* vs the item's uuid() which returns the current instance of the item's unique identifier.
*
* Note that template UUIDs are only available while a layout is being restored from XML.
*
* \see itemByUuid()
* \see multiFrameByUuid()
* \see itemById()
*/
QgsLayoutItem *itemByTemplateUuid( const QString &uuid ) const;

/**
* Returns a layout item given its \a id.
* Since item IDs are not necessarely unique, this function returns the first matching
Expand All @@ -268,7 +290,11 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
* if a matching multiframe could not be found.
*
* If \a includeTemplateUuids is true, then the multiframe's QgsLayoutMultiFrame::templateUuid()
* will also be tested when trying to match the uuid.
* will also be tested when trying to match the uuid. Template UUIDs are valid only for items
* which have been added to an existing layout from a template. In this case
* the template UUID is the original item UUID at the time the template was created,
* vs the item's uuid() which returns the current instance of the item's unique identifier.
* Note that template UUIDs are only available while a layout is being restored from XML.
*
* \see itemByUuid()
*/
Expand Down
11 changes: 1 addition & 10 deletions src/core/layout/qgslayoutitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,19 +243,9 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
* \note There is no corresponding setter for the uuid - it's created automatically.
* \see id()
* \see setId()
* \see templateUuid()
*/
virtual QString uuid() const { return mUuid; }

/**
* Returns the item's original identification string. This may differ from the item's uuid()
* for items which have been added to an existing layout from a template. In this case
* templateUuid() returns the original item UUID at the time the template was created,
* while uuid() returns the current instance of the item's unique identifier.
* \see uuid()
*/
QString templateUuid() const { return mTemplateUuid; }

/**
* Returns the item's ID name. This is not necessarily unique, and duplicate ID names may exist
* for a layout.
Expand Down Expand Up @@ -1083,6 +1073,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt

friend class TestQgsLayoutItem;
friend class TestQgsLayoutView;
friend class QgsLayout;
friend class QgsLayoutItemGroup;
friend class QgsCompositionConverter;
};
Expand Down
21 changes: 18 additions & 3 deletions src/core/layout/qgslayoutmultiframe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,24 @@ void QgsLayoutMultiFrame::finalizeRestoreFromXml()
{
for ( int i = 0; i < mFrameUuids.count(); ++i )
{
const QString uuid = mFrameUuids.at( i ).isEmpty() ? mFrameTemplateUuids.at( i ) : mFrameUuids.at( i );
QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
if ( QgsLayoutFrame *frame = qobject_cast< QgsLayoutFrame * >( item ) )
QgsLayoutFrame *frame = nullptr;
const QString uuid = mFrameUuids.at( i );
if ( !uuid.isEmpty() )
{
QgsLayoutItem *item = mLayout->itemByUuid( uuid, true );
frame = qobject_cast< QgsLayoutFrame * >( item );
}
if ( !frame )
{
const QString templateUuid = mFrameTemplateUuids.at( i );
if ( !templateUuid.isEmpty() )
{
QgsLayoutItem *item = mLayout->itemByTemplateUuid( templateUuid );
frame = qobject_cast< QgsLayoutFrame * >( item );
}
}

if ( frame )
{
addFrame( frame );
}
Expand Down
11 changes: 1 addition & 10 deletions src/core/layout/qgslayoutmultiframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,19 +128,9 @@ class CORE_EXPORT QgsLayoutMultiFrame: public QgsLayoutObject, public QgsLayoutU
* Returns the multiframe identification string. This is a unique random string set for the multiframe
* upon creation.
* \note There is no corresponding setter for the uuid - it's created automatically.
* \see templateUuid()
*/
QString uuid() const { return mUuid; }

/**
* Returns the multiframe's original identification string. This may differ from the multiframes's uuid()
* for multiframes which have been added to an existing layout from a template. In this case
* templateUuid() returns the original UUID at the time the template was created,
* while uuid() returns the current instance of the multiframes's unique identifier.
* \see uuid()
*/
QString templateUuid() const { return mTemplateUuid; }

/**
* Returns the total size of the multiframe's content, in layout units.
*/
Expand Down Expand Up @@ -447,6 +437,7 @@ class CORE_EXPORT QgsLayoutMultiFrame: public QgsLayoutObject, public QgsLayoutU
QString mUuid;
QString mTemplateUuid;
friend class QgsLayoutFrame;
friend class QgsLayout;
};


Expand Down
33 changes: 32 additions & 1 deletion src/gui/layout/qgslayoutview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#include "qgslayoutview.h"
#include "qgslayout.h"
#include "qgslayoutframe.h"
#include "qgslayoutmultiframe.h"
#include "qgslayoutviewtool.h"
#include "qgslayoutviewmouseevent.h"
#include "qgslayoutviewtooltemporarykeypan.h"
Expand Down Expand Up @@ -149,7 +151,6 @@ void QgsLayoutView::setTool( QgsLayoutViewTool *tool )
tool->activate();
mTool = tool;
connect( mTool, &QgsLayoutViewTool::itemFocused, this, &QgsLayoutView::itemFocused );

emit toolSet( mTool );
}

Expand Down Expand Up @@ -320,6 +321,9 @@ void QgsLayoutView::copyItems( const QList<QgsLayoutItem *> &items, QgsLayoutVie
QDomElement documentElement = doc.createElement( QStringLiteral( "LayoutItemClipboard" ) );
if ( operation == ClipboardCut )
currentLayout()->undoStack()->beginMacro( tr( "Cut Items" ) );

QSet< QgsLayoutMultiFrame * > copiedMultiFrames;

for ( QgsLayoutItem *item : items )
{
// copy every child from a group
Expand All @@ -331,6 +335,15 @@ void QgsLayoutView::copyItems( const QList<QgsLayoutItem *> &items, QgsLayoutVie
groupedItem->writeXml( documentElement, doc, context );
}
}
else if ( QgsLayoutFrame *frame = qobject_cast<QgsLayoutFrame *>( item ) )
{
// copy multiframe too
if ( !copiedMultiFrames.contains( frame->multiFrame() ) )
{
frame->multiFrame()->writeXml( documentElement, doc, context );
copiedMultiFrames.insert( frame->multiFrame() );
}
}
item->writeXml( documentElement, doc, context );
if ( operation == ClipboardCut )
currentLayout()->removeLayoutItem( item );
Expand All @@ -352,6 +365,24 @@ void QgsLayoutView::copyItems( const QList<QgsLayoutItem *> &items, QgsLayoutVie
itemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
}
}
QDomNodeList multiFrameNodes = doc.elementsByTagName( QStringLiteral( "LayoutMultiFrame" ) );
for ( int i = 0; i < multiFrameNodes.count(); ++i )
{
QDomNode multiFrameNode = multiFrameNodes.at( i );
if ( multiFrameNode.isElement() )
{
multiFrameNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
QDomNodeList frameNodes = multiFrameNode.toElement().elementsByTagName( QStringLiteral( "childFrame" ) );
for ( int j = 0; j < frameNodes.count(); ++j )
{
QDomNode itemNode = frameNodes.at( j );
if ( itemNode.isElement() )
{
itemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
}
}
}
}

QMimeData *mimeData = new QMimeData;
mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
Expand Down

0 comments on commit 7a2ab1c

Please sign in to comment.