Skip to content
Permalink
Browse files

[layouts] Fix multiframe items (tables, html) cannot be pasted

Fixes #10456, #17882
  • Loading branch information
nyalldawson committed Jan 22, 2018
1 parent cbe6416 commit 7a2ab1cc7cec419005325c5f21616f7faa106d13
@@ -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`

@@ -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
@@ -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;
@@ -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;
@@ -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;
}

@@ -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;
}

@@ -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.
@@ -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
@@ -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()
*/
@@ -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.
@@ -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;
};
@@ -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 );
}
@@ -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.
*/
@@ -447,6 +437,7 @@ class CORE_EXPORT QgsLayoutMultiFrame: public QgsLayoutObject, public QgsLayoutU
QString mUuid;
QString mTemplateUuid;
friend class QgsLayoutFrame;
friend class QgsLayout;
};


@@ -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"
@@ -149,7 +151,6 @@ void QgsLayoutView::setTool( QgsLayoutViewTool *tool )
tool->activate();
mTool = tool;
connect( mTool, &QgsLayoutViewTool::itemFocused, this, &QgsLayoutView::itemFocused );

emit toolSet( mTool );
}

@@ -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
@@ -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 );
@@ -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() );

0 comments on commit 7a2ab1c

Please sign in to comment.
You can’t perform that action at this time.