Skip to content

Commit

Permalink
Add custom drop handlers for layout windows
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 6, 2017
1 parent b0dea08 commit eea36c0
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 3 deletions.
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -290,6 +290,7 @@
%Include layertree/qgslayertreemapcanvasbridge.sip
%Include layertree/qgslayertreeview.sip
%Include layertree/qgslayertreeviewdefaultactions.sip
%Include layout/qgslayoutcustomdrophandler.sip
%Include layout/qgslayoutdesignerinterface.sip
%Include layout/qgslayoutitemcombobox.sip
%Include layout/qgslayoutitemguiregistry.sip
Expand Down
42 changes: 42 additions & 0 deletions python/gui/layout/qgslayoutcustomdrophandler.sip
@@ -0,0 +1,42 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layout/qgslayoutcustomdrophandler.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/



class QgsLayoutCustomDropHandler : QObject
{
%Docstring
Abstract base class that may be implemented to handle new types of data to be dropped in QGIS layouts.

.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgslayoutcustomdrophandler.h"
%End
public:
virtual ~QgsLayoutCustomDropHandler();

virtual bool handleFileDrop( const QString &file );
%Docstring
Called when the specified ``file`` has been dropped onto a QGIS layout. If true
is returned, then the handler has accepted this file and it should not
be further processed (e.g. by other QgsLayoutCustomDropHandler).

The base class implementation does nothing.
:rtype: bool
%End
};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layout/qgslayoutcustomdrophandler.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
18 changes: 18 additions & 0 deletions python/gui/qgisinterface.sip
Expand Up @@ -1116,6 +1116,24 @@ Unregister a previously registered action. (e.g. when plugin is going to be unlo
.. seealso:: :py:func:`registerCustomDropHandler()`
%End

virtual void registerCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler ) = 0;
%Docstring
Register a new custom drop ``handler`` for layout windows.
.. versionadded:: 3.0
.. note::

Ownership of the factory is not transferred, and the factory must
be unregistered when plugin is unloaded.
.. seealso:: unregisterCustomLayoutDropHandler() *
%End

virtual void unregisterCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler ) = 0;
%Docstring
Unregister a previously registered custom drop ``handler`` for layout windows.
.. versionadded:: 3.0
.. seealso:: registerCustomLayoutDropHandler() *
%End


virtual void openURL( const QString &url, bool useQgisDocDirectory = true ) = 0 /Deprecated/;
%Docstring
Expand Down
91 changes: 91 additions & 0 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgslogger.h"
#include "qgslayout.h"
#include "qgslayoutappmenuprovider.h"
#include "qgslayoutcustomdrophandler.h"
#include "qgslayoutview.h"
#include "qgslayoutviewtooladditem.h"
#include "qgslayoutviewtooladdnodeitem.h"
Expand Down Expand Up @@ -900,6 +901,96 @@ void QgsLayoutDesignerDialog::closeEvent( QCloseEvent * )
saveWindowState();
}

