X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fqt4%2Fcomponents%2Fplaylist%2Fplaylist_model.cpp;h=33e6dd26dc97274681735c7c8f7221202a3b0a36;hb=926bbf142f297f9715e3cc5ad746b98a448c3451;hp=32d151ee7cad6ee004cfdb97764775c3faebc829;hpb=6bdcdafe1a22fd738a32acfb141d623f57296d0c;p=vlc diff --git a/modules/gui/qt4/components/playlist/playlist_model.cpp b/modules/gui/qt4/components/playlist/playlist_model.cpp index 32d151ee7c..c86a701086 100644 --- a/modules/gui/qt4/components/playlist/playlist_model.cpp +++ b/modules/gui/qt4/components/playlist/playlist_model.cpp @@ -5,6 +5,8 @@ * $Id$ * * 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 @@ -26,11 +28,13 @@ #endif #include "qt4.hpp" +#include "dialogs_provider.hpp" #include "components/playlist/playlist_model.hpp" #include "dialogs/mediainfo.hpp" +#include "dialogs/playlist.hpp" #include -#include "pixmaps/type_unknown.xpm" +#include "pixmaps/types/type_unknown.xpm" #include #include @@ -38,230 +42,269 @@ #include #include #include +#include +#include +#include +#include -QIcon PLModel::icons[ITEM_TYPE_NUMBER]; +#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:" ) ) -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 ItemChanged( 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_items_to_append = 0; - b_need_update = false; i_cached_id = -1; i_cached_input_id = -1; i_popup_item = i_popup_parent = -1; + sortingMenu = NULL; rootItem = NULL; /* PLItem rootItem, will be set in rebuild( ) */ /* 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, ":/pixmaps/type_file.png" ); - ADD_ICON( DIRECTORY, ":/pixmaps/type_directory.png" ); - ADD_ICON( DISC, ":/pixmaps/disc_16px.png" ); - ADD_ICON( CDDA, ":/pixmaps/cdda_16px.png" ); - ADD_ICON( CARD, ":/pixmaps/capture-card_16px.png" ); - ADD_ICON( NET, ":/pixmaps/type_net.png" ); - ADD_ICON( PLAYLIST, ":/pixmaps/type_playlist.png" ); - ADD_ICON( NODE, ":/pixmaps/type_node.png" ); + ADD_ICON( FILE, ":/type/file" ); + ADD_ICON( DIRECTORY, ":/type/directory" ); + ADD_ICON( DISC, ":/type/disc" ); + ADD_ICON( CDDA, ":/type/cdda" ); + ADD_ICON( CARD, ":/type/capture-card" ); + ADD_ICON( NET, ":/type/net" ); + ADD_ICON( PLAYLIST, ":/type/playlist" ); + ADD_ICON( NODE, ":/type/node" ); #undef ADD_ICON - addCallbacks(); 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 ), + this, processItemRemoval( int ) ); } PLModel::~PLModel() { - QSettings settings( "vlc", "vlc-qt-interface" ); - settings.setValue( "qt-pl-showflags", rootItem->i_showflags ); - delCallbacks(); 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 defaultFlags = QAbstractItemModel::flags( index ); - if( index.isValid() ) - return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; - else - return Qt::ItemIsDropEnabled | defaultFlags; + Qt::ItemFlags flags = QAbstractItemModel::flags( index ); + + PLItem *item = index.isValid() ? getItem( index ) : rootItem; + + if( canEdit() ) + { + PL_LOCK; + playlist_item_t *plItem = + playlist_ItemGetById( p_playlist, item->i_id ); + + if ( plItem && ( plItem->i_children > -1 ) ) + flags |= Qt::ItemIsDropEnabled; + + PL_UNLOCK; + + } + flags |= Qt::ItemIsDragEnabled; + + return flags; } -/* A list of model indexes are a playlist */ QStringList PLModel::mimeTypes() const { QStringList types; - types << "vlc/playlist-item-id"; + 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( QModelIndex index, indexes ) { + foreach( const QModelIndex &index, indexes ) { if( index.isValid() && index.column() == 0 ) - stream << itemId( index ); + list.append(index); } - mimeData->setData( "vlc/playlist-item-id", encodedData ); - return mimeData; + + qSort(list.begin(), list.end(), modelIndexLessThen); + + PLItem *item = NULL; + foreach( const QModelIndex &index, list ) { + 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 ); + } + + return plMimeData; } /* Drop operation */ bool PLModel::dropMimeData( const QMimeData *data, Qt::DropAction action, - int row, int column, const QModelIndex &target ) + int row, int column, const QModelIndex &parent ) { - if( data->hasFormat( "vlc/playlist-item-id" ) ) - { - if( action == Qt::IgnoreAction ) - return true; + bool copy = action == Qt::CopyAction; + if( !copy && action != Qt::MoveAction ) + return true; - PLItem *targetItem; - if( target.isValid() ) - targetItem = static_cast( target.internalPointer() ); + const PlMimeData *plMimeData = qobject_cast( data ); + if( plMimeData ) + { + if( copy ) + dropAppendCopy( plMimeData, getItem( parent ), row ); else - targetItem = rootItem; + dropMove( plMimeData, getItem( parent ), row ); + } + return true; +} - QByteArray encodedData = data->data( "vlc/playlist-item-id" ); - QDataStream stream( &encodedData, QIODevice::ReadOnly ); +void PLModel::dropAppendCopy( const PlMimeData *plMimeData, PLItem *target, int pos ) +{ + PL_LOCK; - PLItem *newParentItem; - while( !stream.atEnd() ) - { - int i; - int srcId; - stream >> srcId; + playlist_item_t *p_parent = + playlist_ItemGetByInput( p_playlist, target->p_input ); + if( !p_parent ) return; - PL_LOCK; - playlist_item_t *p_target = - playlist_ItemGetById( p_playlist, targetItem->i_id, - VLC_TRUE ); - playlist_item_t *p_src = playlist_ItemGetById( p_playlist, srcId, - VLC_TRUE ); + if( pos == -1 ) pos = PLAYLIST_END; - 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, - VLC_TRUE ); - 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 = FindById( rootItem, p_src->i_id ); - // We dropped on the source selector. Ask the dialog to forward - // to the main view - if( !srcItem ) + QList inputItems = plMimeData->inputItems(); + + foreach( input_item_t* p_input, inputItems ) + { + playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input ); + if( !p_item ) continue; + pos = playlist_NodeAddCopy( p_playlist, p_item, p_parent, pos ); + } + + PL_UNLOCK; +} + +void PLModel::dropMove( const PlMimeData * plMimeData, PLItem *target, int row ) +{ + QList inputItems = plMimeData->inputItems(); + QList model_items; + 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; + int i = 0; + + foreach( input_item_t *p_input, inputItems ) + { + playlist_item_t *p_item = playlist_ItemGetByInput( p_playlist, p_input ); + if( !p_item ) continue; + + 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 ) { - emit shouldRemove( p_src->i_id ); + PL_UNLOCK; return; } - else - 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; + climber = climber->parentItem; } + + if( item->parentItem == target && + target->children.indexOf( item ) < new_pos ) + model_pos--; + + model_items.append( item ); + pp_items[i] = p_item; + i++; } - return true; -} -/* remove item with its id */ -void PLModel::removeItem( int i_id ) -{ - PLItem *item = FindById( rootItem, i_id ); - if( item ) item->remove( item ); -} + if( model_items.isEmpty() ) + { + PL_UNLOCK; return; + } -/* 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, "playlist-current", PlaylistNext, this ); - /* One item has been updated */ - var_AddCallback( p_playlist, "item-change", ItemChanged, this ); - var_AddCallback( p_playlist, "item-append", ItemAppended, this ); - var_AddCallback( p_playlist, "item-deleted", ItemDeleted, this ); + playlist_TreeMoveMany( p_playlist, i, pp_items, p_parent, new_pos ); + + PL_UNLOCK; + + foreach( PLItem *item, model_items ) + takeItem( item ); + + insertChildren( target, model_items, model_pos ); } -void PLModel::delCallbacks() +/* remove item with its id */ +void PLModel::removeItem( int i_id ) { - var_DelCallback( p_playlist, "item-change", ItemChanged, this ); - var_DelCallback( p_playlist, "playlist-current", PlaylistNext, this ); - var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this ); - var_DelCallback( p_playlist, "item-append", ItemAppended, this ); - var_DelCallback( p_playlist, "item-deleted", ItemDeleted, this ); + PLItem *item = findById( rootItem, i_id ); + removeItem( item ); } void PLModel::activateItem( const QModelIndex &index ) { assert( index.isValid() ); - PLItem *item = static_cast(index.internalPointer()); + PLItem *item = getItem( index ); assert( item ); PL_LOCK; - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id, - VLC_TRUE); + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); activateItem( p_item ); PL_UNLOCK; } @@ -277,7 +320,7 @@ void PLModel::activateItem( playlist_item_t *p_item ) p_parent = p_parent->p_parent; } if( p_parent ) - playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE, + playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked, p_parent, p_item ); } @@ -285,55 +328,83 @@ void PLModel::activateItem( playlist_item_t *p_item ) QVariant PLModel::data( const QModelIndex &index, int role ) const { if( !index.isValid() ) return QVariant(); - PLItem *item = static_cast(index.internalPointer()); + PLItem *item = getItem( index ); if( role == Qt::DisplayRole ) { - return QVariant( item->columnString( index.column() ) ); + int metadata = columnToMeta( index.column() ); + if( metadata == COLUMN_END ) return QVariant(); + + QString returninfo; + if( metadata == COLUMN_NUMBER ) + returninfo = QString::number( index.row() + 1 ); + else + { + char *psz = psz_column_meta( item->p_input, metadata ); + returninfo = qfu( psz ); + free( psz ); + } + return QVariant( returninfo ); } else if( role == Qt::DecorationRole && index.column() == 0 ) { - /* Use to segfault here because i_type wasn't always initialized */ - if( item->i_type >= 0 ) - return QVariant( PLModel::icons[item->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 ) { - if( item->b_current == true ) + if( isCurrent( index ) ) { 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 ) ); + 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; + } return QVariant(); } -bool PLModel::isCurrent( const QModelIndex &index ) +bool PLModel::isCurrent( const QModelIndex &index ) const { - assert( index.isValid() ); - return static_cast(index.internalPointer())->b_current; + return getItem( index )->p_input == THEMIM->currentInputItem(); } int PLModel::itemId( const QModelIndex &index ) const { - assert( index.isValid() ); - return static_cast(index.internalPointer())->i_id; + return getItem( index )->i_id; } QVariant PLModel::headerData( int section, Qt::Orientation orientation, int role ) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - return QVariant( rootItem->columnString( section ) ); - return QVariant(); + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant(); + + int meta_col = columnToMeta( section ); + + if( meta_col == COLUMN_END ) return QVariant(); + + return QVariant( qfu( psz_column_title( meta_col ) ) ); } QModelIndex PLModel::index( int row, int column, const QModelIndex &parent ) const { - PLItem *parentItem; - if( !parent.isValid() ) - parentItem = rootItem; - else - parentItem = static_cast(parent.internalPointer()); + PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; PLItem *childItem = parentItem->child( row ); if( childItem ) @@ -342,6 +413,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 { @@ -353,11 +429,19 @@ 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(); - PLItem *childItem = static_cast(index.internalPointer()); + PLItem *childItem = getItem( index ); if( !childItem ) { msg_Err( p_playlist, "NULL CHILD" ); @@ -378,76 +462,60 @@ QModelIndex PLModel::parent( const QModelIndex &index ) const int PLModel::columnCount( const QModelIndex &i) const { - return rootItem->item_col_strings.count(); -} - -int PLModel::childrenCount( const QModelIndex &parent ) const -{ - return rowCount( parent ); + return columnFromMeta( COLUMN_END ); } int PLModel::rowCount( const QModelIndex &parent ) const { - PLItem *parentItem; - - if( !parent.isValid() ) - parentItem = rootItem; - else - parentItem = static_cast(parent.internalPointer()); - + PLItem *parentItem = parent.isValid() ? getItem( parent ) : rootItem; return parentItem->childCount(); } -/************************* General playlist status ***********************/ - -bool PLModel::hasRandom() -{ - if( var_GetBool( p_playlist, "random" ) ) return true; - return false; -} -bool PLModel::hasRepeat() -{ - if( var_GetBool( p_playlist, "repeat" ) ) return true; - return false; -} -bool PLModel::hasLoop() -{ - if( var_GetBool( p_playlist, "loop" ) ) return true; - return false; -} -void PLModel::setLoop( bool on ) +QStringList PLModel::selectedURIs() { - var_SetBool( p_playlist, "loop", on ? VLC_TRUE:VLC_FALSE ); - config_PutInt( p_playlist, "loop", on ? 1: 0 ); -} -void PLModel::setRepeat( bool on ) -{ - var_SetBool( p_playlist, "repeat", on ? VLC_TRUE:VLC_FALSE ); - config_PutInt( p_playlist, "repeat", on ? 1: 0 ); -} -void PLModel::setRandom( bool on ) -{ - var_SetBool( p_playlist, "random", on ? VLC_TRUE:VLC_FALSE ); - config_PutInt( p_playlist, "random", on ? 1: 0 ); + QStringList lst; + for( int i = 0; i < current_selection.size(); i++ ) + { + PLItem *item = getItem( current_selection[i] ); + if( item ) + { + PL_LOCK; + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id ); + if( p_item ) + { + char *psz = input_item_GetURI( p_item->p_input ); + if( psz ) + { + lst.append( qfu(psz) ); + free( psz ); + } + } + PL_UNLOCK; + } + } + return lst; } + /************************* 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 ) { - return 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 ) ) { @@ -459,7 +527,7 @@ PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input ) CACHE( i_id, root ); return root; } - else if( b_input && root->i_input_id == i_id ) + else if( b_input && root->p_input->i_id == i_id ) { ICACHE( i_id, root ); return root; @@ -473,14 +541,14 @@ PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input ) CACHE( i_id, (*it) ); return p_cached_item; } - else if( b_input && (*it)->i_input_id == i_id ) + else if( b_input && (*it)->p_input->i_id == i_id ) { ICACHE( i_id, (*it) ); return p_cached_item_bi; } 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 ) @@ -497,68 +565,107 @@ PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input ) #undef CACHE #undef ICACHE +int PLModel::columnToMeta( int _column ) +{ + int meta = 1; + int column = 0; -/************************* Updates handling *****************************/ -void PLModel::customEvent( QEvent *event ) + while( column != _column && meta != COLUMN_END ) + { + meta <<= 1; + column++; + } + + return meta; +} + +int PLModel::columnFromMeta( int meta_col ) { - int type = event->type(); - if( type != ItemUpdate_Type && type != ItemAppend_Type && - type != ItemDelete_Type && type != PLUpdate_Type ) - return; + int meta = 1; + int column = 0; - PLEvent *ple = static_cast(event); + while( meta != meta_col && meta != COLUMN_END ) + { + meta <<= 1; + column++; + } - if( type == ItemUpdate_Type ) - ProcessInputItemUpdate( ple->i_id ); - else if( type == ItemAppend_Type ) - ProcessItemAppend( ple->p_add ); - else if( type == ItemDelete_Type ) - ProcessItemRemoval( ple->i_id ); - else - rebuild(); + return column; } +bool PLModel::canEdit() const +{ + 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( int i_input_id ) +void PLModel::processInputItemUpdate( input_thread_t *p_input ) +{ + if( !p_input ) return; + if( p_input && !( p_input->b_dead || !vlc_object_alive( p_input ) ) ) + { + 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 ) { - if( i_input_id <= 0 ) return; - PLItem *item = FindByInput( rootItem, i_input_id ); + if( !p_item || p_item->i_id <= 0 ) return; + PLItem *item = findByInput( rootItem, p_item->i_id ); if( item ) - UpdateTreeItem( item, 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( playlist_add_t *p_add ) +void PLModel::processItemAppend( int i_item, int i_parent ) { playlist_item_t *p_item = NULL; PLItem *newItem = NULL; - i_items_to_append--; - if( b_need_update ) return; + input_thread_t *currentInputThread; + int pos; + + PLItem *nodeItem = findById( rootItem, i_parent ); + if( !nodeItem ) return; + + foreach( PLItem *existing, nodeItem->children ) + if( existing->i_id == i_item ) return; - PLItem *nodeItem = FindById( rootItem, p_add->i_node ); PL_LOCK; - if( !nodeItem ) goto end; - - p_item = playlist_ItemGetById( p_playlist, p_add->i_item, VLC_TRUE ); - 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; - - newItem = new PLItem( p_item, nodeItem, this ); - nodeItem->appendChild( newItem ); - UpdateTreeItem( p_item, newItem, true ); -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; - return; + + beginInsertRows( index( nodeItem, 0 ), pos, pos ); + nodeItem->insertChild( newItem, pos ); + endInsertRows(); + + if( newItem->p_input == THEMIM->currentInputItem() ) + emit currentChanged( index( newItem, 0 ) ); } @@ -569,114 +676,130 @@ void PLModel::rebuild() void PLModel::rebuild( playlist_item_t *p_root ) { - /* Remove callbacks before locking to avoid deadlocks */ - delCallbacks(); + playlist_item_t* p_item; + /* Invalidate cache */ i_cached_id = i_cached_input_id = -1; + if( rootItem ) rootItem->removeChildren(); + PL_LOCK; - /* Clear the tree */ - if( rootItem ) - { - if( rootItem->children.size() ) - { - beginRemoveRows( index( rootItem, 0 ), 0, - rootItem->children.size() -1 ); - qDeleteAll( rootItem->children ); - rootItem->children.clear(); - endRemoveRows(); - } - } if( p_root ) { - //if( rootItem ) delete rootItem; - rootItem = new PLItem( p_root, NULL, this ); + delete rootItem; + rootItem = new PLItem( p_root ); } assert( rootItem ); /* Recreate from root */ - UpdateNodeChildren( rootItem ); - if( p_playlist->status.p_item ) - { - PLItem *currentItem = FindByInput( rootItem, - p_playlist->status.p_item->p_input->i_id ); - if( currentItem ) - { - UpdateTreeItem( p_playlist->status.p_item, currentItem, - true, false ); - } - } + updateChildren( rootItem ); PL_UNLOCK; /* And signal the view */ - emit layoutChanged(); - addCallbacks(); + reset(); + + if( p_root ) emit rootChanged(); } -/* This function must be entered WITH the playlist lock */ -void PLModel::UpdateNodeChildren( PLItem *root ) +void PLModel::takeItem( PLItem *item ) { - playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id, - VLC_TRUE ); - UpdateNodeChildren( p_node, root ); + assert( item ); + PLItem *parent = item->parentItem; + assert( parent ); + int i_index = parent->children.indexOf( item ); + + beginRemoveRows( index( parent, 0 ), i_index, i_index ); + parent->takeChildAt( i_index ); + endRemoveRows(); } -/* This function must be entered WITH the playlist lock */ -void PLModel::UpdateNodeChildren( playlist_item_t *p_node, PLItem *root ) +void PLModel::insertChildren( PLItem *node, QList& items, int i_pos ) { - for( int i = 0; i < p_node->i_children ; i++ ) + 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++ ) { - if( p_node->pp_children[i]->i_flags & PLAYLIST_DBL_FLAG ) continue; - PLItem *newItem = new PLItem( p_node->pp_children[i], root, this ); - root->appendChild( newItem, false ); - UpdateTreeItem( newItem, false, true ); - if( i_depth == DEPTH_PL && p_node->pp_children[i]->i_children != -1 ) - UpdateNodeChildren( p_node->pp_children[i], newItem ); + node->children.insert( i_pos + i, items[i] ); + items[i]->parentItem = node; + } + endInsertRows(); +} + +void PLModel::removeItem( PLItem *item ) +{ + if( !item ) return; + + 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::UpdateTreeItem( PLItem *item, bool signal, bool force ) +void PLModel::updateChildren( PLItem *root ) { - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id, - VLC_TRUE ); - UpdateTreeItem( p_item, item, signal, force ); + playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id ); + updateChildren( p_node, root ); } /* This function must be entered WITH the playlist lock */ -void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item, - bool signal, bool force ) +void PLModel::updateChildren( playlist_item_t *p_node, PLItem *root ) { - if ( !p_item ) - return; - if( !force && i_depth == DEPTH_SEL && p_item->p_parent && - p_item->p_parent->i_id != rootItem->i_id ) - return; - item->update( p_item, p_item == p_playlist->status.p_item ); - if( signal ) - emit dataChanged( index( item, 0 ) , index( item, 1 ) ); + 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_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 ) +{ + if( !item ) return; + emit dataChanged( index( item, 0 ) , index( item, columnCount( QModelIndex() ) ) ); } /************************* 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? + * Lets not worry about nodes children, we do refersh anyway when + * core tells that playlist has changed, should give some more speed */ 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 = static_cast(index.internalPointer()); - if( item ) - { - if( item->children.size() ) - recurseDelete( item->children, &selected ); - doDeleteItem( item, &selected ); - } + + PLItem *item = getItem( index ); + + PL_LOCK; + playlist_DeleteFromInput( p_playlist, item->p_input, pl_Locked ); + PL_UNLOCK; + + removeItem( item ); } } @@ -687,205 +810,198 @@ 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 ) +/******* Volume III: Sorting and searching ********/ +void PLModel::sort( int column, Qt::SortOrder order ) +{ + sort( rootItem->i_id, column, order ); +} + +void PLModel::sort( int i_root_id, int column, Qt::SortOrder order ) { - QModelIndex deleteIndex = index( item, 0 ); - fullList->removeAll( deleteIndex ); + msg_Dbg( p_intf, "Sorting by column %i, order %i", column, order ); - PL_LOCK; - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id, - VLC_TRUE ); - if( !p_item ) + int meta = columnToMeta( column ); + if( meta == COLUMN_END ) return; + + PLItem *item = findById( rootItem, i_root_id ); + if( !item ) return; + QModelIndex qIndex = index( item, 0 ); + int count = item->children.size(); + if( count ) { - PL_UNLOCK; return; + beginRemoveRows( qIndex, 0, count - 1 ); + item->removeChildren(); + endRemoveRows( ); } - if( p_item->i_children == -1 ) - playlist_DeleteFromInput( p_playlist, item->i_input_id, VLC_TRUE ); - else - playlist_NodeDelete( p_playlist, p_item, VLC_TRUE, VLC_FALSE ); - /* And finally, remove it from the tree */ - item->remove( item ); - PL_UNLOCK; -} -/******* Volume III: Sorting and searching ********/ -void PLModel::sort( int column, Qt::SortOrder order ) -{ - int i_index = -1; - int i_flag = 0; - -#define CHECK_COLUMN( meta ) \ -{ \ - if( ( shownFlags() & VLC_META_ENGINE_##meta ) ) \ - i_index++; \ - if( column == i_index ) \ - { \ - i_flag = VLC_META_ENGINE_##meta; \ - goto next; \ - } \ -} - CHECK_COLUMN( TITLE ); - CHECK_COLUMN( DURATION ); - CHECK_COLUMN( ARTIST ); - CHECK_COLUMN( GENRE ); - CHECK_COLUMN( COLLECTION ); - CHECK_COLUMN( SEQ_NUM ); - CHECK_COLUMN( DESCRIPTION ); - CHECK_COLUMN( TRACKID ); - -#undef CHECK_COLUMN - -next: PL_LOCK; { playlist_item_t *p_root = playlist_ItemGetById( p_playlist, - rootItem->i_id, - VLC_TRUE ); - int i_mode; - switch( i_flag ) - { - case VLC_META_ENGINE_TITLE: i_mode = SORT_TITLE_NODES_FIRST;break; - case VLC_META_ENGINE_DURATION: i_mode = SORT_DURATION; break; - case VLC_META_ENGINE_ARTIST: i_mode = SORT_ARTIST; break; - case VLC_META_ENGINE_GENRE: i_mode = SORT_GENRE; break; - case VLC_META_ENGINE_COLLECTION: i_mode = SORT_ALBUM; break; - case VLC_META_ENGINE_SEQ_NUM: i_mode = SORT_TRACK_NUMBER; break; - case VLC_META_ENGINE_DESCRIPTION:i_mode = SORT_DESCRIPTION; break; - case VLC_META_ENGINE_TRACKID: i_mode = SORT_ID; break; - default: i_mode = SORT_TITLE_NODES_FIRST;break; - } + i_root_id ); if( p_root ) { - playlist_RecursiveNodeSort( p_playlist, p_root, i_mode, + playlist_RecursiveNodeSort( p_playlist, p_root, + i_column_sorting( meta ), order == Qt::AscendingOrder ? ORDER_NORMAL : ORDER_REVERSE ); - p_playlist->b_reset_currently_playing = VLC_TRUE; } } + + i_cached_id = i_cached_input_id = -1; + + if( count ) + { + beginInsertRows( qIndex, 0, count - 1 ); + updateChildren( item ); + endInsertRows( ); + } PL_UNLOCK; - rebuild(); + /* 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( 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, - VLC_TRUE ); + itemId( idx ) ); assert( p_root ); - 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( QModelIndex & index, QPoint &point, QModelIndexList list ) +bool PLModel::popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list ) { - assert( index.isValid() ); + int i_id = index.isValid() ? itemId( index ) : rootItem->i_id; + PL_LOCK; - playlist_item_t *p_item = playlist_ItemGetById( p_playlist, - itemId( index ), VLC_TRUE ); - if( p_item ) + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); + if( !p_item ) { - i_popup_item = p_item->i_id; - i_popup_parent = p_item->p_parent ? p_item->p_parent->i_id : -1; PL_UNLOCK; - current_selection = list; - QMenu *menu = new QMenu; - menu->addAction( qfu(I_POP_PLAY), this, SLOT( popupPlay() ) ); - menu->addAction( qfu(I_POP_DEL), this, SLOT( popupDel() ) ); - menu->addSeparator(); - menu->addAction( qfu(I_POP_STREAM), this, SLOT( popupStream() ) ); - menu->addAction( qfu(I_POP_SAVE), this, SLOT( popupSave() ) ); - menu->addSeparator(); - menu->addAction( qfu(I_POP_INFO), this, SLOT( popupInfo() ) ); - if( p_item->i_children > -1 ) - { - menu->addSeparator(); - menu->addAction( qfu(I_POP_SORT), this, SLOT( popupSort() ) ); - menu->addAction( qfu(I_POP_ADD), this, SLOT( popupAdd() ) ); - } -#ifdef WIN32 - menu->addSeparator(); - menu->addAction( qfu( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); -#endif - menu->popup( point ); + return false; } - else - PL_UNLOCK; -} + 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 ) : + ( rootItem->i_id ); + i_popup_column = index.column(); -void PLModel::viewchanged( int meta ) -{ - if( rootItem ) + bool tree = ( rootItem && rootItem->i_id != p_playlist->p_playing->i_id ) || + var_InheritBool( p_intf, "playlist-tree" ); + + PL_UNLOCK; + + current_selection = list; + + QMenu menu; + if( i_popup_item > -1 ) { - int index=0; - switch( meta ) + 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() ) ); + menu.addAction( QIcon( ":/type/folder-grey" ), + qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) ); + menu.addSeparator(); + } + if( canEdit() ) + { + 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 ) { - case VLC_META_ENGINE_TITLE: - index=0; break; - case VLC_META_ENGINE_DURATION: - index=1; break; - case VLC_META_ENGINE_ARTIST: - index=2; break; - case VLC_META_ENGINE_GENRE: - index=3; break; - case VLC_META_ENGINE_COPYRIGHT: - index=4; break; - case VLC_META_ENGINE_COLLECTION: - index=5; break; - case VLC_META_ENGINE_SEQ_NUM: - index=6; break; - case VLC_META_ENGINE_DESCRIPTION: - index=7; break; - case VLC_META_ENGINE_TRACKID: - index=8; break; - default: - break; + 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()) ); } - /* UNUSED emit layoutAboutToBeChanged(); */ - index = __MIN( index, rootItem->item_col_strings.count() ); - QModelIndex parent = createIndex( 0, 0, rootItem ); - - if( rootItem->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 ); - rootItem->i_showflags &= ~( meta ); - rootItem->updateColumnHeaders(); - 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 + } + if( i_popup_item > -1 ) + { + menu.addAction( QIcon( ":/buttons/playlist/playlist_remove" ), + qtr(I_POP_DEL), this, SLOT( popupDel() ) ); + menu.addSeparator(); + if( !sortingMenu ) { - /* Adding columns */ - beginInsertColumns( createIndex( 0, 0, rootItem), index, index+1 ); - rootItem->i_showflags |= meta; - rootItem->updateColumnHeaders(); - endInsertColumns(); + 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 ) ); } - rebuild(); + menu.addMenu( sortingMenu ); + } + if( !menu.isEmpty() ) + { + menu.exec( point ); return true; } + else return false; } void PLModel::popupDel() { doDelete( current_selection ); } + void PLModel::popupPlay() { PL_LOCK; { playlist_item_t *p_item = playlist_ItemGetById( p_playlist, - i_popup_item,VLC_TRUE ); + i_popup_item ); activateItem( p_item ); } PL_UNLOCK; @@ -893,89 +1009,119 @@ void PLModel::popupPlay() void PLModel::popupInfo() { + PL_LOCK; playlist_item_t *p_item = playlist_ItemGetById( p_playlist, - i_popup_item, - VLC_TRUE ); + i_popup_item ); if( p_item ) { - MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_item->p_input ); + input_item_t* p_input = p_item->p_input; + vlc_gc_incref( p_input ); + PL_UNLOCK; + MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_input ); + vlc_gc_decref( p_input ); + mid->setParent( PlaylistDialog::getInstance( p_intf ), + Qt::Dialog ); mid->show(); - } + } else + PL_UNLOCK; } void PLModel::popupStream() { - msg_Err( p_playlist, "Stream not implemented" ); + QStringList mrls = selectedURIs(); + if( !mrls.isEmpty() ) + THEDP->streamingDialog( NULL, mrls[0], false ); + } void PLModel::popupSave() { - msg_Err( p_playlist, "Save not implemented" ); + QStringList mrls = selectedURIs(); + if( !mrls.isEmpty() ) + THEDP->streamingDialog( NULL, mrls[0] ); } -#ifdef WIN32 -#include void PLModel::popupExplore() { - ShellExecute( NULL, "explore", "C:\\", NULL, NULL, SW_SHOWNORMAL ); + PL_LOCK; + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, + 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( decode_URI( psz_path ) ) ); + QDesktopServices::openUrl( + QUrl::fromLocalFile( info.absolutePath() ) ); + } + free( psz_meta ); + } + } + else + PL_UNLOCK; } -#endif -/********************************************************************** - * Playlist callbacks - **********************************************************************/ -static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) +void PLModel::popupAddNode() { - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( PLUpdate_Type, 0 ); - QApplication::postEvent( p_model, static_cast(event) ); - return VLC_SUCCESS; + bool ok; + QString name = QInputDialog::getText( PlaylistDialog::getInstance( p_intf ), + 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, PLAYLIST_END, 0, NULL ); + } + PL_UNLOCK; } -static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) +void PLModel::popupSort( int column ) { - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int ); - QApplication::postEvent( p_model, static_cast(event) ); - event = new PLEvent( ItemUpdate_Type, nval.i_int ); - QApplication::postEvent( p_model, static_cast(event) ); - return VLC_SUCCESS; + sort( i_popup_parent, + column > 0 ? column - 1 : -column - 1, + column > 0 ? Qt::AscendingOrder : Qt::DescendingOrder ); } -static int ItemChanged( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) +/******************* Drag and Drop helper class ******************/ + +PlMimeData::PlMimeData( ) +{ } + +PlMimeData::~PlMimeData() { - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int ); - QApplication::postEvent( p_model, static_cast(event) ); - return VLC_SUCCESS; + foreach( input_item_t *p_item, _inputItems ) + vlc_gc_decref( p_item ); } -static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) +void PlMimeData::appendItem( input_item_t *p_item ) { - PLModel *p_model = (PLModel *) param; - PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int ); - QApplication::postEvent( p_model, static_cast(event) ); - return VLC_SUCCESS; + vlc_gc_incref( p_item ); + _inputItems.append( p_item ); } -static int ItemAppended( vlc_object_t *p_this, const char *psz_variable, - vlc_value_t oval, vlc_value_t nval, void *param ) +QList PlMimeData::inputItems() const { - PLModel *p_model = (PLModel *) param; - playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t)); - memcpy( p_add, nval.p_address, sizeof( playlist_add_t ) ); - - if( ++p_model->i_items_to_append >= 50 ) - { -// p_model->b_need_update = VLC_TRUE; -// return VLC_SUCCESS; - } - PLEvent *event = new PLEvent( p_add ); - QApplication::postEvent( p_model, static_cast(event) ); - return VLC_SUCCESS; + return _inputItems; } +QStringList PlMimeData::formats () const +{ + QStringList fmts; + fmts << "vlc/qt-input-items"; + return fmts; +}