]> git.sesse.net Git - vlc/blobdiff - modules/gui/qt4/playlist_model.cpp
Some cleanup here and there
[vlc] / modules / gui / qt4 / playlist_model.cpp
index 450b5ce29b9ebc59540ff60fd96ce8fcf29b6bb4..3223b17f2cc906d8d746c9a44f44c3bbf1226d83 100644 (file)
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
+#include <assert.h>
 #include <QIcon>
 #include <QFont>
-#include "qt4.hpp"
+#include <QMenu>
 #include <QApplication>
+
+#include "qt4.hpp"
 #include "playlist_model.hpp"
-#include <assert.h>
+#include <vlc_intf_strings.h>
 
 #include "pixmaps/type_unknown.xpm"
 #include "pixmaps/type_afile.xpm"
@@ -71,6 +74,7 @@ void PLItem::init( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
     strings.append( "" );
     strings.append( "" );
     strings.append( "" );
+    strings.append( "" );
 }
 
 PLItem::PLItem( int _i_id, int _i_input_id, PLItem *parent, PLModel *m)
@@ -86,6 +90,7 @@ PLItem::PLItem( playlist_item_t * p_item, PLItem *parent, PLModel *m )
 PLItem::~PLItem()
 {
     qDeleteAll(children);
+    children.clear();
 }
 
 void PLItem::insertChild( PLItem *item, int i_pos, bool signal )
@@ -109,19 +114,22 @@ void PLItem::remove( PLItem *removed )
 
 int PLItem::row() const
 {
-    if (parentItem)
+    if( parentItem )
         return parentItem->children.indexOf(const_cast<PLItem*>(this));
     return 0;
 }
 
 void PLItem::update( playlist_item_t *p_item, bool iscurrent )
 {
+    char psz_duration[MSTRTIME_MAX_SIZE];
     assert( p_item->p_input->i_id == i_input_id );
     strings[0] = QString::fromUtf8( p_item->p_input->psz_name );
     if( p_item->p_input->p_meta )
     {
         strings[1] = QString::fromUtf8( p_item->p_input->p_meta->psz_artist );
     }
+    secstotimestr( psz_duration, p_item->p_input->i_duration / 1000000 );
+    strings[2] = QString( psz_duration );
     type = p_item->p_input->i_type;
     current = iscurrent;
 }
@@ -141,6 +149,7 @@ PLModel::PLModel( playlist_t *_p_playlist,
     b_need_update     = false;
     i_cached_id       = -1;
     i_cached_input_id = -1;
+    i_popup_item = i_popup_parent = -1;
 
 #define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( QPixmap( type_##x##_xpm ) );
     ADD_ICON( UNKNOWN , unknown );
@@ -155,18 +164,10 @@ PLModel::PLModel( playlist_t *_p_playlist,
     ADD_ICON( NODE, node );
 
     rootItem = NULL;
-    rebuildRoot( p_root );
     addCallbacks();
-
+    rebuild( p_root );
 }
 
-void PLModel::rebuildRoot( playlist_item_t *p_root )
-{
-    if( rootItem ) delete rootItem;
-    rootItem = new PLItem( p_root, NULL, this );
-    rootItem->strings[0] = qtr("Name");
-    rootItem->strings[1] = qtr("Artist");
-}
 
 PLModel::~PLModel()
 {
@@ -202,6 +203,13 @@ void PLModel::activateItem( const QModelIndex &index )
     assert( item );
     PL_LOCK;
     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
+    activateItem( p_item );
+    PL_UNLOCK;
+}
+/* Must be entered with lock */
+void PLModel::activateItem( playlist_item_t *p_item )
+{
+    if( !p_item ) return;
     playlist_item_t *p_parent = p_item;
     while( p_parent )
     {
@@ -209,10 +217,7 @@ void PLModel::activateItem( const QModelIndex &index )
         p_parent = p_parent->p_parent;
     }
     if( p_parent )
-    {
         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, p_parent, p_item );
-    }
-    PL_UNLOCK;
 }
 
 /****************** Base model mandatory implementations *****************/
@@ -287,7 +292,8 @@ QModelIndex PLModel::index( PLItem *item, int column ) const
     if( !item ) return QModelIndex();
     const PLItem *parent = item->parent();
     if( parent )
-        return createIndex( parent->children.lastIndexOf( item ), column, item );
+        return createIndex( parent->children.lastIndexOf( item ),
+                            column, item );
     return QModelIndex();
 }
 
@@ -296,16 +302,23 @@ QModelIndex PLModel::parent(const QModelIndex &index) const
     if( !index.isValid() ) return QModelIndex();
 
     PLItem *childItem = static_cast<PLItem*>(index.internalPointer());
+    if( !childItem ) { msg_Err( p_playlist, "NULL CHILD \n" ); return QModelIndex(); }
     PLItem *parentItem = childItem->parent();
-
-    if (parentItem == rootItem) return QModelIndex();
-    return createIndex(parentItem->row(), 0, parentItem);
+    if( !parentItem || parentItem == rootItem ) return QModelIndex();
+    if( ! parentItem->parentItem )
+    {
+        msg_Err( p_playlist, "No parent parent, trying row 0 " );
+        msg_Err( p_playlist, "----- PLEASE REPORT THIS ------" );
+        return createIndex( 0, 0, parentItem );
+    }
+    QModelIndex ind = createIndex(parentItem->row(), 0, parentItem);
+    return ind;
 }
 
 int PLModel::columnCount( const QModelIndex &i) const
 {
     if( i_depth == 1 ) return 1;
-    return 2;
+    return 3;
 }
 
 int PLModel::childrenCount(const QModelIndex &parent) const