void QgsLayoutDesignerDialog::dropEvent( QDropEvent *event )
{
// dragging app is locked for the duration of dropEvent. This causes explorer windows to hang
// while large projects/layers are loaded. So instead we return from dropEvent as quickly as possible
// and do the actual handling of the drop after a very short timeout
QTimer *timer = new QTimer( this );
timer->setSingleShot( true );
timer->setInterval( 50 );

// get the file list
QList<QUrl>::iterator i;
QList<QUrl>urls = event->mimeData()->urls();
QStringList files;
for ( i = urls.begin(); i != urls.end(); ++i )
{
QString fileName = i->toLocalFile();
#ifdef Q_OS_MAC
// Mac OS X 10.10, under Qt4.8 ,changes dropped URL format
// https://bugreports.qt.io/browse/QTBUG-40449
// [pzion 20150805] Work around
if ( fileName.startsWith( "/.file/id=" ) )
{
QgsDebugMsg( "Mac dropped URL with /.file/id= (converting)" );
CFStringRef relCFStringRef =
CFStringCreateWithCString(
kCFAllocatorDefault,
fileName.toUtf8().constData(),
kCFStringEncodingUTF8
);
CFURLRef relCFURL =
CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
relCFStringRef,
kCFURLPOSIXPathStyle,
false // isDirectory
);
CFErrorRef error = 0;
CFURLRef absCFURL =
CFURLCreateFilePathURL(
kCFAllocatorDefault,
relCFURL,
&error
);
if ( !error )
{
static const CFIndex maxAbsPathCStrBufLen = 4096;
char absPathCStr[maxAbsPathCStrBufLen];
if ( CFURLGetFileSystemRepresentation(
absCFURL,
true, // resolveAgainstBase
reinterpret_cast<UInt8 *>( &absPathCStr[0] ),
maxAbsPathCStrBufLen ) )
{
fileName = QString( absPathCStr );
}
}
CFRelease( absCFURL );
CFRelease( relCFURL );
CFRelease( relCFStringRef );
}
#endif
// seems that some drag and drop operations include an empty url
// so we test for length to make sure we have something
if ( !fileName.isEmpty() )
{
files << fileName;
}
}

connect( timer, &QTimer::timeout, this, [this, timer, files]
{
for ( const QString &file : qgis::as_const( files ) )
{
const QVector<QPointer<QgsLayoutCustomDropHandler >> handlers = QgisApp::instance()->customLayoutDropHandlers();
for ( QgsLayoutCustomDropHandler *handler : handlers )
{
if ( handler && handler->handleFileDrop( file ) )
{
break;
}
}
}

timer->deleteLater();
} );

event->acceptProposedAction();
timer->start();
}

void QgsLayoutDesignerDialog::itemTypeAdded( int id )
{
if ( QgsGui::layoutItemGuiRegistry()->itemMetadata( id )->flags() & QgsLayoutItemAbstractGuiMetadata::FlagNoCreationTools )
Expand Down
1 change: 1 addition & 0 deletions src/app/layout/qgslayoutdesignerdialog.h
Expand Up @@ -238,6 +238,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner
protected:

virtual void closeEvent( QCloseEvent * ) override;
virtual void dropEvent( QDropEvent *event ) override;

private slots:

Expand Down
18 changes: 18 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -205,6 +205,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeutils.h"
#include "qgslayertreeview.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayoutcustomdrophandler.h"
#include "qgslayoutdesignerdialog.h"
#include "qgslayoutmanager.h"
#include "qgslayoutapputils.h"
Expand Down Expand Up @@ -1400,6 +1401,7 @@ QgisApp::~QgisApp()
delete mTray;
delete mDataSourceManagerDialog;
qDeleteAll( mCustomDropHandlers );
qDeleteAll( mCustomLayoutDropHandlers );

// This function *MUST* be the last one called, as it destroys in
// particular GDAL. As above objects can hold GDAL/OGR objects, it is not
Expand Down Expand Up @@ -1560,6 +1562,22 @@ void QgisApp::unregisterCustomDropHandler( QgsCustomDropHandler *handler )
mCustomDropHandlers.removeOne( handler );
}

void QgisApp::registerCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler )
{
if ( !mCustomLayoutDropHandlers.contains( handler ) )
mCustomLayoutDropHandlers << handler;
}

void QgisApp::unregisterCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler )
{
mCustomLayoutDropHandlers.removeOne( handler );
}

QVector<QPointer<QgsLayoutCustomDropHandler> > QgisApp::customLayoutDropHandlers() const
{
return mCustomLayoutDropHandlers;
}

