X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Fplaylist%2Fplaylist_model.cpp;h=ca718aeafaee94e92871145562310ec83c0f3a8c;hb=50c12eaee379b1b1722d82eb59587fd6cea7a9c0;hp=ed70f4bba2937ec3931ee68040348185a37eb18a;hpb=fcc3616f567ed1b6cc5113e49d7c4c307ba6cafc;p=vlc diff --git a/modules/gui/qt4/components/playlist/playlist_model.cpp b/modules/gui/qt4/components/playlist/playlist_model.cpp index ed70f4bba2..ca718aeafa 100644 --- a/modules/gui/qt4/components/playlist/playlist_model.cpp +++ b/modules/gui/qt4/components/playlist/playlist_model.cpp @@ -108,14 +108,14 @@ PLModel::~PLModel() Qt::DropActions PLModel::supportedDropActions() const { - return Qt::CopyAction; /* Why not Qt::MoveAction */ + return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags PLModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); - PLItem *item = index.isValid() ? getItem( index ) : rootItem; + const PLItem *item = index.isValid() ? getItem( index ) : rootItem; if( canEdit() ) { @@ -137,15 +137,22 @@ Qt::ItemFlags PLModel::flags( const QModelIndex &index ) const QStringList PLModel::mimeTypes() const { QStringList types; - types << "vlc/qt-playlist-item"; + types << "vlc/qt-input-items"; return types; } +bool modelIndexLessThen( const QModelIndex &i1, const QModelIndex &i2 ) +{ + if( !i1.isValid() || !i2.isValid() ) return false; + PLItem *item1 = static_cast( i1.internalPointer() ); + PLItem *item2 = static_cast( i2.internalPointer() ); + if( item1->parent() == item2->parent() ) return i1.row() < i2.row(); + else return *item1 < *item2; +} + QMimeData *PLModel::mimeData( const QModelIndexList &indexes ) const { - QMimeData *mimeData = new QMimeData(); - QByteArray encodedData; - QDataStream stream( &encodedData, QIODevice::WriteOnly ); + PlMimeData *plMimeData = new PlMimeData(); QModelIndexList list; foreach( const QModelIndex &index, indexes ) { @@ -153,136 +160,135 @@ QMimeData *PLModel::mimeData( const QModelIndexList &indexes ) const list.append(index); } - qSort(list); + qSort(list.begin(), list.end(), modelIndexLessThen); + PLItem *item = NULL; foreach( const QModelIndex &index, list ) { - PLItem *item = getItem( index ); - stream.writeRawData( (char*) &item, sizeof( PLItem* ) ); + if( item ) + { + PLItem *testee = getItem( index ); + while( testee->parent() ) + { + if( testee->parent() == item || + testee->parent() == item->parent() ) break; + testee = testee->parent(); + } + if( testee->parent() == item ) continue; + item = getItem( index ); + } + else + item = getItem( index ); + + plMimeData->appendItem( item->p_input ); } - mimeData->setData( "vlc/qt-playlist-item", encodedData ); - return mimeData; + + return plMimeData; } /* Drop operation */ bool PLModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { - if( data->hasFormat( "vlc/qt-playlist-item" ) ) - { - if( action == Qt::IgnoreAction ) - return true; - - PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; - - PL_LOCK; - playlist_item_t *p_parent = - playlist_ItemGetById( p_playlist, parentItem->i_id ); - if( !p_parent || p_parent->i_children == -1 ) - { - PL_UNLOCK; - return false; - } - - bool copy = false; - playlist_item_t *p_pl = p_playlist->p_playing; - playlist_item_t *p_ml = p_playlist->p_media_library; - if - ( - row == -1 && ( - ( p_pl && p_parent == p_pl ) || - ( p_ml && p_parent == p_ml ) ) - ) - copy = true; - PL_UNLOCK; + bool copy = action == Qt::CopyAction; + if( !copy && action != Qt::MoveAction ) + return true; - QByteArray encodedData = data->data( "vlc/qt-playlist-item" ); + const PlMimeData *plMimeData = qobject_cast( data ); + if( plMimeData ) + { if( copy ) - dropAppendCopy( encodedData, parentItem ); + dropAppendCopy( plMimeData, getItem( parent ), row ); else - dropMove( encodedData, parentItem, row ); + dropMove( plMimeData, getItem( parent ), row ); } return true; } -void PLModel::dropAppendCopy( QByteArray& data, PLItem *target ) +void PLModel::dropAppendCopy( const PlMimeData *plMimeData, PLItem *target, int pos ) { - QDataStream stream( &data, QIODevice::ReadOnly ); - PL_LOCK; + playlist_item_t *p_parent = - playlist_ItemGetById( p_playlist, target->i_id ); - while( !stream.atEnd() ) + playlist_ItemGetByInput( p_playlist, target->p_input ); + if( !p_parent ) return; + + if( pos == -1 ) pos = PLAYLIST_END; + + QList inputItems = plMimeData->inputItems(); + + foreach( input_item_t* p_input, inputItems ) { - PLItem *item; - stream.readRawData( (char*)&item, sizeof(PLItem*) ); - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); + playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input ); if( !p_item ) continue; - input_item_t *p_input = p_item->p_input; - playlist_AddExt ( p_playlist, - p_input->psz_uri, p_input->psz_name, - PLAYLIST_APPEND | PLAYLIST_SPREPARSE, PLAYLIST_END, - p_input->i_duration, - p_input->i_options, p_input->ppsz_options, p_input->optflagc, - p_parent == p_playlist->p_playing, - true ); + pos = playlist_NodeAddCopy( p_playlist, p_item, p_parent, pos ); } + PL_UNLOCK; } -void PLModel::dropMove( QByteArray& data, PLItem *target, int row ) +void PLModel::dropMove( const PlMimeData * plMimeData, PLItem *target, int row ) { - QDataStream stream( &data, QIODevice::ReadOnly ); + QList inputItems = plMimeData->inputItems(); QList model_items; - QList ids; - int new_pos = row == -1 ? target->children.size() : row; + playlist_item_t *pp_items[inputItems.size()]; + + PL_LOCK; + + playlist_item_t *p_parent = + playlist_ItemGetByInput( p_playlist, target->p_input ); + + if( !p_parent || row > p_parent->i_children ) + { + PL_UNLOCK; return; + } + + int new_pos = row == -1 ? p_parent->i_children : row; int model_pos = new_pos; - while( !stream.atEnd() ) + int i = 0; + + foreach( input_item_t *p_input, inputItems ) { - PLItem *item; - stream.readRawData( (char*)&item, sizeof(PLItem*) ); + playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input ); + if( !p_item ) continue; - /* better not try to move a node into itself: */ + PLItem *item = findByInput( rootItem, p_input->i_id ); + if( !item ) continue; + + /* Better not try to move a node into itself. + Abort the whole operation in that case, + because it is ambiguous. */ PLItem *climber = target; while( climber ) { - if( climber == item ) break; + if( climber == item ) + { + PL_UNLOCK; return; + } climber = climber->parentItem; } - if( climber ) continue; if( item->parentItem == target && - target->children.indexOf( item ) < model_pos ) + target->children.indexOf( item ) < new_pos ) model_pos--; - ids.append( item->i_id ); model_items.append( item ); - - takeItem( item ); + pp_items[i] = p_item; + i++; } - int count = ids.size(); - if( count ) + + if( model_items.isEmpty() ) { - playlist_item_t *pp_items[count]; + PL_UNLOCK; return; + } - PL_LOCK; - for( int i = 0; i < count; i++ ) - { - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, ids[i] ); - if( !p_item ) - { - PL_UNLOCK; - return; - } - pp_items[i] = p_item; - } - playlist_item_t *p_parent = - playlist_ItemGetById( p_playlist, target->i_id ); - playlist_TreeMoveMany( p_playlist, count, pp_items, p_parent, - new_pos ); - PL_UNLOCK; + playlist_TreeMoveMany( p_playlist, i, pp_items, p_parent, new_pos ); - insertChildren( target, model_items, model_pos ); - } + PL_UNLOCK; + + foreach( PLItem *item, model_items ) + takeItem( item ); + + insertChildren( target, model_items, model_pos ); } /* remove item with its id */ @@ -295,7 +301,7 @@ void PLModel::removeItem( int i_id ) void PLModel::activateItem( const QModelIndex &index ) { assert( index.isValid() ); - PLItem *item = getItem( index ); + const PLItem *item = getItem( index ); assert( item ); PL_LOCK; playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); @@ -319,10 +325,10 @@ void PLModel::activateItem( playlist_item_t *p_item ) } /****************** Base model mandatory implementations *****************/ -QVariant PLModel::data( const QModelIndex &index, int role ) const +QVariant PLModel::data( const QModelIndex &index, const int role ) const { if( !index.isValid() ) return QVariant(); - PLItem *item = getItem( index ); + const PLItem *item = getItem( index ); if( role == Qt::DisplayRole ) { int metadata = columnToMeta( index.column() ); @@ -346,10 +352,11 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const } else if( role == Qt::FontRole ) { + QFont f; + f.setPointSize( f.pointSize() - 1 ); if( isCurrent( index ) ) - { - QFont f; f.setBold( true ); return QVariant( f ); - } + f.setBold( true ); + return QVariant( f ); } else if( role == Qt::BackgroundRole && isCurrent( index ) ) { @@ -369,9 +376,28 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const PL_UNLOCK; return isLeaf; } + else if( role == IsCurrentsParentNodeRole ) + { + return QVariant( isParent( index, currentIndex() ) ); + } return QVariant(); } +/* Seek from current index toward the top and see if index is one of parent nodes */ +bool PLModel::isParent( const QModelIndex &index, const QModelIndex ¤t ) const +{ + if( !index.isValid() ) + return false; + + if( index == current ) + return true; + + if( !current.isValid() || !current.parent().isValid() ) + return false; + + return isParent( index, current.parent() ); +} + bool PLModel::isCurrent( const QModelIndex &index ) const { return getItem( index )->p_input == THEMIM->currentInputItem(); @@ -395,7 +421,7 @@ QVariant PLModel::headerData( int section, Qt::Orientation orientation, return QVariant( qfu( psz_column_title( meta_col ) ) ); } -QModelIndex PLModel::index( int row, int column, const QModelIndex &parent ) +QModelIndex PLModel::index( const int row, const int column, const QModelIndex &parent ) const { PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; @@ -407,7 +433,7 @@ QModelIndex PLModel::index( int row, int column, const QModelIndex &parent ) return QModelIndex(); } -QModelIndex PLModel::index( int i_id, int c ) +QModelIndex PLModel::index( const int i_id, const int c ) { return index( findById( rootItem, i_id ), c ); } @@ -423,7 +449,7 @@ QModelIndex PLModel::index( PLItem *item, int column ) const return QModelIndex(); } -QModelIndex PLModel::currentIndex() +QModelIndex PLModel::currentIndex() const { input_thread_t *p_input_thread = THEMIM->getInput(); if( !p_input_thread ) return QModelIndex(); @@ -450,8 +476,7 @@ QModelIndex PLModel::parent( const QModelIndex &index ) const msg_Err( p_playlist, "----- PLEASE REPORT THIS ------" ); return createIndex( 0, 0, parentItem ); } - QModelIndex ind = createIndex(parentItem->row(), 0, parentItem); - return ind; + return createIndex(parentItem->row(), 0, parentItem); } int PLModel::columnCount( const QModelIndex &i) const @@ -461,7 +486,7 @@ int PLModel::columnCount( const QModelIndex &i) const int PLModel::rowCount( const QModelIndex &parent ) const { - PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; + const PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; return parentItem->childCount(); } @@ -470,7 +495,7 @@ QStringList PLModel::selectedURIs() QStringList lst; for( int i = 0; i < current_selection.size(); i++ ) { - PLItem *item = getItem( current_selection[i] ); + const PLItem *item = getItem( current_selection[i] ); if( item ) { PL_LOCK; @@ -493,71 +518,46 @@ QStringList PLModel::selectedURIs() /************************* Lookups *****************************/ -PLItem *PLModel::findById( PLItem *root, int i_id ) +PLItem *PLModel::findById( PLItem *root, int i_id ) const { return findInner( root, i_id, false ); } -PLItem *PLModel::findByInput( PLItem *root, int i_id ) +PLItem *PLModel::findByInput( PLItem *root, int i_id ) const { PLItem *result = findInner( root, i_id, true ); return result; } -#define CACHE( i, p ) { i_cached_id = i; p_cached_item = p; } -#define ICACHE( i, p ) { i_cached_input_id = i; p_cached_item_bi = p; } - -PLItem * PLModel::findInner( PLItem *root, int i_id, bool b_input ) +PLItem * PLModel::findInner( PLItem *root, int i_id, bool b_input ) const { if( !root ) return NULL; - if( ( !b_input && i_cached_id == i_id) || - ( b_input && i_cached_input_id ==i_id ) ) - { - return b_input ? p_cached_item_bi : p_cached_item; - } if( !b_input && root->i_id == i_id ) - { - CACHE( i_id, root ); return root; - } + else if( b_input && root->p_input->i_id == i_id ) - { - ICACHE( i_id, root ); return root; - } QList::iterator it = root->children.begin(); while ( it != root->children.end() ) { if( !b_input && (*it)->i_id == i_id ) - { - CACHE( i_id, (*it) ); - return p_cached_item; - } + return (*it); + else if( b_input && (*it)->p_input->i_id == i_id ) - { - ICACHE( i_id, (*it) ); - return p_cached_item_bi; - } + return (*it); + if( (*it)->children.size() ) { PLItem *childFound = findInner( (*it), i_id, b_input ); if( childFound ) - { - if( b_input ) - ICACHE( i_id, childFound ) - else - CACHE( i_id, childFound ) - return childFound; - } + return childFound; } it++; } return NULL; } -#undef CACHE -#undef ICACHE int PLModel::columnToMeta( int _column ) { @@ -638,7 +638,7 @@ void PLModel::processItemAppend( int i_item, int i_parent ) PLItem *nodeItem = findById( rootItem, i_parent ); if( !nodeItem ) return; - foreach( PLItem *existing, nodeItem->children ) + foreach( const PLItem *existing, nodeItem->children ) if( existing->i_id == i_item ) return; PL_LOCK; @@ -724,7 +724,7 @@ void PLModel::removeItem( PLItem *item ) { if( !item ) return; - if( item->i_id == i_cached_id ) i_cached_id = -1; + i_cached_id = -1; i_cached_input_id = -1; if( item->parentItem ) { @@ -773,10 +773,8 @@ void PLModel::updateTreeItem( PLItem *item ) /************************* Actions ******************************/ /** - * Deletion, here we have to do a ugly slow hack as we retrieve the full - * list of indexes to delete at once: when we delete a node and all of - * its children, we need to update the list. - * Todo: investigate whethere we can use ranges to be sure to delete all items? + * Deletion, don't delete items childrens if item is going to be + * delete allready, so we remove childrens from selection-list. */ void PLModel::doDelete( QModelIndexList selected ) { @@ -813,12 +811,12 @@ void PLModel::recurseDelete( QList children, QModelIndexList *fullList } /******* Volume III: Sorting and searching ********/ -void PLModel::sort( int column, Qt::SortOrder order ) +void PLModel::sort( const int column, Qt::SortOrder order ) { sort( rootItem->i_id, column, order ); } -void PLModel::sort( int i_root_id, int column, Qt::SortOrder order ) +void PLModel::sort( const int i_root_id, const int column, Qt::SortOrder order ) { msg_Dbg( p_intf, "Sorting by column %i, order %i", column, order ); @@ -858,6 +856,15 @@ void PLModel::sort( int i_root_id, int column, Qt::SortOrder order ) endInsertRows( ); } PL_UNLOCK; + /* if we have popup item, try to make sure that you keep that item visible */ + if( i_popup_item > -1 ) + { + PLItem *popupitem = findById( rootItem, i_popup_item ); + if( popupitem ) emit currentChanged( index( popupitem, 0 ) ); + /* reset i_popup_item as we don't show it as selected anymore anyway */ + i_popup_item = -1; + } + else if( currentIndex().isValid() ) emit currentChanged( currentIndex() ); } void PLModel::search( const QString& search_text, const QModelIndex & idx, bool b_recursive ) @@ -904,6 +911,9 @@ bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode return false; } + input_item_t *p_input = p_item->p_input; + vlc_gc_incref( p_input ); + i_popup_item = index.isValid() ? p_item->i_id : -1; i_popup_parent = index.isValid() ? ( p_item->p_parent ? p_item->p_parent->i_id : -1 ) : @@ -921,16 +931,14 @@ bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode if( i_popup_item > -1 ) { menu.addAction( QIcon( ":/menu/play" ), qtr(I_POP_PLAY), this, SLOT( popupPlay() ) ); - menu.addAction( QIcon( ":/buttons/playlist/playlist_remove" ), - qtr(I_POP_DEL), this, SLOT( popupDel() ) ); - menu.addSeparator(); menu.addAction( QIcon( ":/menu/stream" ), qtr(I_POP_STREAM), this, SLOT( popupStream() ) ); menu.addAction( qtr(I_POP_SAVE), this, SLOT( popupSave() ) ); - menu.addSeparator(); menu.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO), this, SLOT( popupInfo() ) ); - menu.addAction( QIcon( ":/type/folder-grey" ), - qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); + if( !strncasecmp( p_input->psz_uri, "file://", 7 ) ) + menu.addAction( QIcon( ":/type/folder-grey" ), + qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); + menu.addSeparator(); } if( canEdit() ) { @@ -953,6 +961,8 @@ bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode } if( i_popup_item > -1 ) { + menu.addAction( QIcon( ":/buttons/playlist/playlist_remove" ), + qtr(I_POP_DEL), this, SLOT( popupDel() ) ); menu.addSeparator(); if( !sortingMenu ) { @@ -974,6 +984,8 @@ bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode } menu.addMenu( sortingMenu ); } + vlc_gc_decref( p_input ); + if( !menu.isEmpty() ) { menu.exec( point ); return true; @@ -1035,29 +1047,35 @@ void PLModel::popupExplore() { PL_LOCK; playlist_item_t *p_item = playlist_ItemGetById( p_playlist, - i_popup_item ); + i_popup_item ); if( p_item ) { - input_item_t *p_input = p_item->p_input; - char *psz_meta = input_item_GetURI( p_input ); - PL_UNLOCK; - if( psz_meta ) - { - const char *psz_access; - const char *psz_demux; - char *psz_path; - input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_meta ); - - if( !EMPTY_STR( psz_access ) && ( + input_item_t *p_input = p_item->p_input; + char *psz_meta = input_item_GetURI( p_input ); + PL_UNLOCK; + if( psz_meta ) + { + const char *psz_access; + const char *psz_demux; + char *psz_path; + input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_meta ); + + if( !EMPTY_STR( psz_access ) && ( !strncasecmp( psz_access, "file", 4 ) || !strncasecmp( psz_access, "dire", 4 ) )) - { - QFileInfo info( qfu( psz_path ) ); - QDesktopServices::openUrl( - QUrl::fromLocalFile( info.absolutePath() ) ); - } - free( psz_meta ); - } + { +#ifdef WIN32 + /* Qt openURL doesn't know to open files that starts with a / or \ */ + if( psz_path[0] == '/' || psz_path[0] == '\\' ) + psz_path++; +#endif + + QFileInfo info( qfu( decode_URI( psz_path ) ) ); + QDesktopServices::openUrl( + QUrl::fromLocalFile( info.absolutePath() ) ); + } + free( psz_meta ); + } } else PL_UNLOCK; @@ -1070,13 +1088,12 @@ void PLModel::popupAddNode() qtr( I_NEW_DIR ), qtr( I_NEW_DIR_NAME ), QLineEdit::Normal, QString(), &ok); if( !ok || name.isEmpty() ) return; + PL_LOCK; playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_popup_parent ); if( p_item ) - { - playlist_NodeCreate( p_playlist, qtu( name ), p_item, 0, NULL ); - } + playlist_NodeCreate( p_playlist, qtu( name ), p_item, PLAYLIST_END, 0, NULL ); PL_UNLOCK; } @@ -1086,3 +1103,32 @@ void PLModel::popupSort( int column ) column > 0 ? column - 1 : -column - 1, column > 0 ? Qt::AscendingOrder : Qt::DescendingOrder ); } + +/******************* Drag and Drop helper class ******************/ + +PlMimeData::PlMimeData( ) +{ } + +PlMimeData::~PlMimeData() +{ + foreach( input_item_t *p_item, _inputItems ) + vlc_gc_decref( p_item ); +} + +void PlMimeData::appendItem( input_item_t *p_item ) +{ + vlc_gc_incref( p_item ); + _inputItems.append( p_item ); +} + +QList PlMimeData::inputItems() const +{ + return _inputItems; +} + +QStringList PlMimeData::formats () const +{ + QStringList fmts; + fmts << "vlc/qt-input-items"; + return fmts; +}