Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] API to allow drag'n'drop of custom browser items
QgsDataItem implementations may provide hasDragEnabled(), mimeUri()
and QgsCustomDropHandler implementation to deal with drop of custom items.
  • Loading branch information
wonder-sk committed Aug 9, 2016
1 parent 2ea828e commit b4fe900
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 75 deletions.
6 changes: 6 additions & 0 deletions doc/api_break.dox
Expand Up @@ -450,6 +450,12 @@ be returned instead of a null pointer if no transformation is required.</li>
plugins calling this method will need to be updated.</li>
</ul>

\subsection qgis_api_break_3_0_QgsMimeDataUtils QgsMimeDataUtils

<ul>
<li>Constructor taking QgsLayerItem argument has been removed. Use QgsDataItem::mimeUri() instead.</li>
</ul>

\subsection qgis_api_break_3_0_QgsOSMElement QgsOSMElement

<ul>
Expand Down
30 changes: 25 additions & 5 deletions python/core/qgsdataitem.sip
Expand Up @@ -135,6 +135,21 @@ class QgsDataItem : QObject
*/
virtual bool handleDrop( const QMimeData * /*data*/, Qt::DropAction /*action*/ );

/** Returns true if the item may be dragged.
* Default implementation returns false.
* A draggable item has to implement mimeUri() that will be used to pass data.
* @see mimeUri()
* @note added in 3.0
*/
virtual bool hasDragEnabled() const;

/** Return mime URI for the data item.
* Items that return valid URI will be returned in mime data when dragging a selection from browser model.
* @see hasDragEnabled()
* @note added in 3.0
*/
virtual QgsMimeDataUtils::Uri mimeUri() const;

