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 "dialogs_provider.hpp" /* THEDP */
33 #include "input_manager.hpp" /* THEMIM */
34 #include "dialogs/mediainfo.hpp" /* MediaInfo Dialog */
35 #include "dialogs/playlist.hpp" /* Playlist Dialog */
37 #include <vlc_intf_strings.h> /* I_DIR */
39 #include "pixmaps/types/type_unknown.xpm"
48 #include <QDesktopServices>
49 #include <QInputDialog>
50 #include <QSignalMapper>
53 I_DIR_OR_FOLDER( N_("Create Directory"), N_( "Create Folder" ) )
54 #define I_NEW_DIR_NAME \
55 I_DIR_OR_FOLDER( N_( "Enter name for new directory:" ), \
56 N_( "Enter name for new folder:" ) )
58 QIcon
PLModel::icons
[ITEM_TYPE_NUMBER
];
60 /*************************************************************************
61 * Playlist model implementation
62 *************************************************************************/
64 PLModel::PLModel( playlist_t
*_p_playlist
, /* THEPL */
65 intf_thread_t
*_p_intf
, /* main Qt p_intf */
66 playlist_item_t
* p_root
,
67 QObject
*parent
) /* Basic Qt parent */
68 : VLCModel( _p_intf
, parent
)
70 p_playlist
= _p_playlist
;
72 i_cached_input_id
= -1;
73 i_popup_item
= i_popup_parent
= -1;
76 rootItem
= NULL
; /* PLItem rootItem, will be set in rebuild( ) */
78 /* Icons initialization */
79 #define ADD_ICON(type, x) icons[ITEM_TYPE_##type] = QIcon( x )
80 ADD_ICON( UNKNOWN
, type_unknown_xpm
);
81 ADD_ICON( FILE, ":/type/file" );
82 ADD_ICON( DIRECTORY
, ":/type/directory" );
83 ADD_ICON( DISC
, ":/type/disc" );
84 ADD_ICON( CDDA
, ":/type/cdda" );
85 ADD_ICON( CARD
, ":/type/capture-card" );
86 ADD_ICON( NET
, ":/type/net" );
87 ADD_ICON( PLAYLIST
, ":/type/playlist" );
88 ADD_ICON( NODE
, ":/type/node" );
92 DCONNECT( THEMIM
->getIM(), metaChanged( input_item_t
*),
93 this, processInputItemUpdate( input_item_t
*) );
94 DCONNECT( THEMIM
, inputChanged( input_thread_t
* ),
95 this, processInputItemUpdate( input_thread_t
* ) );
96 CONNECT( THEMIM
, playlistItemAppended( int, int ),
97 this, processItemAppend( int, int ) );
98 CONNECT( THEMIM
, playlistItemRemoved( int ),
99 this, processItemRemoval( int ) );
108 Qt::DropActions
PLModel::supportedDropActions() const
110 return Qt::CopyAction
| Qt::MoveAction
;
113 Qt::ItemFlags
PLModel::flags( const QModelIndex
&index
) const
115 Qt::ItemFlags flags
= QAbstractItemModel::flags( index
);
117 const PLItem
*item
= index
.isValid() ? getItem( index
) : rootItem
;
122 playlist_item_t
*plItem
=
123 playlist_ItemGetById( p_playlist
, item
->i_id
);
125 if ( plItem
&& ( plItem
->i_children
> -1 ) )
126 flags
|= Qt::ItemIsDropEnabled
;
131 flags
|= Qt::ItemIsDragEnabled
;
136 QStringList
PLModel::mimeTypes() const
139 types
<< "vlc/qt-input-items";
143 bool modelIndexLessThen( const QModelIndex
&i1
, const QModelIndex
&i2
)
145 if( !i1
.isValid() || !i2
.isValid() ) return false;
146 PLItem
*item1
= static_cast<PLItem
*>( i1
.internalPointer() );
147 PLItem
*item2
= static_cast<PLItem
*>( i2
.internalPointer() );
148 if( item1
->parent() == item2
->parent() ) return i1
.row() < i2
.row();
149 else return *item1
< *item2
;
152 QMimeData
*PLModel::mimeData( const QModelIndexList
&indexes
) const
154 PlMimeData
*plMimeData
= new PlMimeData();
155 QModelIndexList list
;
157 foreach( const QModelIndex
&index
, indexes
) {
158 if( index
.isValid() && index
.column() == 0 )
162 qSort(list
.begin(), list
.end(), modelIndexLessThen
);
165 foreach( const QModelIndex
&index
, list
) {
168 PLItem
*testee
= getItem( index
);
169 while( testee
->parent() )
171 if( testee
->parent() == item
||
172 testee
->parent() == item
->parent() ) break;
173 testee
= testee
->parent();
175 if( testee
->parent() == item
) continue;
176 item
= getItem( index
);
179 item
= getItem( index
);
181 plMimeData
->appendItem( item
->inputItem() );
188 bool PLModel::dropMimeData( const QMimeData
*data
, Qt::DropAction action
,
189 int row
, int, const QModelIndex
&parent
)
191 bool copy
= action
== Qt::CopyAction
;
192 if( !copy
&& action
!= Qt::MoveAction
)
195 const PlMimeData
*plMimeData
= qobject_cast
<const PlMimeData
*>( data
);
199 dropAppendCopy( plMimeData
, getItem( parent
), row
);
201 dropMove( plMimeData
, getItem( parent
), row
);
206 void PLModel::dropAppendCopy( const PlMimeData
*plMimeData
, PLItem
*target
, int pos
)
210 playlist_item_t
*p_parent
=
211 playlist_ItemGetByInput( p_playlist
, target
->inputItem() );
212 if( !p_parent
) return;
214 if( pos
== -1 ) pos
= PLAYLIST_END
;
216 QList
<input_item_t
*> inputItems
= plMimeData
->inputItems();
218 foreach( input_item_t
* p_input
, inputItems
)
220 playlist_item_t
*p_item
= playlist_ItemGetByInput( p_playlist
, p_input
);
221 if( !p_item
) continue;
222 pos
= playlist_NodeAddCopy( p_playlist
, p_item
, p_parent
, pos
);
228 void PLModel::dropMove( const PlMimeData
* plMimeData
, PLItem
*target
, int row
)
230 QList
<input_item_t
*> inputItems
= plMimeData
->inputItems();
231 QList
<PLItem
*> model_items
;
232 playlist_item_t
*pp_items
[inputItems
.size()];
236 playlist_item_t
*p_parent
=
237 playlist_ItemGetByInput( p_playlist
, target
->inputItem() );
239 if( !p_parent
|| row
> p_parent
->i_children
)
244 int new_pos
= row
== -1 ? p_parent
->i_children
: row
;
245 int model_pos
= new_pos
;
248 foreach( input_item_t
*p_input
, inputItems
)
250 playlist_item_t
*p_item
= playlist_ItemGetByInput( p_playlist
, p_input
);
251 if( !p_item
) continue;
253 PLItem
*item
= findByInput( rootItem
, p_input
->i_id
);
254 if( !item
) continue;
256 /* Better not try to move a node into itself.
257 Abort the whole operation in that case,
258 because it is ambiguous. */
259 PLItem
*climber
= target
;
262 if( climber
== item
)
266 climber
= climber
->parent();
269 if( item
->parent() == target
&&
270 target
->children
.indexOf( item
) < new_pos
)
273 model_items
.append( item
);
274 pp_items
[i
] = p_item
;
278 if( model_items
.isEmpty() )
283 playlist_TreeMoveMany( p_playlist
, i
, pp_items
, p_parent
, new_pos
);
287 foreach( PLItem
*item
, model_items
)
290 insertChildren( target
, model_items
, model_pos
);
293 /* remove item with its id */
294 void PLModel::removeItem( int i_id
)
296 PLItem
*item
= findById( rootItem
, i_id
);
300 void PLModel::activateItem( const QModelIndex
&index
)
302 assert( index
.isValid() );
303 const PLItem
*item
= getItem( index
);
306 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, item
->i_id
);
307 activateItem( p_item
);
311 /* Must be entered with lock */
312 void PLModel::activateItem( playlist_item_t
*p_item
)
314 if( !p_item
) return;
315 playlist_item_t
*p_parent
= p_item
;
318 if( p_parent
->i_id
== rootItem
->id() ) break;
319 p_parent
= p_parent
->p_parent
;
322 playlist_Control( p_playlist
, PLAYLIST_VIEWPLAY
, pl_Locked
,
326 /****************** Base model mandatory implementations *****************/
327 QVariant
PLModel::data( const QModelIndex
&index
, const int role
) const
329 if( !index
.isValid() ) return QVariant();
330 const PLItem
*item
= getItem( index
);
331 if( role
== Qt::DisplayRole
)
333 int metadata
= columnToMeta( index
.column() );
334 if( metadata
== COLUMN_END
) return QVariant();
337 if( metadata
== COLUMN_NUMBER
)
338 returninfo
= QString::number( index
.row() + 1 );
339 else if( metadata
== COLUMN_COVER
)
342 artUrl
= InputManager::decodeArtURL( item
->inputItem() );
343 if( artUrl
.isEmpty() )
345 for( int i
= 0; i
< item
->childCount(); i
++ )
347 artUrl
= InputManager::decodeArtURL( item
->child( i
)->inputItem() );
348 if( !artUrl
.isEmpty() )
352 return QVariant( artUrl
);
356 char *psz
= psz_column_meta( item
->inputItem(), metadata
);
357 returninfo
= qfu( psz
);
360 return QVariant( returninfo
);
362 else if( role
== Qt::DecorationRole
&& index
.column() == 0 )
364 /* Used to segfault here because i_type wasn't always initialized */
365 return QVariant( PLModel::icons
[item
->inputItem()->i_type
] );
367 else if( role
== Qt::FontRole
)
370 f
.setPointSize( f
.pointSize() - 1 );
371 if( isCurrent( index
) )
373 return QVariant( f
);
375 else if( role
== Qt::BackgroundRole
&& isCurrent( index
) )
377 return QVariant( QBrush( Qt::gray
) );
379 else if( role
== IsCurrentRole
) return QVariant( isCurrent( index
) );
380 else if( role
== IsLeafNodeRole
)
384 playlist_item_t
*plItem
=
385 playlist_ItemGetById( p_playlist
, item
->i_id
);
388 isLeaf
= plItem
->i_children
== -1;
393 else if( role
== IsCurrentsParentNodeRole
)
395 return QVariant( isParent( index
, currentIndex() ) );
400 /* Seek from current index toward the top and see if index is one of parent nodes */
401 bool PLModel::isParent( const QModelIndex
&index
, const QModelIndex
¤t
) const
403 if( !index
.isValid() )
406 if( index
== current
)
409 if( !current
.isValid() || !current
.parent().isValid() )
412 return isParent( index
, current
.parent() );
415 bool PLModel::isCurrent( const QModelIndex
&index
) const
417 return getItem( index
)->inputItem() == THEMIM
->currentInputItem();
420 int PLModel::itemId( const QModelIndex
&index
) const
422 return getItem( index
)->id();
425 QVariant
PLModel::headerData( int section
, Qt::Orientation orientation
,
428 if (orientation
!= Qt::Horizontal
|| role
!= Qt::DisplayRole
)
431 int meta_col
= columnToMeta( section
);
433 if( meta_col
== COLUMN_END
) return QVariant();
435 return QVariant( qfu( psz_column_title( meta_col
) ) );
438 QModelIndex
PLModel::index( const int row
, const int column
, const QModelIndex
&parent
)
441 PLItem
*parentItem
= parent
.isValid() ? getItem( parent
) : rootItem
;
443 PLItem
*childItem
= parentItem
->child( row
);
445 return createIndex( row
, column
, childItem
);
447 return QModelIndex();
450 QModelIndex
PLModel::index( const int i_id
, const int c
)
452 return index( findById( rootItem
, i_id
), c
);
455 /* Return the index of a given item */
456 QModelIndex
PLModel::index( PLItem
*item
, int column
) const
458 if( !item
) return QModelIndex();
459 const PLItem
*parent
= item
->parent();
461 return createIndex( parent
->children
.lastIndexOf( item
),
463 return QModelIndex();
466 QModelIndex
PLModel::currentIndex() const
468 input_thread_t
*p_input_thread
= THEMIM
->getInput();
469 if( !p_input_thread
) return QModelIndex();
470 PLItem
*item
= findByInput( rootItem
, input_GetItem( p_input_thread
)->i_id
);
471 return index( item
, 0 );
474 QModelIndex
PLModel::parent( const QModelIndex
&index
) const
476 if( !index
.isValid() ) return QModelIndex();
478 PLItem
*childItem
= getItem( index
);
481 msg_Err( p_playlist
, "NULL CHILD" );
482 return QModelIndex();
485 PLItem
*parentItem
= childItem
->parent();
486 if( !parentItem
|| parentItem
== rootItem
) return QModelIndex();
487 if( !parentItem
->parent() )
489 msg_Err( p_playlist
, "No parent parent, trying row 0 " );
490 msg_Err( p_playlist
, "----- PLEASE REPORT THIS ------" );
491 return createIndex( 0, 0, parentItem
);
493 return createIndex(parentItem
->row(), 0, parentItem
);
496 int PLModel::columnCount( const QModelIndex
&) const
498 return columnFromMeta( COLUMN_END
);
501 int PLModel::rowCount( const QModelIndex
&parent
) const
503 const PLItem
*parentItem
= parent
.isValid() ? getItem( parent
) : rootItem
;
504 return parentItem
->childCount();
507 QStringList
PLModel::selectedURIs()
510 for( int i
= 0; i
< current_selection
.size(); i
++ )
512 const PLItem
*item
= getItem( current_selection
[i
] );
516 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, item
->i_id
);
519 char *psz
= input_item_GetURI( p_item
->p_input
);
522 lst
.append( qfu(psz
) );
532 /************************* Lookups *****************************/
533 PLItem
*PLModel::findById( PLItem
*root
, int i_id
) const
535 return findInner( root
, i_id
, false );
538 PLItem
*PLModel::findByInput( PLItem
*root
, int i_id
) const
540 PLItem
*result
= findInner( root
, i_id
, true );
544 PLItem
* PLModel::findInner( PLItem
*root
, int i_id
, bool b_input
) const
546 if( !root
) return NULL
;
548 if( !b_input
&& root
->id() == i_id
)
551 else if( b_input
&& root
->inputItem()->i_id
== i_id
)
554 QList
<PLItem
*>::iterator it
= root
->children
.begin();
555 while ( it
!= root
->children
.end() )
557 if( !b_input
&& (*it
)->id() == i_id
)
560 else if( b_input
&& (*it
)->inputItem()->i_id
== i_id
)
563 if( (*it
)->childCount() )
565 PLItem
*childFound
= findInner( (*it
), i_id
, b_input
);
574 bool PLModel::canEdit() const
579 rootItem
->inputItem() == p_playlist
->p_playing
->p_input
||
580 ( p_playlist
->p_media_library
&&
581 rootItem
->inputItem() == p_playlist
->p_media_library
->p_input
)
586 /************************* Updates handling *****************************/
588 /**** Events processing ****/
589 void PLModel::processInputItemUpdate( input_thread_t
*p_input
)
591 if( !p_input
) return;
592 if( p_input
&& !( p_input
->b_dead
|| !vlc_object_alive( p_input
) ) )
594 PLItem
*item
= findByInput( rootItem
, input_GetItem( p_input
)->i_id
);
595 if( item
) emit
currentChanged( index( item
, 0 ) );
597 processInputItemUpdate( input_GetItem( p_input
) );
600 void PLModel::processInputItemUpdate( input_item_t
*p_item
)
602 if( !p_item
|| p_item
->i_id
<= 0 ) return;
603 PLItem
*item
= findByInput( rootItem
, p_item
->i_id
);
605 updateTreeItem( item
);
608 void PLModel::processItemRemoval( int i_id
)
610 if( i_id
<= 0 ) return;
614 void PLModel::processItemAppend( int i_item
, int i_parent
)
616 playlist_item_t
*p_item
= NULL
;
617 PLItem
*newItem
= NULL
;
620 /* Find the Parent */
621 PLItem
*nodeParentItem
= findById( rootItem
, i_parent
);
622 if( !nodeParentItem
) return;
624 /* Search for an already matching children */
625 foreach( const PLItem
*existing
, nodeParentItem
->children
)
626 if( existing
->i_id
== i_item
) return;
630 p_item
= playlist_ItemGetById( p_playlist
, i_item
);
631 if( !p_item
|| p_item
->i_flags
& PLAYLIST_DBL_FLAG
)
636 for( pos
= 0; pos
< p_item
->p_parent
->i_children
; pos
++ )
637 if( p_item
->p_parent
->pp_children
[pos
] == p_item
) break;
639 newItem
= new PLItem( p_item
, nodeParentItem
);
642 /* We insert the newItem (children) inside the parent */
643 beginInsertRows( index( nodeParentItem
, 0 ), pos
, pos
);
644 nodeParentItem
->insertChild( newItem
, pos
);
647 if( newItem
->inputItem() == THEMIM
->currentInputItem() )
648 emit
currentChanged( index( newItem
, 0 ) );
651 void PLModel::rebuild( playlist_item_t
*p_root
)
653 /* Invalidate cache */
654 i_cached_id
= i_cached_input_id
= -1;
656 if( rootItem
) rootItem
->removeChildren();
659 if( p_root
) // Can be NULL
662 rootItem
= new PLItem( p_root
);
665 /* Recreate from root */
666 updateChildren( rootItem
);
669 /* And signal the view */
672 if( p_root
) emit
rootChanged();
675 void PLModel::takeItem( PLItem
*item
)
678 PLItem
*parent
= item
->parent();
680 int i_index
= parent
->children
.indexOf( item
);
682 beginRemoveRows( index( parent
, 0 ), i_index
, i_index
);
683 parent
->takeChildAt( i_index
);
687 void PLModel::insertChildren( PLItem
*node
, QList
<PLItem
*>& items
, int i_pos
)
690 int count
= items
.size();
692 printf( "Here I am\n");
693 beginInsertRows( index( node
, 0 ), i_pos
, i_pos
+ count
- 1 );
694 for( int i
= 0; i
< count
; i
++ )
696 node
->children
.insert( i_pos
+ i
, items
[i
] );
697 items
[i
]->parentItem
= node
;
702 void PLModel::removeItem( PLItem
*item
)
707 i_cached_input_id
= -1;
709 if( item
->parent() ) {
710 int i
= item
->parent()->children
.indexOf( item
);
711 beginRemoveRows( index( item
->parent(), 0), i
, i
);
712 item
->parent()->children
.removeAt(i
);
721 rebuild( p_playlist
->p_playing
);
725 /* This function must be entered WITH the playlist lock */
726 void PLModel::updateChildren( PLItem
*root
)
728 playlist_item_t
*p_node
= playlist_ItemGetById( p_playlist
, root
->id() );
729 updateChildren( p_node
, root
);
732 /* This function must be entered WITH the playlist lock */
733 void PLModel::updateChildren( playlist_item_t
*p_node
, PLItem
*root
)
735 for( int i
= 0; i
< p_node
->i_children
; i
++ )
737 if( p_node
->pp_children
[i
]->i_flags
& PLAYLIST_DBL_FLAG
) continue;
738 PLItem
*newItem
= new PLItem( p_node
->pp_children
[i
], root
);
739 root
->appendChild( newItem
);
740 if( p_node
->pp_children
[i
]->i_children
!= -1 )
741 updateChildren( p_node
->pp_children
[i
], newItem
);
745 /* Function doesn't need playlist-lock, as we don't touch playlist_item_t stuff here*/
746 void PLModel::updateTreeItem( PLItem
*item
)
749 emit
dataChanged( index( item
, 0 ) , index( item
, columnCount( QModelIndex() ) ) );
752 /************************* Actions ******************************/
755 * Deletion, don't delete items childrens if item is going to be
756 * delete allready, so we remove childrens from selection-list.
758 void PLModel::doDelete( QModelIndexList selected
)
760 if( !canEdit() ) return;
762 while( !selected
.isEmpty() )
764 QModelIndex index
= selected
[0];
765 selected
.removeAt( 0 );
767 if( index
.column() != 0 ) continue;
769 PLItem
*item
= getItem( index
);
770 if( item
->childCount() )
771 recurseDelete( item
->children
, &selected
);
774 playlist_DeleteFromInput( p_playlist
, item
->inputItem(), pl_Locked
);
781 void PLModel::recurseDelete( QList
<PLItem
*> children
, QModelIndexList
*fullList
)
783 for( int i
= children
.size() - 1; i
>= 0 ; i
-- )
785 PLItem
*item
= children
[i
];
786 if( item
->childCount() )
787 recurseDelete( item
->children
, fullList
);
788 fullList
->removeAll( index( item
, 0 ) );
792 /******* Volume III: Sorting and searching ********/
793 void PLModel::sort( const int column
, Qt::SortOrder order
)
795 sort( rootItem
->id(), column
, order
);
798 void PLModel::sort( const int i_root_id
, const int column
, Qt::SortOrder order
)
800 msg_Dbg( p_intf
, "Sorting by column %i, order %i", column
, order
);
802 int meta
= columnToMeta( column
);
803 if( meta
== COLUMN_END
) return;
805 PLItem
*item
= findById( rootItem
, i_root_id
);
807 QModelIndex qIndex
= index( item
, 0 );
808 int count
= item
->childCount();
811 beginRemoveRows( qIndex
, 0, count
- 1 );
812 item
->removeChildren();
818 playlist_item_t
*p_root
= playlist_ItemGetById( p_playlist
,
822 playlist_RecursiveNodeSort( p_playlist
, p_root
,
823 i_column_sorting( meta
),
824 order
== Qt::AscendingOrder
?
825 ORDER_NORMAL
: ORDER_REVERSE
);
829 i_cached_id
= i_cached_input_id
= -1;
833 beginInsertRows( qIndex
, 0, count
- 1 );
834 updateChildren( item
);
838 /* if we have popup item, try to make sure that you keep that item visible */
839 if( i_popup_item
> -1 )
841 PLItem
*popupitem
= findById( rootItem
, i_popup_item
);
842 if( popupitem
) emit
currentChanged( index( popupitem
, 0 ) );
843 /* reset i_popup_item as we don't show it as selected anymore anyway */
846 else if( currentIndex().isValid() ) emit
currentChanged( currentIndex() );
849 void PLModel::search( const QString
& search_text
, const QModelIndex
& idx
, bool b_recursive
)
851 /** \todo Fire the search with a small delay ? */
854 playlist_item_t
*p_root
= playlist_ItemGetById( p_playlist
,
857 const char *psz_name
= qtu( search_text
);
858 playlist_LiveSearchUpdate( p_playlist
, p_root
, psz_name
, b_recursive
);
862 PLItem
*searchRoot
= getItem( idx
);
864 beginRemoveRows( idx
, 0, searchRoot
->childCount() - 1 );
865 searchRoot
->removeChildren();
868 beginInsertRows( idx
, 0, searchRoot
->childCount() - 1 );
869 updateChildren( searchRoot
); // The PL_LOCK is needed here
880 /*********** Popup *********/
881 bool PLModel::popup( const QModelIndex
& index
, const QPoint
&point
, const QModelIndexList
&list
)
883 int i_id
= index
.isValid() ? itemId( index
) : rootItem
->id();
886 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, i_id
);
893 input_item_t
*p_input
= p_item
->p_input
;
894 vlc_gc_incref( p_input
);
896 i_popup_item
= index
.isValid() ? p_item
->i_id
: -1;
897 i_popup_parent
= index
.isValid() ?
898 ( p_item
->p_parent
? p_item
->p_parent
->i_id
: -1 ) :
900 i_popup_column
= index
.column();
902 bool tree
= ( rootItem
&& rootItem
->id() != p_playlist
->p_playing
->i_id
) ||
903 var_InheritBool( p_intf
, "playlist-tree" );
907 current_selection
= list
;
910 if( i_popup_item
> -1 )
912 menu
.addAction( QIcon( ":/menu/play" ), qtr(I_POP_PLAY
), this, SLOT( popupPlay() ) );
913 menu
.addAction( QIcon( ":/menu/stream" ),
914 qtr(I_POP_STREAM
), this, SLOT( popupStream() ) );
915 menu
.addAction( qtr(I_POP_SAVE
), this, SLOT( popupSave() ) );
916 menu
.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO
), this, SLOT( popupInfo() ) );
917 if( p_input
->psz_uri
&& !strncasecmp( p_input
->psz_uri
, "file://", 7 ) )
919 menu
.addAction( QIcon( ":/type/folder-grey" ),
920 qtr( I_POP_EXPLORE
), this, SLOT( popupExplore() ) );
924 vlc_gc_decref( p_input
);
928 QIcon
addIcon( ":/buttons/playlist/playlist_add" );
930 if( tree
) menu
.addAction( addIcon
, qtr(I_POP_NEWFOLDER
), this, SLOT( popupAddNode() ) );
931 if( rootItem
->id() == THEPL
->p_playing
->i_id
)
933 menu
.addAction( addIcon
, qtr(I_PL_ADDF
), THEDP
, SLOT( simplePLAppendDialog()) );
934 menu
.addAction( addIcon
, qtr(I_PL_ADDDIR
), THEDP
, SLOT( PLAppendDir()) );
935 menu
.addAction( addIcon
, qtr(I_OP_ADVOP
), THEDP
, SLOT( PLAppendDialog()) );
937 else if( THEPL
->p_media_library
&&
938 rootItem
->id() == THEPL
->p_media_library
->i_id
)
940 menu
.addAction( addIcon
, qtr(I_PL_ADDF
), THEDP
, SLOT( simpleMLAppendDialog()) );
941 menu
.addAction( addIcon
, qtr(I_PL_ADDDIR
), THEDP
, SLOT( MLAppendDir() ) );
942 menu
.addAction( addIcon
, qtr(I_OP_ADVOP
), THEDP
, SLOT( MLAppendDialog() ) );
945 if( i_popup_item
> -1 )
947 if( rootItem
->id() != THEPL
->p_playing
->i_id
)
948 menu
.addAction( qtr( "Add to playlist"), this, SLOT( popupAddToPlaylist() ) );
949 menu
.addAction( QIcon( ":/buttons/playlist/playlist_remove" ),
950 qtr(I_POP_DEL
), this, SLOT( popupDel() ) );
954 sortingMenu
= new QMenu( qtr( "Sort by" ) );
955 sortingMapper
= new QSignalMapper( this );
956 for( int i
= 1, j
= 1; i
< COLUMN_END
; i
<<= 1, j
++ )
958 if( i
== COLUMN_NUMBER
) continue;
959 QMenu
*m
= sortingMenu
->addMenu( qfu( psz_column_title( i
) ) );
960 QAction
*asc
= m
->addAction( qtr("Ascending") );
961 QAction
*desc
= m
->addAction( qtr("Descending") );
962 sortingMapper
->setMapping( asc
, j
);
963 sortingMapper
->setMapping( desc
, -j
);
964 CONNECT( asc
, triggered(), sortingMapper
, map() );
965 CONNECT( desc
, triggered(), sortingMapper
, map() );
967 CONNECT( sortingMapper
, mapped( int ), this, popupSort( int ) );
969 menu
.addMenu( sortingMenu
);
972 if( !menu
.isEmpty() )
974 menu
.exec( point
); return true;
979 void PLModel::popupDel()
981 doDelete( current_selection
);
984 void PLModel::popupPlay()
988 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
,
990 activateItem( p_item
);
995 void PLModel::popupAddToPlaylist()
997 playlist_Lock( THEPL
);
999 foreach( QModelIndex currentIndex
, current_selection
)
1001 playlist_item_t
*p_item
= playlist_ItemGetById( THEPL
, getId( currentIndex
) );
1002 if( !p_item
) continue;
1004 playlist_NodeAddCopy( THEPL
, p_item
,
1008 playlist_Unlock( THEPL
);
1011 void PLModel::popupInfo()
1014 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
,
1018 input_item_t
* p_input
= p_item
->p_input
;
1019 vlc_gc_incref( p_input
);
1021 MediaInfoDialog
*mid
= new MediaInfoDialog( p_intf
, p_input
);
1022 vlc_gc_decref( p_input
);
1023 mid
->setParent( PlaylistDialog::getInstance( p_intf
),
1030 void PLModel::popupStream()
1032 QStringList mrls
= selectedURIs();
1033 if( !mrls
.isEmpty() )
1034 THEDP
->streamingDialog( NULL
, mrls
[0], false );
1037 void PLModel::popupSave()
1039 QStringList mrls
= selectedURIs();
1040 if( !mrls
.isEmpty() )
1041 THEDP
->streamingDialog( NULL
, mrls
[0] );
1044 void PLModel::popupExplore()
1047 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
, i_popup_item
);
1050 input_item_t
*p_input
= p_item
->p_input
;
1051 char *psz_meta
= input_item_GetURI( p_input
);
1055 const char *psz_access
;
1056 const char *psz_demux
;
1058 input_SplitMRL( &psz_access
, &psz_demux
, &psz_path
, psz_meta
);
1060 if( !EMPTY_STR( psz_access
) && (
1061 !strncasecmp( psz_access
, "file", 4 ) ||
1062 !strncasecmp( psz_access
, "dire", 4 ) ))
1064 #if defined( WIN32 ) || defined( __OS2__ )
1065 /* Qt openURL doesn't know to open files that starts with a / or \ */
1066 if( psz_path
[0] == '/' || psz_path
[0] == '\\' )
1070 QFileInfo
info( qfu( decode_URI( psz_path
) ) );
1071 QDesktopServices::openUrl(
1072 QUrl::fromLocalFile( info
.absolutePath() ) );
1081 void PLModel::popupAddNode()
1084 QString name
= QInputDialog::getText( PlaylistDialog::getInstance( p_intf
),
1085 qtr( I_NEW_DIR
), qtr( I_NEW_DIR_NAME
),
1086 QLineEdit::Normal
, QString(), &ok
);
1087 if( !ok
|| name
.isEmpty() ) return;
1090 playlist_item_t
*p_item
= playlist_ItemGetById( p_playlist
,
1093 playlist_NodeCreate( p_playlist
, qtu( name
), p_item
, PLAYLIST_END
, 0, NULL
);
1097 void PLModel::popupSort( int column
)
1099 sort( i_popup_parent
,
1100 column
> 0 ? column
- 1 : -column
- 1,
1101 column
> 0 ? Qt::AscendingOrder
: Qt::DescendingOrder
);
1104 /******************* Drag and Drop helper class ******************/
1105 PlMimeData::~PlMimeData()
1107 foreach( input_item_t
*p_item
, _inputItems
)
1108 vlc_gc_decref( p_item
);
1111 void PlMimeData::appendItem( input_item_t
*p_item
)
1113 vlc_gc_incref( p_item
);
1114 _inputItems
.append( p_item
);
1117 QList
<input_item_t
*> PlMimeData::inputItems() const
1122 QStringList
PlMimeData::formats () const
1125 fmts
<< "vlc/qt-input-items";