Skip to content
Permalink
Browse files

Start restoring copy/paste functionality

  • Loading branch information
nyalldawson committed Dec 1, 2017
1 parent f60da58 commit b03ce047db769af7f102582851e640af0b9cd321
@@ -24,6 +24,19 @@ class QgsLayoutView: QGraphicsView
%End
public:

enum ClipboardOperation
{
ClipboardCut,
ClipboardCopy,
};

enum PasteMode
{
PasteModeCursor,
PasteModeCenter,
PasteModeInPlace,
};

QgsLayoutView( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLayoutView.
@@ -179,6 +192,22 @@ class QgsLayoutView: QGraphicsView
.. seealso:: :py:func:`distributeSelectedItems()`
%End

void copySelectedItems( ClipboardOperation operation );
%Docstring
Cuts or copies the selected items, respecting the specified ``operation``.
.. seealso:: pasteItems()
%End

QList< QgsLayoutItem * > pasteItems( PasteMode mode );
%Docstring
Pastes items from clipboard, using the specified ``mode``.

A list of pasted items is returned.

.. seealso:: copySelectedItems()
:rtype: list of QgsLayoutItem
%End

QPointF deltaForKeyEvent( QKeyEvent *event );
%Docstring
Returns the delta (in layout coordinates) by which to move items
@@ -157,6 +157,8 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
connect( mActionShowBoxes, &QAction::triggered, this, &QgsLayoutDesignerDialog::showBoxes );
connect( mActionShowPage, &QAction::triggered, this, &QgsLayoutDesignerDialog::showPages );

connect( mActionPasteInPlace, &QAction::triggered, this, &QgsLayoutDesignerDialog::pasteInPlace );

mView = new QgsLayoutView();
//mView->setMapCanvas( mQgis->mapCanvas() );
mView->setContentsMargins( 0, 0, 0, 0 );
@@ -405,6 +407,36 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
mView->ungroupSelectedItems();
} );

//cut/copy/paste actions. Note these are not included in the ui file
//as ui files have no support for QKeySequence shortcuts
mActionCut = new QAction( tr( "Cu&t" ), this );
mActionCut->setShortcuts( QKeySequence::Cut );
mActionCut->setStatusTip( tr( "Cut" ) );
mActionCut->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCut.svg" ) ) );
connect( mActionCut, &QAction::triggered, this, [ = ]
{
mView->copySelectedItems( QgsLayoutView::ClipboardCut );
} );

mActionCopy = new QAction( tr( "&Copy" ), this );
mActionCopy->setShortcuts( QKeySequence::Copy );
mActionCopy->setStatusTip( tr( "Copy" ) );
mActionCopy->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );
connect( mActionCopy, &QAction::triggered, this, [ = ]
{
mView->copySelectedItems( QgsLayoutView::ClipboardCopy );
} );

mActionPaste = new QAction( tr( "&Paste" ), this );
mActionPaste->setShortcuts( QKeySequence::Paste );
mActionPaste->setStatusTip( tr( "Paste" ) );
mActionPaste->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ) );
connect( mActionPaste, &QAction::triggered, this, &QgsLayoutDesignerDialog::paste );

menuEdit->insertAction( mActionPasteInPlace, mActionCut );
menuEdit->insertAction( mActionPasteInPlace, mActionCopy );
menuEdit->insertAction( mActionPasteInPlace, mActionPaste );

//create status bar labels
mStatusCursorXLabel = new QLabel( mStatusBar );
mStatusCursorXLabel->setMinimumWidth( 100 );
@@ -1098,6 +1130,41 @@ void QgsLayoutDesignerDialog::undoRedoOccurredForItems( const QSet<QString> item
showItemOptions( focusItem );
}

void QgsLayoutDesignerDialog::paste()
{
QPointF pt = mView->mapToScene( mView->mapFromGlobal( QCursor::pos() ) );
//TODO - use a better way of determining whether paste was triggered by keystroke
//or menu item
QList< QgsLayoutItem * > items;
if ( ( pt.x() < 0 ) || ( pt.y() < 0 ) )
{
//action likely triggered by menu, paste items in center of screen
items = mView->pasteItems( QgsLayoutView::PasteModeCenter );
}
else
{
//action likely triggered by keystroke, paste items at cursor position
items = mView->pasteItems( QgsLayoutView::PasteModeCursor );
}

whileBlocking( currentLayout() )->deselectAll();
selectItems( items );

//switch back to select tool so that pasted items can be moved/resized (#8958)
mView->setTool( mSelectTool );
}