enum Capability
{
NoCapabilities,
Expand Down Expand Up @@ -262,26 +277,30 @@ class QgsLayerItem : QgsDataItem

virtual bool equal( const QgsDataItem *other );

virtual bool hasDragEnabled() const;

virtual QgsMimeDataUtils::Uri mimeUri() const;

// --- New virtual methods for layer item derived classes ---

// Returns QgsMapLayer::LayerType
QgsMapLayer::LayerType mapLayerType();
QgsMapLayer::LayerType mapLayerType() const;

// Returns layer uri or empty string if layer cannot be created
QString uri();
QString uri() const;

// Returns provider key
QString providerKey();
QString providerKey() const;

/** Returns the supported CRS
* @note Added in 2.8
*/
QStringList supportedCrs();
QStringList supportedCrs() const;

/** Returns the supported formats
* @note Added in 2.8
*/
QStringList supportedFormats();
QStringList supportedFormats() const;

/** Returns comments of the layer
* @note added in 2.12
Expand Down Expand Up @@ -389,6 +408,7 @@ class QgsProjectItem : QgsDataItem
QgsProjectItem( QgsDataItem* parent, const QString& name, const QString& path );
~QgsProjectItem();

virtual bool hasDragEnabled() const;
};

/**
Expand Down
17 changes: 15 additions & 2 deletions python/core/qgsmimedatautils.sip
Expand Up @@ -7,14 +7,27 @@ class QgsMimeDataUtils

struct Uri
{
Uri( QgsLayerItem* layer );
Uri( QString& encData );
//! Constructs invalid URI
Uri();
//! Constructs URI from encoded data
explicit Uri( QString& encData );

//! Returns whether the object contains valid data
//! @note added in 3.0
bool isValid() const;

//! Returns encoded representation of the object
QString data() const;

//! Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom"
QString layerType;
//! For "vector" / "raster" type: provider id.
//! For "plugin" type: plugin layer type name.
//! For "custom" type: key of its QgsCustomDropHandler
QString providerKey;
//! Human readable name to be used e.g. in layer tree
QString name;
//! Identifier of the data source recognized by its providerKey
QString uri;
QStringList supportedCrs;
QStringList supportedFormats;
Expand Down
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -57,6 +57,7 @@
%Include qgscompoundcolorwidget.sip
%Include qgsconfigureshortcutsdialog.sip
%Include qgscredentialdialog.sip
%Include qgscustomdrophandler.sip
%Include qgsdatadefinedbutton.sip
%Include qgsdetaileditemdata.sip
%Include qgsdetaileditemdelegate.sip
Expand Down
12 changes: 12 additions & 0 deletions python/gui/qgisinterface.sip
Expand Up @@ -292,6 +292,18 @@ class QgisInterface : QObject
*/
virtual void unregisterMapLayerConfigWidgetFactory( QgsMapLayerConfigWidgetFactory* factory ) = 0;

/** Register a new custom drop handler.
* @note added in QGIS 3.0
* @note Ownership of the factory is not transferred, and the factory must
* be unregistered when plugin is unloaded.
* @see unregisterCustomDropHandler() */
virtual void registerCustomDropHandler( QgsCustomDropHandler* handler ) = 0;

/** Unregister a previously registered custom drop handler.
* @note added in QGIS 3.0
* @see registerCustomDropHandler() */
virtual void unregisterCustomDropHandler( QgsCustomDropHandler* handler ) = 0;

// @todo is this deprecated in favour of QgsContextHelp?
/** Open a url in the users browser. By default the QGIS doc directory is used
* as the base for the URL. To open a URL that is not relative to the installed
Expand Down
21 changes: 21 additions & 0 deletions python/gui/qgscustomdrophandler.sip
@@ -0,0 +1,21 @@
/** \ingroup gui
* Abstract base class that may be implemented to handle new types of data to be dropped in QGIS.
* Implementations will be used when a QgsMimeDataUtils::Uri has layerType equal to "custom",
* and the providerKey is equal to key() returned by the implementation.
* @note added in QGIS 3.0
*/
class QgsCustomDropHandler
{
%TypeHeaderCode
#include <qgscustomdrophandler.h>
%End

public:
virtual ~QgsCustomDropHandler();

//! Type of custom URI recognized by the handler
virtual QString key() const = 0;

//! Method called from QGIS after a drop event with custom URI known by the handler
virtual void handleDrop( const QgsMimeDataUtils::Uri& uri ) const = 0;
};
62 changes: 46 additions & 16 deletions src/app/qgisapp.cpp
Expand Up @@ -128,6 +128,7 @@
#include "qgscoordinateutils.h"
#include "qgscredentialdialog.h"
#include "qgscursors.h"
#include "qgscustomdrophandler.h"
#include "qgscustomization.h"
#include "qgscustomlayerorderwidget.h"
#include "qgscustomprojectiondialog.h"
Expand Down Expand Up @@ -1341,29 +1342,58 @@ void QgisApp::dropEvent( QDropEvent *event )
if ( QgsMimeDataUtils::isUriList( event->mimeData() ) )
{
QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( event->mimeData() );
Q_FOREACH ( const QgsMimeDataUtils::Uri& u, lst )
{
QString uri = crsAndFormatAdjustedLayerUri( u.uri, u.supportedCrs, u.supportedFormats );
handleDropUriList( lst );
}
mMapCanvas->freeze( false );
mMapCanvas->refresh();
event->acceptProposedAction();
}

if ( u.layerType == "vector" )
{
addVectorLayer( uri, u.name, u.providerKey );
}
else if ( u.layerType == "raster" )
{
addRasterLayer( uri, u.name, u.providerKey );
}
else if ( u.layerType == "plugin" )

void QgisApp::registerCustomDropHandler( QgsCustomDropHandler* handler )
{
if ( !mCustomDropHandlers.contains( handler ) )
mCustomDropHandlers << handler;
}

void QgisApp::unregisterCustomDropHandler( QgsCustomDropHandler* handler )
{
mCustomDropHandlers.removeOne( handler );
}

void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList& lst )
{
Q_FOREACH ( const QgsMimeDataUtils::Uri& u, lst )
{
QString uri = crsAndFormatAdjustedLayerUri( u.uri, u.supportedCrs, u.supportedFormats );

if ( u.layerType == "vector" )
{
addVectorLayer( uri, u.name, u.providerKey );
}
else if ( u.layerType == "raster" )
{
addRasterLayer( uri, u.name, u.providerKey );
}
else if ( u.layerType == "plugin" )
{
addPluginLayer( uri, u.name, u.providerKey );
}
else if ( u.layerType == "custom" )
{
Q_FOREACH ( QgsCustomDropHandler* handler, mCustomDropHandlers )
{
addPluginLayer( uri, u.name, u.providerKey );
if ( handler->key() == u.providerKey )
{
handler->handleDrop( u );
break;
}
}
}
}
mMapCanvas->freeze( false );
mMapCanvas->refresh();
event->acceptProposedAction();
}


