X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Fplaylist%2Fplaylist_model.cpp;h=39f10343fe35effdd1942a5c1af3e5e48bf4049c;hb=544625f19e2bcff397f6b591759f486d0a325075;hp=e8b34822d1e1145f266fd78e55f924d8b4c8d220;hpb=2dc5dff316fa49fb48bd1f75a109c6d939d5a9f1;p=vlc diff --git a/modules/gui/qt4/components/playlist/playlist_model.cpp b/modules/gui/qt4/components/playlist/playlist_model.cpp index e8b34822d1..39f10343fe 100644 --- a/modules/gui/qt4/components/playlist/playlist_model.cpp +++ b/modules/gui/qt4/components/playlist/playlist_model.cpp @@ -6,6 +6,7 @@ * * Authors: Clément Stenac * Ilkka Ollakkka + * Jakob Leben * * 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 @@ -41,60 +42,41 @@ #include #include #include +#include +#include +#include +#include #include "sorting.h" -QIcon PLModel::icons[ITEM_TYPE_NUMBER]; +#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:" ) ) -static int PlaylistChanged( vlc_object_t *, const char *, - vlc_value_t, vlc_value_t, void * ); -static int PlaylistNext( vlc_object_t *, const char *, - vlc_value_t, vlc_value_t, void * ); -static int ItemAppended( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ); -static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ); +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 */ - int _i_depth, /* -1 for StandPL, 1 for SelectPL */ QObject *parent ) /* Basic Qt parent */ : QAbstractItemModel( parent ) { - i_depth = _i_depth; - assert( i_depth == DEPTH_SEL || i_depth == DEPTH_PL ); p_intf = _p_intf; p_playlist = _p_playlist; i_cached_id = -1; i_cached_input_id = -1; i_popup_item = i_popup_parent = -1; - currentItem = NULL; rootItem = NULL; /* PLItem rootItem, will be set in rebuild( ) */ - if( i_depth == DEPTH_SEL ) - i_showflags = 0; - else - { - i_showflags = getSettings()->value( "qt-pl-showflags", COLUMN_DEFAULT ).toInt(); - if( i_showflags < 1) - i_showflags = COLUMN_DEFAULT; /* reasonable default to show something */ - else if ( i_showflags >= COLUMN_END ) - i_showflags = COLUMN_END - 1; /* show everything */ - } - /* Icons initialization */ -#define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( QPixmap( x ) ) +#define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( x ) ADD_ICON( UNKNOWN , type_unknown_xpm ); ADD_ICON( FILE, ":/type/file" ); ADD_ICON( DIRECTORY, ":/type/directory" ); @@ -108,16 +90,17 @@ PLModel::PLModel( playlist_t *_p_playlist, /* THEPL */ rebuild( p_root ); CONNECT( THEMIM->getIM(), metaChanged( input_item_t *), - this, ProcessInputItemUpdate( input_item_t *) ); + this, processInputItemUpdate( input_item_t *) ); CONNECT( THEMIM, inputChanged( input_thread_t * ), - this, ProcessInputItemUpdate( input_thread_t* ) ); + this, processInputItemUpdate( input_thread_t* ) ); + CONNECT( THEMIM, playlistItemAppended( int, int ), + this, processItemAppend( int, int ) ); + CONNECT( THEMIM, playlistItemRemoved( int ), + this, processItemRemoval( int ) ); } PLModel::~PLModel() { - if(i_depth == -1) - getSettings()->setValue( "qt-pl-showflags", i_showflags ); - delCallbacks(); delete rootItem; } @@ -132,21 +115,7 @@ Qt::ItemFlags PLModel::flags( const QModelIndex &index ) const PLItem *item = index.isValid() ? getItem( index ) : rootItem; - input_item_t *pl_input = - p_playlist->p_local_category ? - p_playlist->p_local_category->p_input : NULL; - input_item_t *ml_input = - p_playlist->p_ml_category ? - p_playlist->p_ml_category->p_input : NULL; - - if( i_depth == DEPTH_SEL ) - { - if( ( pl_input && item->p_input == pl_input ) || - ( ml_input && item->p_input == ml_input ) ) - flags |= Qt::ItemIsDropEnabled; - } - else if( ( pl_input && rootItem->p_input == pl_input ) || - ( ml_input && rootItem->p_input == ml_input ) ) + if( canEdit() ) { PL_LOCK; playlist_item_t *plItem = @@ -163,11 +132,10 @@ Qt::ItemFlags PLModel::flags( const QModelIndex &index ) const return flags; } -/* A list of model indexes are a playlist */ QStringList PLModel::mimeTypes() const { QStringList types; - types << "vlc/playlist-item-id"; + types << "vlc/qt-playlist-item"; return types; } @@ -186,9 +154,10 @@ QMimeData *PLModel::mimeData( const QModelIndexList &indexes ) const qSort(list); foreach( const QModelIndex &index, list ) { - stream << itemId( index ); + PLItem *item = getItem( index ); + stream.writeRawData( (char*) &item, sizeof( PLItem* ) ); } - mimeData->setData( "vlc/playlist-item-id", encodedData ); + mimeData->setData( "vlc/qt-playlist-item", encodedData ); return mimeData; } @@ -196,28 +165,16 @@ QMimeData *PLModel::mimeData( const QModelIndexList &indexes ) const bool PLModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { - if( data->hasFormat( "vlc/playlist-item-id" ) ) + if( data->hasFormat( "vlc/qt-playlist-item" ) ) { if( action == Qt::IgnoreAction ) return true; - PL_LOCK; - - playlist_item_t *p_parent; - - if( !parent.isValid()) - { - if( row > -1) - p_parent = playlist_ItemGetById( p_playlist, rootItem->i_id ); - else - { - PL_UNLOCK; - return true; - } - } - else - p_parent = playlist_ItemGetById( p_playlist, itemId ( parent ) ); + 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; @@ -225,102 +182,112 @@ bool PLModel::dropMimeData( const QMimeData *data, Qt::DropAction action, } bool copy = false; - if( row == -1 && - ( p_parent->p_input == p_playlist->p_local_category->p_input - || p_parent->p_input == p_playlist->p_ml_category->p_input ) ) - copy = true; - - QByteArray encodedData = data->data( "vlc/playlist-item-id" ); - QDataStream stream( &encodedData, QIODevice::ReadOnly ); + 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; + QByteArray encodedData = data->data( "vlc/qt-playlist-item" ); if( copy ) + dropAppendCopy( encodedData, parentItem ); + else + dropMove( encodedData, parentItem, row ); + } + return true; +} + +void PLModel::dropAppendCopy( QByteArray& data, PLItem *target ) +{ + QDataStream stream( &data, QIODevice::ReadOnly ); + + PL_LOCK; + playlist_item_t *p_parent = + playlist_ItemGetById( p_playlist, target->i_id ); + while( !stream.atEnd() ) + { + PLItem *item; + stream.readRawData( (char*)&item, sizeof(PLItem*) ); + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); + 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 ); + } + PL_UNLOCK; +} + +void PLModel::dropMove( QByteArray& data, PLItem *target, int row ) +{ + QDataStream stream( &data, QIODevice::ReadOnly ); + QList model_items; + QList ids; + int new_pos = row == -1 ? target->children.size() : row; + int model_pos = new_pos; + while( !stream.atEnd() ) + { + PLItem *item; + stream.readRawData( (char*)&item, sizeof(PLItem*) ); + + /* better not try to move a node into itself: */ + PLItem *climber = target; + while( climber ) { - while( !stream.atEnd() ) - { - int i_id; - stream >> i_id; - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); - if( !p_item ) - { - PL_UNLOCK; - return false; - } - 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, true ); - } + if( climber == item ) break; + climber = climber->parentItem; } - else + if( climber ) continue; + + if( item->parentItem == target && + target->children.indexOf( item ) < model_pos ) + model_pos--; + + ids.append( item->i_id ); + model_items.append( item ); + + takeItem( item ); + } + int count = ids.size(); + if( count ) + { + playlist_item_t *pp_items[count]; + + PL_LOCK; + for( int i = 0; i < count; i++ ) { - QList items; - while( !stream.atEnd() ) - { - int id; - stream >> id; - playlist_item_t *item = playlist_ItemGetById( p_playlist, id ); - if( !item ) continue; - /* better not try to move a node into itself: */ - if( item->i_children > 0 ) - { - playlist_item_t *climber = p_parent; - while( climber ) - { - if( climber == item ) break; - climber = climber->p_parent; - } - if( climber ) continue; - } - items.append( item ); - } - int count = items.size(); - if( count ) + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, ids[i] ); + if( !p_item ) { - playlist_item_t *pp_items[count]; - for( int i = 0; i < count; i++ ) pp_items[i] = items[i]; - playlist_TreeMoveMany( p_playlist, count, pp_items, p_parent, - (row == -1 ? p_parent->i_children : row) ); + 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; - /*TODO: That's not a good idea to rebuild the playlist */ - rebuild(); + + insertChildren( target, model_items, model_pos ); } - return true; } /* remove item with its id */ void PLModel::removeItem( int i_id ) { - PLItem *item = FindById( rootItem, i_id ); - RemoveItem( item ); -} - -/* callbacks and slots */ -void PLModel::addCallbacks() -{ - /* Some global changes happened -> Rebuild all */ - var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this ); - /* We went to the next item - var_AddCallback( p_playlist, "item-current", PlaylistNext, this ); - */ - /* One item has been updated */ - var_AddCallback( p_playlist, "playlist-item-append", ItemAppended, this ); - var_AddCallback( p_playlist, "playlist-item-deleted", ItemDeleted, this ); -} - -void PLModel::delCallbacks() -{ - /* - var_DelCallback( p_playlist, "item-current", PlaylistNext, this ); - */ - var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this ); - var_DelCallback( p_playlist, "playlist-item-append", ItemAppended, this ); - var_DelCallback( p_playlist, "playlist-item-deleted", ItemDeleted, this ); + PLItem *item = findById( rootItem, i_id ); + removeItem( item ); } void PLModel::activateItem( const QModelIndex &index ) @@ -356,15 +323,7 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const PLItem *item = getItem( index ); if( role == Qt::DisplayRole ) { - if( i_depth == DEPTH_SEL ) - { - vlc_mutex_lock( &item->p_input->lock ); - QString returninfo = QString( qfu( item->p_input->psz_name ) ); - vlc_mutex_unlock( &item->p_input->lock ); - return QVariant(returninfo); - } - - int metadata = metaColumn( index.column() ); + int metadata = columnToMeta( index.column() ); if( metadata == COLUMN_END ) return QVariant(); QString returninfo; @@ -380,9 +339,8 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const } else if( role == Qt::DecorationRole && index.column() == 0 ) { - /* Use to segfault here because i_type wasn't always initialized */ - if( item->p_input->i_type >= 0 ) - return QVariant( PLModel::icons[item->p_input->i_type] ); + /* Used to segfault here because i_type wasn't always initialized */ + return QVariant( PLModel::icons[item->p_input->i_type] ); } else if( role == Qt::FontRole ) { @@ -391,13 +349,18 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const QFont 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 ) ); + return QVariant(); } 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 @@ -411,9 +374,7 @@ QVariant PLModel::headerData( int section, Qt::Orientation orientation, if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant(); - if( i_depth == DEPTH_SEL ) return QVariant( QString("") ); - - int meta_col = metaColumn( section ); + int meta_col = columnToMeta( section ); if( meta_col == COLUMN_END ) return QVariant(); @@ -432,6 +393,11 @@ QModelIndex PLModel::index( int row, int column, const QModelIndex &parent ) return QModelIndex(); } +QModelIndex PLModel::index( int i_id, int c ) +{ + return index( findById( rootItem, i_id ), c ); +} + /* Return the index of a given item */ QModelIndex PLModel::index( PLItem *item, int column ) const { @@ -443,6 +409,14 @@ QModelIndex PLModel::index( PLItem *item, int column ) const return QModelIndex(); } +QModelIndex PLModel::currentIndex() +{ + 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(); @@ -468,22 +442,7 @@ QModelIndex PLModel::parent( const QModelIndex &index ) const int PLModel::columnCount( const QModelIndex &i) const { - int columnCount=0; - int metadata=1; - if( i_depth == DEPTH_SEL ) return 1; - - while( metadata < COLUMN_END ) - { - if( metadata & i_showflags ) - columnCount++; - metadata <<= 1; - } - return columnCount; -} - -int PLModel::childrenCount( const QModelIndex &parent ) const -{ - return rowCount( parent ); + return columnFromMeta( COLUMN_END ); } int PLModel::rowCount( const QModelIndex &parent ) const @@ -507,7 +466,7 @@ QStringList PLModel::selectedURIs() char *psz = input_item_GetURI( p_item->p_input ); if( psz ) { - lst.append( psz ); + lst.append( qfu(psz) ); free( psz ); } } @@ -517,54 +476,26 @@ QStringList PLModel::selectedURIs() return lst; } -/************************* General playlist status ***********************/ - -bool PLModel::hasRandom() -{ - return var_GetBool( p_playlist, "random" ); -} -bool PLModel::hasRepeat() -{ - return var_GetBool( p_playlist, "repeat" ); -} -bool PLModel::hasLoop() -{ - return var_GetBool( p_playlist, "loop" ); -} -void PLModel::setLoop( bool on ) -{ - var_SetBool( p_playlist, "loop", on ? true:false ); - config_PutInt( p_playlist, "loop", on ? 1: 0 ); -} -void PLModel::setRepeat( bool on ) -{ - var_SetBool( p_playlist, "repeat", on ? true:false ); - config_PutInt( p_playlist, "repeat", on ? 1: 0 ); -} -void PLModel::setRandom( bool on ) -{ - var_SetBool( p_playlist, "random", on ? true:false ); - config_PutInt( p_playlist, "random", on ? 1: 0 ); -} /************************* Lookups *****************************/ -PLItem *PLModel::FindById( PLItem *root, int i_id ) +PLItem *PLModel::findById( PLItem *root, int i_id ) { - return FindInner( root, i_id, false ); + return findInner( root, i_id, false ); } -PLItem *PLModel::FindByInput( PLItem *root, int i_id ) +PLItem *PLModel::findByInput( PLItem *root, int i_id ) { - PLItem *result = FindInner( root, i_id, true ); + 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 ) { + if( !root ) return NULL; if( ( !b_input && i_cached_id == i_id) || ( b_input && i_cached_input_id ==i_id ) ) { @@ -597,7 +528,7 @@ PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input ) } if( (*it)->children.size() ) { - PLItem *childFound = FindInner( (*it), i_id, b_input ); + PLItem *childFound = findInner( (*it), i_id, b_input ); if( childFound ) { if( b_input ) @@ -614,108 +545,107 @@ PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input ) #undef CACHE #undef ICACHE -PLItem *PLModel::getItem( QModelIndex index ) +int PLModel::columnToMeta( int _column ) { - assert( index.isValid() ); - return static_cast( index.internalPointer() ); + int meta = 1; + int column = 0; + + while( column != _column && meta != COLUMN_END ) + { + meta <<= 1; + column++; + } + + return meta; } -/* computes column id of meta data from visible column index */ -int PLModel::metaColumn( int column ) const +int PLModel::columnFromMeta( int meta_col ) { - int metadata = 1; - int running_index = -1; + int meta = 1; + int column = 0; - while( metadata < COLUMN_END ) + while( meta != meta_col && meta != COLUMN_END ) { - if( metadata & i_showflags ) - running_index++; - if( running_index == column ) - break; - metadata <<= 1; + meta <<= 1; + column++; } - if( running_index != column ) return COLUMN_END; - return metadata; + return column; } -/************************* Updates handling *****************************/ -void PLModel::customEvent( QEvent *event ) +bool PLModel::canEdit() const { - int type = event->type(); - if( type != ItemAppend_Type && - type != ItemDelete_Type && type != PLUpdate_Type ) - return; - - PLEvent *ple = static_cast(event); - - if( type == ItemAppend_Type ) - ProcessItemAppend( &ple->add ); - else if( type == ItemDelete_Type ) - ProcessItemRemoval( ple->i_id ); - else - rebuild(); + return ( + rootItem != NULL && + ( + rootItem->p_input == p_playlist->p_playing->p_input || + ( + p_playlist->p_media_library && + rootItem->p_input == p_playlist->p_media_library->p_input + ) + ) + ); } +/************************* Updates handling *****************************/ /**** Events processing ****/ -void PLModel::ProcessInputItemUpdate( input_thread_t *p_input ) +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; + PLItem *item = findByInput( rootItem, input_GetItem( p_input )->i_id ); + if( item ) emit currentChanged( index( item, 0 ) ); } + processInputItemUpdate( input_GetItem( p_input ) ); } -void PLModel::ProcessInputItemUpdate( input_item_t *p_item ) + +void PLModel::processInputItemUpdate( input_item_t *p_item ) { if( !p_item || p_item->i_id <= 0 ) return; - PLItem *item = FindByInput( rootItem, p_item->i_id ); + PLItem *item = findByInput( rootItem, p_item->i_id ); if( item ) - UpdateTreeItem( item, true, true); + updateTreeItem( item ); } -void PLModel::ProcessItemRemoval( int i_id ) +void PLModel::processItemRemoval( int i_id ) { if( i_id <= 0 ) return; - if( i_id == i_cached_id ) i_cached_id = -1; - i_cached_input_id = -1; - removeItem( i_id ); } -void PLModel::ProcessItemAppend( const playlist_add_t *p_add ) +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, p_add->i_node ); + PLItem *nodeItem = findById( rootItem, i_parent ); if( !nodeItem ) return; + foreach( PLItem *existing, nodeItem->children ) + if( existing->i_id == i_item ) return; + PL_LOCK; - p_item = playlist_ItemGetById( p_playlist, p_add->i_item ); - if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end; - if( i_depth == DEPTH_SEL && p_item->p_parent && - p_item->p_parent->i_id != rootItem->i_id ) - goto end; + p_item = playlist_ItemGetById( p_playlist, i_item ); + 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, true ); - return; -end: - PL_UNLOCK; - return; + + if( newItem->p_input == THEMIM->currentInputItem() ) + emit currentChanged( index( newItem, 0 ) ); } @@ -727,12 +657,11 @@ void PLModel::rebuild() void PLModel::rebuild( playlist_item_t *p_root ) { playlist_item_t* p_item; - /* Remove callbacks before locking to avoid deadlocks */ - delCallbacks(); + /* Invalidate cache */ i_cached_id = i_cached_input_id = -1; - if( rootItem ) RemoveChildren( rootItem ); + if( rootItem ) rootItem->removeChildren(); PL_LOCK; if( p_root ) @@ -742,73 +671,89 @@ void PLModel::rebuild( playlist_item_t *p_root ) } assert( rootItem ); /* Recreate from root */ - UpdateChildren( rootItem ); - if( (p_item = playlist_CurrentPlayingItem(p_playlist)) ) - currentItem = FindByInput( rootItem, p_item->p_input->i_id ); - else - currentItem = NULL; + updateChildren( rootItem ); PL_UNLOCK; /* And signal the view */ reset(); - emit currentChanged( index( currentItem, 0 ) ); - addCallbacks(); + if( p_root ) emit rootChanged(); } -void PLModel::RemoveItem( PLItem *item ) +void PLModel::takeItem( PLItem *item ) { - if( !item ) return; - if( currentItem && currentItem->p_input == item->p_input ) - { - currentItem = NULL; - emit currentChanged( QModelIndex() ); - } + assert( item ); PLItem *parent = item->parentItem; assert( parent ); int i_index = parent->children.indexOf( item ); - parent->children.removeAt( i_index ); - delete item; + + beginRemoveRows( index( parent, 0 ), i_index, i_index ); + parent->takeChildAt( i_index ); + endRemoveRows(); +} + +void PLModel::insertChildren( PLItem *node, QList& items, int i_pos ) +{ + assert( node ); + int count = items.size(); + if( !count ) return; + beginInsertRows( index( node, 0 ), i_pos, i_pos + count - 1 ); + for( int i = 0; i < count; i++ ) + { + node->children.insert( i_pos + i, items[i] ); + items[i]->parentItem = node; + } + endInsertRows(); } -void PLModel::RemoveChildren( PLItem *root ) + +void PLModel::removeItem( PLItem *item ) { - if( root->children.size() ) - { - qDeleteAll( root->children ); - root->children.clear(); - } + if( !item ) return; + + if( item->i_id == i_cached_id ) i_cached_id = -1; + i_cached_input_id = -1; + + if( item->parentItem ) { + int i = item->parentItem->children.indexOf( item ); + beginRemoveRows( index( item->parentItem, 0), i, i ); + item->parentItem->children.removeAt(i); + delete item; + endRemoveRows(); + } + 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 ) +void PLModel::updateChildren( PLItem *root ) { playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id ); - UpdateChildren( p_node, root ); + updateChildren( p_node, root ); } /* This function must be entered WITH the playlist lock */ -void PLModel::UpdateChildren( playlist_item_t *p_node, PLItem *root ) +void PLModel::updateChildren( playlist_item_t *p_node, PLItem *root ) { 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( i_depth == DEPTH_PL && p_node->pp_children[i]->i_children != -1 ) - UpdateChildren( p_node->pp_children[i], newItem ); + if( p_node->pp_children[i]->i_children != -1 ) + updateChildren( p_node->pp_children[i], newItem ); } } /* Function doesn't need playlist-lock, as we don't touch playlist_item_t stuff here*/ -void PLModel::UpdateTreeItem( PLItem *item, bool signal, bool force ) +void PLModel::updateTreeItem( PLItem *item ) { - if ( !item || !item->p_input ) - return; - if( !force && i_depth == DEPTH_SEL && item->parentItem && - item->parentItem->p_input != rootItem->p_input ) - return; - if( signal ) - emit dataChanged( index( item, 0 ) , index( item, columnCount( QModelIndex() ) ) ); + if( !item ) return; + emit dataChanged( index( item, 0 ) , index( item, columnCount( QModelIndex() ) ) ); } /************************* Actions ******************************/ @@ -821,18 +766,24 @@ void PLModel::UpdateTreeItem( PLItem *item, bool signal, bool force ) */ void PLModel::doDelete( QModelIndexList selected ) { - for( int i = selected.size() -1 ; i >= 0; i-- ) + if( !canEdit() ) return; + + 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 ); } } @@ -843,34 +794,10 @@ 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 */ - int itemIndex = item->parentItem->children.indexOf( item ); - beginRemoveRows( index( item->parentItem, 0), itemIndex, itemIndex ); - RemoveItem( item ); - endRemoveRows(); -} - /******* Volume III: Sorting and searching ********/ void PLModel::sort( int column, Qt::SortOrder order ) { @@ -879,29 +806,19 @@ void PLModel::sort( int column, Qt::SortOrder order ) void PLModel::sort( int i_root_id, int column, Qt::SortOrder order ) { - int i_index = -1; - int i_flag = 0; + msg_Dbg( p_intf, "Sorting by column %i, order %i", column, order ); - int i_column = 1; - for( i_column = 1; i_column != COLUMN_END; i_column<<=1 ) - { - if( ( shownFlags() & i_column ) ) - i_index++; - if( column == i_index ) - { - i_flag = i_column; - break; - } - } + int meta = columnToMeta( column ); + if( meta == COLUMN_END ) return; - PLItem *item = FindById( rootItem, i_root_id ); + PLItem *item = findById( rootItem, i_root_id ); if( !item ) return; QModelIndex qIndex = index( item, 0 ); int count = item->children.size(); if( count ) { beginRemoveRows( qIndex, 0, count - 1 ); - RemoveChildren( item ); + item->removeChildren(); endRemoveRows( ); } @@ -909,40 +826,59 @@ void PLModel::sort( int i_root_id, int column, Qt::SortOrder order ) { playlist_item_t *p_root = playlist_ItemGetById( p_playlist, i_root_id ); - if( p_root && i_flag ) + if( p_root ) { playlist_RecursiveNodeSort( p_playlist, p_root, - i_column_sorting( i_flag ), + i_column_sorting( meta ), order == Qt::AscendingOrder ? ORDER_NORMAL : ORDER_REVERSE ); } } + + i_cached_id = i_cached_input_id = -1; + if( count ) { beginInsertRows( qIndex, 0, count - 1 ); - UpdateChildren( item ); + updateChildren( item ); endInsertRows( ); } PL_UNLOCK; } -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 ); + 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( QModelIndex & index, QPoint &point, QModelIndexList list ) +bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list ) { int i_id = index.isValid() ? itemId( index ) : rootItem->i_id; @@ -950,88 +886,71 @@ void PLModel::popup( QModelIndex & index, QPoint &point, QModelIndexList list ) playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); if( !p_item ) { - PL_UNLOCK; return; + PL_UNLOCK; + return false; } + 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 = var_InheritBool( p_intf, "playlist-tree" ); + PL_UNLOCK; current_selection = list; - QMenu *menu = new QMenu; - 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( qtr(I_POP_SAVE), this, SLOT( popupSave() ) ); - menu->addSeparator(); - menu->addAction( qtr(I_POP_INFO), this, SLOT( popupInfo() ) ); - menu->addSeparator(); - QMenu *sort_menu = menu->addMenu( qtr( "Sort by ") + - qfu( psz_column_title( metaColumn( index.column() ) ) ) ); - sort_menu->addAction( qtr( "Ascending" ), - this, SLOT( popupSortAsc() ) ); - sort_menu->addAction( qtr( "Descending" ), - this, SLOT( popupSortDesc() ) ); - } - if( tree ) - menu->addAction( qtr(I_POP_ADD), this, SLOT( popupAddNode() ) ); + + QMenu menu; if( i_popup_item > -1 ) { - menu->addSeparator(); - menu->addAction( qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); + 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() ) ); } - menu->popup( point ); -} - - -void PLModel::viewchanged( int meta ) -{ - assert( meta ); - int _meta = meta; - if( rootItem ) + if( canEdit() ) { - int index=-1; - while( _meta ) + QIcon addIcon( ":/buttons/playlist/playlist_add" ); + menu.addSeparator(); + if( tree ) menu.addAction( addIcon, qtr(I_POP_NEWFOLDER), this, SLOT( popupAddNode() ) ); + if( rootItem->i_id == THEPL->p_playing->i_id ) { - index++; - _meta >>= 1; + 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()) ); } - - index = __MIN( index, columnCount() ); - QModelIndex parent = createIndex( 0, 0, rootItem ); - - if( i_showflags & meta ) - /* Removing columns */ + else if( THEPL->p_media_library && + rootItem->i_id == THEPL->p_media_library->i_id ) { - beginRemoveColumns( parent, index, index+1 ); - i_showflags &= ~( meta ); - getSettings()->setValue( "qt-pl-showflags", i_showflags ); - endRemoveColumns(); + 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() ) ); } - else - { - /* Adding columns */ - beginInsertColumns( parent, index, index+1 ); - i_showflags |= meta; - getSettings()->setValue( "qt-pl-showflags", i_showflags ); - endInsertColumns(); - } - - emit columnsChanged( meta ); } + if( i_popup_item > -1 ) + { + menu.addSeparator(); + QMenu *sort_menu = menu.addMenu( qtr( "Sort by" ) + QString(" ") + + qfu( psz_column_title( columnToMeta( index.column() ) ) ) ); + sort_menu->addAction( qtr( "Ascending" ), + this, SLOT( popupSortAsc() ) ); + sort_menu->addAction( qtr( "Descending" ), + this, SLOT( popupSortDesc() ) ); + } + if( !menu.isEmpty() ) + { + menu.exec( point ); return true; + } + else return false; } void PLModel::popupDel() @@ -1084,9 +1003,6 @@ void PLModel::popupSave() THEDP->streamingDialog( NULL, mrls[0] ); } -#include -#include -#include void PLModel::popupExplore() { PL_LOCK; @@ -1104,11 +1020,11 @@ void PLModel::popupExplore() 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 ) ) + if( !EMPTY_STR( psz_access ) && ( + !strncasecmp( psz_access, "file", 4 ) || + !strncasecmp( psz_access, "dire", 4 ) )) { - QFileInfo info( qfu( psz_meta ) ); + QFileInfo info( qfu( psz_path ) ); QDesktopServices::openUrl( QUrl::fromLocalFile( info.absolutePath() ) ); } @@ -1119,12 +1035,11 @@ void PLModel::popupExplore() 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; @@ -1146,45 +1061,3 @@ void PLModel::popupSortDesc() { sort( i_popup_parent, i_popup_column, Qt::DescendingOrder ); } -/********************************************************************** - * Playlist callbacks - **********************************************************************/ -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; - PLEvent *event = new PLEvent( PLUpdate_Type, 0 ); - QApplication::postEvent( p_model, event ); - return VLC_SUCCESS; -} - -static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) -{ - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int ); - QApplication::postEvent( p_model, event ); - event = new PLEvent( ItemUpdate_Type, nval.i_int ); - QApplication::postEvent( p_model, event ); - return VLC_SUCCESS; -} - -static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) -{ - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int ); - QApplication::postEvent( p_model, event ); - return VLC_SUCCESS; -} - -static int ItemAppended( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) -{ - PLModel *p_model = (PLModel *) param; - const playlist_add_t *p_add = (playlist_add_t *)nval.p_address; - PLEvent *event = new PLEvent( p_add ); - QApplication::postEvent( p_model, event ); - return VLC_SUCCESS; -} -