void QgsLayoutDesignerDialog::pasteInPlace()
{
QList< QgsLayoutItem * > items = mView->pasteItems( QgsLayoutView::PasteModeInPlace );

whileBlocking( currentLayout() )->deselectAll();
selectItems( items );

//switch back to select tool so that pasted items can be moved/resized (#8958)
mView->setTool( mSelectTool );
}

QgsLayoutView *QgsLayoutDesignerDialog::view()
{
return mView;
@@ -1178,4 +1245,26 @@ void QgsLayoutDesignerDialog::initializeRegistry()

}

void QgsLayoutDesignerDialog::selectItems( const QList<QgsLayoutItem *> items )
{
for ( QGraphicsItem *item : items )
{
if ( item )
{
item->setSelected( true );
}
}

//update item panel
const QList<QgsLayoutItem *> selectedItemList = currentLayout()->selectedLayoutItems();
if ( !selectedItemList.isEmpty() )
{
showItemOptions( selectedItemList.at( 0 ) );
}
else
{
showItemOptions( nullptr );
}
}


@@ -247,6 +247,9 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
void dockVisibilityChanged( bool visible );
void undoRedoOccurredForItems( const QSet< QString > itemUuids );

void paste();
void pasteInPlace();

private:

static bool sInitializedRegistry;
@@ -301,6 +304,10 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner

QAction *mUndoAction = nullptr;
QAction *mRedoAction = nullptr;
//! Copy/cut/paste actions
QAction *mActionCut = nullptr;
QAction *mActionCopy = nullptr;
QAction *mActionPaste = nullptr;

struct PanelStatus
{
@@ -328,6 +335,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner

void initializeRegistry();

void selectItems( const QList< QgsLayoutItem * > items );

};

#endif // QGSLAYOUTDESIGNERDIALOG_H
@@ -737,11 +737,6 @@ QList< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentEl
//next, calculate how much each item needs to be shifted from its original position
//so that it's placed at the correct relative position
pasteShiftPos = *position - minItemPos;

#if 0 // TODO - move to gui
//since we are pasting items, clear the existing selection
setAllDeselected();
#endif
if ( pasteInPlace )
{
int pageNumber = mPageCollection->pageNumberForPoint( *position );
@@ -777,10 +772,6 @@ QList< QgsLayoutItem * > QgsLayout::addItemsFromXml( const QDomElement &parentEl
#endif
}

#if 0 //TODO - move to gui
newLabel->setSelected( true );
#endif

QgsLayoutItem *layoutItem = item.get();
addLayoutItem( item.release() );
layoutItem->setZValue( layoutItem->zValue() + zOrderOffset );
@@ -34,6 +34,7 @@
#include <memory>
#include <QDesktopWidget>
#include <QMenu>
#include <QClipboard>

#define MIN_VIEW_SCALE 0.05
#define MAX_VIEW_SCALE 1000.0
@@ -279,6 +280,91 @@ void QgsLayoutView::resizeSelectedItems( QgsLayoutAligner::Resize resize )
QgsLayoutAligner::resizeItems( currentLayout(), selectedItems, resize );
}

void QgsLayoutView::copySelectedItems( QgsLayoutView::ClipboardOperation operation )
{
const QList<QgsLayoutItem *> selectedItems = currentLayout()->selectedLayoutItems();
QgsReadWriteContext context;
QDomDocument doc;
QDomElement documentElement = doc.createElement( QStringLiteral( "LayoutItemClipboard" ) );
if ( operation == ClipboardCut )
currentLayout()->undoStack()->beginMacro( tr( "Cut Items" ) );
for ( QgsLayoutItem *item : selectedItems )
{
// copy every child from a group
if ( QgsLayoutItemGroup *itemGroup = qobject_cast<QgsLayoutItemGroup *>( item ) )
{
const QList<QgsLayoutItem *> groupedItems = itemGroup->items();
for ( const QgsLayoutItem *groupedItem : groupedItems )
{
groupedItem->writeXml( documentElement, doc, context );
}
}
item->writeXml( documentElement, doc, context );
if ( operation == ClipboardCut )
currentLayout()->removeLayoutItem( item );
}
doc.appendChild( documentElement );
if ( operation == ClipboardCut )
{
currentLayout()->undoStack()->endMacro();
currentLayout()->update();
}

//remove the UUIDs since we don't want any duplicate UUID
QDomNodeList itemsNodes = doc.elementsByTagName( QStringLiteral( "LayoutItem" ) );
for ( int i = 0; i < itemsNodes.count(); ++i )
{
QDomNode itemNode = itemsNodes.at( i );
if ( itemNode.isElement() )
{
itemNode.toElement().removeAttribute( QStringLiteral( "uuid" ) );
}
}

QMimeData *mimeData = new QMimeData;
mimeData->setData( QStringLiteral( "text/xml" ), doc.toByteArray() );
QClipboard *clipboard = QApplication::clipboard();
clipboard->setMimeData( mimeData );
}

