Qt: correctly display the right treeView columns
[vlc.git] / modules / gui / qt4 / components / playlist / ml_model.cpp
blob3aae7ddb59ed66c3ee0fd3dabe9c843c2652d4da
1 /*****************************************************************************
2 * ml_model.cpp: the media library's model
3 *****************************************************************************
4 * Copyright (C) 2008-2011 the VideoLAN Team and AUTHORS
5 * $Id$
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 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
32 #ifdef MEDIA_LIBRARY
34 #include <QUrl>
35 #include <QMenu>
36 #include <QMimeData>
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 */
45 #include <assert.h>
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,
50 void *data );
51 static int mediaDeleted( vlc_object_t *p_this, char const *psz_var,
52 vlc_value_t oldval, vlc_value_t newval,
53 void *data );
54 static int mediaUpdated( vlc_object_t *p_this, char const *psz_var,
55 vlc_value_t oldval, vlc_value_t newval,
56 void *data );
58 /**
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 );
75 /**
76 * @brief Simple destructor for the model
78 MLModel::~MLModel()
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 );
85 void MLModel::clear()
87 int rows = rowCount();
88 if( rows > 0 )
90 beginRemoveRows( createIndex( 0, 0 ), 0, rows-1 );
91 items.clear();
92 endRemoveRows();
93 emit layoutChanged();
97 QModelIndex MLModel::index( int row, int column,
98 const QModelIndex &parent ) const
100 if( parent.isValid() || row >= items.count() )
101 return QModelIndex();
102 else
104 QModelIndex idx = createIndex( row, column, items.value( row ) );
105 return idx;
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,
144 int role ) const
146 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
147 return QVariant( psz_column_title( columnToMeta( section ) ) );
148 else
149 return QVariant();
152 Qt::ItemFlags MLModel::flags(const QModelIndex &index) const
154 if( !index.isValid() )
155 return 0;
157 if( isEditable( index ) )
158 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
159 | Qt::ItemIsEditable;
160 else
161 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
164 bool MLModel::isEditable( const QModelIndex &index ) const
166 if( !index.isValid() )
167 return false;
169 ml_select_e type = columnType( index.column() );
170 switch( type )
172 // Read-only members: not editable
173 case ML_ALBUM_ID:
174 case ML_ARTIST_ID:
175 case ML_DURATION:
176 case ML_ID:
177 case ML_LAST_PLAYED:
178 case ML_PLAYED_COUNT:
179 case ML_TYPE:
180 return false;
181 // Read-write members: editable
182 case ML_ALBUM:
183 case ML_ARTIST:
184 case ML_COVER:
185 case ML_EXTRA:
186 case ML_GENRE:
187 case ML_ORIGINAL_TITLE:
188 // case ML_ROLE:
189 case ML_SCORE:
190 case ML_TITLE:
191 case ML_TRACK_NUMBER:
192 case ML_URI:
193 case ML_VOTE:
194 case ML_YEAR:
195 return true;
196 default:
197 return false;
201 QMimeData* MLModel::mimeData( const QModelIndexList &indexes ) const
203 QList< QUrl > urls;
204 QList< int > rows;
205 foreach( QModelIndex idx, indexes )
207 if( rows.contains( idx.row() ) )
208 continue;
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 );
215 return data;
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();
227 return 0;
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 )
247 if( !idx.isValid() )
248 return;
249 else
251 beginRemoveRows( createIndex( 0, 0 ), idx.row(), idx.row() );
252 items.removeAt( idx.row() );
253 endRemoveRows();
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() );
271 return tmp;
273 else if( role == IsLeafNodeRole )
274 return QVariant( true );
275 else if( role == IsCurrentsParentNodeRole )
276 return QVariant( false );
278 return QVariant();
281 bool MLModel::setData( const QModelIndex &idx, const QVariant &value,
282 int role )
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 );
288 return true;
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,
300 bool bSignal )
302 // Some checks
303 if( !p_media || row < -1 || row > rowCount() )
304 return VLC_EGENERIC;
306 if( row == -1 )
307 row = rowCount();
309 if( bSignal )
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 );
316 if( bSignal )
317 endInsertRows();
319 return VLC_SUCCESS;
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 );
345 if( !count )
346 return i_ok;
348 if( row == -1 )
349 row = rowCount();
351 // Signal Qt that we will insert rows
352 if( bSignal )
353 beginInsertRows( createIndex( -1, -1 ), row, row + count-1 );
355 // Loop
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 )
361 break;
364 if( bSignal )
365 endInsertRows();
367 return i_ok;
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,
379 bool bSignal )
381 if( !p_result || p_result->type != ML_TYPE_MEDIA )
382 return VLC_EGENERIC;
383 else
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 );
412 if( !count )
413 return i_ok;
415 if( row == -1 )
416 row = rowCount();
418 // Signal Qt that we will insert rows
419 if( bSignal )
420 beginInsertRows( createIndex( -1, -1 ), row, row + count-1 );
422 // Loop and insert
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 )
428 continue;
429 i_ok = insertMedia( p_result->value.p_media, row + i, false );
430 if( i_ok != VLC_SUCCESS )
431 break;
433 // Signal we're done
434 if( bSignal )
435 endInsertRows();
437 return i_ok;
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,
447 bool bRenew )
450 input_item_t *p_item = ml_CreateInputItem( p_ml, i_media_id );
451 if( !p_item )
453 msg_Dbg( p_ml, "unable to create input item for media %d",
454 i_media_id );
455 return;
457 playlist_t *p_playlist = pl_Get( p_ml );
458 playlist_item_t *p_playlist_item = NULL;
460 playlist_Lock( p_playlist );
461 if( !bRenew )
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,
469 PLAYLIST_APPEND,
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__ );
480 return;
483 /* Auto play item */
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 )
494 play( index );
497 void MLModel::play( const QModelIndex &idx )
499 if( !idx.isValid() )
500 return;
501 MLItem *item = static_cast< MLItem* >( idx.internalPointer() );
502 if( !item )
503 return;
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;
511 QMenu menu;
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() ) );
519 menu.addSeparator();
523 QIcon addIcon( ":/buttons/playlist/playlist_add" );
524 menu.addSeparator();
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() ) );
533 menu.addSeparator();
535 if( !menu.isEmpty() )
537 menu.exec( point ); return true;
539 else return false;
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 ),
558 Qt::Dialog );
559 mid->show();
562 QStringList MLModel::selectedURIs()
564 QStringList list;
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() ) );
571 return list;
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,
591 void *data )
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 );
603 return VLC_EGENERIC;
605 p_model->insertResultArray( p_result );
606 vlc_array_destroy( p_result );
607 return VLC_SUCCESS;
610 static int mediaDeleted( vlc_object_t *p_this, char const *psz_var,
611 vlc_value_t oldval, vlc_value_t newval,
612 void *data )
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 )
624 remove_idx = idx;
625 break;
628 if( remove_idx.isValid() )
629 p_model->remove( remove_idx );
630 return VLC_SUCCESS;
633 static int mediaUpdated( vlc_object_t *p_this, char const *psz_var,
634 vlc_value_t oldval, vlc_value_t newval,
635 void *data )
637 VLC_UNUSED( p_this ); VLC_UNUSED( psz_var ); VLC_UNUSED( oldval );
638 VLC_UNUSED( newval ); VLC_UNUSED( data );
640 return VLC_SUCCESS;
643 #endif