Skip to content

Commit 6248295

Browse files
committed
Merge pull request #127 from mhugo/copy_paste_styles
[FEATURE] Copy / paste of styles
2 parents fc373b3 + e1c934a commit 6248295

File tree

6 files changed

+187
-4
lines changed

6 files changed

+187
-4
lines changed

src/app/legend/qgslegend.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "qgsrasterlayer.h"
3434
#include "qgsvectorlayer.h"
3535
#include "qgsgenericprojectionselector.h"
36+
#include "qgsclipboard.h"
3637

3738
#include <QFont>
3839
#include <QDomDocument>
@@ -42,6 +43,7 @@
4243
#include <QMouseEvent>
4344
#include <QPixmap>
4445
#include <QTreeWidgetItem>
46+
#include <QClipboard>
4547

4648
const int AUTOSCROLL_MARGIN = 16;
4749

@@ -697,6 +699,16 @@ void QgsLegend::handleRightClickEvent( QTreeWidgetItem* item, const QPoint& posi
697699
// ends here
698700
}
699701

702+
if ( selectedLayers().length() == 1 )
703+
{
704+
QgisApp* app = QgisApp::instance();
705+
theMenu.addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );
706+
if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
707+
{
708+
theMenu.addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
709+
}
710+
}
711+
700712
theMenu.addAction( QgisApp::getThemeIcon( "/folder_new.png" ), tr( "&Add New Group" ), this, SLOT( addGroupToCurrentItem() ) );
701713
theMenu.addAction( QgisApp::getThemeIcon( "/mActionExpandTree.png" ), tr( "&Expand All" ), this, SLOT( expandAll() ) );
702714
theMenu.addAction( QgisApp::getThemeIcon( "/mActionCollapseTree.png" ), tr( "&Collapse All" ), this, SLOT( collapseAll() ) );

src/app/qgisapp.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,8 @@ void QgisApp::createActions()
824824
connect( mActionCutFeatures, SIGNAL( triggered() ), this, SLOT( editCut() ) );
825825
connect( mActionCopyFeatures, SIGNAL( triggered() ), this, SLOT( editCopy() ) );
826826
connect( mActionPasteFeatures, SIGNAL( triggered() ), this, SLOT( editPaste() ) );
827+
connect( mActionCopyStyle, SIGNAL( triggered() ), this, SLOT( copyStyle() ) );
828+
connect( mActionPasteStyle, SIGNAL( triggered() ), this, SLOT( pasteStyle() ) );
827829
connect( mActionAddFeature, SIGNAL( triggered() ), this, SLOT( addFeature() ) );
828830
connect( mActionMoveFeature, SIGNAL( triggered() ), this, SLOT( moveFeature() ) );
829831
connect( mActionReshapeFeatures, SIGNAL( triggered() ), this, SLOT( reshapeFeatures() ) );
@@ -4297,7 +4299,6 @@ void QgisApp::editCut( QgsMapLayer * layerContainingSelection )
42974299
}
42984300
}
42994301

4300-
43014302
void QgisApp::editCopy( QgsMapLayer * layerContainingSelection )
43024303
{
43034304
if ( mMapCanvas && mMapCanvas->isDrawing() )
@@ -4376,6 +4377,72 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
43764377
}
43774378
}
43784379