QList< QgsLayoutItem * > QgsLayoutView::pasteItems( QgsLayoutView::PasteMode mode )
{
QList< QgsLayoutItem * > pastedItems;
QDomDocument doc;
QClipboard *clipboard = QApplication::clipboard();
if ( doc.setContent( clipboard->mimeData()->data( QStringLiteral( "text/xml" ) ) ) )
{
QDomElement docElem = doc.documentElement();
if ( docElem.tagName() == QLatin1String( "LayoutItemClipboard" ) )
{
QPointF pt;
switch ( mode )
{
case PasteModeCursor:
case PasteModeInPlace:
{
// place items at cursor position
pt = mapToScene( mapFromGlobal( QCursor::pos() ) );
break;
}
case PasteModeCenter:
{
// place items in center of viewport
pt = mapToScene( viewport()->rect().center() );
break;
}
}
bool pasteInPlace = ( mode == PasteModeInPlace );
currentLayout()->undoStack()->beginMacro( tr( "Paste Items" ) );
currentLayout()->undoStack()->beginCommand( currentLayout(), tr( "Paste Items" ) );
pastedItems = currentLayout()->addItemsFromXml( docElem, doc, QgsReadWriteContext(), &pt, pasteInPlace );
currentLayout()->undoStack()->endCommand();
currentLayout()->undoStack()->endMacro();
}
}
return pastedItems;
}

QPointF QgsLayoutView::deltaForKeyEvent( QKeyEvent *event )
{
// increment used for cursor key item movement
@@ -55,6 +55,21 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView

public:

//! Clipboard operations
enum ClipboardOperation
{
ClipboardCut, //!< Cut items
ClipboardCopy, //!< Copy items
};

//! Paste modes
enum PasteMode
{
PasteModeCursor, //!< Paste items at cursor position
PasteModeCenter, //!< Paste items in center of view
PasteModeInPlace, //!< Paste items in place
};

/**
* Constructor for QgsLayoutView.
*/
@@ -208,6 +223,21 @@ class GUI_EXPORT QgsLayoutView: public QGraphicsView
*/
void resizeSelectedItems( QgsLayoutAligner::Resize resize );

/**
* Cuts or copies the selected items, respecting the specified \a operation.
* \see pasteItems()
*/
void copySelectedItems( ClipboardOperation operation );

/**
* Pastes items from clipboard, using the specified \a mode.
*
* A list of pasted items is returned.
*
* \see copySelectedItems()
*/
QList< QgsLayoutItem * > pasteItems( PasteMode mode );

/**
* Returns the delta (in layout coordinates) by which to move items
* for the given key \a event.
@@ -160,6 +160,8 @@
<addaction name="separator"/>
<addaction name="mActionDeleteSelection"/>
<addaction name="separator"/>
<addaction name="mActionPasteInPlace"/>
<addaction name="separator"/>
<addaction name="mActionSelectAll"/>
<addaction name="mActionDeselectAll"/>
<addaction name="mActionInvertSelection"/>
@@ -1044,6 +1046,17 @@
<string>C</string>
</property>
</action>
<action name="mActionPasteInPlace">
<property name="text">
<string>Paste in P&amp;lace</string>
</property>
<property name="toolTip">
<string>Paste in place</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+V</string>
</property>
</action>
</widget>
<resources>
<include location="../../../images/images.qrc"/>
@@ -1072,6 +1085,7 @@
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
</ui>

0 comments on commit b03ce04

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