void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList &lst )
{
// insert items in reverse order as each one is inserted on top of previous one
Expand Down
11 changes: 11 additions & 0 deletions src/app/qgisapp.h
Expand Up @@ -62,6 +62,7 @@ class QgsGeometry;
class QgsLayerTreeMapCanvasBridge;
class QgsLayerTreeView;
class QgsLayout;
class QgsLayoutCustomDropHandler;
class QgsLayoutDesignerDialog;
class QgsLayoutDesignerInterface;
class QgsMapCanvas;
Expand Down Expand Up @@ -644,6 +645,15 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Unregister a previously registered custom drop handler.
void unregisterCustomDropHandler( QgsCustomDropHandler *handler );

//! Register a new custom layout drop handler.
void registerCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler );

//! Unregister a previously registered custom layout drop handler.
void unregisterCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler );

//! Returns a list of registered custom layout drop handlers.
QVector<QPointer<QgsLayoutCustomDropHandler >> customLayoutDropHandlers() const;

//! Returns the active map layer.
QgsMapLayer *activeLayer();

Expand Down Expand Up @@ -2119,6 +2129,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
QList<QPointer<QgsOptionsWidgetFactory>> mOptionsWidgetFactories;

QVector<QPointer<QgsCustomDropHandler>> mCustomDropHandlers;
QVector<QPointer<QgsLayoutCustomDropHandler>> mCustomLayoutDropHandlers;

QDateTime mProjectLastModified;

Expand Down
10 changes: 10 additions & 0 deletions src/app/qgisappinterface.cpp
Expand Up @@ -584,6 +584,16 @@ void QgisAppInterface::registerCustomDropHandler( QgsCustomDropHandler *handler
qgis->registerCustomDropHandler( handler );
}

void QgisAppInterface::registerCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler )
{
qgis->registerCustomLayoutDropHandler( handler );
}

void QgisAppInterface::unregisterCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler )
{
qgis->unregisterCustomLayoutDropHandler( handler );
}

void QgisAppInterface::unregisterCustomDropHandler( QgsCustomDropHandler *handler )
{
qgis->unregisterCustomDropHandler( handler );
Expand Down
8 changes: 5 additions & 3 deletions src/app/qgisappinterface.h
Expand Up @@ -24,7 +24,6 @@

class QgisApp;


/**
* \class QgisAppInterface
* \brief Interface class to provide access to private methods in QgisApp
Expand Down Expand Up @@ -330,19 +329,22 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
virtual void unregisterOptionsWidgetFactory( QgsOptionsWidgetFactory *factory ) override;

/**
* Register a new custom drop handler.
* Register a new custom drop \a handler.
* \since 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.
* Unregister a previously registered custom drop \a handler.
* \since QGIS 3.0
* \see registerCustomDropHandler() */
virtual void unregisterCustomDropHandler( QgsCustomDropHandler *handler ) override;

void registerCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler ) override;
void unregisterCustomLayoutDropHandler( QgsLayoutCustomDropHandler *handler ) override;

