Navigation Menu

Skip to content

Commit

Permalink
Allow copying and pasting symbols between QgsSymbolButtons
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jul 24, 2017
1 parent 15f3bbf commit 78b05c1
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 4 deletions.
2 changes: 1 addition & 1 deletion python/core/symbology-ng/qgssymbol.sip
Expand Up @@ -88,7 +88,7 @@ return new default symbol for specified geometry type
:rtype: QgsSymbolLayer
%End

int symbolLayerCount();
int symbolLayerCount() const;
%Docstring
Returns total number of symbol layers contained in the symbol.
:return: count of symbol layers
Expand Down
2 changes: 2 additions & 0 deletions python/core/symbology-ng/qgssymbollayer.sip
Expand Up @@ -231,8 +231,10 @@ class QgsSymbolLayer

virtual QgsSymbol *subSymbol();
%Docstring
Returns the symbol's sub symbol, if present.
:rtype: QgsSymbol
%End

virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
%Docstring
set layer's subsymbol. takes ownership of the passed symbol
Expand Down
19 changes: 19 additions & 0 deletions python/core/symbology-ng/qgssymbollayerutils.sip
Expand Up @@ -548,6 +548,25 @@ Writes a collection of symbols to XML with specified tagName for the top-level e

static void clearSymbolMap( QgsSymbolMap &symbols );

static QMimeData *symbolToMimeData( QgsSymbol *symbol ) /Factory/;
%Docstring
Creates new mime data from a ``symbol``.
This also sets the mime color data to match the symbol's color, so that copied symbols
can be paste in places where a color is expected.
.. seealso:: symbolFromMimeData()
.. versionadded:: 3.0
:rtype: QMimeData
%End

static QgsSymbol *symbolFromMimeData( const QMimeData *data ) /Factory/;
%Docstring
Attempts to parse ``mime`` data as a symbol. A new symbol instance will be returned
if the data was successfully converted to a symbol.
.. seealso:: symbolToMimeData()
.. versionadded:: 3.0
:rtype: QgsSymbol
%End

static QgsColorRamp *loadColorRamp( QDomElement &element ) /Factory/;
%Docstring
Creates a color ramp from the settings encoded in an XML element
Expand Down
28 changes: 28 additions & 0 deletions python/gui/qgssymbolbutton.sip
Expand Up @@ -32,6 +32,21 @@ class QgsSymbolButton : QToolButton

virtual QSize minimumSizeHint() const;

void setSymbolType( QgsSymbol::SymbolType type );
%Docstring
Sets the symbol ``type`` which the button requires.
If the type differs from the current symbol type, the symbol will be reset
to a default symbol style of the new type.
.. seealso:: symbolType()
%End

QgsSymbol::SymbolType symbolType() const;
%Docstring
Returns the symbol type which the button requires.
.. seealso:: setSymbolType()
:rtype: QgsSymbol.SymbolType
%End

void setDialogTitle( const QString &title );
%Docstring
Sets the ``title`` for the symbol settings dialog window.
Expand Down Expand Up @@ -105,6 +120,19 @@ class QgsSymbolButton : QToolButton
to the previous symbol color.
%End

void copySymbol();
%Docstring
Copies the current symbol to the clipboard.
.. seealso:: pasteSymbol()
%End

void pasteSymbol();
%Docstring
Pastes a symbol from the clipboard. If clipboard does not contain a valid
symbol then no change is applied.
.. seealso:: copySymbol()
%End

void copyColor();
%Docstring
Copies the current symbol color to the clipboard.
Expand Down
2 changes: 2 additions & 0 deletions src/app/composer/qgscomposershapewidget.cpp
Expand Up @@ -45,6 +45,8 @@ QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape *composerShape
mShapeComboBox->addItem( tr( "Rectangle" ) );
mShapeComboBox->addItem( tr( "Triangle" ) );

mShapeStyleButton->setSymbolType( QgsSymbol::Fill );

setGuiElementValues();

