From: Clément Stenac Date: Sat, 14 Oct 2006 16:44:55 +0000 (+0000) Subject: Initial drag and drop support for Qt X-Git-Tag: 0.9.0-test0~9968 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=ac471f21023adf4d3784ab3856b9a6040689d65c;p=vlc Initial drag and drop support for Qt --- diff --git a/modules/gui/qt4/components/playlist/selector.cpp b/modules/gui/qt4/components/playlist/selector.cpp index 347e7c7b47..ea0f7d21e9 100644 --- a/modules/gui/qt4/components/playlist/selector.cpp +++ b/modules/gui/qt4/components/playlist/selector.cpp @@ -38,6 +38,10 @@ PLSelector::PLSelector( QWidget *p, intf_thread_t *_p_intf, view->header()->hide(); view->setModel( model ); + view->setDragEnabled(true); + view->setAcceptDrops(true); + view->setDropIndicatorShown(true); + CONNECT( view, activated( const QModelIndex& ), this, setSource( const QModelIndex& ) ); CONNECT( view, clicked( const QModelIndex& ), diff --git a/modules/gui/qt4/components/playlist/standardpanel.cpp b/modules/gui/qt4/components/playlist/standardpanel.cpp index 74f6b48933..57b4b69311 100644 --- a/modules/gui/qt4/components/playlist/standardpanel.cpp +++ b/modules/gui/qt4/components/playlist/standardpanel.cpp @@ -56,7 +56,11 @@ StandardPLPanel::StandardPLPanel( BasePlaylistWidget *_parent, view->header()->resizeSection( 2, 60 ); view->header()->setSortIndicatorShown( true ); view->header()->setClickable( true ); + view->setSelectionMode( QAbstractItemView::ExtendedSelection ); + view->setDragEnabled(true); + view->setAcceptDrops(true); + view->setDropIndicatorShown(true); CONNECT( view, activated( const QModelIndex& ) , model,activateItem( const QModelIndex& ) ); diff --git a/modules/gui/qt4/dialogs/playlist.cpp b/modules/gui/qt4/dialogs/playlist.cpp index 0cbc2bea7d..47457438aa 100644 --- a/modules/gui/qt4/dialogs/playlist.cpp +++ b/modules/gui/qt4/dialogs/playlist.cpp @@ -30,6 +30,7 @@ #include "dialogs_provider.hpp" #include "menus.hpp" +#include #include #include #include @@ -75,3 +76,31 @@ void PlaylistDialog::dock() QEvent *event = new QEvent( (QEvent::Type)(PLDockEvent_Type) ); QApplication::postEvent( p_intf->p_sys->p_mi, event ); } + + +void PlaylistDialog::dropEvent(QDropEvent *event) +{ + const QMimeData *mimeData = event->mimeData(); + foreach( QUrl url, mimeData->urls() ) { + QString s = url.toString(); + if( s.length() > 0 ) { + playlist_PlaylistAdd( THEPL, qtu(s), NULL, + PLAYLIST_APPEND, PLAYLIST_END ); + } + } + event->acceptProposedAction(); +} +void PlaylistDialog::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} +void PlaylistDialog::dragMoveEvent(QDragMoveEvent *event) +{ + event->acceptProposedAction(); +} +void PlaylistDialog::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); +} + + diff --git a/modules/gui/qt4/dialogs/playlist.hpp b/modules/gui/qt4/dialogs/playlist.hpp index c2ef45a297..ab84f503dd 100644 --- a/modules/gui/qt4/dialogs/playlist.hpp +++ b/modules/gui/qt4/dialogs/playlist.hpp @@ -49,6 +49,12 @@ private: void createPlMenuBar( QMenuBar *bar, intf_thread_t *p_intf ); PlaylistDialog( intf_thread_t * ); + + void dropEvent( QDropEvent *); + void dragEnterEvent( QDragEnterEvent * ); + void dragMoveEvent( QDragMoveEvent * ); + void dragLeaveEvent( QDragLeaveEvent * ); + static PlaylistDialog *instance; PLSelector *selector; diff --git a/modules/gui/qt4/main_interface.cpp b/modules/gui/qt4/main_interface.cpp index 1688a22961..341170aca2 100644 --- a/modules/gui/qt4/main_interface.cpp +++ b/modules/gui/qt4/main_interface.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,8 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf ) settings = new QSettings( "VideoLAN", "VLC" ); settings->beginGroup( "MainWindow" ); + setAcceptDrops(true); + need_components_update = false; bgWidget = NULL; videoWidget = NULL; playlistWidget = NULL; embeddedPlaylistWasActive = videoIsActive = false; @@ -137,6 +140,51 @@ MainInterface::MainInterface( intf_thread_t *_p_intf ) : QVLCMW( _p_intf ) p_intf->b_interaction = VLC_TRUE; } +void MainInterface::dropEvent(QDropEvent *event) +{ + const QMimeData *mimeData = event->mimeData(); + + /* D&D of a subtitles file, add it on the fly */ + if( mimeData->urls().size() == 1 ) + { + if( THEMIM->getIM()->hasInput() ) + { + if( input_AddSubtitles( THEMIM->getInput(), + qtu( mimeData->urls()[0].toString() ), + VLC_TRUE ) ) + { + event->acceptProposedAction(); + return; + } + } + } + bool first = true; + foreach( QUrl url, mimeData->urls() ) { + QString s = url.toString(); + if( s.length() > 0 ) { + playlist_PlaylistAdd( THEPL, qtu(s), NULL, + PLAYLIST_APPEND | (first ? PLAYLIST_GO:0), + PLAYLIST_END ); + first = false; + } + } + event->acceptProposedAction(); +} +void MainInterface::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} +void MainInterface::dragMoveEvent(QDragMoveEvent *event) +{ + event->acceptProposedAction(); +} +void MainInterface::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); +} + + + MainInterface::~MainInterface() { settings->setValue( "playlist-embedded", playlistEmbeddedFlag ); diff --git a/modules/gui/qt4/main_interface.hpp b/modules/gui/qt4/main_interface.hpp index 2905c14e1a..ed4b8d07f0 100644 --- a/modules/gui/qt4/main_interface.hpp +++ b/modules/gui/qt4/main_interface.hpp @@ -57,6 +57,10 @@ public: int controlVideo( void *p_window, int i_query, va_list args ); protected: void resizeEvent( QResizeEvent * ); + void dropEvent( QDropEvent *); + void dragEnterEvent( QDragEnterEvent * ); + void dragMoveEvent( QDragMoveEvent * ); + void dragLeaveEvent( QDragLeaveEvent * ); void closeEvent( QCloseEvent *); Ui::MainInterfaceUI ui; friend class VolumeClickHandler; diff --git a/modules/gui/qt4/playlist_model.cpp b/modules/gui/qt4/playlist_model.cpp index 2cfacba81f..be360f5bc2 100644 --- a/modules/gui/qt4/playlist_model.cpp +++ b/modules/gui/qt4/playlist_model.cpp @@ -20,6 +20,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ +#define PLI_NAME( p ) p ? p->p_input->psz_name : "null" #include #include @@ -98,7 +99,7 @@ void PLItem::insertChild( PLItem *item, int i_pos, bool signal ) assert( model ); if( signal ) model->beginInsertRows( model->index( this , 0 ), i_pos, i_pos ); - children.append( item ); + children.insert( i_pos, item ); if( signal ) model->endInsertRows(); } @@ -181,6 +182,115 @@ PLModel::~PLModel() delete rootItem; } +Qt::DropActions PLModel::supportedDropActions() const +{ + return Qt::CopyAction; +} + +Qt::ItemFlags PLModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); + if( index.isValid() ) + return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; + else + return Qt::ItemIsDropEnabled | defaultFlags; +} + +QStringList PLModel::mimeTypes() const +{ + QStringList types; + types << "vlc/playlist-item-id"; + return types; +} + +QMimeData *PLModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + foreach (QModelIndex index, indexes) { + if (index.isValid() && index.column() == 0 ) + stream << itemId(index); + } + mimeData->setData("vlc/playlist-item-id", encodedData); + return mimeData; +} + +bool PLModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &target) +{ + if ( data->hasFormat("vlc/playlist-item-id") ) + { + if (action == Qt::IgnoreAction) + return true; + + PLItem *targetItem; + if( target.isValid() ) + targetItem = static_cast( target.internalPointer() ); + else + targetItem = rootItem; + + QByteArray encodedData = data->data("vlc/playlist-item-id"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + + PLItem *newParentItem; + while (!stream.atEnd()) + { + int i; + int srcId; + stream >> srcId; + + PL_LOCK; + playlist_item_t *p_target = + playlist_ItemGetById( p_playlist, targetItem->i_id ); + playlist_item_t *p_src = playlist_ItemGetById( p_playlist, srcId ); + + if( !p_target || !p_src ) + { + PL_UNLOCK; + return false; + } + + if( p_target->i_children == -1 ) /* A leaf */ + { + PLItem *parentItem = targetItem->parent(); + assert( parentItem ); + playlist_item_t *p_parent = + playlist_ItemGetById( p_playlist, parentItem->i_id ); + if( !p_parent ) + { + PL_UNLOCK; + return false; + } + for( i = 0 ; i< p_parent->i_children ; i++ ) + if( p_parent->pp_children[i] == p_target ) break; + playlist_TreeMove( p_playlist, p_src, p_parent, i ); + newParentItem = parentItem; + } + else + { + /* \todo: if we drop on a top-level node, use copy instead ? */ + playlist_TreeMove( p_playlist, p_src, p_target, 0 ); + i = 0; + newParentItem = targetItem; + } + /* Remove from source */ + PLItem *srcItem = FindByInput( rootItem, p_src->p_input->i_id ); + srcItem->remove( srcItem ); + /* Display at new destination */ + PLItem *newItem = new PLItem( p_src, newParentItem, this ); + newParentItem->insertChild( newItem, i, true ); + UpdateTreeItem( p_src, newItem, true ); + if( p_src->i_children != -1 ) + UpdateNodeChildren( newItem ); + PL_UNLOCK; + } + } + return true; + } + + void PLModel::addCallbacks() { /* Some global changes happened -> Rebuild all */ @@ -229,7 +339,7 @@ void PLModel::activateItem( playlist_item_t *p_item ) /****************** Base model mandatory implementations *****************/ QVariant PLModel::data(const QModelIndex &index, int role) const { - assert( index.isValid() ); + if(!index.isValid() ) return QVariant(); PLItem *item = static_cast(index.internalPointer()); if( role == Qt::DisplayRole ) { @@ -262,12 +372,6 @@ int PLModel::itemId( const QModelIndex &index ) const return static_cast(index.internalPointer())->i_id; } -Qt::ItemFlags PLModel::flags(const QModelIndex &index) const -{ - if( !index.isValid() ) return Qt::ItemIsEnabled; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - QVariant PLModel::headerData( int section, Qt::Orientation orientation, int role) const { @@ -449,7 +553,7 @@ void PLModel::customEvent( QEvent *event ) { int type = event->type(); if( type != ItemUpdate_Type && type != ItemAppend_Type && - type != ItemDelete_Type ) + type != ItemDelete_Type && type != PLUpdate_Type ) return; PLEvent *ple = static_cast(event); @@ -458,8 +562,10 @@ void PLModel::customEvent( QEvent *event ) ProcessInputItemUpdate( ple->i_id ); else if( type == ItemAppend_Type ) ProcessItemAppend( ple->p_add ); - else + else if( type == ItemDelete_Type ) ProcessItemRemoval( ple->i_id ); + else + rebuild(); } /**** Events processing ****/ @@ -669,6 +775,7 @@ void PLModel::sort( int column, Qt::SortOrder order ) case 0: i_mode = SORT_TITLE_NODES_FIRST;break; case 1: i_mode = SORT_ARTIST;break; case 2: i_mode = SORT_DURATION; break; + default: i_mode = SORT_TITLE_NODES_FIRST; break; } if( p_root ) playlist_RecursiveNodeSort( p_playlist, p_root, i_mode, @@ -756,7 +863,8 @@ static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable, vlc_value_t oval, vlc_value_t nval, void *param ) { PLModel *p_model = (PLModel *) param; -// p_model->b_need_update = VLC_TRUE; + PLEvent *event = new PLEvent( PLUpdate_Type, 0 ); + QApplication::postEvent( p_model, static_cast(event) ); return VLC_SUCCESS; } diff --git a/modules/gui/qt4/playlist_model.hpp b/modules/gui/qt4/playlist_model.hpp index d7cbf652cc..68a7340e3a 100644 --- a/modules/gui/qt4/playlist_model.hpp +++ b/modules/gui/qt4/playlist_model.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,7 @@ private: static int ItemUpdate_Type = QEvent::User + 2; static int ItemDelete_Type = QEvent::User + 3; static int ItemAppend_Type = QEvent::User + 4; +static int PLUpdate_Type = QEvent::User + 5; class PLEvent : public QEvent { @@ -122,6 +124,13 @@ public: void search( QString search ); void sort( int column, Qt::SortOrder order ); + /* DnD handling */ + Qt::DropActions supportedDropActions() const; + QMimeData* mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &target); + QStringList mimeTypes() const; + void sendArt( QString url ); private: void addCallbacks(); diff --git a/src/playlist/item.c b/src/playlist/item.c index ee600bf5dd..96f4b1481b 100644 --- a/src/playlist/item.c +++ b/src/playlist/item.c @@ -482,6 +482,7 @@ int playlist_TreeMove( playlist_t * p_playlist, playlist_item_t *p_item, /* Attach to new parent */ INSERT_ELEM( p_node->pp_children, p_node->i_children, i_newpos, p_item ); + p_item->p_parent = p_node; return VLC_SUCCESS; } diff --git a/src/playlist/tree.c b/src/playlist/tree.c index 95efa4ed55..39ef9e1e71 100644 --- a/src/playlist/tree.c +++ b/src/playlist/tree.c @@ -430,6 +430,7 @@ playlist_item_t *playlist_GetNextLeaf( playlist_t *p_playlist, { vlc_bool_t b_ena_ok = VLC_TRUE, b_unplayed_ok = VLC_TRUE; p_next = GetNextItem( p_playlist, p_root, p_next ); + PL_DEBUG( "Got next item %s, testing suitability", PLI_NAME(p_next) ); if( !p_next || p_next == p_root ) break; if( p_next->i_children == -1 ) @@ -520,7 +521,8 @@ playlist_item_t *GetNextItem( playlist_t *p_playlist, p_parent = p_item->p_parent; else p_parent = p_root; - + PL_DEBUG( "Parent %s has %i children", PLI_NAME(p_parent), + p_parent->i_children ); for( i= 0 ; i < p_parent->i_children ; i++ ) { if( p_item == NULL || p_parent->pp_children[i] == p_item )