4380+
void QgisApp::copyStyle( QgsMapLayer * sourceLayer )
4381+
{
4382+
QgsMapLayer *selectionLayer = sourceLayer ? sourceLayer : activeLayer();
4383+
if ( selectionLayer )
4384+
{
4385+
QDomImplementation DomImplementation;
4386+
QDomDocumentType documentType =
4387+
DomImplementation.createDocumentType(
4388+
"qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
4389+
QDomDocument doc( documentType );
4390+
QDomElement rootNode = doc.createElement( "qgis" );
4391+
rootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
4392+
doc.appendChild( rootNode );
4393+
QString errorMsg;
4394+
if ( !selectionLayer->writeSymbology( rootNode, doc, errorMsg ) )
4395+
{
4396+
QMessageBox::warning( this,
4397+
tr( "Error" ),
4398+
tr( "Cannot copy style: %1" )
4399+
.arg( errorMsg ),
4400+
QMessageBox::Ok );
4401+
return;
4402+
}
4403+
// Copies data in text form as well, so the XML can be pasted into a text editor
4404+
clipboard()->setData( QGSCLIPBOARD_STYLE_MIME, doc.toByteArray(), doc.toString() );
4405+
// Enables the paste menu element
4406+
mActionPasteStyle->setEnabled( true );
4407+
}
4408+
}
4409+
4410+
void QgisApp::pasteStyle( QgsMapLayer * destinationLayer )
4411+
{
4412+
QgsMapLayer *selectionLayer = destinationLayer ? destinationLayer : activeLayer();
4413+
if ( selectionLayer )
4414+
{
4415+
if ( clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
4416+
{
4417+
QDomDocument doc( "qgis" );
4418+
QString errorMsg;
4419+
int errorLine, errorColumn;
4420+
if ( !doc.setContent ( clipboard()->data( QGSCLIPBOARD_STYLE_MIME ), false, &errorMsg, &errorLine, &errorColumn ) )
4421+
{
4422+
QMessageBox::information( this,
4423+
tr( "Error" ),
4424+
tr( "Cannot parse style: %1:%2:%3" )
4425+
.arg( errorMsg )
4426+
.arg( errorLine )
4427+
.arg( errorColumn ),
4428+
QMessageBox::Ok );
4429+
return;
4430+
}
4431+
QDomElement rootNode = doc.firstChildElement( "qgis" );
4432+
if ( !selectionLayer->readSymbology( rootNode, errorMsg ) )
4433+
{
4434+
QMessageBox::information( this,
4435+
tr( "Error" ),
4436+
tr( "Cannot read style: %1" )
4437+
.arg( errorMsg ),
4438+
QMessageBox::Ok );
4439+
return;
4440+
}
4441+
4442+
mMapLegend->refreshLayerSymbology( selectionLayer->id(), false );
4443+
}
4444+
}
4445+
}
43794446

43804447
void QgisApp::pasteTransformations()
43814448
{
@@ -6299,6 +6366,8 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
62996366
mActionCutFeatures->setEnabled( false );
63006367
mActionCopyFeatures->setEnabled( false );
63016368
mActionPasteFeatures->setEnabled( false );
6369+
mActionCopyStyle->setEnabled( false );
6370+
mActionPasteStyle->setEnabled( false );
63026371

63036372
mActionUndo->setEnabled( false );
63046373
mActionRedo->setEnabled( false );
@@ -6329,6 +6398,9 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
63296398
mActionAddToOverview->setEnabled( true );
63306399
mActionZoomToLayer->setEnabled( true );
63316400

6401+
mActionCopyStyle->setEnabled( true );
6402+
mActionPasteStyle->setEnabled( clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) );
6403+
63326404
/***********Vector layers****************/
63336405
if ( layer->type() == QgsMapLayer::VectorLayer )
63346406
{

src/app/qgisapp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,17 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
429429
*/
430430
void editPaste( QgsMapLayer * destinationLayer = 0 );
431431

432+
/**
433+
\param sourceLayer The layer where the style will be taken from
434+
(defaults to the active layer on the legend)
435+
*/
436+
void copyStyle( QgsMapLayer * sourceLayer = 0 );
437+
//! copies style on the clipboard to the active layer
438+
/**
439+
\param destinatioLayer The layer that the clipboard will be pasted to
440+
(defaults to the active layer on the legend)
441+
*/
442+
void pasteStyle( QgsMapLayer * destinationLayer = 0 );
432443
void loadOGRSublayers( QString layertype, QString uri, QStringList list );
433444
void loadGDALSublayers( QString uri, QStringList list );
434445

src/app/qgsclipboard.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <QStringList>
2323
#include <QClipboard>
2424
#include <QSettings>
25+
#include <QMimeData>
2526

2627
#include "qgsclipboard.h"
2728
#include "qgsfeature.h"
@@ -104,9 +105,9 @@ void QgsClipboard::replaceWithCopyOf( const QgsFieldMap& fields, QgsFeatureList&
104105
// docs). With a Linux X server, ::Clipboard was required.
105106
// The simple solution was to put the text into both clipboards.
106107

107-
// The ::Selection setText() below one may need placing inside so
108-
// #ifdef so that it doesn't get compiled under Windows.
108+
#ifndef Q_OS_WIN
109109
cb->setText( textCopy, QClipboard::Selection );
110+
#endif
110111
cb->setText( textCopy, QClipboard::Clipboard );
111112

112113
QgsDebugMsg( QString( "replaced system clipboard with: %1." ).arg( textCopy ) );
@@ -163,3 +164,38 @@ QgsCoordinateReferenceSystem QgsClipboard::crs()
163164
{
164165
return mCRS;
165166
}
167+
168+
void QgsClipboard::setData( const QString& mimeType, const QByteArray& data, const QString* text )
169+
{
170+
QMimeData *mdata = new QMimeData();
171+
mdata->setData( mimeType, data );
172+
if ( text )
173+
{
174+
mdata->setText( *text );
175+
}
176+
// Transfers ownership to the clipboard object
177+
#ifndef Q_OS_WIN
178+
QApplication::clipboard()->setMimeData( mdata, QClipboard::Selection );
179+
#endif
180+
QApplication::clipboard()->setMimeData( mdata, QClipboard::Clipboard );
181+
}
182+
183+
void QgsClipboard::setData( const QString& mimeType, const QByteArray& data, const QString& text )
184+
{
185+
setData( mimeType, data, &text );
186+
}
187+
188+
void QgsClipboard::setData( const QString& mimeType, const QByteArray& data )
189+
{
190+
setData( mimeType, data, 0 );
191+
}
192+
193+
bool QgsClipboard::hasFormat( const QString& mimeType )
194+
{
195+
return QApplication::clipboard()->mimeData()->hasFormat( mimeType );
196+
}
197+
198+
QByteArray QgsClipboard::data( const QString& mimeType )
199+
{
200+
return QApplication::clipboard()->mimeData()->data( mimeType );
201+
}

src/app/qgsclipboard.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
TODO: Make it work
4242
*/
4343

44+
/*
45+
* Constants used to describe copy-paste MIME types
46+
*/
47+
#define QGSCLIPBOARD_STYLE_MIME "application/qgis.style"
48+
4449
class QgsClipboard
4550
{
4651
public:
@@ -98,6 +103,32 @@ class QgsClipboard
98103
*/
99104
QgsCoordinateReferenceSystem crs();
100105

106+
/*
107+
* Stores a MimeData together with a text into the system clipboard
108+
*/
109+
void setData( const QString& mimeType, const QByteArray& data, const QString* text = 0 );
110+
/*
111+
* Stores a MimeData together with a text into the system clipboard
112+
*/
113+
void setData( const QString& mimeType, const QByteArray& data, const QString& text );
114+
/*
115+
* Stores a MimeData into the system clipboard
116+
*/
117+
void setData( const QString& mimeType, const QByteArray& data );
118+
/*
119+
* Stores a text into the system clipboard
120+
*/
121+
void setText( const QString& text );
122+
/*
123+
* Proxy to QMimeData::hasFormat
124+
* Tests whether the system clipboard contains data of a given MIME type
125+
*/
126+
bool hasFormat( const QString& mimeType );
127+
/*
128+
* Retrieve data from the system clipboard.
129+
* No copy is involved, since the return QByteArray is implicitly shared
130+
*/
131+
QByteArray data( const QString& mimeType );
101132
private:
102133

103134
/** QGIS-internal vector feature clipboard.

src/ui/qgisapp.ui

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<x>0</x>
1818
<y>0</y>
1919
<width>1052</width>
20-
<height>21</height>
20+
<height>25</height>
2121
</rect>
2222
</property>
2323
<widget class="QMenu" name="mEditMenu">
@@ -152,6 +152,9 @@
152152
<addaction name="mActionAddLayerSeparator"/>
153153
<addaction name="mActionAddWfsLayer"/>
154154
<addaction name="separator"/>
155+
<addaction name="mActionCopyStyle"/>
156+
<addaction name="mActionPasteStyle"/>
157+
<addaction name="separator"/>
155158
<addaction name="mActionOpenTable"/>
156159
<addaction name="mActionSaveEdits"/>
157160
<addaction name="mActionToggleEditing"/>
@@ -1648,6 +1651,24 @@
16481651
<string>Offset Curve</string>
16491652
</property>
16501653
</action>
1654+
<action name="mActionCopyStyle">
1655+
<property name="icon">
1656+
<iconset resource="../../images/images.qrc">
1657+
<normaloff>:/images/themes/default/mActionEditCopy.png</normaloff>:/images/themes/default/mActionEditCopy.png</iconset>
1658+
</property>
1659+
<property name="text">
1660+
<string>Copy style</string>
1661+
</property>
1662+
</action>
1663+
<action name="mActionPasteStyle">
1664+
<property name="icon">
1665+
<iconset resource="../../images/images.qrc">
1666+
<normaloff>:/images/themes/default/mActionEditPaste.png</normaloff>:/images/themes/default/mActionEditPaste.png</iconset>
1667+
</property>
1668+
<property name="text">
1669+
<string>Paste style</string>
1670+
</property>
1671+
</action>
16511672
</widget>
16521673
<resources>
16531674
<include location="../../images/images.qrc"/>

0 commit comments

Comments
 (0)