mpdata/src/mpcollectiondatamodel.cpp
branchRCL_3
changeset 52 14979e23cb5e
equal deleted inserted replaced
50:26a1709b9fec 52:14979e23cb5e
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Music Player collection data model.
       
    15 *
       
    16 */
       
    17 
       
    18 #include <QIcon>
       
    19 #include <QList>
       
    20 #include <QMimeData>
       
    21 #include <hbicon.h>
       
    22 #include <hbnamespace.h>
       
    23 
       
    24 #include <hgwidgets.h>
       
    25 
       
    26 
       
    27 #include "mpcollectiondatamodel.h"
       
    28 #include "mpcollectionalbumartmanager.h"
       
    29 #include "mpplaybackdata.h"
       
    30 #include "mpmpxcollectiondata.h"
       
    31 #include "mptrace.h"
       
    32 
       
    33 /*!
       
    34     \class MpCollectionDataModel
       
    35     \brief Music Player collection data model.
       
    36 
       
    37     Collection data model implements the interface specified by HbAbstractDataModel,
       
    38     which defines the standard interface that item models must use to be able to
       
    39     interoperate with other components in the model/view architecture.
       
    40 
       
    41     Every item of data that can be accessed via a model has an associated model
       
    42     index.
       
    43 
       
    44     Each item has a number of data elements associated with it and they can be
       
    45     retrieved by specifying a role (see Qt::ItemDataRole) to the model's data
       
    46     returned by itemData() function.
       
    47 
       
    48     \sa HbAbstractDataModel
       
    49 */
       
    50 
       
    51 /*!
       
    52     \fn void orderChanged( int containerId, int itemId, int itemOrdinal, int newOrdinal )
       
    53 
       
    54     This signal is emitted when a reorder operations is propagated to the 
       
    55     model, indicates that the item with \a containerId , \a itemId is to 
       
    56     be moved from \a itemOrdinal to \a newOrdinal.
       
    57  */
       
    58 
       
    59 /*!
       
    60     \fn void dataReloaded()
       
    61 
       
    62     This signal is emitted when there has been a changed in the data represented
       
    63     by this model. This signal is emitted so that the view owning this model can
       
    64     refresh the view. This can happen after operations like delete and several
       
    65     playlist related operations (save, rearrange, remove).
       
    66  */
       
    67 
       
    68 /*!
       
    69  Constructs the collection data model.
       
    70  */
       
    71 MpCollectionDataModel::MpCollectionDataModel( MpMpxCollectionData *data, MpPlaybackData *playbackData, QObject *parent )
       
    72     : QAbstractListModel(parent),
       
    73       mCollectionData(data),
       
    74       mPlaybackData(playbackData),
       
    75       mRowCount(0),
       
    76       mAlbumIndexOffset(0),
       
    77       mHiddenItemIndex(-1),
       
    78       mCollectionLayout(ECollectionListView)
       
    79 {
       
    80     TX_ENTRY
       
    81     connect( mCollectionData, SIGNAL(contextChanged(TCollectionContext)), this, SLOT(setContext(TCollectionContext)) );
       
    82     connect( mCollectionData, SIGNAL(dataChanged()), this, SLOT(reloadData()) );
       
    83     connect( mCollectionData, SIGNAL(dataChanged(int, int)), this, SLOT(reloadData(int, int)) );
       
    84     mAlbumArtManager = new MpCollectionAlbumArtManager(mCollectionData);
       
    85     connect( mAlbumArtManager, SIGNAL(albumArtReady(int)), this, SLOT(updateAlbumArt(int)) );
       
    86     
       
    87     if ( mPlaybackData ) {         
       
    88         connect( mPlaybackData, SIGNAL(fileCorrupted(int)), 
       
    89                  this, SLOT(fileCorrupted(int)) );
       
    90     }
       
    91     TX_EXIT
       
    92 }
       
    93 
       
    94 /*!
       
    95  Destructs the collection data model.
       
    96  */
       
    97 MpCollectionDataModel::~MpCollectionDataModel()
       
    98 {
       
    99     TX_ENTRY
       
   100     delete mAlbumArtManager;
       
   101     TX_EXIT
       
   102 }
       
   103 
       
   104 /*!
       
   105  Returns the number of rows under the given \a parent.
       
   106 
       
   107  View will request for the row count immediately after a model is set.
       
   108  To prevent it from reading data while caching the album art for the first
       
   109  screen, return row count as zero.
       
   110 
       
   111  \reimp
       
   112  */
       
   113 int MpCollectionDataModel::rowCount( const QModelIndex &parent ) const
       
   114 {
       
   115     TX_LOG
       
   116     Q_UNUSED(parent);
       
   117     return mRowCount - mAlbumIndexOffset;
       
   118 }
       
   119 
       
   120 /*!
       
   121  Returns the data stored for the item referred to by the \a index.
       
   122 
       
   123  \reimp
       
   124  */
       
   125 QVariant MpCollectionDataModel::data(const QModelIndex &index, int role) const
       
   126 {
       
   127     TX_ENTRY
       
   128     QVariant returnValue = QVariant();
       
   129     if ( !index.isValid() ) {
       
   130         return returnValue;
       
   131     }
       
   132 
       
   133     int row = index.row() + mAlbumIndexOffset;
       
   134     TX_LOG_ARGS("index=" << row << ", role=" << role);
       
   135     TCollectionContext context = mCollectionData->context();
       
   136     if ( role == Qt::DisplayRole ) {
       
   137         QStringList display;
       
   138         // Fetch the primary text, which is the title, if available.
       
   139         QString primaryText;
       
   140         switch ( context ) {
       
   141             case ECollectionContextAllSongs:
       
   142             case ECollectionContextArtists:
       
   143             case ECollectionContextAlbums:
       
   144             case ECollectionContextArtistAlbums:
       
   145             case ECollectionContextPlaylists:
       
   146             case ECollectionContextPlaylistSongs:
       
   147             case ECollectionContextAlbumsTBone:
       
   148             case ECollectionContextArtistAlbumsTBone:
       
   149             case ECollectionContextArtistAllSongs:
       
   150                 primaryText = mCollectionData->itemData(row, MpMpxCollectionData::Title);
       
   151                 if ( !primaryText.isEmpty() ) {
       
   152                     display << primaryText;
       
   153                 }
       
   154                 else {
       
   155                     switch ( context ) {
       
   156                         case ECollectionContextArtists:
       
   157                             display << hbTrId("txt_mus_list_unknown");
       
   158                             break;
       
   159                         case ECollectionContextArtistAlbums:
       
   160                         case ECollectionContextAlbums:
       
   161                             display << hbTrId("txt_mus_dblist_unknown");
       
   162                             break;
       
   163                         case ECollectionContextAlbumsTBone:
       
   164                         case ECollectionContextArtistAlbumsTBone:
       
   165                             display << hbTrId("txt_mus_other_unknown8");
       
   166                             break;
       
   167                         default:
       
   168                             // Otherwise the primary text should be the Song Title or File Name
       
   169                             // but can not be empty, if so there is an error.
       
   170                             TX_LOG_ARGS("Error: Song title empty.");
       
   171                             break;
       
   172                     }
       
   173                 }
       
   174                 break;
       
   175             case ECollectionContextAlbumsMediaWall:
       
   176                 primaryText = mCollectionData->itemData(row, MpMpxCollectionData::Artist);
       
   177                 if ( !primaryText.isEmpty() ) {
       
   178                     display << primaryText;
       
   179                 }
       
   180                 else {
       
   181                     display << hbTrId("txt_mus_other_unknown2");
       
   182                 }    
       
   183                 break;
       
   184             default:
       
   185                 break;
       
   186         }
       
   187         
       
   188         // Fetch the secondary text, which depends on the current context, if available.
       
   189         QString secondaryText;
       
   190         switch ( context ) {
       
   191             case ECollectionContextAllSongs:
       
   192             case ECollectionContextAlbums:
       
   193             case ECollectionContextPlaylistSongs:
       
   194             case ECollectionContextAlbumsTBone:
       
   195                 secondaryText = mCollectionData->itemData(row, MpMpxCollectionData::Artist);
       
   196                 if ( !secondaryText.isEmpty() ) {
       
   197                     display << secondaryText;
       
   198                 }
       
   199                 else {
       
   200                     switch ( context ) {
       
   201                         case ECollectionContextAllSongs:
       
   202                         case ECollectionContextPlaylistSongs:
       
   203                             if ( mCollectionLayout == ECollectionSelectionDialog ) {
       
   204                                 display << hbTrId("txt_mus_dblist_val_unknown4");
       
   205                             }
       
   206                             else if ( mCollectionLayout == ECollectionArrangeSongsDialog ) {
       
   207                                 display << hbTrId("txt_mus_other_unknown9");
       
   208                             }
       
   209                             else {
       
   210                                 display << hbTrId("txt_mus_dblist_val_unknown");
       
   211                             }
       
   212                             break;
       
   213                         case ECollectionContextAlbums:
       
   214                             display << hbTrId("txt_mus_dblist_val_unknown2");
       
   215                             break;
       
   216                         case ECollectionContextAlbumsTBone:
       
   217                             display << hbTrId("txt_mus_other_unknown5");
       
   218                             break;
       
   219                     }
       
   220                 }
       
   221                 break;
       
   222             case ECollectionContextArtistAlbumsTBone:
       
   223                 secondaryText = mCollectionData->collectionTitle();
       
   224                 if ( !secondaryText.isEmpty() ) {
       
   225                     display << secondaryText;
       
   226                 }
       
   227                 else {
       
   228                     display << hbTrId("txt_mus_other_unknown5");
       
   229                 }
       
   230                 break;
       
   231             case ECollectionContextArtistAllSongs:
       
   232                 secondaryText = mCollectionData->itemData(row, MpMpxCollectionData::Album);
       
   233                 if ( !secondaryText.isEmpty() ) {
       
   234                     display << secondaryText;
       
   235                 }
       
   236                 else {
       
   237                     display << hbTrId("txt_mus_dblist_val_unknown3");
       
   238                 }
       
   239                 break;
       
   240             case ECollectionContextAlbumsMediaWall:
       
   241                 secondaryText = mCollectionData->itemData(row, MpMpxCollectionData::Title);
       
   242                 if ( !secondaryText.isEmpty() ) {
       
   243                     display << secondaryText;
       
   244                 }
       
   245                 else {
       
   246                     display << hbTrId("txt_mus_other_unknown1");
       
   247                 }
       
   248                 break;
       
   249             default:
       
   250                 break;
       
   251         }
       
   252         returnValue = display;
       
   253     }
       
   254     else if ( role == Qt::DecorationRole ) {
       
   255         switch ( context ) {
       
   256             case ECollectionContextAlbums:
       
   257             case ECollectionContextAlbumsMediaWall:
       
   258             case ECollectionContextArtistAlbumsTBone:
       
   259             case ECollectionContextAlbumsTBone:
       
   260                     returnValue = mAlbumArtManager->albumArt( row ) ;
       
   261                 break;
       
   262             case ECollectionContextArtistAlbums:
       
   263                 if ( row == 0 ) {
       
   264                     returnValue = HbIcon( "qtg_small_sound" );
       
   265                 }
       
   266                 else {
       
   267                     returnValue = mAlbumArtManager->albumArt( row );
       
   268                 }
       
   269                 break;
       
   270             case ECollectionContextAllSongs:
       
   271             case ECollectionContextArtistAllSongs:
       
   272             case ECollectionContextPlaylistSongs:
       
   273                 if( mCollectionData->hasItemProperty(row, MpMpxCollectionData::Corrupted) ){
       
   274 					QList<QVariant> iconList;
       
   275                     iconList << QVariant();
       
   276                     iconList << HbIcon("qtg_mono_corrupted");
       
   277                     returnValue = iconList;
       
   278                 }
       
   279                 else if ( mCollectionData->hasItemProperty(row, MpMpxCollectionData::DrmExpired) ) {
       
   280                     QList<QVariant> iconList;
       
   281                     iconList << QVariant();
       
   282                     iconList << HbIcon("qtg_small_drm_rights_expired");
       
   283                     returnValue = iconList;
       
   284                 } 
       
   285                 break;
       
   286             default:
       
   287                 break;
       
   288         }
       
   289     }
       
   290     else if ( role == Hb::IndexFeedbackRole ) {
       
   291         QString feedbackIndex;
       
   292         switch ( context ) {
       
   293             case ECollectionContextAllSongs:
       
   294             case ECollectionContextArtists:
       
   295             case ECollectionContextAlbums:
       
   296             case ECollectionContextArtistAlbums:
       
   297             case ECollectionContextPlaylists:
       
   298             case ECollectionContextPlaylistSongs:
       
   299             case ECollectionContextAlbumsTBone:
       
   300             case ECollectionContextArtistAlbumsTBone:
       
   301             case ECollectionContextArtistAllSongs:
       
   302                 feedbackIndex = mCollectionData->itemData(row, MpMpxCollectionData::Title);
       
   303                 break;
       
   304             case ECollectionContextAlbumsMediaWall:
       
   305                 feedbackIndex = mCollectionData->itemData(row, MpMpxCollectionData::Artist);
       
   306                 break;
       
   307             default:
       
   308                 break;
       
   309         }
       
   310         returnValue = feedbackIndex;
       
   311     }
       
   312     else if ( role == HgWidget::HgVisibilityRole 
       
   313         && context == ECollectionContextAlbumsMediaWall) {
       
   314         returnValue = !( row == mHiddenItemIndex );
       
   315         }
       
   316     TX_EXIT
       
   317     return returnValue;
       
   318 }
       
   319 
       
   320 /*!
       
   321  \reimp
       
   322  */
       
   323 Qt::DropActions MpCollectionDataModel::supportedDropActions() const
       
   324 {
       
   325     return Qt::MoveAction;
       
   326 }
       
   327 
       
   328 /*! 
       
   329  \reimp
       
   330 */
       
   331 bool MpCollectionDataModel::removeRows(int row, int count, const QModelIndex &parent )
       
   332 {
       
   333     if ( count != 1 || row >= mRowCount ) {
       
   334         return false;
       
   335     }
       
   336     beginRemoveRows ( parent, row, row);
       
   337     //This call internally caches the item, to be inserted if it it drag and drop.
       
   338     mCollectionData->removeItem(row);
       
   339     mRowCount--;
       
   340     endRemoveRows();
       
   341     return true;
       
   342 }
       
   343 
       
   344 /*! 
       
   345  \reimp
       
   346 */
       
   347 QStringList MpCollectionDataModel::mimeTypes() const
       
   348 {
       
   349     QStringList types;
       
   350     types << QLatin1String("application/x-mpcollectiondatamodelrowandids");
       
   351     return types;
       
   352 }
       
   353 
       
   354 /*! 
       
   355  \reimp
       
   356 */
       
   357 QMimeData *MpCollectionDataModel::mimeData(const QModelIndexList &indexes) const
       
   358 {
       
   359     if (indexes.count() != 1)
       
   360         return 0;
       
   361     QStringList types = mimeTypes();
       
   362     if (types.isEmpty())
       
   363         return 0;
       
   364     QMimeData *data = new QMimeData();
       
   365     QString format = types.at(0);
       
   366     QByteArray encoded;
       
   367     QDataStream stream(&encoded, QIODevice::WriteOnly);
       
   368     stream << indexes.at(0).row();
       
   369     stream << mCollectionData->containerId();
       
   370     stream << mCollectionData->itemId( indexes.at(0).row() );
       
   371     
       
   372     data->setData(format, encoded);
       
   373     return data;
       
   374 }
       
   375 
       
   376 /*! 
       
   377  \reimp
       
   378 */
       
   379 bool MpCollectionDataModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
       
   380                           int row, int column, const QModelIndex &parent)
       
   381 {
       
   382     Q_UNUSED(column);
       
   383     // check if the action is supported
       
   384     if (!data || action != Qt::MoveAction | row > mRowCount ) {
       
   385         return false;
       
   386     }
       
   387     // check if the format is supported
       
   388     QStringList types = mimeTypes();
       
   389     if (types.isEmpty()) {
       
   390         return false;
       
   391     }
       
   392     QString format = types.at(0);
       
   393     if (!data->hasFormat(format)) {
       
   394         return false;
       
   395     }
       
   396     // decode and insert
       
   397     QByteArray encoded = data->data(format);
       
   398     QDataStream stream(&encoded, QIODevice::ReadOnly);
       
   399     int rowFrom = -1;
       
   400     int mpxContainerId = -1;
       
   401     int mpxItemId = -1;
       
   402     if (!stream.atEnd()) {
       
   403         stream >> rowFrom;
       
   404         stream >> mpxContainerId;
       
   405         stream >> mpxItemId;
       
   406     } 
       
   407     if ( rowFrom == -1 || mpxContainerId == -1 || mpxItemId == -1  || 
       
   408             !mCollectionData->testCachedItem( mpxItemId ) || rowFrom > mRowCount) {
       
   409         return false;
       
   410     }    
       
   411         
       
   412     beginInsertRows( parent, row, row );
       
   413     emit orderChanged( mpxContainerId, mpxItemId, rowFrom, row );
       
   414     mCollectionData->insertCachedItem( row );
       
   415     mRowCount++;
       
   416     endInsertRows();
       
   417     return true;
       
   418 }
       
   419 
       
   420 /*! 
       
   421  Sets the item visibility, model will report \a visible value as 
       
   422  HgWidget::HgVisibilityRole for the item at \a index. 
       
   423  datachanged() signal is emited when calling this function.
       
   424  Currently this is only used by Media Wall View.
       
   425 */
       
   426 void MpCollectionDataModel::setItemVisibility(const QModelIndex &index, bool visible)
       
   427 {
       
   428     mHiddenItemIndex = visible ? -1 : index.row();
       
   429     emit dataChanged(index, index);
       
   430 }
       
   431 
       
   432 /*!
       
   433  Returns pointer to MpMpxCollectionData, which is the collection data.
       
   434  */
       
   435 MpMpxCollectionData *MpCollectionDataModel::collectionData()
       
   436 {
       
   437     return mCollectionData;
       
   438 }
       
   439 
       
   440 /*!
       
   441  Sets the layout where the collection is being displayed.
       
   442  */
       
   443 void MpCollectionDataModel::setLayout(TCollectionLayout layout)
       
   444 {
       
   445     mCollectionLayout = layout;
       
   446 }
       
   447 
       
   448 /*!
       
   449  Slot to be called when collection context is changed.
       
   450  */
       
   451 void MpCollectionDataModel::setContext( TCollectionContext context )
       
   452 {
       
   453     TX_ENTRY_ARGS( "context=" << context );
       
   454     // Reset the album index offset for navigation
       
   455     mAlbumIndexOffset = 0;
       
   456     switch ( context ) {
       
   457         case ECollectionContextArtistAlbums:
       
   458         case ECollectionContextAlbums:
       
   459             mAlbumArtManager->setThumbnailSize( MpCommon::ListThumb );
       
   460             mAlbumArtManager->enableDefaultArt( true );
       
   461             break;
       
   462         case ECollectionContextArtistAlbumsTBone:
       
   463             if ( mCollectionData->count() > 1 ) {
       
   464                 // Selected artist has more than 1 album and therefore the
       
   465                 // artist's "All songs" exist. Since we don't show artist's
       
   466                 // "All songs" in TBone, we need to set an offset.
       
   467                 mAlbumIndexOffset = 1;
       
   468             }
       
   469             //intentional fallthrough
       
   470        case ECollectionContextAlbumsTBone:
       
   471             mAlbumArtManager->setThumbnailSize( MpCommon::TBoneThumb );
       
   472             mAlbumArtManager->enableDefaultArt( false );
       
   473             break;
       
   474         case ECollectionContextAlbumsMediaWall:
       
   475             mAlbumArtManager->setThumbnailSize( MpCommon::MediaWallThumb );
       
   476             mAlbumArtManager->enableDefaultArt( false );
       
   477             break;
       
   478         default:
       
   479             break;
       
   480     }
       
   481     TX_EXIT
       
   482 }
       
   483 
       
   484 /*!
       
   485  Slot to be called when album art for the \a index needs to be updated.
       
   486  */
       
   487 void MpCollectionDataModel::updateAlbumArt( int index )
       
   488 {
       
   489     TX_ENTRY_ARGS("index=" << index);
       
   490 
       
   491     index -= mAlbumIndexOffset;
       
   492     if ( index >= 0 && index < mRowCount ) {
       
   493         emit dataChanged(this->index(index, 0), this->index(index, 0));
       
   494     }
       
   495     TX_EXIT
       
   496 }
       
   497 
       
   498 /*!
       
   499  Slot to be called when data has changed and model needs to be refreshed
       
   500  to reflect the new data.
       
   501  */
       
   502 void MpCollectionDataModel::refreshModel()
       
   503 {
       
   504     TX_ENTRY
       
   505     // Cancel all outstanding album art request first, then reset the model.
       
   506     mAlbumArtManager->cancel();
       
   507     mRowCount = mCollectionData->count();
       
   508 
       
   509     TCollectionContext context = mCollectionData->context();
       
   510     if ( context == ECollectionContextAlbums || 
       
   511          context == ECollectionContextArtistAlbums ||
       
   512          context == ECollectionContextAlbumsMediaWall ) {
       
   513         // Before providing the new data to the view (list, grid, etc.), we want
       
   514         // to make sure that we have enough album arts for the first screen.
       
   515         mAlbumArtManager->cacheFirstScreen();
       
   516     }
       
   517     reset();
       
   518     TX_EXIT
       
   519 }
       
   520 
       
   521 /*!
       
   522  Slot to be called when data has changed (same context) and model needs to reload
       
   523  the data.
       
   524  */
       
   525 void MpCollectionDataModel::reloadData()
       
   526 {
       
   527     TX_ENTRY
       
   528     mAlbumArtManager->cancel();
       
   529     mRowCount = mCollectionData->count();
       
   530     reset();
       
   531     emit dataReloaded();
       
   532     TX_EXIT
       
   533 }
       
   534 
       
   535 /*!
       
   536  Slot to be called when data has changed (same context) and model needs to reload
       
   537  the data in the specified range.
       
   538  */
       
   539 void MpCollectionDataModel::reloadData( int fromIndex, int toIndex )
       
   540 {
       
   541     TX_ENTRY_ARGS("fromIndex=" << fromIndex << ", toIndex=" << toIndex);
       
   542     emit dataChanged(this->index(fromIndex,0), this->index(toIndex,0));
       
   543     TX_EXIT
       
   544 }
       
   545 
       
   546 /*!
       
   547  Slot to be called when a song is marked as corrupted
       
   548  */
       
   549 void MpCollectionDataModel::fileCorrupted(int songId)
       
   550 {
       
   551     TX_ENTRY
       
   552     QModelIndex corruptedSongIndex;
       
   553     QList<int> indexList = mCollectionData->songIndex( songId );
       
   554     for (int i = 0; i < indexList.count(); i++){
       
   555         corruptedSongIndex = index( indexList.at(i) );
       
   556         if ( corruptedSongIndex.isValid() && (!(mCollectionData->hasItemProperty(corruptedSongIndex.row(),
       
   557                 MpMpxCollectionData::Corrupted)))) {
       
   558         
       
   559             mCollectionData->setCorruptValue(corruptedSongIndex, false);
       
   560             emit dataChanged( corruptedSongIndex, corruptedSongIndex );
       
   561         }
       
   562     }
       
   563     TX_EXIT   
       
   564 }
       
   565 
       
   566