/**
* Accessors for inserting items into menus and toolbars.
* An item can be inserted before any existing action.
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -160,6 +160,7 @@ SET(QGIS_GUI_SRCS
layertree/qgslayertreeview.cpp
layertree/qgslayertreeviewdefaultactions.cpp

layout/qgslayoutcustomdrophandler.cpp
layout/qgslayoutitemguiregistry.cpp
layout/qgslayoutitemcombobox.cpp
layout/qgslayoutitemwidget.cpp
Expand Down Expand Up @@ -678,6 +679,7 @@ SET(QGIS_GUI_MOC_HDRS
layertree/qgslayertreeview.h
layertree/qgslayertreeviewdefaultactions.h

layout/qgslayoutcustomdrophandler.h
layout/qgslayoutdesignerinterface.h
layout/qgslayoutitemcombobox.h
layout/qgslayoutitemguiregistry.h
Expand Down
22 changes: 22 additions & 0 deletions src/gui/layout/qgslayoutcustomdrophandler.cpp
@@ -0,0 +1,22 @@
/***************************************************************************
qgslayoutcustomdrophandler.cpp
------------------------------
begin : December 2017
copyright : (C) 2017 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 "qgslayoutcustomdrophandler.h"

bool QgsLayoutCustomDropHandler::handleFileDrop( const QString &file )
{
Q_UNUSED( file );
return false;
}
45 changes: 45 additions & 0 deletions src/gui/layout/qgslayoutcustomdrophandler.h
@@ -0,0 +1,45 @@
/***************************************************************************
qgslayoutcustomdrophandler.h
---------------------
begin : December 2017
copyright : (C) 2017 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 QGSLAYOUTCUSTOMDROPHANDLER_H
#define QGSLAYOUTCUSTOMDROPHANDLER_H

#include "qgis_gui.h"
#include <QObject>

/**
* \ingroup gui
* Abstract base class that may be implemented to handle new types of data to be dropped in QGIS layouts.
*
* \since QGIS 3.0
*/
class GUI_EXPORT QgsLayoutCustomDropHandler : public QObject
{
Q_OBJECT

public:
virtual ~QgsLayoutCustomDropHandler() = default;

/**
* Called when the specified \a file has been dropped onto a QGIS layout. If true
* is returned, then the handler has accepted this file and it should not
* be further processed (e.g. by other QgsLayoutCustomDropHandler).
*
* The base class implementation does nothing.
*/
virtual bool handleFileDrop( const QString &file );
};

#endif // QGSLAYOUTCUSTOMDROPHANDLER_H

3 comments on commit eea36c0

@Gustry
Copy link
Contributor

@Gustry Gustry commented on eea36c0 Dec 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this commit, I can't compile master on MacOS:

Scanning dependencies of target qgis_app
[ 84%] Building CXX object src/app/CMakeFiles/qgis_app.dir/layout/qgslayoutdesignerdialog.cpp.o
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:948:11: error: use of undeclared identifier 'kCFAllocatorDefault'
          kCFAllocatorDefault,
          ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:950:11: error: use of undeclared identifier 'kCFStringEncodingUTF8'
          kCFStringEncodingUTF8
          ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:954:11: error: use of undeclared identifier 'kCFAllocatorDefault'
          kCFAllocatorDefault,
          ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:956:11: error: use of undeclared identifier 'kCFURLPOSIXPathStyle'
          kCFURLPOSIXPathStyle,
          ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:962:11: error: use of undeclared identifier 'kCFAllocatorDefault'
          kCFAllocatorDefault,
          ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:968:22: error: unknown type name 'CFIndex'
        static const CFIndex maxAbsPathCStrBufLen = 4096;
                     ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:973:33: error: unknown type name 'UInt8'
               reinterpret_cast<UInt8 *>( &absPathCStr[0] ),
                                ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:980:7: error: use of undeclared identifier 'CFRelease'
      CFRelease( relCFURL );
      ^
/Users/etienne/dev/cpp/QGIS/src/app/layout/qgslayoutdesignerdialog.cpp:1263:3: error: use of undeclared identifier 'mQgis'
  mQgis->activateWindow();
  ^
9 errors generated.
make[2]: *** [src/app/CMakeFiles/qgis_app.dir/layout/qgslayoutdesignerdialog.cpp.o] Error 1
make[1]: *** [src/app/CMakeFiles/qgis_app.dir/all] Error 2
make: *** [all] Error 2
14:54:37: The process "/usr/local/bin/cmake" exited with code 2.
Error while building/deploying project qgis2.99.0 (kit: QGIS Master)
The kit QGIS Master has configuration issues which might be the root cause for this problem.
When executing step "Make"
14:54:37: Elapsed time: 00:28.

For the error line L 1263, missing variable.
But for the block between L 948 and L 980, I don't know which library is missing to get compilation working again.

CC @dakcarto

@jef-n
Copy link
Member

@jef-n jef-n commented on eea36c0 Dec 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gustry
Copy link
Contributor

@Gustry Gustry commented on eea36c0 Dec 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It works now.
I will make a PR

Please sign in to comment.