@@ -325,6 +338,39 @@ int PLModel::rowCount(const QModelIndex &parent) const
     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 )
+{
+    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 );
+}
+
 /************************* Lookups *****************************/
 
 PLItem *PLModel::FindById( PLItem *root, int i_id )
@@ -438,9 +484,9 @@ void PLModel::ProcessItemAppend( playlist_add_t *p_add )
     if( b_need_update ) return;
 
     PLItem *nodeItem = FindById( rootItem, p_add->i_node );
+    PL_LOCK;
     if( !nodeItem ) goto end;
 
-    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 == 1 && p_item->p_parent &&
@@ -455,7 +501,13 @@ end:
     return;
 }
 
-void PLModel::Rebuild()
+
+void PLModel::rebuild()
+{
+    rebuild( NULL );
+}
+
+void PLModel::rebuild( playlist_item_t *p_root )
 {
     /* Remove callbacks before locking to avoid deadlocks */
     delCallbacks();
@@ -464,15 +516,39 @@ void PLModel::Rebuild()
 
     PL_LOCK;
     /* Clear the tree */
-    qDeleteAll( rootItem->children );
+    if( rootItem )
+    {
+        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 );
+        rootItem->strings[0] = qtr("Name");
+        rootItem->strings[1] = qtr("Artist");
+        rootItem->strings[2] = qtr("Duration");
+    }
+    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 );
+        }
+    }
     PL_UNLOCK;
 
     /* And signal the view */
     emit layoutChanged();
-    /// \todo  Force current item to be updated
-
     addCallbacks();
 }
 
@@ -488,6 +564,7 @@ void PLModel::UpdateNodeChildren( 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, this );
         root->appendChild( newItem, false );
         UpdateTreeItem( newItem, false, true );
@@ -515,6 +592,135 @@ void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item,
         emit dataChanged( index( item, 0 ) , index( item, 1 ) );
 }
 
+/************************* 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?
+ */
+void PLModel::doDelete( QModelIndexList selected )
+{
+    for( int i = selected.size() -1 ; i >= 0; i-- )
+    {
+        QModelIndex index = selected[i];
+        if( index.column() != 0 ) continue;
+        PLItem *item = static_cast<PLItem*>(index.internalPointer());
+        if( item )
+        {
+            if( item->children.size() )
+                recurseDelete( item->children, &selected );
+            doDeleteItem( item, &selected );
+        }
+    }
+}
+
+void PLModel::recurseDelete( QList<PLItem*> children, QModelIndexList *fullList)
+{
+    for( int i = children.size() - 1; i >= 0 ; i-- )
+    {
+        PLItem *item = children[i];
+        if( item->children.size() )
+            recurseDelete( item->children, fullList );
+        doDeleteItem( item, fullList );
+    }
+}
+
+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_DeleteAllFromInput( p_playlist, item->i_input_id );
+    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 )
+{
+    PL_LOCK;
+    playlist_item_t *p_root = playlist_ItemGetById( p_playlist, rootItem->i_id );
+    int i_mode;
+    switch( column )
+    {
+    case 0: i_mode = SORT_TITLE_NODES_FIRST;break;
+    case 1: i_mode = SORT_ARTIST;break;
+    case 2: i_mode = SORT_DURATION; break;
+    }
+    if( p_root )
+        playlist_RecursiveNodeSort( p_playlist, p_root, i_mode,
+                                    order == Qt::AscendingOrder ? ORDER_NORMAL :
+                                                            ORDER_REVERSE );
+    PL_UNLOCK
+    rebuild();
+}
+
+void PLModel::search( QString search_text )
+{
+    /** \todo Fire the search with a small delay ? */
+    PL_LOCK;
+    playlist_item_t *p_root = playlist_ItemGetById( p_playlist,rootItem->i_id );
+    assert( p_root );
+    char *psz_name = search_text.toUtf8().data();
+    playlist_LiveSearchUpdate( p_playlist , p_root, psz_name );
+    PL_UNLOCK;
+    rebuild();
+}
+
+/*********** Popup *********/
+void PLModel::popup( QModelIndex & index, QPoint &point, QModelIndexList list )
+{
+    assert( index.isValid() );
+    PL_LOCK;
+    playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
+                                                    itemId( index ) );
+    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_PREPARSE), this, SLOT( popupPreparse() ) );
+        menu->addAction( qfu(I_POP_DEL), this, SLOT( popupDel() ) );
+        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() ) );
+        }
+        menu->popup( point );
+    }
+    else
+        PL_UNLOCK;
+}
+
+void PLModel::popupDel()
+{
+    doDelete( current_selection );
+}
+void PLModel::popupPlay()
+{
+    PL_LOCK;
+    playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_popup_item );
+    activateItem( p_item );
+    PL_UNLOCK;
+}
+
 /**********************************************************************
  * Playlist callbacks
  **********************************************************************/