diff --git a/python/gui/auto_generated/qgsprojectionselectionwidget.sip.in b/python/gui/auto_generated/qgsprojectionselectionwidget.sip.in index f64bd8d7d71f..0cc0ec4af8aa 100644 --- a/python/gui/auto_generated/qgsprojectionselectionwidget.sip.in +++ b/python/gui/auto_generated/qgsprojectionselectionwidget.sip.in @@ -119,6 +119,15 @@ to the preset CRSes shown in the widget. Opens the dialog for selecting a new CRS %End + protected: + + virtual void dragEnterEvent( QDragEnterEvent *event ); + + virtual void dragLeaveEvent( QDragLeaveEvent *event ); + + virtual void dropEvent( QDropEvent *event ); + + }; /************************************************************************ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e6b345bcc9c1..384c53c2d253 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -317,6 +317,7 @@ SET(QGIS_GUI_SRCS qgsgui.cpp qgsguiutils.cpp qgshighlight.cpp + qgshighlightablecombobox.cpp qgshistogramwidget.cpp qgshelp.cpp qgsidentifymenu.cpp @@ -520,6 +521,7 @@ SET(QGIS_GUI_HDRS qgsguiutils.h qgshelp.h qgshighlight.h + qgshighlightablecombobox.h qgshistogramwidget.h qgsidentifymenu.h qgskeyvaluewidget.h diff --git a/src/gui/qgshighlightablecombobox.cpp b/src/gui/qgshighlightablecombobox.cpp new file mode 100644 index 000000000000..60e46d684ea1 --- /dev/null +++ b/src/gui/qgshighlightablecombobox.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + qgshighlightablecombobox.cpp + --------------------------- + Date : 20/12/2019 + Copyright : (C) 2019 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgshighlightablecombobox.h" +#include + +QgsHighlightableComboBox::QgsHighlightableComboBox( QWidget *parent ) + : QComboBox( parent ) +{} + +void QgsHighlightableComboBox::paintEvent( QPaintEvent *e ) +{ + QComboBox::paintEvent( e ); + if ( mHighlight ) + { + QPainter p( this ); + int width = 2; // width of highlight rectangle inside frame + p.setPen( QPen( palette().highlight(), width ) ); + QRect r = rect().adjusted( width, width, -width, -width ); + p.drawRect( r ); + } +} + +void QgsHighlightableComboBox::setHighlighted( bool highlighted ) +{ + mHighlight = highlighted; + update(); +} diff --git a/src/gui/qgshighlightablecombobox.h b/src/gui/qgshighlightablecombobox.h new file mode 100644 index 000000000000..cf0f40a22ca9 --- /dev/null +++ b/src/gui/qgshighlightablecombobox.h @@ -0,0 +1,65 @@ +/*************************************************************************** + qgshighlightablecombobox.h + ------------------------- + Date : 20/12/2019 + Copyright : (C) 2019 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSHIGHLIGHTABLECOMBOBOX_H +#define QGSHIGHLIGHTABLECOMBOBOX_H + +#include + +#include "qgscoordinatereferencesystem.h" +#include "qgis_gui.h" + +#define SIP_NO_FILE + +/** + * \class QgsHighlightableComboBox + * \ingroup gui + * + * A QComboBox subclass with the ability to "highlight" the edges of the widget. + * + * \since QGIS 3.12 + */ +class GUI_EXPORT QgsHighlightableComboBox : public QComboBox +{ + Q_OBJECT + public: + + /** + * Constructor for QgsHighlightableComboBox with the specified parent widget. + */ + QgsHighlightableComboBox( QWidget *parent = nullptr ); + + /** + * Returns TRUE if the combo box is currently highlighted. + * \see setHighlighted() + */ + bool isHighlighted() const { return mHighlight; } + + /** + * Sets whether the combo box is currently \a highlighted. + * \see isHighlighted() + */ + void setHighlighted( bool highlighted ); + + protected: + void paintEvent( QPaintEvent *e ) override; + + private: + + bool mHighlight = false; + +}; + +#endif // QGSHIGHLIGHTABLECOMBOBOX_H diff --git a/src/gui/qgsprojectionselectionwidget.cpp b/src/gui/qgsprojectionselectionwidget.cpp index 0bdd86144ece..703eed5357a7 100644 --- a/src/gui/qgsprojectionselectionwidget.cpp +++ b/src/gui/qgsprojectionselectionwidget.cpp @@ -20,18 +20,17 @@ #include "qgsprojectionselectiondialog.h" #include "qgsproject.h" #include "qgssettings.h" +#include "qgshighlightablecombobox.h" QgsProjectionSelectionWidget::QgsProjectionSelectionWidget( QWidget *parent ) : QWidget( parent ) { - - QHBoxLayout *layout = new QHBoxLayout(); layout->setContentsMargins( 0, 0, 0, 0 ); layout->setSpacing( 6 ); setLayout( layout ); - mCrsComboBox = new QComboBox( this ); + mCrsComboBox = new QgsHighlightableComboBox( this ); mCrsComboBox->addItem( tr( "invalid projection" ), QgsProjectionSelectionWidget::CurrentCrs ); mCrsComboBox->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred ); @@ -58,6 +57,7 @@ QgsProjectionSelectionWidget::QgsProjectionSelectionWidget( QWidget *parent ) setFocusPolicy( Qt::StrongFocus ); setFocusProxy( mButton ); + setAcceptDrops( true ); connect( mButton, &QToolButton::clicked, this, &QgsProjectionSelectionWidget::selectCrs ); connect( mCrsComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsProjectionSelectionWidget::comboIndexChanged ); @@ -65,7 +65,7 @@ QgsProjectionSelectionWidget::QgsProjectionSelectionWidget( QWidget *parent ) QgsCoordinateReferenceSystem QgsProjectionSelectionWidget::crs() const { - switch ( ( CrsOption )mCrsComboBox->currentData().toInt() ) + switch ( static_cast< CrsOption >( mCrsComboBox->currentData().toInt() ) ) { case QgsProjectionSelectionWidget::LayerCrs: return mLayerCrs; @@ -194,6 +194,68 @@ void QgsProjectionSelectionWidget::selectCrs() } } +void QgsProjectionSelectionWidget::dragEnterEvent( QDragEnterEvent *event ) +{ + if ( !( event->possibleActions() & Qt::CopyAction ) ) + { + event->ignore(); + return; + } + + if ( mapLayerFromMimeData( event->mimeData() ) ) + { + // dragged an acceptable layer, phew + event->setDropAction( Qt::CopyAction ); + event->accept(); + mCrsComboBox->setHighlighted( true ); + update(); + } + else + { + event->ignore(); + } +} + +void QgsProjectionSelectionWidget::dragLeaveEvent( QDragLeaveEvent *event ) +{ + if ( mCrsComboBox->isHighlighted() ) + { + event->accept(); + mCrsComboBox->setHighlighted( false ); + update(); + } + else + { + event->ignore(); + } +} + +void QgsProjectionSelectionWidget::dropEvent( QDropEvent *event ) +{ + if ( !( event->possibleActions() & Qt::CopyAction ) ) + { + event->ignore(); + return; + } + + if ( QgsMapLayer *layer = mapLayerFromMimeData( event->mimeData() ) ) + { + // dropped a map layer + setFocus( Qt::MouseFocusReason ); + event->setDropAction( Qt::CopyAction ); + event->accept(); + + if ( layer->crs().isValid() ) + setCrs( layer->crs() ); + } + else + { + event->ignore(); + } + mCrsComboBox->setHighlighted( false ); + update(); +} + void QgsProjectionSelectionWidget::addNotSetOption() { mCrsComboBox->insertItem( 0, mNotSetText, QgsProjectionSelectionWidget::CrsNotSet ); @@ -203,7 +265,7 @@ void QgsProjectionSelectionWidget::addNotSetOption() void QgsProjectionSelectionWidget::comboIndexChanged( int idx ) { - switch ( ( CrsOption )mCrsComboBox->itemData( idx ).toInt() ) + switch ( static_cast< CrsOption >( mCrsComboBox->itemData( idx ).toInt() ) ) { case QgsProjectionSelectionWidget::LayerCrs: emit crsChanged( mLayerCrs ); @@ -349,7 +411,7 @@ int QgsProjectionSelectionWidget::firstRecentCrsIndex() const { for ( int i = 0; i < mCrsComboBox->count(); ++i ) { - if ( ( CrsOption )mCrsComboBox->itemData( i ).toInt() == RecentCrs ) + if ( static_cast< CrsOption >( mCrsComboBox->itemData( i ).toInt() ) == RecentCrs ) { return i; } @@ -365,3 +427,17 @@ void QgsProjectionSelectionWidget::updateTooltip() else setToolTip( QString() ); } + +QgsMapLayer *QgsProjectionSelectionWidget::mapLayerFromMimeData( const QMimeData *data ) const +{ + const QgsMimeDataUtils::UriList uriList = QgsMimeDataUtils::decodeUriList( data ); + for ( const QgsMimeDataUtils::Uri &u : uriList ) + { + // is this uri from the current project? + if ( QgsMapLayer *layer = u.mapLayer() ) + { + return layer; + } + } + return nullptr; +} diff --git a/src/gui/qgsprojectionselectionwidget.h b/src/gui/qgsprojectionselectionwidget.h index 5ef41084b4c2..ebf9b7503073 100644 --- a/src/gui/qgsprojectionselectionwidget.h +++ b/src/gui/qgsprojectionselectionwidget.h @@ -27,6 +27,7 @@ #include "qgis_gui.h" class QgsProjectionSelectionDialog; +class QgsHighlightableComboBox; /** * \class QgsProjectionSelectionWidget @@ -132,13 +133,19 @@ class GUI_EXPORT QgsProjectionSelectionWidget : public QWidget */ void selectCrs(); + protected: + + void dragEnterEvent( QDragEnterEvent *event ) override; + void dragLeaveEvent( QDragLeaveEvent *event ) override; + void dropEvent( QDropEvent *event ) override; + private: QgsCoordinateReferenceSystem mCrs; QgsCoordinateReferenceSystem mLayerCrs; QgsCoordinateReferenceSystem mProjectCrs; QgsCoordinateReferenceSystem mDefaultCrs; - QComboBox *mCrsComboBox = nullptr; + QgsHighlightableComboBox *mCrsComboBox = nullptr; QToolButton *mButton = nullptr; QgsProjectionSelectionDialog *mDialog = nullptr; QString mNotSetText; @@ -155,6 +162,8 @@ class GUI_EXPORT QgsProjectionSelectionWidget : public QWidget int firstRecentCrsIndex() const; void updateTooltip(); + QgsMapLayer *mapLayerFromMimeData( const QMimeData *data ) const; + private slots: void comboIndexChanged( int idx );