X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Fplaylist%2Fplaylist_model.cpp;h=ca718aeafaee94e92871145562310ec83c0f3a8c;hb=50c12eaee379b1b1722d82eb59587fd6cea7a9c0;hp=455291888b78f5dc2079784380d2c7efcac67e91;hpb=cdd26d2f59ab41c5e6cd6bcbb124f3582e376abb;p=vlc diff --git a/modules/gui/qt4/components/playlist/playlist_model.cpp b/modules/gui/qt4/components/playlist/playlist_model.cpp index 455291888b..ca718aeafa 100644 --- a/modules/gui/qt4/components/playlist/playlist_model.cpp +++ b/modules/gui/qt4/components/playlist/playlist_model.cpp @@ -42,23 +42,28 @@ #include #include #include +#include +#include +#include +#include #include "sorting.h" +#define I_NEW_DIR \ + I_DIR_OR_FOLDER( N_("Create Directory"), N_( "Create Folder" ) ) +#define I_NEW_DIR_NAME \ + I_DIR_OR_FOLDER( N_( "Enter name for new directory:" ), \ + N_( "Enter name for new folder:" ) ) + QIcon PLModel::icons[ITEM_TYPE_NUMBER]; /************************************************************************* * Playlist model implementation *************************************************************************/ -/* - This model is called two times, for the selector and the standard panel -*/ PLModel::PLModel( playlist_t *_p_playlist, /* THEPL */ intf_thread_t *_p_intf, /* main Qt p_intf */ playlist_item_t * p_root, - /*playlist_GetPreferredNode( THEPL, THEPL->p_local_category ); - and THEPL->p_root_category for SelectPL */ QObject *parent ) /* Basic Qt parent */ : QAbstractItemModel( parent ) { @@ -67,7 +72,7 @@ PLModel::PLModel( playlist_t *_p_playlist, /* THEPL */ i_cached_id = -1; i_cached_input_id = -1; i_popup_item = i_popup_parent = -1; - currentItem = NULL; + sortingMenu = NULL; rootItem = NULL; /* PLItem rootItem, will be set in rebuild( ) */ @@ -84,11 +89,11 @@ PLModel::PLModel( playlist_t *_p_playlist, /* THEPL */ ADD_ICON( NODE, ":/type/node" ); #undef ADD_ICON - rebuild( p_root, true ); - CONNECT( THEMIM->getIM(), metaChanged( input_item_t *), - this, processInputItemUpdate( input_item_t *) ); - CONNECT( THEMIM, inputChanged( input_thread_t * ), - this, processInputItemUpdate( input_thread_t* ) ); + rebuild( p_root ); + DCONNECT( THEMIM->getIM(), metaChanged( input_item_t *), + this, processInputItemUpdate( input_item_t *) ); + DCONNECT( THEMIM, inputChanged( input_thread_t * ), + this, processInputItemUpdate( input_thread_t* ) ); CONNECT( THEMIM, playlistItemAppended( int, int ), this, processItemAppend( int, int ) ); CONNECT( THEMIM, playlistItemRemoved( int ), @@ -98,18 +103,19 @@ PLModel::PLModel( playlist_t *_p_playlist, /* THEPL */ PLModel::~PLModel() { delete rootItem; + delete sortingMenu; } 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() ) { @@ -131,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 ) { @@ -147,137 +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; + bool copy = action == Qt::CopyAction; + if( !copy && action != Qt::MoveAction ) + 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_local_category; - playlist_item_t *p_ml = p_playlist->p_ml_category; - if - ( - row == -1 && ( - ( p_pl && p_parent->p_input == p_pl->p_input ) || - ( p_ml && p_parent->p_input == p_ml->p_input ) ) - ) - copy = true; - PL_UNLOCK; - - 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_local_category || - p_parent == p_playlist->p_local_onelevel ), - 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 */ @@ -290,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 ); @@ -314,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() ); @@ -341,18 +352,55 @@ 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 ) ) + { + return QVariant( QBrush( Qt::gray ) ); + } + else if( role == IsCurrentRole ) return QVariant( isCurrent( index ) ); + else if( role == IsLeafNodeRole ) + { + QVariant isLeaf; + PL_LOCK; + playlist_item_t *plItem = + playlist_ItemGetById( p_playlist, item->i_id ); + + if( plItem ) + isLeaf = plItem->i_children == -1; + + 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 { - if( !currentItem ) return false; - return getItem( index )->p_input == currentItem->p_input; + return getItem( index )->p_input == THEMIM->currentInputItem(); } int PLModel::itemId( const QModelIndex &index ) const @@ -373,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; @@ -385,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 ); } @@ -401,6 +449,14 @@ QModelIndex PLModel::index( PLItem *item, int column ) const return QModelIndex(); } +QModelIndex PLModel::currentIndex() const +{ + input_thread_t *p_input_thread = THEMIM->getInput(); + if( !p_input_thread ) return QModelIndex(); + PLItem *item = findByInput( rootItem, input_GetItem( p_input_thread )->i_id ); + return index( item, 0 ); +} + QModelIndex PLModel::parent( const QModelIndex &index ) const { if( !index.isValid() ) return QModelIndex(); @@ -420,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 @@ -431,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(); } @@ -440,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; @@ -463,73 +518,48 @@ 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 ) const +int PLModel::columnToMeta( int _column ) { int meta = 1; int column = 0; @@ -543,7 +573,7 @@ int PLModel::columnToMeta( int _column ) const return meta; } -int PLModel::columnFromMeta( int meta_col ) const +int PLModel::columnFromMeta( int meta_col ) { int meta = 1; int column = 0; @@ -562,10 +592,10 @@ bool PLModel::canEdit() const return ( rootItem != NULL && ( - rootItem->p_input == p_playlist->p_local_category->p_input || + rootItem->p_input == p_playlist->p_playing->p_input || ( - p_playlist->p_ml_category && - rootItem->p_input == p_playlist->p_ml_category->p_input + p_playlist->p_media_library && + rootItem->p_input == p_playlist->p_media_library->p_input ) ) ); @@ -576,18 +606,14 @@ bool PLModel::canEdit() const void PLModel::processInputItemUpdate( input_thread_t *p_input ) { if( !p_input ) return; - processInputItemUpdate( input_GetItem( p_input ) ); if( p_input && !( p_input->b_dead || !vlc_object_alive( p_input ) ) ) { PLItem *item = findByInput( rootItem, input_GetItem( p_input )->i_id ); - currentItem = item; - emit currentChanged( index( item, 0 ) ); - } - else - { - currentItem = NULL; + if( item ) emit currentChanged( index( item, 0 ) ); } + processInputItemUpdate( input_GetItem( p_input ) ); } + void PLModel::processInputItemUpdate( input_item_t *p_item ) { if( !p_item || p_item->i_id <= 0 ) return; @@ -606,37 +632,43 @@ void PLModel::processItemAppend( int i_item, int i_parent ) { playlist_item_t *p_item = NULL; PLItem *newItem = NULL; + input_thread_t *currentInputThread; + int pos; 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; p_item = playlist_ItemGetById( p_playlist, i_item ); - if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end; + if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) + { + PL_UNLOCK; return; + } + + for( pos = 0; pos < p_item->p_parent->i_children; pos++ ) + if( p_item->p_parent->pp_children[pos] == p_item ) break; newItem = new PLItem( p_item, nodeItem ); PL_UNLOCK; - beginInsertRows( index( nodeItem, 0 ), nodeItem->childCount(), nodeItem->childCount() ); - nodeItem->appendChild( newItem ); + beginInsertRows( index( nodeItem, 0 ), pos, pos ); + nodeItem->insertChild( newItem, pos ); endInsertRows(); - updateTreeItem( newItem ); - return; -end: - PL_UNLOCK; - return; + + if( newItem->p_input == THEMIM->currentInputItem() ) + emit currentChanged( index( newItem, 0 ) ); } void PLModel::rebuild() { - rebuild( NULL, false ); + rebuild( NULL ); } -void PLModel::rebuild( playlist_item_t *p_root, bool b_first ) +void PLModel::rebuild( playlist_item_t *p_root ) { playlist_item_t* p_item; @@ -658,6 +690,8 @@ void PLModel::rebuild( playlist_item_t *p_root, bool b_first ) /* And signal the view */ reset(); + + if( p_root ) emit rootChanged(); } void PLModel::takeItem( PLItem *item ) @@ -690,18 +724,9 @@ 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( currentItem == item || rootItem == item) - { - currentItem = NULL; - emit currentChanged( QModelIndex() ); - } - - if(item == rootItem) - rootItem = NULL; - if( item->parentItem ) { int i = item->parentItem->children.indexOf( item ); beginRemoveRows( index( item->parentItem, 0), i, i ); @@ -711,30 +736,28 @@ void PLModel::removeItem( PLItem *item ) } else delete item; + if(item == rootItem) + { + rootItem = NULL; + rebuild( p_playlist->p_playing ); + } } /* This function must be entered WITH the playlist lock */ void PLModel::updateChildren( PLItem *root ) { playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id ); - currentItem = NULL; updateChildren( p_node, root ); - emit currentChanged( index( currentItem, 0 ) ); } /* This function must be entered WITH the playlist lock */ void PLModel::updateChildren( playlist_item_t *p_node, PLItem *root ) { - playlist_item_t *p_item = playlist_CurrentPlayingItem(p_playlist); for( int i = 0; i < p_node->i_children ; i++ ) { if( p_node->pp_children[i]->i_flags & PLAYLIST_DBL_FLAG ) continue; PLItem *newItem = new PLItem( p_node->pp_children[i], root ); root->appendChild( newItem ); - if( p_item && newItem->p_input == p_item->p_input ) - { - currentItem = newItem; - } if( p_node->pp_children[i]->i_children != -1 ) updateChildren( p_node->pp_children[i], newItem ); } @@ -750,27 +773,29 @@ 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 ) { if( !canEdit() ) return; - for( int i = selected.size() -1 ; i >= 0; i-- ) + while( !selected.isEmpty() ) { - QModelIndex index = selected[i]; + QModelIndex index = selected[0]; + selected.removeAt( 0 ); + if( index.column() != 0 ) continue; + PLItem *item = getItem( index ); - if( item ) - { - if( item->children.size() ) - recurseDelete( item->children, &selected ); - doDeleteItem( item, &selected ); - } - if( i > selected.size() ) i = selected.size(); + if( item->children.size() ) + recurseDelete( item->children, &selected ); + + PL_LOCK; + playlist_DeleteFromInput( p_playlist, item->p_input, pl_Locked ); + PL_UNLOCK; + + removeItem( item ); } } @@ -781,39 +806,17 @@ void PLModel::recurseDelete( QList children, QModelIndexList *fullList PLItem *item = children[i]; if( item->children.size() ) recurseDelete( item->children, fullList ); - doDeleteItem( item, fullList ); + fullList->removeAll( index( item, 0 ) ); } } -void PLModel::doDeleteItem( PLItem *item, QModelIndexList *fullList ) -{ - QModelIndex deleteIndex = index( item, 0 ); - fullList->removeAll( deleteIndex ); - - PL_LOCK; - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); - if( !p_item ) - { - PL_UNLOCK; - return; - } - if( p_item->i_children == -1 ) - playlist_DeleteFromInput( p_playlist, p_item->p_input, pl_Locked ); - else - playlist_NodeDelete( p_playlist, p_item, true, false ); - PL_UNLOCK; - - /* And finally, remove it from the tree */ - removeItem( item ); -} - /******* 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 ); @@ -853,25 +856,50 @@ 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 ) +void PLModel::search( const QString& search_text, const QModelIndex & idx, bool b_recursive ) { /** \todo Fire the search with a small delay ? */ PL_LOCK; { playlist_item_t *p_root = playlist_ItemGetById( p_playlist, - rootItem->i_id ); + itemId( idx ) ); assert( p_root ); - const char *psz_name = search_text.toUtf8().data(); - playlist_LiveSearchUpdate( p_playlist , p_root, psz_name ); + const char *psz_name = qtu( search_text ); + playlist_LiveSearchUpdate( p_playlist , p_root, psz_name, b_recursive ); + + if( idx.isValid() ) + { + PLItem *searchRoot = getItem( idx ); + + beginRemoveRows( idx, 0, searchRoot->children.size() - 1 ); + searchRoot->removeChildren(); + endRemoveRows( ); + + beginInsertRows( idx, 0, searchRoot->children.size() - 1 ); + updateChildren( searchRoot ); + endInsertRows(); + + PL_UNLOCK; + return; + } } PL_UNLOCK; rebuild(); } /*********** Popup *********/ -void PLModel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list ) +bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list ) { int i_id = index.isValid() ? itemId( index ) : rootItem->i_id; @@ -879,21 +907,22 @@ void PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); if( !p_item ) { - PL_UNLOCK; return; + PL_UNLOCK; + 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 ) : - ( p_item->i_id ); + ( rootItem->i_id ); i_popup_column = index.column(); - /* check whether we are in tree view */ - bool tree = false; - playlist_item_t *p_up = p_item; - while( p_up ) - { - if ( p_up == p_playlist->p_root_category ) tree = true; - p_up = p_up->p_parent; - } + + bool tree = ( rootItem && rootItem->i_id != p_playlist->p_playing->i_id ) || + var_InheritBool( p_intf, "playlist-tree" ); + PL_UNLOCK; current_selection = list; @@ -901,29 +930,67 @@ void PLModel::popup( const QModelIndex & index, const QPoint &point, const QMode QMenu menu; if( i_popup_item > -1 ) { - menu.addAction( qtr(I_POP_PLAY), this, SLOT( popupPlay() ) ); - menu.addAction( qtr(I_POP_DEL), this, SLOT( popupDel() ) ); - menu.addSeparator(); - menu.addAction( qtr(I_POP_STREAM), this, SLOT( popupStream() ) ); + menu.addAction( QIcon( ":/menu/play" ), qtr(I_POP_PLAY), this, SLOT( popupPlay() ) ); + menu.addAction( QIcon( ":/menu/stream" ), + qtr(I_POP_STREAM), this, SLOT( popupStream() ) ); menu.addAction( qtr(I_POP_SAVE), this, SLOT( popupSave() ) ); + menu.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO), this, SLOT( popupInfo() ) ); + if( !strncasecmp( p_input->psz_uri, "file://", 7 ) ) + menu.addAction( QIcon( ":/type/folder-grey" ), + qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); menu.addSeparator(); - menu.addAction( qtr(I_POP_INFO), this, SLOT( popupInfo() ) ); + } + if( canEdit() ) + { + QIcon addIcon( ":/buttons/playlist/playlist_add" ); menu.addSeparator(); - QMenu *sort_menu = menu.addMenu( qtr( "Sort by ") + - qfu( psz_column_title( columnToMeta( index.column() ) ) ) ); - sort_menu->addAction( qtr( "Ascending" ), - this, SLOT( popupSortAsc() ) ); - sort_menu->addAction( qtr( "Descending" ), - this, SLOT( popupSortDesc() ) ); + if( tree ) menu.addAction( addIcon, qtr(I_POP_NEWFOLDER), this, SLOT( popupAddNode() ) ); + if( rootItem->i_id == THEPL->p_playing->i_id ) + { + menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simplePLAppendDialog()) ); + menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( PLAppendDir()) ); + menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( PLAppendDialog()) ); + } + else if( THEPL->p_media_library && + rootItem->i_id == THEPL->p_media_library->i_id ) + { + menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) ); + menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) ); + menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) ); + } } - if( tree && canEdit() ) - menu.addAction( qtr(I_POP_ADD), this, SLOT( popupAddNode() ) ); if( i_popup_item > -1 ) { + menu.addAction( QIcon( ":/buttons/playlist/playlist_remove" ), + qtr(I_POP_DEL), this, SLOT( popupDel() ) ); menu.addSeparator(); - menu.addAction( qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); + if( !sortingMenu ) + { + sortingMenu = new QMenu( qtr( "Sort by" ) ); + sortingMapper = new QSignalMapper( this ); + int i, j; + for( i = 1, j = 1; i < COLUMN_END; i <<= 1, j++ ) + { + if( i == COLUMN_NUMBER ) continue; + QMenu *m = sortingMenu->addMenu( qfu( psz_column_title( i ) ) ); + QAction *asc = m->addAction( qtr("Ascending") ); + QAction *desc = m->addAction( qtr("Descending") ); + sortingMapper->setMapping( asc, j ); + sortingMapper->setMapping( desc, -j ); + CONNECT( asc, triggered(), sortingMapper, map() ); + CONNECT( desc, triggered(), sortingMapper, map() ); + } + CONNECT( sortingMapper, mapped( int ), this, popupSort( int ) ); + } + menu.addMenu( sortingMenu ); + } + vlc_gc_decref( p_input ); + + if( !menu.isEmpty() ) + { + menu.exec( point ); return true; } - if( !menu.isEmpty() ) menu.exec( point ); + else return false; } void PLModel::popupDel() @@ -976,65 +1043,92 @@ void PLModel::popupSave() THEDP->streamingDialog( NULL, mrls[0] ); } -#include -#include -#include 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 ) || - !strncasecmp( psz_access, "file", 4 ) || - !strncasecmp( psz_access, "dire", 4 ) ) - { - QFileInfo info( qfu( psz_meta ) ); - QDesktopServices::openUrl( - QUrl::fromLocalFile( info.absolutePath() ) ); - } - free( psz_meta ); - } + 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 ) )) + { +#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; } -#include void PLModel::popupAddNode() { bool ok; QString name = QInputDialog::getText( PlaylistDialog::getInstance( p_intf ), - qtr( I_POP_ADD ), qtr( "Enter name for new node:" ), + 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; } -void PLModel::popupSortAsc() +void PLModel::popupSort( int column ) +{ + sort( i_popup_parent, + 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 { - sort( i_popup_parent, i_popup_column, Qt::AscendingOrder ); + return _inputItems; } -void PLModel::popupSortDesc() +QStringList PlMimeData::formats () const { - sort( i_popup_parent, i_popup_column, Qt::DescendingOrder ); + QStringList fmts; + fmts << "vlc/qt-input-items"; + return fmts; }