1 /*****************************************************************************
2 * playlist_model.cpp : Manage playlist model
3 ****************************************************************************
4 * Copyright (C) 2006-2011 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Ilkka Ollakkka <ileoo (at) videolan dot org>
9 * Jakob Leben <jleben@videolan.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
31 #include "components/playlist/playlist_model.hpp"
32 #include "input_manager.hpp" /* THEMIM */
34 #include <vlc_intf_strings.h> /* I_DIR */
36 #include "pixmaps/types/type_unknown.xpm"
46 QIcon
PLModel::icons
[ITEM_TYPE_NUMBER
];
48 /*************************************************************************
49 * Playlist model implementation
50 *************************************************************************/
52 PLModel::PLModel( playlist_t
*_p_playlist
, /* THEPL */
53 intf_thread_t
*_p_intf
, /* main Qt p_intf */
54 playlist_item_t
* p_root
,
55 QObject
*parent
) /* Basic Qt parent */
56 : VLCModel( _p_intf
, parent
)
58 p_playlist
= _p_playlist
;
60 i_cached_input_id
= -1;
62 rootItem
= NULL
; /* PLItem rootItem, will be set in rebuild( ) */
63 latestSearch
= QString();
65 /* Icons initialization */
66 #define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( x )
67 ADD_ICON( UNKNOWN
, type_unknown_xpm
);
68 ADD_ICON( FILE, ":/type/file" );
69 ADD_ICON( DIRECTORY
, ":/type/directory" );
70 ADD_ICON( DISC
, ":/type/disc" );
71 ADD_ICON( CDDA
, ":/type/cdda" );
72 ADD_ICON( CARD
, ":/type/capture-card" );
73 ADD_ICON( NET
, ":/type/net" );
74 ADD_ICON( PLAYLIST
, ":/type/playlist" );
75 ADD_ICON( NODE
, ":/type/node" );
79 DCONNECT( THEMIM
->getIM(), metaChanged( input_item_t
*),
80 this, processInputItemUpdate( input_item_t
*) );
81 DCONNECT( THEMIM
, inputChanged( input_thread_t
* ),
82 this, processInputItemUpdate( input_thread_t
* ) );
83 CONNECT( THEMIM
, playlistItemAppended( int, int ),
84 this, processItemAppend( int, int ) );
85 CONNECT( THEMIM
, playlistItemRemoved( int ),
86 this, processItemRemoval( int ) );
87 CONNECT( &insertBufferCommitTimer
, timeout(), this, commitBufferedRowInserts() );
95 Qt::DropActions
PLModel::supportedDropActions() const
97 return Qt::CopyAction
| Qt::MoveAction
;
100 Qt::ItemFlags
PLModel::flags( const QModelIndex
&index
) const
102 Qt::ItemFlags flags
= QAbstractItemModel::flags( index
);
104 const PLItem
*item
= index
.isValid() ? getItem( index
) : rootItem
;
109 playlist_item_t
*plItem
=
110 playlist_ItemGetById( p_playlist
, item
->i_id
);
112 if ( plItem
&& ( plItem
->i_children
> -1 ) )
113 flags
|= Qt::ItemIsDropEnabled
;
118 flags
|= Qt::ItemIsDragEnabled
;
123 QStringList
PLModel::mimeTypes() const
126 types
<< "vlc/qt-input-items";
130 bool modelIndexLessThen( const QModelIndex
&i1
, const QModelIndex
&i2
)
132 if( !i1
.isValid() || !i2
.isValid() ) return false;
133 PLItem
*item1
= static_cast<PLItem
*>( i1
.internalPointer() );
134 PLItem
*item2
= static_cast<PLItem
*>( i2
.internalPointer() );
135 if( item1
->hasSameParent( item2
) ) return i1
.row() < i2
.row();
136 else return *item1
< *item2
;
139 QMimeData
*PLModel::mimeData( const QModelIndexList
&indexes
) const
141 PlMimeData
*plMimeData
= new PlMimeData();
142 QModelIndexList list
;
144 foreach( const QModelIndex
&index
, indexes
) {
145 if( index
.isValid() && index
.column() == 0 )
149 qSort(list
.begin(), list
.end(), modelIndexLessThen
);
151 AbstractPLItem
*item
= NULL
;
152 foreach( const QModelIndex
&index
, list
) {
155 AbstractPLItem
*testee
= getItem( index
);
156 while( testee
->parent() )
158 if( testee
->parent() == item
||
159 testee
->parent() == item
->parent() ) break;
160 testee
= testee
->parent();
162 if( testee
->parent() == item
) continue;
163 item
= getItem( index
);
166 item
= getItem( index
);
168 plMimeData
->appendItem( static_cast<PLItem
*>(item
)->inputItem() );
175 bool PLModel::dropMimeData( const QMimeData
*data
, Qt::DropAction action
,
176 int row
, int, const QModelIndex
&parent
)
178 bool copy
= action
== Qt::CopyAction
;
179 if( !copy
&& action
!= Qt::MoveAction
)
182 const PlMimeData
*plMimeData
= qobject_cast
<const PlMimeData
*>( data
);
186 dropAppendCopy( plMimeData
, getItem( parent
), row
);
188 dropMove( plMimeData
, getItem( parent
), row
);
193 void PLModel::dropAppendCopy( const PlMimeData
*plMimeData
, PLItem
*target
, int pos
)
197 playlist_item_t
*p_parent
=
198 playlist_ItemGetByInput( p_playlist
, target
->inputItem() );
199 if( !p_parent
) return;
201 if( pos
== -1 ) pos
= PLAYLIST_END
;
203 QList
<input_item_t
*> inputItems
= plMimeData
->inputItems();
205 foreach( input_item_t
* p_input
, inputItems
)
207 playlist_item_t
*p_item
= playlist_ItemGetByInput( p_playlist
, p_input
);
208 if( !p_item
) continue;
209 pos
= playlist_NodeAddCopy( p_playlist
, p_item
, p_parent
, pos
);
215 void PLModel::dropMove( const PlMimeData
* plMimeData
, PLItem
*target
, int row
)
217 QList
<input_item_t
*> inputItems
= plMimeData
->inputItems();
218 QList
<PLItem
*> model_items
;
219 playlist_item_t
**pp_items
;
220 pp_items
= (playlist_item_t
**)
221 calloc( inputItems
.count(), sizeof( playlist_item_t
* ) );
222 if ( !pp_items
) return;
226 playlist_item_t
*p_parent
=
227 playlist_ItemGetByInput( p_playlist
, target
->inputItem() );
229 if( !p_parent
|| row
> p_parent
->i_children
)
236 int new_pos
= row
== -1 ? p_parent
->i_children
: row
;
237 int model_pos
= new_pos
;
240 foreach( input_item_t
*p_input
, inputItems
)
242 playlist_item_t
*p_item
= playlist_ItemGetByInput( p_playlist
, p_input
);
243 if( !p_item
) continue;
245 PLItem
*item
= findByInput( rootItem
, p_input
->i_id
);
246 if( !item
) continue;
248 /* Better not try to move a node into itself.
249 Abort the whole operation in that case,
250 because it is ambiguous. */
251 AbstractPLItem
*climber
= target
;
254 if( climber
== item
)
260 climber
= climber
->parent();
263 if( item
->parent() == target
&&
264 target
->children
.indexOf( item
) < new_pos
)
267 model_items
.append( item
);
268 pp_items
[i
] = p_item
;
272 if( model_items
.isEmpty() )
279 playlist_TreeMoveMany( p_playlist
, i
, pp_items
, p_parent
, new_pos
);
283 foreach( PLItem
*item
, model_items
)
286 insertChildren( target
, model_items
, model_pos
);
290 /* remove item with its id */
291 void PLModel::removeItem( int i_id
)
293 PLItem
*item
= findById( rootItem
, i_id
);
297 void PLModel::activateItem( const QModelIndex
&index
)
299 assert( index
.isValid() );
300 const PLItem
*item
= getItem( index
);
303 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, item
->i_id
);
304 activateItem( p_item
);
308 /* Convenient overloaded private version of activateItem
309 * Must be entered with PL lock */
310 void PLModel::activateItem( playlist_item_t
*p_item
)
312 if( !p_item
) return;
313 playlist_item_t
*p_parent
= p_item
;
316 if( p_parent
->i_id
== rootItem
->id() ) break;
317 p_parent
= p_parent
->p_parent
;
320 playlist_Control( p_playlist
, PLAYLIST_VIEWPLAY
, pl_Locked
,
324 /****************** Base model mandatory implementations *****************/
325 QVariant
PLModel::data( const QModelIndex
&index
, const int role
) const
327 if( !index
.isValid() ) return QVariant();
328 PLItem
*item
= getItem( index
);
329 if( role
== Qt::DisplayRole
)
331 int metadata
= columnToMeta( index
.column() );
332 if( metadata
== COLUMN_END
) return QVariant();
335 if( metadata
== COLUMN_NUMBER
)
336 returninfo
= QString::number( index
.row() + 1 );
337 else if( metadata
== COLUMN_COVER
)
340 artUrl
= InputManager::decodeArtURL( item
->inputItem() );
341 if( artUrl
.isEmpty() )
343 for( int i
= 0; i
< item
->childCount(); i
++ )
345 artUrl
= InputManager::decodeArtURL( item
->child( i
)->inputItem() );
346 if( !artUrl
.isEmpty() )
350 return QVariant( artUrl
);
354 char *psz
= psz_column_meta( item
->inputItem(), metadata
);
355 returninfo
= qfu( psz
);
358 return QVariant( returninfo
);
360 else if( role
== Qt::DecorationRole
&& index
.column() == 0 )
362 /* Used to segfault here because i_type wasn't always initialized */
363 return QVariant( PLModel::icons
[item
->inputItem()->i_type
] );
365 else if( role
== Qt::FontRole
)
367 return QVariant( QFont() );
369 else if( role
== Qt::ToolTipRole
)
371 int i_art_policy
= var_GetInteger( p_playlist
, "album-art" );
373 /* FIXME: Skip, as we don't want the pixmap and do not know the cached art file */
374 if ( i_art_policy
== ALBUM_ART_ALL
)
375 artUrl
= getArtUrl( index
);
376 if ( artUrl
.isEmpty() ) artUrl
= ":/noart";
377 QString duration
= qtr( "unknown" );
380 input_item_t
*p_item
= item
->inputItem();
386 if ( p_item
->i_duration
> 0 )
388 char *psz
= psz_column_meta( item
->inputItem(), COLUMN_DURATION
);
389 duration
= qfu( psz
);
392 name
= qfu( p_item
->psz_name
);
394 QPixmap image
= getArtPixmap( index
, QSize( 128, 128 ) );
396 QBuffer
buffer( &bytes
);
397 buffer
.open( QIODevice::WriteOnly
);
398 image
.save(&buffer
, "BMP"); /* uncompressed, see qpixmap#reading-and-writing-image-files */
399 return QVariant( QString("<img width=\"128\" height=\"128\" align=\"left\" src=\"data:image/bmp;base64,%1\"/><div><b>%2</b><br/>%3</div>")
400 .arg( bytes
.toBase64().constData() )
402 .arg( qtr("Duration") + ": " + duration
)
405 else if( role
== Qt::BackgroundRole
&& isCurrent( index
) )
407 return QVariant( QBrush( Qt::gray
) );
409 else if( role
== IsCurrentRole
)
411 return QVariant( isCurrent( index
) );
413 else if( role
== IsLeafNodeRole
)
417 playlist_item_t
*plItem
=
418 playlist_ItemGetById( p_playlist
, item
->i_id
);
421 isLeaf
= plItem
->i_children
== -1;
426 else if( role
== IsCurrentsParentNodeRole
)
428 return QVariant( isParent( index
, currentIndex() ) );
433 /* Seek from current index toward the top and see if index is one of parent nodes */
434 bool PLModel::isParent( const QModelIndex
&index
, const QModelIndex
¤t
) const
436 if( !index
.isValid() )
439 if( index
== current
)
442 if( !current
.isValid() || !current
.parent().isValid() )
445 return isParent( index
, current
.parent() );
448 bool PLModel::isCurrent( const QModelIndex
&index
) const
450 return getItem( index
)->inputItem() == THEMIM
->currentInputItem();
453 int PLModel::itemId( const QModelIndex
&index
) const
455 return getItem( index
)->id();
458 input_item_t
* PLModel::getInputItem( const QModelIndex
&index
) const
460 return getItem( index
)->inputItem();
463 QString
PLModel::getURI( const QModelIndex
&index
) const
466 input_item_t
*p_item
= getItem( index
)->inputItem();
467 /* no PL lock as item gets refcount +1 from PLItem, which only depends of events */
468 vlc_mutex_lock( &p_item
->lock
);
469 uri
= qfu( p_item
->psz_uri
);
470 vlc_mutex_unlock( &p_item
->lock
);
474 QString
PLModel::getTitle( const QModelIndex
&index
) const
477 input_item_t
*p_item
= getItem( index
)->inputItem();
478 char *fb_name
= input_item_GetTitle( p_item
);
479 if( EMPTY_STR( fb_name
) )
482 fb_name
= input_item_GetName( p_item
);
484 title
= qfu(fb_name
);
489 bool PLModel::isCurrentItem( const QModelIndex
&index
, playLocation where
) const
491 if ( where
== IN_PLAYLIST
)
493 return itemId( index
) == THEPL
->p_playing
->i_id
;
495 else if ( where
== IN_MEDIALIBRARY
)
497 return THEPL
->p_media_library
&&
498 itemId( index
) == THEPL
->p_media_library
->i_id
;
503 QVariant
PLModel::headerData( int section
, Qt::Orientation orientation
,
506 if (orientation
!= Qt::Horizontal
|| role
!= Qt::DisplayRole
)
509 int meta_col
= columnToMeta( section
);
511 if( meta_col
== COLUMN_END
) return QVariant();
513 return QVariant( qfu( psz_column_title( meta_col
) ) );
516 QModelIndex
PLModel::index( const int row
, const int column
, const QModelIndex
&parent
)
519 PLItem
*parentItem
= parent
.isValid() ? getItem( parent
) : rootItem
;
521 PLItem
*childItem
= static_cast<PLItem
*>(parentItem
->child( row
));
523 return createIndex( row
, column
, childItem
);
525 return QModelIndex();
528 QModelIndex
PLModel::index( const int i_id
, const int c
)
530 return index( findById( rootItem
, i_id
), c
);
533 QModelIndex
PLModel::rootIndex() const
535 return index( findById( rootItem
, rootItem
->id() ), 0 );
538 bool PLModel::isTree() const
540 return ( ( rootItem
&& rootItem
->id() != p_playlist
->p_playing
->i_id
)
541 || var_InheritBool( p_intf
, "playlist-tree" ) );
544 /* Return the index of a given item */
545 QModelIndex
PLModel::index( PLItem
*item
, int column
) const
547 if( !item
) return QModelIndex();
548 AbstractPLItem
*parent
= item
->parent();
550 return createIndex( parent
->lastIndexOf( item
),
552 return QModelIndex();
555 QModelIndex
PLModel::currentIndex() const
557 input_thread_t
*p_input_thread
= THEMIM
->getInput();
558 if( !p_input_thread
) return QModelIndex();
559 PLItem
*item
= findByInput( rootItem
, input_GetItem( p_input_thread
)->i_id
);
560 return index( item
, 0 );
563 QModelIndex
PLModel::parent( const QModelIndex
&index
) const
565 if( !index
.isValid() ) return QModelIndex();
567 PLItem
*childItem
= getItem( index
);
570 msg_Err( p_playlist
, "Item not found" );
571 return QModelIndex();
574 PLItem
*parentItem
= static_cast<PLItem
*>(childItem
->parent());
575 if( !parentItem
|| parentItem
== rootItem
) return QModelIndex();
576 if( !parentItem
->parent() )
578 msg_Err( p_playlist
, "No parent found, trying row 0. Please report this" );
579 return createIndex( 0, 0, parentItem
);
581 return createIndex(parentItem
->row(), 0, parentItem
);
584 int PLModel::rowCount( const QModelIndex
&parent
) const
586 PLItem
*parentItem
= parent
.isValid() ? getItem( parent
) : rootItem
;
587 return parentItem
->childCount();
590 /************************* Lookups *****************************/
591 PLItem
*PLModel::findById( PLItem
*root
, int i_id
) const
593 return findInner( root
, i_id
, false );
596 PLItem
*PLModel::findByInput( PLItem
*root
, int i_id
) const
598 PLItem
*result
= findInner( root
, i_id
, true );
602 PLItem
* PLModel::findInner( PLItem
*root
, int i_id
, bool b_input
) const
604 if( !root
) return NULL
;
606 if( !b_input
&& root
->id() == i_id
)
609 else if( b_input
&& root
->inputItem()->i_id
== i_id
)
612 QList
<AbstractPLItem
*>::iterator it
= root
->children
.begin();
613 while ( it
!= root
->children
.end() )
615 PLItem
*item
= static_cast<PLItem
*>(*it
);
616 if( !b_input
&& item
->id() == i_id
)
619 else if( b_input
&& item
->inputItem()->i_id
== i_id
)
622 if( item
->childCount() )
624 PLItem
*childFound
= findInner( item
, i_id
, b_input
);
633 bool PLModel::canEdit() const
638 rootItem
->inputItem() == p_playlist
->p_playing
->p_input
||
639 ( p_playlist
->p_media_library
&&
640 rootItem
->inputItem() == p_playlist
->p_media_library
->p_input
)
645 /************************* Updates handling *****************************/
647 /**** Events processing ****/
648 void PLModel::processInputItemUpdate( input_thread_t
*p_input
)
650 if( !p_input
) return;
652 PLItem
*item
= findByInput( rootItem
, input_GetItem( p_input
)->i_id
);
653 if( item
) emit
currentIndexChanged( index( item
, 0 ) );
654 processInputItemUpdate( input_GetItem( p_input
) );
657 void PLModel::processInputItemUpdate( input_item_t
*p_item
)
659 if( !p_item
|| p_item
->i_id
<= 0 ) return;
660 PLItem
*item
= findByInput( rootItem
, p_item
->i_id
);
662 updateTreeItem( item
);
665 void PLModel::processItemRemoval( int i_id
)
667 if( i_id
<= 0 ) return;
671 void PLModel::commitBufferedRowInserts()
673 PLItem
*toemit
= NULL
;
674 insertBufferCommitTimer
.stop();
675 insertBufferMutex
.lock();
676 if ( !insertBuffer
.isEmpty() )
678 beginInsertRows( index( insertBufferRoot
, 0 ), insertbuffer_firstrow
, insertbuffer_lastrow
);
679 foreach (PLItem
*item
, insertBuffer
)
681 insertBufferRoot
->insertChild( item
, insertbuffer_firstrow
++ );
682 if( item
->inputItem() == THEMIM
->currentInputItem() )
686 insertBuffer
.clear();
688 insertBufferMutex
.unlock();
690 emit
currentIndexChanged( index( toemit
, 0 ) );
694 Tries to agregate linear inserts of single row. Sends
695 more efficient updates notifications to views and then
696 avoids the flickering effect.
698 void PLModel::bufferedRowInsert( PLItem
*item
, PLItem
*parent
, int pos
)
700 insertBufferMutex
.lock();
701 if ( ! insertBuffer
.isEmpty() )
703 /* Check if we're doing linear insert */
704 if ( parent
!= insertBufferRoot
|| pos
!= insertbuffer_lastrow
+ 1 )
706 insertBufferMutex
.unlock();
707 commitBufferedRowInserts();
708 bufferedRowInsert( item
, parent
, pos
);
713 if ( insertBuffer
.isEmpty() )
715 insertBuffer
<< item
;
716 insertBufferRoot
= parent
;
717 insertbuffer_firstrow
= pos
;
718 insertbuffer_lastrow
= pos
;
720 insertBuffer
<< item
;
721 insertbuffer_lastrow
++;
723 insertBufferMutex
.unlock();
725 /* Schedule commit */
726 if ( ! insertBufferCommitTimer
.isActive() )
728 insertBufferCommitTimer
.setSingleShot( true );
729 insertBufferCommitTimer
.start( 100 );
733 bool PLModel::isBufferedForInsert( PLItem
*parent
, int i_item
)
735 bool b_return
= false;
736 insertBufferMutex
.lock();
737 if ( parent
== insertBufferRoot
)
739 foreach (PLItem
*item
, insertBuffer
)
740 if ( item
->i_id
== i_item
)
746 insertBufferMutex
.unlock();
750 void PLModel::processItemAppend( int i_item
, int i_parent
)
752 playlist_item_t
*p_item
= NULL
;
753 PLItem
*newItem
= NULL
;
756 /* Find the Parent */
757 PLItem
*nodeParentItem
= findById( rootItem
, i_parent
);
758 if( !nodeParentItem
)
759 { /* retry as it might have been in buffer */
760 commitBufferedRowInserts();
761 nodeParentItem
= findById( rootItem
, i_parent
);
763 if( !nodeParentItem
) return;
765 /* Search for an already matching children */
766 if ( isBufferedForInsert( nodeParentItem
, i_item
) ) return;
767 foreach( const AbstractPLItem
*existing
, nodeParentItem
->children
)
768 if( existing
->id() == i_item
) return;
772 p_item
= playlist_ItemGetById( p_playlist
, i_item
);
773 if( !p_item
|| p_item
->i_flags
& PLAYLIST_DBL_FLAG
)
778 for( pos
= p_item
->p_parent
->i_children
- 1; pos
>= 0; pos
-- )
779 if( p_item
->p_parent
->pp_children
[pos
] == p_item
) break;
781 newItem
= new PLItem( p_item
, nodeParentItem
);
784 /* We insert the newItem (children) inside the parent */
785 bufferedRowInsert( newItem
, nodeParentItem
, pos
);
787 if( latestSearch
.isEmpty() ) return;
788 search( latestSearch
, index( rootItem
, 0), false /*FIXME*/ );
791 void PLModel::rebuild( playlist_item_t
*p_root
)
793 commitBufferedRowInserts();
794 /* Invalidate cache */
795 i_cached_id
= i_cached_input_id
= -1;
797 if( rootItem
) rootItem
->clearChildren();
800 if( p_root
) // Can be NULL
803 rootItem
= new PLItem( p_root
);
806 /* Recreate from root */
807 updateChildren( rootItem
);
810 /* And signal the view */
812 if( p_root
) emit
rootIndexChanged();
815 void PLModel::takeItem( PLItem
*item
)
817 commitBufferedRowInserts();
819 PLItem
*parent
= static_cast<PLItem
*>(item
->parent());
821 int i_index
= parent
->indexOf( item
);
823 beginRemoveRows( index( parent
, 0 ), i_index
, i_index
);
824 parent
->takeChildAt( i_index
);
828 void PLModel::insertChildren( PLItem
*node
, QList
<PLItem
*>& items
, int i_pos
)
830 commitBufferedRowInserts();
832 int count
= items
.count();
834 beginInsertRows( index( node
, 0 ), i_pos
, i_pos
+ count
- 1 );
835 for( int i
= 0; i
< count
; i
++ )
837 node
->children
.insert( i_pos
+ i
, items
[i
] );
838 items
[i
]->parentItem
= node
;
843 void PLModel::removeItem( PLItem
*item
)
846 commitBufferedRowInserts();
849 i_cached_input_id
= -1;
851 if( item
->parent() ) {
852 int i
= item
->parent()->indexOf( item
);
853 beginRemoveRows( index( static_cast<PLItem
*>(item
->parent()), 0), i
, i
);
854 item
->parent()->children
.removeAt(i
);
863 rebuild( p_playlist
->p_playing
);
867 /* This function must be entered WITH the playlist lock */
868 void PLModel::updateChildren( PLItem
*root
)
870 playlist_item_t
*p_node
= playlist_ItemGetById( p_playlist
, root
->id() );
871 updateChildren( p_node
, root
);
874 /* This function must be entered WITH the playlist lock */
875 void PLModel::updateChildren( playlist_item_t
*p_node
, PLItem
*root
)
877 for( int i
= 0; i
< p_node
->i_children
; i
++ )
879 if( p_node
->pp_children
[i
]->i_flags
& PLAYLIST_DBL_FLAG
) continue;
880 PLItem
*newItem
= new PLItem( p_node
->pp_children
[i
], root
);
881 root
->appendChild( newItem
);
882 if( p_node
->pp_children
[i
]->i_children
!= -1 )
883 updateChildren( p_node
->pp_children
[i
], newItem
);
887 /* Function doesn't need playlist-lock, as we don't touch playlist_item_t stuff here*/
888 void PLModel::updateTreeItem( PLItem
*item
)
891 emit
dataChanged( index( item
, 0 ) , index( item
, columnCount( QModelIndex() ) - 1 ) );
894 /************************* Actions ******************************/
897 * Deletion, don't delete items childrens if item is going to be
898 * delete allready, so we remove childrens from selection-list.
900 void PLModel::doDelete( QModelIndexList selected
)
902 if( !canEdit() ) return;
904 while( !selected
.isEmpty() )
906 QModelIndex index
= selected
[0];
907 selected
.removeAt( 0 );
909 if( index
.column() != 0 ) continue;
911 PLItem
*item
= getItem( index
);
912 if( item
->childCount() )
913 recurseDelete( item
->children
, &selected
);
916 playlist_DeleteFromInput( p_playlist
, item
->inputItem(), pl_Locked
);
923 void PLModel::recurseDelete( QList
<AbstractPLItem
*> children
, QModelIndexList
*fullList
)
925 for( int i
= children
.count() - 1; i
>= 0 ; i
-- )
927 PLItem
*item
= static_cast<PLItem
*>(children
[i
]);
928 if( item
->childCount() )
929 recurseDelete( item
->children
, fullList
);
930 fullList
->removeAll( index( item
, 0 ) );
934 /******* Volume III: Sorting and searching ********/
935 void PLModel::sort( const int column
, Qt::SortOrder order
)
937 sort( QModelIndex(), index( rootItem
->id(), 0 ) , column
, order
);
940 void PLModel::sort( QModelIndex caller
, QModelIndex rootIndex
, const int column
, Qt::SortOrder order
)
942 msg_Dbg( p_intf
, "Sorting by column %i, order %i", column
, order
);
944 int meta
= columnToMeta( column
);
945 if( meta
== COLUMN_END
) return;
947 PLItem
*item
= ( rootIndex
.isValid() ) ? getItem( rootIndex
)
951 int i_root_id
= item
->id();
953 commitBufferedRowInserts();
955 QModelIndex qIndex
= index( item
, 0 );
956 int count
= item
->childCount();
959 beginRemoveRows( qIndex
, 0, count
- 1 );
960 item
->clearChildren();
966 playlist_item_t
*p_root
= playlist_ItemGetById( p_playlist
,
970 playlist_RecursiveNodeSort( p_playlist
, p_root
,
971 i_column_sorting( meta
),
972 order
== Qt::AscendingOrder
?
973 ORDER_NORMAL
: ORDER_REVERSE
);
977 i_cached_id
= i_cached_input_id
= -1;
981 beginInsertRows( qIndex
, 0, count
- 1 );
982 updateChildren( item
);
986 /* if we have popup item, try to make sure that you keep that item visible */
987 if( caller
.isValid() ) emit
currentIndexChanged( caller
);
989 else if( currentIndex().isValid() ) emit
currentIndexChanged( currentIndex() );
992 void PLModel::search( const QString
& search_text
, const QModelIndex
& idx
, bool b_recursive
)
994 latestSearch
= search_text
;
996 commitBufferedRowInserts();
998 /** \todo Fire the search with a small delay ? */
1001 playlist_item_t
*p_root
= playlist_ItemGetById( p_playlist
,
1004 playlist_LiveSearchUpdate( p_playlist
, p_root
, qtu( search_text
),
1008 PLItem
*searchRoot
= getItem( idx
);
1010 beginRemoveRows( idx
, 0, searchRoot
->childCount() - 1 );
1011 searchRoot
->clearChildren();
1014 beginInsertRows( idx
, 0, searchRoot
->childCount() - 1 );
1015 updateChildren( searchRoot
); // The PL_LOCK is needed here
1026 void PLModel::clearPlaylist()
1028 if( rowCount() < 1 ) return;
1031 for( int i
= 0; i
< rowCount(); i
++)
1033 QModelIndex indexrecord
= index( i
, 0, QModelIndex() );
1034 l
.append( indexrecord
);
1039 void PLModel::ensureArtRequested( const QModelIndex
&index
)
1041 if ( index
.isValid() && hasChildren( index
) )
1043 int i_art_policy
= var_GetInteger( p_playlist
, "album-art" );
1044 if ( i_art_policy
!= ALBUM_ART_ALL
) return;
1045 int nbnodes
= rowCount( index
);
1047 for( int row
= 0 ; row
< nbnodes
; row
++ )
1049 child
= index
.child( row
, 0 );
1050 if ( child
.isValid() && getArtUrl( child
).isEmpty() )
1051 THEMIM
->getIM()->requestArtUpdate( getItem( child
)->inputItem() );
1057 void PLModel::createNode( QModelIndex index
, QString name
)
1059 if( name
.isEmpty() || !index
.isValid() ) return;
1062 index
= index
.parent();
1063 if ( !index
.isValid() ) index
= rootIndex();
1064 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, itemId( index
) );
1066 playlist_NodeCreate( p_playlist
, qtu( name
), p_item
, PLAYLIST_END
, 0, NULL
);
1070 void PLModel::actionSlot( QAction
*action
)
1076 actionsContainerType a
= action
->data().value
<actionsContainerType
>();
1080 case actionsContainerType::ACTION_PLAY
:
1083 if ( a
.indexes
.first().isValid() )
1085 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
,
1086 itemId( a
.indexes
.first() ) );
1087 activateItem( p_item
);
1093 case actionsContainerType::ACTION_ADDTOPLAYLIST
:
1095 foreach( QModelIndex currentIndex
, a
.indexes
)
1097 playlist_item_t
*p_item
= playlist_ItemGetById( THEPL
, itemId( currentIndex
) );
1098 if( !p_item
) continue;
1100 playlist_NodeAddCopy( THEPL
, p_item
,
1107 case actionsContainerType::ACTION_REMOVE
:
1108 doDelete( a
.indexes
);
1111 case actionsContainerType::ACTION_SORT
:
1112 index
= a
.indexes
.first().parent();
1113 if( !index
.isValid() ) index
= rootIndex();
1114 sort( a
.indexes
.first(), index
,
1115 a
.column
> 0 ? a
.column
- 1 : -a
.column
- 1,
1116 a
.column
> 0 ? Qt::AscendingOrder
: Qt::DescendingOrder
);
1122 /******************* Drag and Drop helper class ******************/
1123 PlMimeData::~PlMimeData()
1125 foreach( input_item_t
*p_item
, _inputItems
)
1126 vlc_gc_decref( p_item
);
1129 void PlMimeData::appendItem( input_item_t
*p_item
)
1131 vlc_gc_incref( p_item
);
1132 _inputItems
.append( p_item
);
1135 QList
<input_item_t
*> PlMimeData::inputItems() const
1140 QStringList
PlMimeData::formats () const
1143 fmts
<< "vlc/qt-input-items";