From 48f3ec8d2d2f88d7614a74ca80a784174140baa4 Mon Sep 17 00:00:00 2001 From: sln Date: Thu, 7 Jul 2011 09:25:06 +0000 Subject: [PATCH] Support of drag-and-drop --- src/SUIT/SUIT_DataBrowser.cxx | 67 +++++++++++ src/SUIT/SUIT_DataBrowser.h | 10 +- src/SUIT/SUIT_DataObject.cxx | 2 +- src/SUIT/SUIT_DataObject.h | 4 +- src/SUIT/SUIT_TreeModel.cxx | 202 +++++++++++++++++++++++++++++++++- src/SUIT/SUIT_TreeModel.h | 22 +++- 6 files changed, 296 insertions(+), 11 deletions(-) diff --git a/src/SUIT/SUIT_DataBrowser.cxx b/src/SUIT/SUIT_DataBrowser.cxx index aa099cb90..0a7ab032b 100644 --- a/src/SUIT/SUIT_DataBrowser.cxx +++ b/src/SUIT/SUIT_DataBrowser.cxx @@ -25,6 +25,9 @@ #include #include +#include +#include +#include /*! \class SUIT_DataBrowser @@ -317,6 +320,8 @@ void SUIT_DataBrowser::init( SUIT_DataObject* root ) { SUIT_ProxyModel* m = new SUIT_ProxyModel( root, this ); connect( m, SIGNAL( modelUpdated() ), this, SLOT( onModelUpdated() ) ); + connect( m, SIGNAL( drop( const QList& , SUIT_DataObject* ) ), + this, SIGNAL( drop( const QList& , SUIT_DataObject* ) ) ); setModel( m ); setItemDelegate( qobject_cast( model() )->delegate() ); @@ -330,6 +335,9 @@ void SUIT_DataBrowser::init( SUIT_DataObject* root ) this, SLOT( onExpanded( const QModelIndex& ) ) ); myShortcut = new QShortcut( Qt::Key_F5, this, SIGNAL( requestUpdate() ), SIGNAL( requestUpdate() ) ); + treeView()->installEventFilter( this ); + treeView()->viewport()->installEventFilter( this ); + myAutoSizeFirstColumn = true; myAutoSizeColumns = false; myResizeOnExpandItem = false; @@ -418,3 +426,62 @@ void SUIT_DataBrowser::onExpanded( const QModelIndex& index ) } } +/*! + \brief Activate drag-and-drop in object browser +*/ +void SUIT_DataBrowser::setDragEnabled( const bool enabled ) +{ + QTreeView* tree = treeView(); + + tree->setDragEnabled( enabled ); + + if ( enabled ) + { + tree->setDragDropMode( QAbstractItemView::InternalMove ); + tree->viewport()->setAcceptDrops( true ); + tree->setDropIndicatorShown( true ); + + } +} + +/*! + \brief Check whether drag-and-drop is activated. +*/ +bool SUIT_DataBrowser::dragEnabled() const +{ + return treeView()->dragEnabled(); +} + +/*! + \brief Catch drag events, analyze mimeData, analyze underlying object and call + SUIT_TreeModel::setDropAccepted() with corresponding flag +*/ +bool SUIT_DataBrowser::eventFilter( QObject* obj, QEvent* e ) +{ + QEvent::Type type = e->type(); + QtxTreeView* tree = treeView(); + + if ( obj == tree->viewport() ) + { + if ( type == QEvent::DragEnter || type == QEvent::DragMove ) + { + SUIT_DataObject* destObj = 0; + + QDropEvent* dropEvent = (QDropEvent*)e; + QModelIndex ind = tree->indexAt( dropEvent->pos() ); + SUIT_ProxyModel* m = qobject_cast( model() ); + if ( m && ind.isValid() ) + { + destObj = m->object( ind ); + if ( destObj ) + { + QList srcList; + m->getObjects( dropEvent->mimeData(), srcList ); + m->setDropAccepted( destObj->isDropAccepted( srcList ) ); + } + } + } + } + + return OB_Browser::eventFilter( obj, e ); +} diff --git a/src/SUIT/SUIT_DataBrowser.h b/src/SUIT/SUIT_DataBrowser.h index d9d0e4caf..52054ca3b 100644 --- a/src/SUIT/SUIT_DataBrowser.h +++ b/src/SUIT/SUIT_DataBrowser.h @@ -27,6 +27,7 @@ #include "SUIT_PopupClient.h" #include "SUIT_DataObject.h" #include +#include class QShortcut; @@ -66,23 +67,28 @@ public: void ensureItemVisible( const SUIT_DataObject* ); + void setDragEnabled( const bool enabled ); + bool dragEnabled() const; + protected: virtual void contextMenuEvent( QContextMenuEvent* ); + virtual bool eventFilter( QObject*, QEvent* ); private: void init( SUIT_DataObject* ); - + signals: void requestUpdate(); void clicked( SUIT_DataObject* ); void doubleClicked( SUIT_DataObject* ); + void drop( const QList& , SUIT_DataObject* ); private slots: void onModelUpdated(); void onClicked( const QModelIndex& ); void onDblClicked( const QModelIndex& ); void onExpanded( const QModelIndex& ); - + private: QShortcut* myShortcut; diff --git a/src/SUIT/SUIT_DataObject.cxx b/src/SUIT/SUIT_DataObject.cxx index 98b323b3a..bb64cafa4 100755 --- a/src/SUIT/SUIT_DataObject.cxx +++ b/src/SUIT/SUIT_DataObject.cxx @@ -542,7 +542,7 @@ bool SUIT_DataObject::isDragable() const to this object */ -bool SUIT_DataObject::isDropAccepted( SUIT_DataObject* /*obj*/ ) +bool SUIT_DataObject::isDropAccepted( const QList& /*list*/ ) { return false; } diff --git a/src/SUIT/SUIT_DataObject.h b/src/SUIT/SUIT_DataObject.h index 2ea48adb7..c849c8018 100755 --- a/src/SUIT/SUIT_DataObject.h +++ b/src/SUIT/SUIT_DataObject.h @@ -104,8 +104,8 @@ public: virtual int alignment( const int = NameId ) const; virtual bool isDragable() const; - virtual bool isDropAccepted( SUIT_DataObject* obj ); - + virtual bool isDropAccepted( const QList& ); + virtual bool isEnabled() const; virtual bool isSelectable() const; virtual bool isCheckable( const int = NameId ) const; diff --git a/src/SUIT/SUIT_TreeModel.cxx b/src/SUIT/SUIT_TreeModel.cxx index 716a7dd2a..b5ea72803 100755 --- a/src/SUIT/SUIT_TreeModel.cxx +++ b/src/SUIT/SUIT_TreeModel.cxx @@ -26,6 +26,10 @@ #include #include +#include +#include + +static const QString SUIT_DATAOBJECT_MIME_TYPE = "suit_dataobject.ptr"; SUIT_AbstractModel::SUIT_AbstractModel() { @@ -439,7 +443,8 @@ SUIT_TreeModel::SUIT_TreeModel( QObject* parent ) myRoot( 0 ), myRootItem( 0 ), myAutoDeleteTree( false ), - myAutoUpdate( true ) + myAutoUpdate( true ), + myDropAccepted( false ) { initialize(); } @@ -822,6 +827,10 @@ Qt::ItemFlags SUIT_TreeModel::flags( const QModelIndex& index ) const // data object is checkable if ( obj->isCheckable( index.column() ) ) f = f | Qt::ItemIsUserCheckable; + + // sln: is moveable + if ( obj->isDragable() ) + f = f | Qt::ItemIsDragEnabled; } return f; } @@ -1301,6 +1310,9 @@ SUIT_ProxyModel::SUIT_ProxyModel( QObject* parent ) { SUIT_TreeModel* model = new SUIT_TreeModel( this ); connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) ); + connect( model, SIGNAL( drop( const QList& , SUIT_DataObject* ) ), + this, SIGNAL( drop( const QList& , SUIT_DataObject* ) ) ); + setSourceModel( model ); } @@ -1314,7 +1326,10 @@ SUIT_ProxyModel::SUIT_ProxyModel( SUIT_DataObject* root, QObject* parent ) mySortingEnabled( true ) { SUIT_TreeModel* model = new SUIT_TreeModel( root, this ); + model->setSupportedDragActions( Qt::MoveAction ); connect( model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) ); + connect( model, SIGNAL( drop( const QList& , SUIT_DataObject* ) ), + this, SIGNAL( drop( const QList& , SUIT_DataObject* ) ) ); setSourceModel( model ); } @@ -1328,6 +1343,8 @@ SUIT_ProxyModel::SUIT_ProxyModel( SUIT_AbstractModel* model, QObject* parent ) mySortingEnabled( true ) { connect( *model, SIGNAL( modelUpdated() ), this, SIGNAL( modelUpdated() ) ); + connect( *model, SIGNAL( drop( const QList& , SUIT_DataObject* ) ), + this, SIGNAL( drop( const QList& , SUIT_DataObject* ) ) ); setSourceModel( *model ); } @@ -1614,10 +1631,30 @@ Qtx::Appropriate SUIT_ProxyModel::appropriate( const QString& name ) const return treeModel() ? treeModel()->appropriate( name ) : Qtx::Shown; } +/*! + \brief Retrieve objects from mime data + \param data - mime data + \param lst - out list of objects + \return TRUE if operation has been completed successfully; FALSE otherwise +*/ +bool SUIT_ProxyModel::getObjects( const QMimeData* data, QList& lst ) const +{ + SUIT_TreeModel* tm = dynamic_cast( treeModel() ); + return tm ? tm->getObjects( data, lst ) : false; +} +/*! + \brief Specify whether drop is acceptable - + \param on - TRUE if drop is acceptable; FALSE otherwise +*/ +void SUIT_ProxyModel::setDropAccepted( const bool on ) +{ + SUIT_TreeModel* tm = dynamic_cast( treeModel() ); + if ( tm ) + tm->setDropAccepted( on ); +} /*! \class SUIT_ItemDelegate @@ -1673,3 +1710,164 @@ void SUIT_ItemDelegate::paint( QPainter* painter, } QItemDelegate::paint( painter, opt, index ); } + +// ++++++++++++ TREE MODEL ++++++++++++ + +/*! + \brief to do Gets supported drag and drop actions. Current implementation returns Qt::MoveAction only + + \return Qt::MoveAction +*/ +Qt::DropActions SUIT_TreeModel::supportedDropActions() const +{ + return Qt::MoveAction; +} + +/*! + \brief Gets supported mime types + + \return "suit_dataobject.ptr" +*/ +QStringList SUIT_TreeModel::mimeTypes() const +{ + QStringList types; + if ( myDropAccepted ) + types << SUIT_DATAOBJECT_MIME_TYPE; + return types; +} + +/*! + \brief Create mime data for given objects + + \param indexes - list of model indexes of objects for drag operation + \return mime data +*/ +QMimeData* SUIT_TreeModel::mimeData( const QModelIndexList& indexes ) const +{ + QMimeData* mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream( &encodedData, QIODevice::WriteOnly ); + + bool is64 = ( sizeof( void* ) == 64 ); + + QSet anAdded; + foreach( QModelIndex index, indexes ) + { + if ( !index.isValid() ) + continue; + + SUIT_DataObject* dataObj = object( index ); + if ( dataObj && !anAdded.contains( dataObj ) ) + { + if ( !dataObj->isDragable() ) + { + delete mimeData; + return 0; + } + + if ( is64 ) + stream << (quint64)dataObj; + else + stream << (quint32)dataObj; + anAdded.insert( dataObj ); + } + } + + mimeData->setData( SUIT_DATAOBJECT_MIME_TYPE , encodedData ); + + return mimeData; +} + +/*! + \brief Drop objects coded in mime data under parent object reimplemented from the base class + + \return TRUE if operation has been completed successfully; FALSE otherwise +*/ +bool SUIT_TreeModel::dropMimeData( const QMimeData* data, Qt::DropAction action, + int row, int column, const QModelIndex& parent ) +{ + QList resList; + SUIT_DataObject* targetObj = 0; + + try + { + targetObj = object( parent ); + if ( targetObj ) + getObjects( data, resList ); + } + catch (...) + { + resList.clear(); + } + + if ( resList.count() > 0 && targetObj ) + { + emit drop( resList, targetObj ); + return true; + } + else + return false; +} + +/*! + \brief Retrieve objects from mime data + + \param data - mime data + \param lst - out list of objects + \return TRUE if operation has been completed successfully; FALSE otherwise +*/ +bool SUIT_TreeModel::getObjects( const QMimeData* data, QList& outList ) const +{ + outList.clear(); + + QByteArray encodedData = data->data( SUIT_DATAOBJECT_MIME_TYPE ); + QDataStream stream( &encodedData, QIODevice::ReadOnly ); + + bool is64 = ( sizeof( void* ) == 64 ); + + while( !stream.atEnd() ) + { + SUIT_DataObject* dataObj = 0; + + if ( is64 ) + { + quint64 buff; + stream >> buff; + dataObj = (SUIT_DataObject*)buff; + } + else + { + quint32 buff; + stream >> buff; + dataObj = (SUIT_DataObject*)buff; + } + + if ( dataObj ) + outList.append( dataObj ); + } + + return outList.count() > 0; +} + +/*! + \brief Specify whether drop is acceptable + + Purpose of this method is to enable drop operation for some items and disable + it for other items. QT does not provide corresponding interface. Drop operation + can be enabled or disabled by means of mimeTypes() method only. If out of + mimeTypes() contains type of mime data used for drag and drop, operation is + enabled; disabled otherwise. SUIT_DataBrowser catches drag events, analyzes + mimeData, analyzes underlying object and calls setDropAccepted() with + corresponding flag + + \param on - TRUE if drop is acceptable; FALSE otherwise +*/ +void SUIT_TreeModel::setDropAccepted( const bool on ) +{ + myDropAccepted = on; +} + + + + diff --git a/src/SUIT/SUIT_TreeModel.h b/src/SUIT/SUIT_TreeModel.h index 5ccdf8455..9d1d7a0cb 100755 --- a/src/SUIT/SUIT_TreeModel.h +++ b/src/SUIT/SUIT_TreeModel.h @@ -143,12 +143,22 @@ public: QAbstractItemDelegate* delegate() const; + // sln: tree model + virtual Qt::DropActions supportedDropActions() const; + virtual QStringList mimeTypes() const; + virtual QMimeData* mimeData( const QModelIndexList& indexes ) const; + virtual bool dropMimeData( const QMimeData* data, Qt::DropAction action, + int row, int column, const QModelIndex& parent ); + bool getObjects( const QMimeData* data, QList& ) const; + void setDropAccepted( const bool ); + public slots: virtual void updateTree( const QModelIndex& ); virtual void updateTree( SUIT_DataObject* = 0 ); signals: void modelUpdated(); + void drop( const QList& , SUIT_DataObject* ); private: void initialize(); @@ -171,10 +181,9 @@ private: typedef struct { QString myName; - QMap myIds; - QPixmap myIcon; - Qtx::Appropriate myAppropriate; - + QMap myIds; + QPixmap myIcon; + Qtx::Appropriate myAppropriate; } ColumnInfo; SUIT_DataObject* myRoot; @@ -182,6 +191,7 @@ private: ItemMap myItems; bool myAutoDeleteTree; bool myAutoUpdate; + bool myDropAccepted; QVector myColumns; friend class SUIT_TreeModel::TreeSync; @@ -222,6 +232,9 @@ public: QAbstractItemDelegate* delegate() const; + bool getObjects( const QMimeData* data, QList& ) const; + void setDropAccepted( const bool ); + public slots: virtual void updateTree( const QModelIndex& ); virtual void updateTree( SUIT_DataObject* = 0 ); @@ -229,6 +242,7 @@ public slots: signals: void modelUpdated(); + void drop( const QList& , SUIT_DataObject* ); protected: SUIT_AbstractModel* treeModel() const; -- 2.39.2