playlist_model.cpp
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:30k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * playlist_model.cpp : Manage playlist model
  3.  ****************************************************************************
  4.  * Copyright (C) 2006-2007 the VideoLAN team
  5.  * $Id: add3ddc6e7c01c9970bab64f3110888689dcaced $
  6.  *
  7.  * Authors: Clément Stenac <zorglub@videolan.org>
  8.  *
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  22.  *****************************************************************************/
  23. #ifdef HAVE_CONFIG_H
  24. # include "config.h"
  25. #endif
  26. #include "qt4.hpp"
  27. #include "dialogs_provider.hpp"
  28. #include "components/playlist/playlist_model.hpp"
  29. #include "dialogs/mediainfo.hpp"
  30. #include "dialogs/playlist.hpp"
  31. #include <vlc_intf_strings.h>
  32. #include "pixmaps/types/type_unknown.xpm"
  33. #include <assert.h>
  34. #include <QIcon>
  35. #include <QFont>
  36. #include <QMenu>
  37. #include <QApplication>
  38. #include <QSettings>
  39. #include "sorting.h"
  40. QIcon PLModel::icons[ITEM_TYPE_NUMBER];
  41. static int PlaylistChanged( vlc_object_t *, const char *,
  42.                             vlc_value_t, vlc_value_t, void * );
  43. static int PlaylistNext( vlc_object_t *, const char *,
  44.                          vlc_value_t, vlc_value_t, void * );
  45. static int ItemChanged( vlc_object_t *, const char *,
  46.                         vlc_value_t, vlc_value_t, void * );
  47. static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
  48.                          vlc_value_t oval, vlc_value_t nval, void *param );
  49. static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
  50.                         vlc_value_t oval, vlc_value_t nval, void *param );
  51. /*************************************************************************
  52.  * Playlist model implementation
  53.  *************************************************************************/
  54. /*
  55.   This model is called two times, for the selector and the standard panel
  56. */
  57. PLModel::PLModel( playlist_t *_p_playlist,  /* THEPL */
  58.                   intf_thread_t *_p_intf,   /* main Qt p_intf */
  59.                   playlist_item_t * p_root,
  60.                   /*playlist_GetPreferredNode( THEPL, THEPL->p_local_category );
  61.                     and THEPL->p_root_category for SelectPL */
  62.                   int _i_depth,             /* -1 for StandPL, 1 for SelectPL */
  63.                   QObject *parent )         /* Basic Qt parent */
  64.                   : QAbstractItemModel( parent )
  65. {
  66.     i_depth = _i_depth;
  67.     assert( i_depth == DEPTH_SEL || i_depth == DEPTH_PL );
  68.     p_intf            = _p_intf;
  69.     p_playlist        = _p_playlist;
  70.     i_cached_id       = -1;
  71.     i_cached_input_id = -1;
  72.     i_popup_item      = i_popup_parent = -1;
  73.     rootItem          = NULL; /* PLItem rootItem, will be set in rebuild( ) */
  74.     /* Icons initialization */
  75. #define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( QPixmap( x ) )
  76.     ADD_ICON( UNKNOWN , type_unknown_xpm );
  77.     ADD_ICON( FILE, ":/type_file" );
  78.     ADD_ICON( DIRECTORY, ":/type_directory" );
  79.     ADD_ICON( DISC, ":/disc" );
  80.     ADD_ICON( CDDA, ":/cdda" );
  81.     ADD_ICON( CARD, ":/capture-card" );
  82.     ADD_ICON( NET, ":/type_net" );
  83.     ADD_ICON( PLAYLIST, ":/type_playlist" );
  84.     ADD_ICON( NODE, ":/type_node" );
  85. #undef ADD_ICON
  86.     rebuild( p_root );
  87.     CONNECT( THEMIM->getIM(), metaChanged( int ),
  88.             this, ProcessInputItemUpdate( int ) );
  89.     CONNECT( THEMIM, inputChanged( input_thread_t * ),
  90.             this, ProcessInputItemUpdate( input_thread_t* ) );
  91. }
  92. PLModel::~PLModel()
  93. {
  94.     if(i_depth == -1)
  95.         getSettings()->setValue( "qt-pl-showflags", rootItem->i_showflags );
  96.     delCallbacks();
  97.     delete rootItem;
  98. }
  99. Qt::DropActions PLModel::supportedDropActions() const
  100. {
  101.     return Qt::CopyAction; /* Why not Qt::MoveAction */
  102. }
  103. Qt::ItemFlags PLModel::flags( const QModelIndex &index ) const
  104. {
  105.     Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index );
  106.     if( index.isValid() )
  107.         return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
  108.     else if ( rootItem->i_id != p_playlist->p_root_onelevel->i_id
  109.           && rootItem->i_id != p_playlist->p_root_category->i_id )
  110.               defaultFlags |= Qt::ItemIsDropEnabled;
  111.     return defaultFlags;
  112. }
  113. /* A list of model indexes are a playlist */
  114. QStringList PLModel::mimeTypes() const
  115. {
  116.     QStringList types;
  117.     types << "vlc/playlist-item-id";
  118.     return types;
  119. }
  120. QMimeData *PLModel::mimeData( const QModelIndexList &indexes ) const
  121. {
  122.     QMimeData *mimeData = new QMimeData();
  123.     QByteArray encodedData;
  124.     QDataStream stream( &encodedData, QIODevice::WriteOnly );
  125.     foreach( const QModelIndex &index, indexes ) {
  126.         if( index.isValid() && index.column() == 0 )
  127.             stream << itemId( index );
  128.     }
  129.     mimeData->setData( "vlc/playlist-item-id", encodedData );
  130.     return mimeData;
  131. }
  132. /* Drop operation */
  133. bool PLModel::dropMimeData( const QMimeData *data, Qt::DropAction action,
  134.                            int row, int column, const QModelIndex &target )
  135. {
  136.     if( data->hasFormat( "vlc/playlist-item-id" ) )
  137.     {
  138.         if( action == Qt::IgnoreAction )
  139.             return true;
  140.         if( !target.isValid() )
  141.             /* We don't want to move on an invalid position */
  142.             return true;
  143.         PLItem *targetItem = static_cast<PLItem*>( target.internalPointer() );
  144.         QByteArray encodedData = data->data( "vlc/playlist-item-id" );
  145.         QDataStream stream( &encodedData, QIODevice::ReadOnly );
  146.         PLItem *newParentItem;
  147.         while( !stream.atEnd() )
  148.         {
  149.             int i;
  150.             int srcId;
  151.             stream >> srcId;
  152.             PL_LOCK;
  153.             playlist_item_t *p_target =
  154.                         playlist_ItemGetById( p_playlist, targetItem->i_id );
  155.             playlist_item_t *p_src = playlist_ItemGetById( p_playlist, srcId );
  156.             if( !p_target || !p_src )
  157.             {
  158.                 PL_UNLOCK;
  159.                 return false;
  160.             }
  161.             if( p_target->i_children == -1 ) /* A leaf */
  162.             {
  163.                 PLItem *parentItem = targetItem->parent();
  164.                 assert( parentItem );
  165.                 playlist_item_t *p_parent =
  166.                          playlist_ItemGetById( p_playlist, parentItem->i_id );
  167.                 if( !p_parent )
  168.                 {
  169.                     PL_UNLOCK;
  170.                     return false;
  171.                 }
  172.                 for( i = 0 ; i< p_parent->i_children ; i++ )
  173.                     if( p_parent->pp_children[i] == p_target ) break;
  174.                 // Move the item to the element after i
  175.                 playlist_TreeMove( p_playlist, p_src, p_parent, i + 1 );
  176.                 newParentItem = parentItem;
  177.             }
  178.             else
  179.             {
  180.                 /* todo: if we drop on a top-level node, use copy instead ? */
  181.                 playlist_TreeMove( p_playlist, p_src, p_target, 0 );
  182.                 i = 0;
  183.                 newParentItem = targetItem;
  184.             }
  185.             PL_UNLOCK;
  186.         }
  187.         /*TODO: That's not a good idea to rebuild the playlist */
  188.         rebuild();
  189.     }
  190.     return true;
  191. }
  192. /* remove item with its id */
  193. void PLModel::removeItem( int i_id )
  194. {
  195.     PLItem *item = FindById( rootItem, i_id );
  196.     if( item ) item->remove( item );
  197. }
  198. /* callbacks and slots */
  199. void PLModel::addCallbacks()
  200. {
  201.     /* Some global changes happened -> Rebuild all */
  202.     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, this );
  203.     /* We went to the next item
  204.     var_AddCallback( p_playlist, "item-current", PlaylistNext, this );
  205.     */
  206.     /* One item has been updated */
  207.     var_AddCallback( p_playlist, "playlist-item-append", ItemAppended, this );
  208.     var_AddCallback( p_playlist, "playlist-item-deleted", ItemDeleted, this );
  209. }
  210. void PLModel::delCallbacks()
  211. {
  212.     var_DelCallback( p_playlist, "item-change", ItemChanged, this );
  213.     /*
  214.     var_DelCallback( p_playlist, "item-current", PlaylistNext, this );
  215.     */
  216.     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, this );
  217.     var_DelCallback( p_playlist, "playlist-item-append", ItemAppended, this );
  218.     var_DelCallback( p_playlist, "playlist-item-deleted", ItemDeleted, this );
  219. }
  220. void PLModel::activateItem( const QModelIndex &index )
  221. {
  222.     assert( index.isValid() );
  223.     PLItem *item = static_cast<PLItem*>(index.internalPointer());
  224.     assert( item );
  225.     PL_LOCK;
  226.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
  227.     activateItem( p_item );
  228.     PL_UNLOCK;
  229. }
  230. /* Must be entered with lock */
  231. void PLModel::activateItem( playlist_item_t *p_item )
  232. {
  233.     if( !p_item ) return;
  234.     playlist_item_t *p_parent = p_item;
  235.     while( p_parent )
  236.     {
  237.         if( p_parent->i_id == rootItem->i_id ) break;
  238.         p_parent = p_parent->p_parent;
  239.     }
  240.     if( p_parent )
  241.         playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
  242.                           p_parent, p_item );
  243. }
  244. /****************** Base model mandatory implementations *****************/
  245. QVariant PLModel::data( const QModelIndex &index, int role ) const
  246. {
  247.     if( !index.isValid() ) return QVariant();
  248.     PLItem *item = static_cast<PLItem*>(index.internalPointer());
  249.     if( role == Qt::DisplayRole )
  250.     {
  251.         return QVariant( item->columnString( index.column() ) );
  252.     }
  253.     else if( role == Qt::DecorationRole && index.column() == 0  )
  254.     {
  255.         /* Use to segfault here because i_type wasn't always initialized */
  256.         if( item->i_type >= 0 )
  257.             return QVariant( PLModel::icons[item->i_type] );
  258.     }
  259.     else if( role == Qt::FontRole )
  260.     {
  261.         if( item->b_current == true )
  262.         {
  263.             QFont f; f.setBold( true ); return QVariant( f );
  264.         }
  265.     }
  266.     return QVariant();
  267. }
  268. bool PLModel::isCurrent( const QModelIndex &index )
  269. {
  270.     assert( index.isValid() );
  271.     return static_cast<PLItem*>(index.internalPointer())->b_current;
  272. }
  273. int PLModel::itemId( const QModelIndex &index ) const
  274. {
  275.     assert( index.isValid() );
  276.     return static_cast<PLItem*>(index.internalPointer())->i_id;
  277. }
  278. QVariant PLModel::headerData( int section, Qt::Orientation orientation,
  279.                               int role ) const
  280. {
  281.     if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
  282.             return QVariant( rootItem->columnString( section ) );
  283.     return QVariant();
  284. }
  285. QModelIndex PLModel::index( int row, int column, const QModelIndex &parent )
  286.                   const
  287. {
  288.     PLItem *parentItem;
  289.     if( !parent.isValid() )
  290.         parentItem = rootItem;
  291.     else
  292.         parentItem = static_cast<PLItem*>(parent.internalPointer());
  293.     PLItem *childItem = parentItem->child( row );
  294.     if( childItem )
  295.         return createIndex( row, column, childItem );
  296.     else
  297.         return QModelIndex();
  298. }
  299. /* Return the index of a given item */
  300. QModelIndex PLModel::index( PLItem *item, int column ) const
  301. {
  302.     if( !item ) return QModelIndex();
  303.     const PLItem *parent = item->parent();
  304.     if( parent )
  305.         return createIndex( parent->children.lastIndexOf( item ),
  306.                             column, item );
  307.     return QModelIndex();
  308. }
  309. QModelIndex PLModel::parent( const QModelIndex &index ) const
  310. {
  311.     if( !index.isValid() ) return QModelIndex();
  312.     PLItem *childItem = static_cast<PLItem*>(index.internalPointer());
  313.     if( !childItem )
  314.     {
  315.         msg_Err( p_playlist, "NULL CHILD" );
  316.         return QModelIndex();
  317.     }
  318.     PLItem *parentItem = childItem->parent();
  319.     if( !parentItem || parentItem == rootItem ) return QModelIndex();
  320.     if( !parentItem->parentItem )
  321.     {
  322.         msg_Err( p_playlist, "No parent parent, trying row 0 " );
  323.         msg_Err( p_playlist, "----- PLEASE REPORT THIS ------" );
  324.         return createIndex( 0, 0, parentItem );
  325.     }
  326.     QModelIndex ind = createIndex(parentItem->row(), 0, parentItem);
  327.     return ind;
  328. }
  329. int PLModel::columnCount( const QModelIndex &i) const
  330. {
  331.     return rootItem->item_col_strings.count();
  332. }
  333. int PLModel::childrenCount( const QModelIndex &parent ) const
  334. {
  335.     return rowCount( parent );
  336. }
  337. int PLModel::rowCount( const QModelIndex &parent ) const
  338. {
  339.     PLItem *parentItem;
  340.     if( !parent.isValid() )
  341.         parentItem = rootItem;
  342.     else
  343.         parentItem = static_cast<PLItem*>(parent.internalPointer());
  344.     return parentItem->childCount();
  345. }
  346. QStringList PLModel::selectedURIs()
  347. {
  348.     QStringList lst;
  349.     for( int i = 0; i < current_selection.size(); i++ )
  350.     {
  351.         PL_LOCK;
  352.         PLItem *item = static_cast<PLItem*>
  353.                     (current_selection[i].internalPointer());
  354.         if( item )
  355.         {
  356.             playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
  357.             if( p_item )
  358.             {
  359.                 char *psz = input_item_GetURI( p_item->p_input );
  360.                 if( psz )
  361.                 {
  362.                     lst.append( psz );
  363.                     free( psz );
  364.                 }
  365.             }
  366.         }
  367.         PL_UNLOCK;
  368.     }
  369.     return lst;
  370. }
  371. /************************* General playlist status ***********************/
  372. bool PLModel::hasRandom()
  373. {
  374.     return var_GetBool( p_playlist, "random" );
  375. }
  376. bool PLModel::hasRepeat()
  377. {
  378.     return var_GetBool( p_playlist, "repeat" );
  379. }
  380. bool PLModel::hasLoop()
  381. {
  382.     return var_GetBool( p_playlist, "loop" );
  383. }
  384. void PLModel::setLoop( bool on )
  385. {
  386.     var_SetBool( p_playlist, "loop", on ? true:false );
  387.     config_PutInt( p_playlist, "loop", on ? 1: 0 );
  388. }
  389. void PLModel::setRepeat( bool on )
  390. {
  391.     var_SetBool( p_playlist, "repeat", on ? true:false );
  392.     config_PutInt( p_playlist, "repeat", on ? 1: 0 );
  393. }
  394. void PLModel::setRandom( bool on )
  395. {
  396.     var_SetBool( p_playlist, "random", on ? true:false );
  397.     config_PutInt( p_playlist, "random", on ? 1: 0 );
  398. }
  399. /************************* Lookups *****************************/
  400. PLItem *PLModel::FindById( PLItem *root, int i_id )
  401. {
  402.     return FindInner( root, i_id, false );
  403. }
  404. PLItem *PLModel::FindByInput( PLItem *root, int i_id )
  405. {
  406.     return FindInner( root, i_id, true );
  407. }
  408. #define CACHE( i, p ) { i_cached_id = i; p_cached_item = p; }
  409. #define ICACHE( i, p ) { i_cached_input_id = i; p_cached_item_bi = p; }
  410. PLItem * PLModel::FindInner( PLItem *root, int i_id, bool b_input )
  411. {
  412.     if( ( !b_input && i_cached_id == i_id) ||
  413.         ( b_input && i_cached_input_id ==i_id ) )
  414.     {
  415.         return b_input ? p_cached_item_bi : p_cached_item;
  416.     }
  417.     if( !b_input && root->i_id == i_id )
  418.     {
  419.         CACHE( i_id, root );
  420.         return root;
  421.     }
  422.     else if( b_input && root->i_input_id == i_id )
  423.     {
  424.         ICACHE( i_id, root );
  425.         return root;
  426.     }
  427.     QList<PLItem *>::iterator it = root->children.begin();
  428.     while ( it != root->children.end() )
  429.     {
  430.         if( !b_input && (*it)->i_id == i_id )
  431.         {
  432.             CACHE( i_id, (*it) );
  433.             return p_cached_item;
  434.         }
  435.         else if( b_input && (*it)->i_input_id == i_id )
  436.         {
  437.             ICACHE( i_id, (*it) );
  438.             return p_cached_item_bi;
  439.         }
  440.         if( (*it)->children.size() )
  441.         {
  442.             PLItem *childFound = FindInner( (*it), i_id, b_input );
  443.             if( childFound )
  444.             {
  445.                 if( b_input )
  446.                     ICACHE( i_id, childFound )
  447.                 else
  448.                     CACHE( i_id, childFound )
  449.                 return childFound;
  450.             }
  451.         }
  452.         it++;
  453.     }
  454.     return NULL;
  455. }
  456. #undef CACHE
  457. #undef ICACHE
  458. /************************* Updates handling *****************************/
  459. void PLModel::customEvent( QEvent *event )
  460. {
  461.     int type = event->type();
  462.     if( type != ItemAppend_Type &&
  463.         type != ItemDelete_Type && type != PLUpdate_Type )
  464.         return;
  465.     PLEvent *ple = static_cast<PLEvent *>(event);
  466.     if( type == ItemAppend_Type )
  467.         ProcessItemAppend( &ple->add );
  468.     else if( type == ItemDelete_Type )
  469.         ProcessItemRemoval( ple->i_id );
  470.     else
  471.         rebuild();
  472. }
  473. /**** Events processing ****/
  474. void PLModel::ProcessInputItemUpdate( input_thread_t *p_input )
  475. {
  476.     if( !p_input ) return;
  477.     ProcessInputItemUpdate( input_GetItem( p_input )->i_id );
  478.     if( p_input && !( p_input->b_dead || !vlc_object_alive( p_input ) ) )
  479.     {
  480.         PLItem *item = FindByInput( rootItem, input_GetItem( p_input )->i_id );
  481.         emit currentChanged( index( item, 0 ) );
  482.     }
  483. }
  484. void PLModel::ProcessInputItemUpdate( int i_input_id )
  485. {
  486.     if( i_input_id <= 0 ) return;
  487.     PLItem *item = FindByInput( rootItem, i_input_id );
  488.     if( item )
  489.     {
  490.         QPL_LOCK;
  491.         UpdateTreeItem( item, true );
  492.         QPL_UNLOCK;
  493.     }
  494. }
  495. void PLModel::ProcessItemRemoval( int i_id )
  496. {
  497.     if( i_id <= 0 ) return;
  498.     if( i_id == i_cached_id ) i_cached_id = -1;
  499.     i_cached_input_id = -1;
  500.     removeItem( i_id );
  501. }
  502. void PLModel::ProcessItemAppend( const playlist_add_t *p_add )
  503. {
  504.     playlist_item_t *p_item = NULL;
  505.     PLItem *newItem = NULL;
  506.     PLItem *nodeItem = FindById( rootItem, p_add->i_node );
  507.     PL_LOCK;
  508.     if( !nodeItem ) goto end;
  509.     p_item = playlist_ItemGetById( p_playlist, p_add->i_item );
  510.     if( !p_item || p_item->i_flags & PLAYLIST_DBL_FLAG ) goto end;
  511.     if( i_depth == DEPTH_SEL && p_item->p_parent &&
  512.                         p_item->p_parent->i_id != rootItem->i_id )
  513.         goto end;
  514.     newItem = new PLItem( p_item, nodeItem, this );
  515.     nodeItem->appendChild( newItem );
  516.     UpdateTreeItem( p_item, newItem, true );
  517. end:
  518.     PL_UNLOCK;
  519.     return;
  520. }
  521. void PLModel::rebuild()
  522. {
  523.     rebuild( NULL );
  524. }
  525. void PLModel::rebuild( playlist_item_t *p_root )
  526. {
  527.     playlist_item_t* p_item;
  528.     /* Remove callbacks before locking to avoid deadlocks */
  529.     delCallbacks();
  530.     /* Invalidate cache */
  531.     i_cached_id = i_cached_input_id = -1;
  532.     PL_LOCK;
  533.     /* Clear the tree */
  534.     if( rootItem )
  535.     {
  536.         if( rootItem->children.size() )
  537.         {
  538.             beginRemoveRows( index( rootItem, 0 ), 0,
  539.                     rootItem->children.size() -1 );
  540.             qDeleteAll( rootItem->children );
  541.             rootItem->children.clear();
  542.             endRemoveRows();
  543.         }
  544.     }
  545.     if( p_root )
  546.     {
  547.         delete rootItem;
  548.         rootItem = new PLItem( p_root, getSettings(), this );
  549.     }
  550.     assert( rootItem );
  551.     /* Recreate from root */
  552.     UpdateNodeChildren( rootItem );
  553.     if( (p_item = playlist_CurrentPlayingItem(p_playlist)) )
  554.     {
  555.         PLItem *currentItem = FindByInput( rootItem,
  556.                                            p_item->p_input->i_id );
  557.         if( currentItem )
  558.         {
  559.             UpdateTreeItem( p_item, currentItem,
  560.                             true, false );
  561.         }
  562.     }
  563.     PL_UNLOCK;
  564.     /* And signal the view */
  565.     emit layoutChanged();
  566.     addCallbacks();
  567. }
  568. /* This function must be entered WITH the playlist lock */
  569. void PLModel::UpdateNodeChildren( PLItem *root )
  570. {
  571.     playlist_item_t *p_node = playlist_ItemGetById( p_playlist, root->i_id );
  572.     UpdateNodeChildren( p_node, root );
  573. }
  574. /* This function must be entered WITH the playlist lock */
  575. void PLModel::UpdateNodeChildren( playlist_item_t *p_node, PLItem *root )
  576. {
  577.     for( int i = 0; i < p_node->i_children ; i++ )
  578.     {
  579.         if( p_node->pp_children[i]->i_flags & PLAYLIST_DBL_FLAG ) continue;
  580.         PLItem *newItem =  new PLItem( p_node->pp_children[i], root, this );
  581.         root->appendChild( newItem, false );
  582.         UpdateTreeItem( newItem, false, true );
  583.         if( i_depth == DEPTH_PL && p_node->pp_children[i]->i_children != -1 )
  584.             UpdateNodeChildren( p_node->pp_children[i], newItem );
  585.     }
  586. }
  587. /* This function must be entered WITH the playlist lock */
  588. void PLModel::UpdateTreeItem( PLItem *item, bool signal, bool force )
  589. {
  590.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
  591.     UpdateTreeItem( p_item, item, signal, force );
  592. }
  593. /* This function must be entered WITH the playlist lock */
  594. void PLModel::UpdateTreeItem( playlist_item_t *p_item, PLItem *item,
  595.                               bool signal, bool force )
  596. {
  597.     if ( !p_item )
  598.         return;
  599.     if( !force && i_depth == DEPTH_SEL && p_item->p_parent &&
  600.                                  p_item->p_parent->i_id != rootItem->i_id )
  601.         return;
  602.     item->update( p_item, p_item == playlist_CurrentPlayingItem( p_playlist ) );
  603.     if( signal )
  604.         emit dataChanged( index( item, 0 ) , index( item, 1 ) );
  605. }
  606. /************************* Actions ******************************/
  607. /**
  608.  * Deletion, here we have to do a ugly slow hack as we retrieve the full
  609.  * list of indexes to delete at once: when we delete a node and all of
  610.  * its children, we need to update the list.
  611.  * Todo: investigate whethere we can use ranges to be sure to delete all items?
  612.  */
  613. void PLModel::doDelete( QModelIndexList selected )
  614. {
  615.     for( int i = selected.size() -1 ; i >= 0; i-- )
  616.     {
  617.         QModelIndex index = selected[i];
  618.         if( index.column() != 0 ) continue;
  619.         PLItem *item = static_cast<PLItem*>(index.internalPointer());
  620.         if( item )
  621.         {
  622.             if( item->children.size() )
  623.                 recurseDelete( item->children, &selected );
  624.             doDeleteItem( item, &selected );
  625.         }
  626.     }
  627. }
  628. void PLModel::recurseDelete( QList<PLItem*> children, QModelIndexList *fullList )
  629. {
  630.     for( int i = children.size() - 1; i >= 0 ; i-- )
  631.     {
  632.         PLItem *item = children[i];
  633.         if( item->children.size() )
  634.             recurseDelete( item->children, fullList );
  635.         doDeleteItem( item, fullList );
  636.     }
  637. }
  638. void PLModel::doDeleteItem( PLItem *item, QModelIndexList *fullList )
  639. {
  640.     QModelIndex deleteIndex = index( item, 0 );
  641.     fullList->removeAll( deleteIndex );
  642.     PL_LOCK;
  643.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, item->i_id );
  644.     if( !p_item )
  645.     {
  646.         PL_UNLOCK;
  647.         return;
  648.     }
  649.     if( p_item->i_children == -1 )
  650.         playlist_DeleteFromInput( p_playlist, item->i_input_id, pl_Locked );
  651.     else
  652.         playlist_NodeDelete( p_playlist, p_item, true, false );
  653.     /* And finally, remove it from the tree */
  654.     item->remove( item );
  655.     PL_UNLOCK;
  656. }
  657. /******* Volume III: Sorting and searching ********/
  658. void PLModel::sort( int column, Qt::SortOrder order )
  659. {
  660.     int i_index = -1;
  661.     int i_flag = 0;
  662.     int i_column = 1;
  663.     for( i_column = 1; i_column != COLUMN_END; i_column<<=1 )
  664.     {
  665.         if( ( shownFlags() & i_column ) )
  666.             i_index++;
  667.         if( column == i_index )
  668.         {
  669.             i_flag = i_column;
  670.             goto next;
  671.         }
  672.     }
  673. next:
  674.     PL_LOCK;
  675.     {
  676.         playlist_item_t *p_root = playlist_ItemGetById( p_playlist,
  677.                                                         rootItem->i_id );
  678.         if( p_root && i_flag )
  679.         {
  680.             playlist_RecursiveNodeSort( p_playlist, p_root,
  681.                                         i_column_sorting( i_flag ),
  682.                                         order == Qt::AscendingOrder ?
  683.                                             ORDER_NORMAL : ORDER_REVERSE );
  684.         }
  685.     }
  686.     PL_UNLOCK;
  687.     rebuild();
  688. }
  689. void PLModel::search( const QString& search_text )
  690. {
  691.     /** todo Fire the search with a small delay ? */
  692.     PL_LOCK;
  693.     {
  694.         playlist_item_t *p_root = playlist_ItemGetById( p_playlist,
  695.                                                         rootItem->i_id );
  696.         assert( p_root );
  697.         const char *psz_name = search_text.toUtf8().data();
  698.         playlist_LiveSearchUpdate( p_playlist , p_root, psz_name );
  699.     }
  700.     PL_UNLOCK;
  701.     rebuild();
  702. }
  703. /*********** Popup *********/
  704. void PLModel::popup( QModelIndex & index, QPoint &point, QModelIndexList list )
  705. {
  706.     assert( index.isValid() );
  707.     PL_LOCK;
  708.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist, itemId( index ) );
  709.     if( p_item )
  710.     {
  711.         i_popup_item = p_item->i_id;
  712.         i_popup_parent = p_item->p_parent ? p_item->p_parent->i_id : -1;
  713.         PL_UNLOCK;
  714.         current_selection = list;
  715.         QMenu *menu = new QMenu;
  716.         menu->addAction( qtr(I_POP_PLAY), this, SLOT( popupPlay() ) );
  717.         menu->addAction( qtr(I_POP_DEL), this, SLOT( popupDel() ) );
  718.         menu->addSeparator();
  719.         menu->addAction( qtr(I_POP_STREAM), this, SLOT( popupStream() ) );
  720.         menu->addAction( qtr(I_POP_SAVE), this, SLOT( popupSave() ) );
  721.         menu->addSeparator();
  722.         menu->addAction( qtr(I_POP_INFO), this, SLOT( popupInfo() ) );
  723.         if( p_item->i_children > -1 )
  724.         {
  725.             menu->addSeparator();
  726.             menu->addAction( qtr(I_POP_SORT), this, SLOT( popupSort() ) );
  727.             menu->addAction( qtr(I_POP_ADD), this, SLOT( popupAdd() ) );
  728.         }
  729.         menu->addSeparator();
  730.         menu->addAction( qtr( I_POP_EXPLORE ), this, SLOT( popupExplore() ) );
  731.         menu->popup( point );
  732.     }
  733.     else
  734.         PL_UNLOCK;
  735. }
  736. void PLModel::viewchanged( int meta )
  737. {
  738.     assert( meta );
  739.     int _meta = meta;
  740.     if( rootItem )
  741.     {
  742.         int index=-1;
  743.         while( _meta )
  744.         {
  745.             index++;
  746.             _meta >>= 1;
  747.         }
  748.         /* UNUSED        emit layoutAboutToBeChanged(); */
  749.         index = __MIN( index, rootItem->item_col_strings.count() );
  750.         QModelIndex parent = createIndex( 0, 0, rootItem );
  751.         if( rootItem->i_showflags & meta )
  752.             /* Removing columns */
  753.         {
  754.             beginRemoveColumns( parent, index, index+1 );
  755.             rootItem->i_showflags &= ~( meta );
  756.             getSettings()->setValue( "qt-pl-showflags", rootItem->i_showflags );
  757.             rootItem->updateColumnHeaders();
  758.             endRemoveColumns();
  759.         }
  760.         else
  761.         {
  762.             /* Adding columns */
  763.             beginInsertColumns( parent, index, index+1 );
  764.             rootItem->i_showflags |= meta;
  765.             getSettings()->setValue( "qt-pl-showflags", rootItem->i_showflags );
  766.             rootItem->updateColumnHeaders();
  767.             endInsertColumns();
  768.         }
  769.         emit columnsChanged( meta );
  770.         rebuild();
  771.     }
  772. }
  773. void PLModel::popupDel()
  774. {
  775.     doDelete( current_selection );
  776. }
  777. void PLModel::popupPlay()
  778. {
  779.     PL_LOCK;
  780.     {
  781.         playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
  782.                                                         i_popup_item );
  783.         activateItem( p_item );
  784.     }
  785.     PL_UNLOCK;
  786. }
  787. void PLModel::popupInfo()
  788. {
  789.     PL_LOCK;
  790.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
  791.                                                     i_popup_item );
  792.     if( p_item )
  793.     {
  794.         input_item_t* p_input = p_item->p_input;
  795.         vlc_gc_incref( p_input );
  796.         PL_UNLOCK;
  797.         MediaInfoDialog *mid = new MediaInfoDialog( p_intf, p_input );
  798.         vlc_gc_decref( p_input );
  799.         mid->setParent( PlaylistDialog::getInstance( p_intf ),
  800.                         Qt::Dialog );
  801.         mid->show();
  802.     }
  803. }
  804. void PLModel::popupStream()
  805. {
  806.     QStringList mrls = selectedURIs();
  807.     if( !mrls.isEmpty() )
  808.         THEDP->streamingDialog( NULL, mrls[0], false );
  809. }
  810. void PLModel::popupSave()
  811. {
  812.     QStringList mrls = selectedURIs();
  813.     if( !mrls.isEmpty() )
  814.         THEDP->streamingDialog( NULL, mrls[0] );
  815. }
  816. #include <QUrl>
  817. #include <QFileInfo>
  818. #include <QDesktopServices>
  819. void PLModel::popupExplore()
  820. {
  821.     PL_LOCK;
  822.     playlist_item_t *p_item = playlist_ItemGetById( p_playlist,
  823.                                                     i_popup_item );
  824.     if( p_item )
  825.     {
  826.        input_item_t *p_input = p_item->p_input;
  827.        char *psz_meta = input_item_GetURI( p_input );
  828.        PL_UNLOCK;
  829.        if( psz_meta )
  830.        {
  831.            const char *psz_access;
  832.            const char *psz_demux;
  833.            char  *psz_path;
  834.            input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_meta );
  835.            if( EMPTY_STR( psz_access ) ||
  836.                !strncasecmp( psz_access, "file", 4 ) ||
  837.                !strncasecmp( psz_access, "dire", 4 ) )
  838.            {
  839.                QFileInfo info( qfu( psz_meta ) );
  840.                QDesktopServices::openUrl(
  841.                                QUrl::fromLocalFile( info.absolutePath() ) );
  842.            }
  843.            free( psz_meta );
  844.        }
  845.     }
  846.     else
  847.         PL_UNLOCK;
  848. }
  849. /**********************************************************************
  850.  * Playlist callbacks
  851.  **********************************************************************/
  852. static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
  853.                             vlc_value_t oval, vlc_value_t nval, void *param )
  854. {
  855.     PLModel *p_model = (PLModel *) param;
  856.     PLEvent *event = new PLEvent( PLUpdate_Type, 0 );
  857.     QApplication::postEvent( p_model, event );
  858.     return VLC_SUCCESS;
  859. }
  860. static int PlaylistNext( vlc_object_t *p_this, const char *psz_variable,
  861.                          vlc_value_t oval, vlc_value_t nval, void *param )
  862. {
  863.     PLModel *p_model = (PLModel *) param;
  864.     PLEvent *event = new PLEvent( ItemUpdate_Type, oval.i_int );
  865.     QApplication::postEvent( p_model, event );
  866.     event = new PLEvent( ItemUpdate_Type, nval.i_int );
  867.     QApplication::postEvent( p_model, event );
  868.     return VLC_SUCCESS;
  869. }
  870. static int ItemChanged( vlc_object_t *p_this, const char *psz_variable,
  871.                         vlc_value_t oval, vlc_value_t nval, void *param )
  872. {
  873.     PLModel *p_model = (PLModel *) param;
  874.     PLEvent *event = new PLEvent( ItemUpdate_Type, nval.i_int );
  875.     QApplication::postEvent( p_model, event );
  876.     return VLC_SUCCESS;
  877. }
  878. static int ItemDeleted( vlc_object_t *p_this, const char *psz_variable,
  879.                         vlc_value_t oval, vlc_value_t nval, void *param )
  880. {
  881.     PLModel *p_model = (PLModel *) param;
  882.     PLEvent *event = new PLEvent( ItemDelete_Type, nval.i_int );
  883.     QApplication::postEvent( p_model, event );
  884.     return VLC_SUCCESS;
  885. }
  886. static int ItemAppended( vlc_object_t *p_this, const char *psz_variable,
  887.                          vlc_value_t oval, vlc_value_t nval, void *param )
  888. {
  889.     PLModel *p_model = (PLModel *) param;
  890.     const playlist_add_t *p_add = (playlist_add_t *)nval.p_address;
  891.     PLEvent *event = new PLEvent( p_add );
  892.     QApplication::postEvent( p_model, event );
  893.     return VLC_SUCCESS;
  894. }