1 /*****************************************************************************
2 * ml_model.cpp: the media library's model
3 *****************************************************************************
4 * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
7 * Authors: Antoine Lejeune <phytos@videolan.org>
8 * Jean-Philippe André <jpeg@videolan.org>
9 * Rémi Duraffort <ivoire@videolan.org>
10 * Adrien Maglo <magsoft@videolan.org>
11 * Srikanth Raju <srikiraju#gmail#com>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
37 #include "ml_item.hpp"
38 #include "ml_model.hpp"
39 #include "dialogs/mediainfo.hpp"
40 #include "dialogs/playlist.hpp"
41 #include "components/playlist/sorting.h"
42 #include "dialogs_provider.hpp"
43 #include "input_manager.hpp" /* THEMIM */
46 #include <vlc_intf_strings.h>
48 static int mediaAdded( vlc_object_t
*p_this
, char const *psz_var
,
49 vlc_value_t oldval
, vlc_value_t newval
,
51 static int mediaDeleted( vlc_object_t
*p_this
, char const *psz_var
,
52 vlc_value_t oldval
, vlc_value_t newval
,
54 static int mediaUpdated( vlc_object_t
*p_this
, char const *psz_var
,
55 vlc_value_t oldval
, vlc_value_t newval
,
59 * @brief Definition of the result item model for the result tree
60 * @param parent the parent Qt object
62 MLModel::MLModel( intf_thread_t
* _p_intf
, QObject
*parent
)
63 :VLCModel( _p_intf
, parent
)
65 p_ml
= ml_Get( p_intf
);
66 vlc_array_t
*p_result_array
= vlc_array_new();
67 ml_Find( p_ml
, p_result_array
, ML_MEDIA
);
68 insertResultArray( p_result_array
);
70 var_AddCallback( p_ml
, "media-added", mediaAdded
, this );
71 var_AddCallback( p_ml
, "media-deleted", mediaDeleted
, this );
72 var_AddCallback( p_ml
, "media-meta-change", mediaUpdated
, this );
76 * @brief Simple destructor for the model
80 var_DelCallback( p_ml
, "media-meta-change", mediaUpdated
, this );
81 var_DelCallback( p_ml
, "media-deleted", mediaDeleted
, this );
82 var_DelCallback( p_ml
, "media-added", mediaAdded
, this );
87 int rows
= rowCount();
90 beginRemoveRows( createIndex( 0, 0 ), 0, rows
-1 );
97 QModelIndex
MLModel::index( int row
, int column
,
98 const QModelIndex
&parent
) const
100 if( parent
.isValid() || row
>= items
.count() )
101 return QModelIndex();
104 QModelIndex idx
= createIndex( row
, column
, items
.value( row
) );
109 QModelIndex
MLModel::parent(const QModelIndex
& ) const
111 return QModelIndex();
115 * @brief Return the index of currently playing item
117 QModelIndex
MLModel::currentIndex() const
119 input_thread_t
*p_input_thread
= THEMIM
->getInput();
120 if( !p_input_thread
) return QModelIndex();
122 /*TODO: O(n) not good */
123 input_item_t
* p_iitem
= input_GetItem( p_input_thread
);
124 foreach( MLItem
* item
, items
)
126 if( !QString::compare( item
->getUri().toString(),
127 QString::fromAscii( p_iitem
->psz_uri
) ) )
128 return index( items
.indexOf( item
), 0 );
130 return QModelIndex();
133 * @brief This returns the type of data shown in the specified column
134 * @param column must be valid
135 * @return The type, or ML_END in case of error
137 ml_select_e
MLModel::columnType( int logicalindex
) const
139 if( logicalindex
< 0 || logicalindex
>= columnCount() ) return ML_END
;
140 return meta_to_mlmeta( columnToMeta( logicalindex
) );
143 QVariant
MLModel::headerData( int section
, Qt::Orientation orientation
,
146 if (orientation
== Qt::Horizontal
&& role
== Qt::DisplayRole
)
147 return QVariant( psz_column_title( columnToMeta( section
) ) );
152 Qt::ItemFlags
MLModel::flags(const QModelIndex
&index
) const
154 if( !index
.isValid() )
157 if( isEditable( index
) )
158 return Qt::ItemIsEnabled
| Qt::ItemIsSelectable
| Qt::ItemIsDragEnabled
159 | Qt::ItemIsEditable
;
161 return Qt::ItemIsEnabled
| Qt::ItemIsSelectable
| Qt::ItemIsDragEnabled
;
164 bool MLModel::isEditable( const QModelIndex
&index
) const
166 if( !index
.isValid() )
169 ml_select_e type
= columnType( index
.column() );
172 // Read-only members: not editable
178 case ML_PLAYED_COUNT
:
181 // Read-write members: editable
187 case ML_ORIGINAL_TITLE
:
191 case ML_TRACK_NUMBER
:
201 QMimeData
* MLModel::mimeData( const QModelIndexList
&indexes
) const
205 foreach( QModelIndex idx
, indexes
)
207 if( rows
.contains( idx
.row() ) )
209 rows
.append( idx
.row() );
210 MLItem
* item
= static_cast<MLItem
*>( idx
.internalPointer() );
211 urls
.append( item
->getUri() );
213 QMimeData
*data
= new QMimeData
;
214 data
->setUrls( urls
);
218 int MLModel::columnCount( const QModelIndex
& ) const
220 return columnFromMeta( COLUMN_END
);
223 int MLModel::rowCount( const QModelIndex
& parent
) const
225 if( !parent
.isValid() )
226 return items
.count();
230 void MLModel::remove( MLItem
*item
)
232 int row
= items
.indexOf( item
);
233 remove( createIndex( row
, 0 ) );
236 void MLModel::doDelete( QModelIndexList list
)
238 for (int i
= 0; i
< list
.count(); ++i
)
240 int id
= getId( list
.at(i
) );
241 ml_DeleteSimple( p_ml
, id
);
245 void MLModel::remove( QModelIndex idx
)
251 beginRemoveRows( createIndex( 0, 0 ), idx
.row(), idx
.row() );
252 items
.removeAt( idx
.row() );
257 int MLModel::getId( QModelIndex index
) const
259 return getItem( index
)->id();
262 QVariant
MLModel::data( const QModelIndex
&index
, const int role
) const
264 if( index
.isValid() )
266 if( role
== Qt::DisplayRole
|| role
== Qt::EditRole
)
268 MLItem
*it
= static_cast<MLItem
*>( index
.internalPointer() );
269 if( !it
) return QVariant();
270 QVariant tmp
= it
->data( index
.column() );
273 else if( role
== IsLeafNodeRole
)
274 return QVariant( true );
275 else if( role
== IsCurrentsParentNodeRole
)
276 return QVariant( false );
281 bool MLModel::setData( const QModelIndex
&idx
, const QVariant
&value
,
284 if( role
!= Qt::EditRole
|| !idx
.isValid() ) return false;
285 MLItem
*media
= static_cast<MLItem
*>( idx
.internalPointer() );
286 media
->setData( columnType( idx
.column() ), value
);
287 emit
dataChanged( idx
, idx
);
292 * @brief Insert a media to the model in a given row
293 * @param p_media the media to append
294 * @param row the future row for this media, -1 to append at the end
295 * @param bSignal signal Qt that the model has been modified,
296 * should NOT be used by the user
297 * @return a VLC error code
299 int MLModel::insertMedia( ml_media_t
*p_media
, int row
,
303 if( !p_media
|| row
< -1 || row
> rowCount() )
310 beginInsertRows( createIndex( -1, -1 ), row
, row
);
312 // Create and insert the item
313 MLItem
*item
= new MLItem( this, p_intf
, p_media
, NULL
);
314 items
.append( item
);
323 * @brief Append a media to the model
324 * @param p_media the media to append
325 * @return see insertMedia
326 * @note Always signals. Do not use in a loop.
328 int MLModel::appendMedia( ml_media_t
*p_media
)
330 return insertMedia( p_media
, -1, true );
334 * @brief Insert all medias from an array to the model
335 * @param p_media_array the medias to append
336 * @return see insertMedia
337 * @note if bSignal==true, then it signals only once
339 int MLModel::insertMediaArray( vlc_array_t
*p_media_array
,
340 int row
, bool bSignal
)
342 int i_ok
= VLC_SUCCESS
;
343 int count
= vlc_array_count( p_media_array
);
351 // Signal Qt that we will insert rows
353 beginInsertRows( createIndex( -1, -1 ), row
, row
+ count
-1 );
356 for( int i
= 0; i
< count
; ++i
)
358 i_ok
= insertMedia( (ml_media_t
*)
359 vlc_array_item_at_index( p_media_array
, i
), row
+ i
, false );
360 if( i_ok
!= VLC_SUCCESS
)
371 * @brief Insert the media contained in a result to the model
372 * @param p_result the media to append is p_result->value.p_media
373 * @param row the future row for this media
374 * @param bSignal signal Qt that the model has been modified,
375 * should NOT be used by the user
376 * @return a VLC error code
378 int MLModel::insertResult( const ml_result_t
*p_result
, int row
,
381 if( !p_result
|| p_result
->type
!= ML_TYPE_MEDIA
)
384 return insertMedia( p_result
->value
.p_media
, row
, bSignal
);
388 * @brief Append the media contained in a result to the model
389 * @param p_result the media to append is p_result->value.p_media
390 * @param row the future row for this media
391 * @return a VLC error code
392 * @note Always signals. Do not use in a loop.
394 inline int MLModel::appendResult( const ml_result_t
*p_result
)
396 return insertResult( p_result
, -1, true );
400 * @brief Insert all medias from a result array to the model
401 * @param p_result_array the medias to append
402 * @return see insertMedia
403 * @note if bSignal==true, then it signals only once
404 * not media or NULL items are skipped
406 int MLModel::insertResultArray( vlc_array_t
*p_result_array
,
407 int row
, bool bSignal
)
409 int i_ok
= VLC_SUCCESS
;
410 int count
= vlc_array_count( p_result_array
);
418 // Signal Qt that we will insert rows
420 beginInsertRows( createIndex( -1, -1 ), row
, row
+ count
-1 );
423 for( int i
= 0; i
< count
; ++i
)
425 ml_result_t
*p_result
= (ml_result_t
*)
426 vlc_array_item_at_index( p_result_array
, i
);
427 if( !p_result
|| p_result
->type
!= ML_TYPE_MEDIA
)
429 i_ok
= insertMedia( p_result
->value
.p_media
, row
+ i
, false );
430 if( i_ok
!= VLC_SUCCESS
)
440 /** **************************************************************************
441 * \brief Add a media to the playlist
443 * \param id the item id
444 * @todo this code must definitely be done by the ML core
445 *****************************************************************************/
446 static void AddItemToPlaylist( int i_media_id
, bool bPlay
, media_library_t
* p_ml
,
450 input_item_t
*p_item
= ml_CreateInputItem( p_ml
, i_media_id
);
453 msg_Dbg( p_ml
, "unable to create input item for media %d",
457 playlist_t
*p_playlist
= pl_Get( p_ml
);
458 playlist_item_t
*p_playlist_item
= NULL
;
460 playlist_Lock( p_playlist
);
463 p_playlist_item
= playlist_ItemGetByInput( p_playlist
, p_item
);
466 if( !p_playlist_item
|| p_playlist_item
->i_id
== 1 )
468 playlist_AddInput( p_playlist
, p_item
,
470 PLAYLIST_END
, true, true );
472 p_playlist_item
= playlist_ItemGetByInput( p_playlist
, p_item
);
474 playlist_Unlock( p_playlist
);
476 if( !p_playlist_item
|| p_playlist_item
->i_id
== 1 )
478 msg_Dbg( p_ml
, "could not find playlist item %s (%s:%d)",
479 p_item
->psz_name
, __FILE__
, __LINE__
);
484 if( bPlay
) // || p_playlist->status.i_status == PLAYLIST_STOPPED )
486 playlist_Control( p_playlist
, PLAYLIST_VIEWPLAY
, false,
487 NULL
, p_playlist_item
);
489 vlc_gc_decref( p_item
);
492 void MLModel::activateItem( const QModelIndex
&index
)
497 void MLModel::play( const QModelIndex
&idx
)
501 MLItem
*item
= static_cast< MLItem
* >( idx
.internalPointer() );
504 AddItemToPlaylist( item
->id(), true, p_ml
, true );
507 bool MLModel::popup( const QModelIndex
& index
, const QPoint
&point
, const QModelIndexList
&list
)
509 current_selection
= list
;
510 current_index
= index
;
512 if( index
.isValid() )
514 menu
.addAction( QIcon( ":/menu/play" ), qtr(I_POP_PLAY
), this, SLOT( popupPlay() ) );
515 menu
.addAction( QIcon( ":/menu/stream" ),
516 qtr(I_POP_STREAM
), this, SLOT( popupStream() ) );
517 menu
.addAction( qtr(I_POP_SAVE
), this, SLOT( popupSave() ) );
518 menu
.addAction( QIcon( ":/menu/info" ), qtr(I_POP_INFO
), this, SLOT( popupInfo() ) );
523 QIcon
addIcon( ":/buttons/playlist/playlist_add" );
525 //menu.addAction( addIcon, qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) );
526 //menu.addAction( addIcon, qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) );
527 //menu.addAction( addIcon, qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) );
529 if( index
.isValid() )
531 menu
.addAction( QIcon( ":/buttons/playlist/playlist_remove" ),
532 qtr(I_POP_DEL
), this, SLOT( popupDel() ) );
535 if( !menu
.isEmpty() )
537 menu
.exec( point
); return true;
542 void MLModel::popupPlay()
544 play( current_index
);
547 void MLModel::popupDel()
549 doDelete( current_selection
);
552 void MLModel::popupInfo()
554 MLItem
*item
= static_cast< MLItem
* >( current_index
.internalPointer() );
555 input_item_t
* p_input
= ml_CreateInputItem( p_ml
, item
->id() );
556 MediaInfoDialog
*mid
= new MediaInfoDialog( p_intf
, p_input
);
557 mid
->setParent( PlaylistDialog::getInstance( p_intf
),
562 QStringList
MLModel::selectedURIs()
565 for( int i
= 0; i
< current_selection
.count(); i
++ )
567 QModelIndex idx
= current_selection
.value(i
);
568 MLItem
*item
= static_cast< MLItem
* >( idx
.internalPointer() );
569 list
.append( QString( item
->getUri().toString() ) );
574 void MLModel::popupStream()
576 QStringList mrls
= selectedURIs();
577 if( !mrls
.isEmpty() )
578 THEDP
->streamingDialog( NULL
, mrls
[0], false );
582 void MLModel::popupSave()
584 QStringList mrls
= selectedURIs();
585 if( !mrls
.isEmpty() )
586 THEDP
->streamingDialog( NULL
, mrls
[0] );
589 static int mediaAdded( vlc_object_t
*p_this
, char const *psz_var
,
590 vlc_value_t oldval
, vlc_value_t newval
,
593 VLC_UNUSED( psz_var
); VLC_UNUSED( oldval
);
595 int ret
= VLC_SUCCESS
;
596 media_library_t
*p_ml
= ( media_library_t
* )p_this
;
597 MLModel
* p_model
= ( MLModel
* )data
;
598 vlc_array_t
* p_result
= vlc_array_new();
599 ret
= ml_FindMedia( p_ml
, p_result
, ML_ID
, newval
.i_int
);
600 if( ret
!= VLC_SUCCESS
)
602 vlc_array_destroy( p_result
);
605 p_model
->insertResultArray( p_result
);
606 vlc_array_destroy( p_result
);
610 static int mediaDeleted( vlc_object_t
*p_this
, char const *psz_var
,
611 vlc_value_t oldval
, vlc_value_t newval
,
614 VLC_UNUSED( p_this
); VLC_UNUSED( psz_var
); VLC_UNUSED( oldval
);
616 MLModel
* p_model
= ( MLModel
* )data
;
617 QModelIndex remove_idx
= QModelIndex();
618 for( int i
= 0; i
< p_model
->rowCount( ); i
++ )
620 QModelIndex idx
= p_model
->index( i
, 0 );
621 MLItem
*item
= static_cast< MLItem
* >( idx
.internalPointer() );
622 if( item
->id() == newval
.i_int
)
628 if( remove_idx
.isValid() )
629 p_model
->remove( remove_idx
);
633 static int mediaUpdated( vlc_object_t
*p_this
, char const *psz_var
,
634 vlc_value_t oldval
, vlc_value_t newval
,
637 VLC_UNUSED( p_this
); VLC_UNUSED( psz_var
); VLC_UNUSED( oldval
);
638 VLC_UNUSED( newval
); VLC_UNUSED( data
);