blockAllSignals( false );
Expand Down
3 changes: 3 additions & 0 deletions src/app/qgsannotationwidget.cpp
Expand Up @@ -33,6 +33,9 @@ QgsAnnotationWidget::QgsAnnotationWidget( QgsMapCanvasAnnotationItem *item, QWid
setupUi( this );
mLayerComboBox->setAllowEmptyLayer( true );

mMapMarkerButton->setSymbolType( QgsSymbol::Marker );
mFrameStyleButton->setSymbolType( QgsSymbol::Fill );

if ( mItem && mItem->annotation() )
{
QgsAnnotation *annotation = mItem->annotation();
Expand Down
4 changes: 4 additions & 0 deletions src/app/qgsdecorationgriddialog.cpp
Expand Up @@ -34,6 +34,9 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidg
{
setupUi( this );

mMarkerSymbolButton->setSymbolType( QgsSymbol::Marker );
mLineSymbolButton->setSymbolType( QgsSymbol::Line );

mAnnotationFontButton->setMode( QgsFontButton::ModeQFont );

QgsSettings settings;
Expand Down Expand Up @@ -65,6 +68,7 @@ QgsDecorationGridDialog::QgsDecorationGridDialog( QgsDecorationGrid &deco, QWidg
connect( mAnnotationFontButton, &QgsFontButton::changed, this, &QgsDecorationGridDialog::annotationFontChanged );

mMarkerSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() );
mLineSymbolButton->setMapCanvas( QgisApp::instance()->mapCanvas() );
}

void QgsDecorationGridDialog::updateGuiElements()
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgsdecorationlayoutextentdialog.cpp
Expand Up @@ -35,6 +35,8 @@ QgsDecorationLayoutExtentDialog::QgsDecorationLayoutExtentDialog( QgsDecorationL
{
setupUi( this );

mSymbolButton->setSymbolType( QgsSymbol::Fill );

QgsSettings settings;
restoreGeometry( settings.value( "/Windows/DecorationLayoutExtent/geometry" ).toByteArray() );

Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology-ng/qgssymbol.h
Expand Up @@ -136,7 +136,7 @@ class CORE_EXPORT QgsSymbol
* \see symbolLayers
* \see symbolLayer
*/
int symbolLayerCount() { return mLayers.count(); }
int symbolLayerCount() const { return mLayers.count(); }

/**
* Insert symbol layer to specified index
Expand Down
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgssymbollayer.h
Expand Up @@ -252,7 +252,11 @@ class CORE_EXPORT QgsSymbolLayer

virtual void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size ) = 0;

/**
* Returns the symbol's sub symbol, if present.
*/
virtual QgsSymbol *subSymbol() { return nullptr; }

//! set layer's subsymbol. takes ownership of the passed symbol
virtual bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) { delete symbol; return false; }

Expand Down
42 changes: 42 additions & 0 deletions src/core/symbology-ng/qgssymbollayerutils.cpp
Expand Up @@ -2809,6 +2809,48 @@ void QgsSymbolLayerUtils::clearSymbolMap( QgsSymbolMap &symbols )
symbols.clear();
}

QMimeData *QgsSymbolLayerUtils::symbolToMimeData( QgsSymbol *symbol )
{
if ( !symbol )
return nullptr;

std::unique_ptr< QMimeData >mimeData( new QMimeData );

QDomDocument symbolDoc;
QDomElement symbolElem = saveSymbol( QStringLiteral( "symbol" ), symbol, symbolDoc, QgsReadWriteContext() );
symbolDoc.appendChild( symbolElem );
mimeData->setText( symbolDoc.toString() );

mimeData->setImageData( symbolPreviewPixmap( symbol, QSize( 100, 100 ), 18 ).toImage() );
mimeData->setColorData( symbol->color() );

return mimeData.release();
}

QgsSymbol *QgsSymbolLayerUtils::symbolFromMimeData( const QMimeData *data )
{
if ( !data )
return nullptr;

QString text = data->text();
if ( !text.isEmpty() )
{
QDomDocument doc;
QDomElement elem;

if ( doc.setContent( text ) )
{
elem = doc.documentElement();

if ( elem.nodeName() != QStringLiteral( "symbol" ) )
elem = elem.firstChildElement( QStringLiteral( "symbol" ) );

return loadSymbol( elem, QgsReadWriteContext() );
}
}
return nullptr;
}


QgsColorRamp *QgsSymbolLayerUtils::loadColorRamp( QDomElement &element )
{
Expand Down
17 changes: 17 additions & 0 deletions src/core/symbology-ng/qgssymbollayerutils.h
Expand Up @@ -365,6 +365,23 @@ class CORE_EXPORT QgsSymbolLayerUtils

static void clearSymbolMap( QgsSymbolMap &symbols );

/**
* Creates new mime data from a \a symbol.
* This also sets the mime color data to match the symbol's color, so that copied symbols
* can be paste in places where a color is expected.
* \see symbolFromMimeData()
* \since QGIS 3.0
*/
static QMimeData *symbolToMimeData( QgsSymbol *symbol ) SIP_FACTORY;

/**
* Attempts to parse \a mime data as a symbol. A new symbol instance will be returned
* if the data was successfully converted to a symbol.
* \see symbolToMimeData()
* \since QGIS 3.0
*/
static QgsSymbol *symbolFromMimeData( const QMimeData *data ) SIP_FACTORY;

/** Creates a color ramp from the settings encoded in an XML element
* \param element DOM element
* \returns new color ramp. Caller takes responsibility for deleting the returned value.
Expand Down
1 change: 1 addition & 0 deletions src/gui/attributetable/qgsfieldconditionalformatwidget.cpp
Expand Up @@ -47,6 +47,7 @@ QgsFieldConditionalFormatWidget::QgsFieldConditionalFormatWidget( QWidget *paren
mModel = new QStandardItemModel( listView );
listView->setModel( mModel );
mPresetsList->setModel( mPresetsModel );
btnChangeIcon->setSymbolType( QgsSymbol::Marker );
btnChangeIcon->setSymbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) );

setPresets( defaultPresets() );
Expand Down
53 changes: 53 additions & 0 deletions src/gui/qgssymbolbutton.cpp
Expand Up @@ -52,6 +52,29 @@ QSize QgsSymbolButton::minimumSizeHint() const
return QSize( size.width(), qMax( size.height(), fontHeight ) );
}

void QgsSymbolButton::setSymbolType( QgsSymbol::SymbolType type )
{
if ( type != mType )
{
switch ( type )
{
case QgsSymbol::Marker:
mSymbol.reset( QgsMarkerSymbol::createSimple( QgsStringMap() ) );
break;

case QgsSymbol::Line:
mSymbol.reset( QgsLineSymbol::createSimple( QgsStringMap() ) );
break;

case QgsSymbol::Fill:
mSymbol.reset( QgsFillSymbol::createSimple( QgsStringMap() ) );
break;
}
}
updatePreview();
mType = type;
}

void QgsSymbolButton::showSettingsDialog()
{
QgsExpressionContext context;
Expand Down Expand Up @@ -155,6 +178,18 @@ void QgsSymbolButton::setColor( const QColor &color )
emit changed();
}

void QgsSymbolButton::copySymbol()
{
QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::symbolToMimeData( mSymbol.get() ) );
}

void QgsSymbolButton::pasteSymbol()
{
std::unique_ptr< QgsSymbol > symbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
if ( symbol && symbol->type() == mType )
setSymbol( symbol.release() );
}

void QgsSymbolButton::copyColor()
{
QApplication::clipboard()->setMimeData( QgsSymbolLayerUtils::colorToMimeData( mSymbol->color() ) );
Expand Down Expand Up @@ -265,6 +300,24 @@ void QgsSymbolButton::prepareMenu()
mMenu->addAction( configureAction );
connect( configureAction, &QAction::triggered, this, &QgsSymbolButton::showSettingsDialog );

QAction *copySymbolAction = new QAction( tr( "Copy symbol" ), this );
mMenu->addAction( copySymbolAction );
connect( copySymbolAction, &QAction::triggered, this, &QgsSymbolButton::copySymbol );
QAction *pasteSymbolAction = new QAction( tr( "Paste symbol" ), this );
//enable or disable paste action based on current clipboard contents. We always show the paste
//action, even if it's disabled, to give hint to the user that pasting symbols is possible
std::unique_ptr< QgsSymbol > tempSymbol( QgsSymbolLayerUtils::symbolFromMimeData( QApplication::clipboard()->mimeData() ) );
if ( tempSymbol && tempSymbol->type() == mType )
{
pasteSymbolAction->setIcon( QgsSymbolLayerUtils::symbolPreviewIcon( tempSymbol.get(), QSize( 16, 16 ), 1 ) );
}
else
{
pasteSymbolAction->setEnabled( false );
}
mMenu->addAction( pasteSymbolAction );
connect( pasteSymbolAction, &QAction::triggered, this, &QgsSymbolButton::pasteSymbol );

mMenu->addSeparator();

QgsColorWheel *colorWheel = new QgsColorWheel( mMenu );
Expand Down
28 changes: 28 additions & 0 deletions src/gui/qgssymbolbutton.h
Expand Up @@ -53,6 +53,20 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton

virtual QSize minimumSizeHint() const override;

/**
* Sets the symbol \a type which the button requires.
* If the type differs from the current symbol type, the symbol will be reset
* to a default symbol style of the new type.
* \see symbolType()
*/
void setSymbolType( QgsSymbol::SymbolType type );

/**
* Returns the symbol type which the button requires.
* \see setSymbolType()
*/
QgsSymbol::SymbolType symbolType() const { return mType; }

/**
* Sets the \a title for the symbol settings dialog window.
* \see dialogTitle()
Expand Down Expand Up @@ -143,6 +157,18 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton
*/
void setColor( const QColor &color );

/** Copies the current symbol to the clipboard.
* \see pasteSymbol()
*/
void copySymbol();

/**
* Pastes a symbol from the clipboard. If clipboard does not contain a valid
* symbol then no change is applied.
* \see copySymbol()
*/
void pasteSymbol();

/**
* Copies the current symbol color to the clipboard.
* \see pasteColor()
Expand Down Expand Up @@ -200,6 +226,8 @@ class GUI_EXPORT QgsSymbolButton : public QToolButton

QString mDialogTitle;

QgsSymbol::SymbolType mType = QgsSymbol::Fill;

QgsMapCanvas *mMapCanvas = nullptr;

QPoint mDragStartPosition;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/symbology-ng/qgspointclusterrendererwidget.cpp
Expand Up @@ -53,6 +53,8 @@ QgsPointClusterRendererWidget::QgsPointClusterRendererWidget( QgsVectorLayer *la
mDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );

mCenterSymbolToolButton->setSymbolType( QgsSymbol::Marker );

if ( renderer )
{
mRenderer = QgsPointClusterRenderer::convertFromRenderer( renderer );
Expand Down
Expand Up @@ -53,6 +53,7 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto
mLabelFontButton->setMode( QgsFontButton::ModeQFont );
mDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
mCenterSymbolToolButton->setSymbolType( QgsSymbol::Marker );

if ( renderer )
{
Expand Down

0 comments on commit 78b05c1

Please sign in to comment.