bool QgisApp::event( QEvent * event )
{
bool done = false;
Expand Down
13 changes: 13 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -44,6 +44,7 @@ class QgsClipboard;
class QgsComposer;
class QgsComposerManager;
class QgsComposerView;
class QgsCustomDropHandler;
class QgsStatusBarCoordinatesWidget;
class QgsStatusBarMagnifierWidget;
class QgsStatusBarScaleWidget;
Expand Down Expand Up @@ -120,6 +121,7 @@ class QgsDiagramProperties;
#include "qgsfeature.h"
#include "qgspoint.h"
#include "qgsmessagebar.h"
#include "qgsmimedatautils.h"
#include "qgswelcomepageitemsmodel.h"
#include "qgsraster.h"

Expand Down Expand Up @@ -513,6 +515,15 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
/** Unregister a previously registered tab in the layer properties dialog */
void unregisterMapLayerPropertiesFactory( QgsMapLayerConfigWidgetFactory* factory );

/** Register a new custom drop handler. */
void registerCustomDropHandler( QgsCustomDropHandler* handler );

/** Unregister a previously registered custom drop handler. */
void unregisterCustomDropHandler( QgsCustomDropHandler* handler );

/** Process the list of URIs that have been dropped in QGIS */
void handleDropUriList( const QgsMimeDataUtils::UriList& lst );

public slots:
void layerTreeViewDoubleClicked( const QModelIndex& index );
//! Make sure the insertion point for new layers is up-to-date with the current item in layer tree view
Expand Down Expand Up @@ -1772,6 +1783,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow

QList<QgsMapLayerConfigWidgetFactory*> mMapLayerPanelFactories;

QList<QgsCustomDropHandler*> mCustomDropHandlers;

QDateTime mProjectLastModified;

QgsWelcomePage* mWelcomePage;
Expand Down
10 changes: 10 additions & 0 deletions src/app/qgisappinterface.cpp
Expand Up @@ -485,6 +485,16 @@ void QgisAppInterface::unregisterMapLayerConfigWidgetFactory( QgsMapLayerConfigW
qgis->unregisterMapLayerPropertiesFactory( factory );
}

void QgisAppInterface::registerCustomDropHandler( QgsCustomDropHandler *handler )
{
qgis->registerCustomDropHandler( handler );
}

void QgisAppInterface::unregisterCustomDropHandler( QgsCustomDropHandler *handler )
{
qgis->unregisterCustomDropHandler( handler );
}

//! Menus
QMenu *QgisAppInterface::projectMenu() { return qgis->projectMenu(); }
QMenu *QgisAppInterface::editMenu() { return qgis->editMenu(); }
Expand Down
12 changes: 12 additions & 0 deletions src/app/qgisappinterface.h
Expand Up @@ -301,6 +301,18 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
*/
virtual void unregisterMapLayerConfigWidgetFactory( QgsMapLayerConfigWidgetFactory* factory ) override;

/** Register a new custom drop handler.
* @note added in QGIS 3.0
* @note Ownership of the factory is not transferred, and the factory must
* be unregistered when plugin is unloaded.
* @see unregisterCustomDropHandler() */
virtual void registerCustomDropHandler( QgsCustomDropHandler* handler ) override;

/** Unregister a previously registered custom drop handler.
* @note added in QGIS 3.0
* @see registerCustomDropHandler() */
virtual void unregisterCustomDropHandler( QgsCustomDropHandler* handler ) override;

/** Accessors for inserting items into menus and toolbars.
* An item can be inserted before any existing action.
*/
Expand Down
23 changes: 3 additions & 20 deletions src/app/qgsbrowserdockwidget.cpp
Expand Up @@ -516,26 +516,9 @@ void QgsBrowserDockWidget::addLayer( QgsLayerItem *layerItem )
if ( !layerItem )
return;

QString uri = QgisApp::instance()->crsAndFormatAdjustedLayerUri( layerItem->uri(), layerItem->supportedCrs(), layerItem->supportedFormats() );
if ( uri.isEmpty() )
return;

QgsMapLayer::LayerType type = layerItem->mapLayerType();
QString providerKey = layerItem->providerKey();

QgsDebugMsg( providerKey + " : " + uri );
if ( type == QgsMapLayer::VectorLayer )
{
QgisApp::instance()->addVectorLayer( uri, layerItem->layerName(), providerKey );
}
if ( type == QgsMapLayer::RasterLayer )
{
QgisApp::instance()->addRasterLayer( uri, layerItem->layerName(), providerKey );
}
if ( type == QgsMapLayer::PluginLayer )
{
QgisApp::instance()->addPluginLayer( uri, layerItem->layerName(), providerKey );
}
QgsMimeDataUtils::UriList list;
list << layerItem->mimeUri();
QgisApp::instance()->handleDropUriList( list );
}

void QgsBrowserDockWidget::addLayerAtIndex( const QModelIndex& index )
Expand Down
11 changes: 5 additions & 6 deletions src/core/qgsbrowsermodel.cpp
Expand Up @@ -186,10 +186,9 @@ Qt::ItemFlags QgsBrowserModel::flags( const QModelIndex & index ) const
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;

QgsDataItem* ptr = reinterpret_cast< QgsDataItem* >( index.internalPointer() );
if ( ptr->type() == QgsDataItem::Layer || ptr->type() == QgsDataItem::Project )
{
if ( ptr->hasDragEnabled() )
flags |= Qt::ItemIsDragEnabled;
}

if ( ptr->acceptDrop() )
flags |= Qt::ItemIsDropEnabled;
return flags;
Expand Down Expand Up @@ -461,9 +460,9 @@ QMimeData * QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
return mimeData;
}

if ( ptr->type() != QgsDataItem::Layer ) continue;
QgsLayerItem *layer = static_cast< QgsLayerItem* >( ptr );
lst.append( QgsMimeDataUtils::Uri( layer ) );
QgsMimeDataUtils::Uri uri = ptr->mimeUri();
if ( uri.isValid() )
lst.append( uri );
}
}
return QgsMimeDataUtils::encodeUriList( lst );
Expand Down

0 comments on commit b4fe900

Please sign in to comment.