Lots of work on album grouping in the playlist. Now handles ragging around stuff...
[amarok.git] / src / playlist / PlaylistModel.cpp
blob58aea2f82e3eb82b966c91b04417901b75c061a8
1 /***************************************************************************
2 * copyright : (C) 2007 Ian Monroe <ian@monroe.nu> *
3 * : (C) 2007 Nikolaj Hald Nielsen <nhnFreespirit@gmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License version 2 *
7 * as published by the Free Software Foundation. *
8 ***************************************************************************/
10 #include "PlaylistModel.h"
12 #include "amarok.h"
13 #include "amarokconfig.h"
14 #include "AmarokMimeData.h"
15 #include "debug.h"
16 #include "enginecontroller.h"
17 #include "PlaylistItem.h"
18 #include "RepeatTrackNavigator.h"
19 #include "StandardTrackNavigator.h"
20 #include "statusbar.h"
21 #include "TheInstances.h"
22 #include "UndoCommands.h"
24 #include "collection/BlockingQuery.h"
25 #include "collection/Collection.h"
26 #include "collection/CollectionManager.h"
27 #include "collection/QueryMaker.h"
28 #include "meta/lastfm/LastFmMeta.h"
30 #include <QAction>
31 #include <QStringList>
32 #include <QUndoStack>
34 #include <KIcon>
35 #include <KUrl>
37 using namespace Playlist;
38 using namespace Meta;
40 namespace Amarok
42 // Sorting of a tracklist.
43 bool trackNumberLessThan( Meta::TrackPtr left, Meta::TrackPtr right )
45 if( left->album() == right->album() )
46 return left->trackNumber() < right->trackNumber();
48 else if( left->artist() == right->artist() )
49 return QString::localeAwareCompare( left->album()->prettyName(), right->album()->prettyName() ) < 0;
50 else // compare artists alphabetically
51 return QString::localeAwareCompare( left->artist()->prettyName(), right->artist()->prettyName() ) < 0;
55 Model *Model::s_instance = 0;
57 Model::Model( QObject* parent )
58 : QAbstractListModel( parent )
59 , m_activeRow( -1 )
60 , m_advancer( new StandardTrackNavigator( this ) )
61 , m_undoStack( new QUndoStack( this ) )
62 , m_playlistHandler ( new PlaylistHandler )
64 connect( EngineController::instance(), SIGNAL( trackFinished() ), this, SLOT( trackFinished() ) );
65 connect( EngineController::instance(), SIGNAL( orderCurrent() ), this, SLOT( playCurrentTrack() ) );
66 s_instance = this;
69 void
70 Model::init()
73 KActionCollection* ac = Amarok::actionCollection();
74 QAction* undoButton = m_undoStack->createUndoAction( this, i18n("Undo") );
75 undoButton->setIcon( KIcon( Amarok::icon( "undo" ) ) );
76 ac->addAction("playlist_undo", undoButton);
77 QAction* redoButton = m_undoStack->createRedoAction( this, i18n("Redo") );
78 ac->addAction("playlist_redo", redoButton);
79 redoButton->setIcon( KIcon( Amarok::icon( "redo" ) ) );
82 Model::~Model()
84 if( AmarokConfig::savePlaylist() )
86 Meta::TrackList list;
87 foreach( Item* item, itemList() )
89 list << item->track();
91 m_playlistHandler->save( list, defaultPlaylistPath() );
93 delete m_advancer;
94 delete m_playlistHandler;
97 int
98 Model::rowCount( const QModelIndex& ) const
100 return m_items.size();
103 QVariant
104 Model::data( const QModelIndex& index, int role ) const
106 int row = index.row();
107 /*if( ( role == Qt::FontRole) && ( row == m_activeRow ) )
109 QFont original;
110 original.setBold( true );
111 return original;
113 else*/
114 if( role == ItemRole && ( row != -1 ) )
116 return QVariant::fromValue( m_items.at( row ) );
118 else if( role == ActiveTrackRole )
119 return ( row == m_activeRow );
120 else if( role == TrackRole && ( row != -1 ) && m_items.at( row )->track() )
122 return QVariant::fromValue( m_items.at( row )->track() );
124 else if ( role == GroupRole ) {
127 //get the track
128 TrackPtr track = m_items.at( row )->track();
130 if ( !track->album() )
131 return None; // no albm set
132 else if ( !m_albumGroups.contains( track->album() ) )
133 return None; // no group for this album, should never happen...
135 AlbumGroup * albumGroup = m_albumGroups.value( track->album() );
136 return albumGroup->groupMode( row );
138 } else if ( role == GroupedTracksRole ) {
139 //get the track
140 TrackPtr track = m_items.at( row )->track();
142 AlbumGroup * albumGroup = m_albumGroups.value( track->album() );
143 return albumGroup->elementsInGroup( row );
145 } else if( role == Qt::DisplayRole && row != -1 )
147 switch ( index.column() ) {
148 case 0:
149 return m_items.at( row )->track()->name();
150 case 1:
151 if ( m_items.at( row )->track()->album() )
152 return m_items.at( row )->track()->album()->name();
153 else
154 return "";
155 case 2:
156 if ( m_items.at( row )->track()->artist() )
157 return m_items.at( row )->track()->artist()->name();
158 else
159 return "";
162 else
164 return QVariant();
166 /* switch( role )
168 case AlbumArtist: return track->album()->albumArtist()->name();
169 case Album: return track->album()->name();
170 case Artist: return track->artist()->name();
171 case Bitrate: return track->bitrate();
172 case Composer: return track->composer()->name();
173 case CoverImage: return track->album()->image( 50 );
174 case Comment: return track->comment();
175 case DiscNumber: return track->discNumber();
176 case Filesize: return track->filesize();
177 case Genre: return track->genre()->name();
178 case Length: return track->length();
179 case Rating: return track->rating();
180 case Score: return track->score();
181 case Title: return track->name();
182 case TrackNumber: return track->trackNumber();
183 case Year: return track->year()->name().toInt();
184 default: return QVariant();
185 } */
188 // void
189 // insertColumns( int column, Column type )
190 // {
191 // beginInsertColumns( QModelIndex(), column, column + 1 );
192 // m_columns.insert( column, type );
193 // endInsertColumns();
194 // return true;
195 // }
197 void
198 Model::insertTrack( int row, TrackPtr track )
200 DEBUG_BLOCK
201 TrackList list;
202 list.append( track );
203 insertTracks( row, list );
207 void
208 Model::insertTracks( int row, TrackList tracks )
210 m_undoStack->push( new AddTracksCmd( 0, row, tracks ) );
213 bool
214 Model::removeRows( int position, int rows, const QModelIndex& /*parent*/ )
216 m_undoStack->push( new RemoveTracksCmd( 0, position, rows ) );
217 return true;
221 void
222 Model::insertTracks( int row, QueryMaker *qm )
224 qm->startTrackQuery();
225 connect( qm, SIGNAL( queryDone() ), SLOT( queryDone() ) );
226 connect( qm, SIGNAL( newResultReady( QString, Meta::TrackList ) ), SLOT( newResultReady( QString, Meta::TrackList ) ) );
227 m_queryMap.insert( qm, row );
228 qm->run();
231 Qt::DropActions
232 Model::supportedDropActions() const
234 return Qt::CopyAction | Qt::MoveAction;
237 void
238 Model::trackFinished()
240 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
241 return;
242 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
243 track->finishedPlaying( 1.0 ); //TODO: get correct value for parameter
244 m_advancer->advanceTrack();
247 void
248 Model::play( const QModelIndex& index )
250 play( index.row() );
253 void
254 Model::play( int row )
257 setActiveRow( row );
258 EngineController::instance()->play( m_items[ m_activeRow ]->track() );
261 void
262 Model::playlistRepeatMode( int item )
264 if( item == 0 )
265 playModeChanged( Playlist::Standard );
266 else //for now just turn on repeat if anything but "off" is clicked
267 playModeChanged( Playlist::Repeat );
270 void
271 Model::next()
273 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
274 return;
275 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
276 track->finishedPlaying( 0.5 ); //TODO: get correct value for parameter
277 m_advancer->userAdvanceTrack();
280 void
281 Model::back()
283 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
284 return;
285 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
286 track->finishedPlaying( 0.5 ); //TODO: get correct value for parameter
287 m_advancer->recedeTrack();
290 void
291 Model::playCurrentTrack()
293 int selected = m_activeRow;
294 if( selected < 0 || selected >= m_items.size() )
296 //play first track if there are tracks in the playlist
297 if( m_items.size() )
298 selected = 0;
299 else
300 return;
302 play( selected );
306 QString
307 Model::prettyColumnName( Column index ) //static
309 switch( index )
311 case Filename: return i18n( "Filename" );
312 case Title: return i18n( "Title" );
313 case Artist: return i18n( "Artist" );
314 case AlbumArtist:return i18n( "Album Artist");
315 case Composer: return i18n( "Composer" );
316 case Year: return i18n( "Year" );
317 case Album: return i18n( "Album" );
318 case DiscNumber: return i18n( "Disc Number" );
319 case TrackNumber:return i18n( "Track" );
320 case Bpm: return i18n( "BPM" );
321 case Genre: return i18n( "Genre" );
322 case Comment: return i18n( "Comment" );
323 case Directory: return i18n( "Directory" );
324 case Type: return i18n( "Type" );
325 case Length: return i18n( "Length" );
326 case Bitrate: return i18n( "Bitrate" );
327 case SampleRate: return i18n( "Sample Rate" );
328 case Score: return i18n( "Score" );
329 case Rating: return i18n( "Rating" );
330 case PlayCount: return i18n( "Play Count" );
331 case LastPlayed: return i18nc( "Column name", "Last Played" );
332 case Mood: return i18n( "Mood" );
333 case Filesize: return i18n( "File Size" );
334 default: return "This is a bug.";
339 void
340 Model::playModeChanged( int row )
342 delete m_advancer;
343 switch (row)
345 case Playlist::Standard:
346 m_advancer = new StandardTrackNavigator(this);
347 break;
348 case Playlist::Repeat:
349 m_advancer = new RepeatTrackNavigator(this);
350 break;
354 void
355 Model::setActiveRow( int row )
357 DEBUG_BLOCK
359 int max = qMax( row, m_activeRow );
360 int min = qMin( row, m_activeRow );
361 if( ( max - min ) == 1 )
362 emit dataChanged( createIndex( min, 0 ), createIndex( max, 0 ) );
363 else
365 emit dataChanged( createIndex( min, 0 ), createIndex( min, 0 ) );
366 emit dataChanged( createIndex( max, 0 ), createIndex( max, 0 ) );
368 debug() << "between " << min << " and " << max;
371 //make sure that the group containg this track is expanded
372 if ( m_albumGroups.contains( m_items[ row ]->track()->album() ) ) {
373 m_albumGroups[ m_items[ row ]->track()->album() ]->setCollapsed( row, false );
374 debug() << "Here";
375 emit( playlistGroupingChanged() );
378 m_activeRow = row;
381 void
382 Model::metadataChanged( Meta::Track *track )
384 DEBUG_BLOCK
385 const int size = m_items.size();
386 const Meta::TrackPtr needle = Meta::TrackPtr( track );
387 for( int i = 0; i < size; i++ )
389 if( m_items.at( i )->track() == needle )
391 debug() << "Track in playlist";
392 emit dataChanged( createIndex( i, 0 ), createIndex( i, 0 ) );
393 break;
397 #if 0
398 int index = m_tracks.indexOf( Meta::TrackPtr( track ), 0 );
399 if( index != -1 )
400 emit dataChanged( createIndex( index, 0 ), createIndex( index, 0 ) );
401 #endif
404 void
405 Model::metadataChanged(Meta::Album * album)
407 DEBUG_BLOCK
408 //process each track
409 TrackList tracks = album->tracks();
410 foreach( TrackPtr track, tracks ) {
411 metadataChanged( track.data() );
416 void
417 Model::clear()
419 if( m_items.size() < 1 )
420 return;
421 removeRows( 0, m_items.size() );
422 m_albumGroups.clear();
423 m_lastAddedTrackAlbum = AlbumPtr();
424 // m_activeRow = -1;
427 void
428 Model::insertOptioned( Meta::TrackList list, int options )
431 DEBUG_BLOCK
433 //TODO: we call insertOptioned on resume before the statusbar is fully created... We need a better way to handle this
434 if( list.isEmpty() ) {
435 // Amarok::StatusBar::instance()->shortMessage( i18n("Attempted to insert nothing into playlist.") );
436 return; // don't add empty items
440 if( options & Unique )
442 int alreadyOnPlaylist = 0;
443 for( int i = 0; i < list.size(); ++i )
446 Item* item;
447 foreach( item, m_items )
449 if( item->track() == list.at( i ) )
451 list.removeAt( i );
452 alreadyOnPlaylist++;
453 break;
458 if ( alreadyOnPlaylist )
459 Amarok::StatusBar::instance()->shortMessage( i18np("One track was already in the playlist, so it was not added.", "%1 tracks were already in the playlist, so they were not added.", alreadyOnPlaylist ) );
462 int orgCount = rowCount(); //needed because recursion messes up counting
463 bool playlistAdded = false;
465 //HACK! Check if any of the incomming tracks is really a playlist. Warning, this can get highly recursive
466 for( int i = 0; i < list.size(); ++i )
468 if ( m_playlistHandler->isPlaylist( list.at( i )->url() ) ) {
469 playlistAdded = true;
470 m_playlistHandler->load( list.takeAt( i )->url() );
475 int firstItemAdded = -1;
476 if( options & Replace )
478 clear();
479 firstItemAdded = 0;
480 insertTracks( 0, list );
482 else if( options & Append )
484 if ( playlistAdded )
485 firstItemAdded = orgCount;
486 else
487 firstItemAdded = rowCount();
488 insertTracks( firstItemAdded, list );
489 if( orgCount == 0 && (EngineController::engine()->state() != Engine::Playing ) )
490 play( firstItemAdded );
492 else if( options & Queue )
494 //TODO implement queue
496 if( options & DirectPlay )
498 if ( rowCount() > firstItemAdded )
499 play( firstItemAdded );
501 else if( ( options & StartPlay ) && ( EngineController::engine()->state() != Engine::Playing ) && ( rowCount() != 0 ) )
503 play( firstItemAdded );
505 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
508 void
509 Model::insertOptioned( Meta::TrackPtr track, int options )
511 Meta::TrackList list;
512 list.append( track );
513 insertOptioned( list, options );
516 void
517 Model::insertOptioned( QueryMaker *qm, int options )
519 qm->startTrackQuery();
520 connect( qm, SIGNAL( queryDone() ), SLOT( queryDone() ) );
521 connect( qm, SIGNAL( newResultReady( QString, Meta::TrackList ) ), SLOT( newResultReady( QString, Meta::TrackList ) ) );
522 m_optionedQueryMap.insert( qm, options );
523 qm->run();
526 void
527 Model::insertMedia( KUrl::List list, int options )
529 KUrl url;
530 Meta::TrackList trackList;
531 foreach( url, list )
533 Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( url );
534 if( track )
535 trackList.push_back( track );
537 if( trackList.isEmpty() )
538 debug() << "Attempted to insert nothing into the playlist!";
540 insertOptioned( trackList, options );
543 bool
544 Model::saveM3U( const QString &path ) const
546 Meta::TrackList tl;
547 foreach( Item* item, itemList() )
548 tl << item->track();
549 if( m_playlistHandler->save( tl, path ) )
550 return true;
551 return false;
554 Qt::ItemFlags
555 Model::flags(const QModelIndex &index) const
557 if( index.isValid() )
559 return ( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled |
560 Qt::ItemIsDragEnabled | Qt::ItemIsSelectable );
562 return Qt::ItemIsDropEnabled;
565 QStringList
566 Model::mimeTypes() const //reimplemented
568 QStringList ret = QAbstractListModel::mimeTypes();
569 ret << AmarokMimeData::TRACK_MIME;
570 return ret;
573 QMimeData*
574 Model::mimeData( const QModelIndexList &indexes ) const //reimplemented
576 AmarokMimeData* mime = new AmarokMimeData();
577 Meta::TrackList selectedTracks;
579 foreach( QModelIndex it, indexes )
580 selectedTracks << m_items.at( it.row() )->track();
582 mime->setTracks( selectedTracks );
583 return mime;
586 bool
587 Model::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) //reimplemented
589 Q_UNUSED( column ); Q_UNUSED( parent );
590 DEBUG_BLOCK
592 if( action == Qt::IgnoreAction )
593 return true;
595 if( data->hasFormat( AmarokMimeData::TRACK_MIME ) )
597 debug() << "Found track mime type";
599 const AmarokMimeData* trackListDrag = dynamic_cast<const AmarokMimeData*>( data );
600 if( trackListDrag )
602 if( row < 0 )
604 debug() << "Inserting at row: " << row << " so we're appending to the list.";
605 insertOptioned( trackListDrag->tracks(), Playlist::Append );
607 else
609 debug() << "Inserting at row: " << row <<" so its inserted correctly.";
610 insertTracks( row, trackListDrag->tracks() );
612 return true;
615 else if( data->hasUrls() )
617 //probably a drop from an external source
618 debug() << "Drop from external source";
619 QList<QUrl> urls = data->urls();
620 Meta::TrackList tracks;
621 foreach( QUrl url, urls )
623 Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( KUrl( url ) );
624 if( track )
625 tracks.append( track );
627 if( !tracks.isEmpty() )
628 insertOptioned( tracks, Playlist::Append );
629 return true;
631 return false;
634 ////////////
635 //Private Methods
636 ///////////
639 void
640 Model::insertTracksCommand( int row, TrackList list )
642 DEBUG_BLOCK
643 debug() << "inserting... " << row << ' ' << list.count();
644 if( !list.size() )
645 return;
647 beginInsertRows( QModelIndex(), row, row + list.size() - 1 );
648 int i = 0;
649 foreach( TrackPtr track , list )
651 if( track )
653 track->subscribe( this );
654 if( track->album() )
655 track->album()->subscribe( this );
657 m_items.insert( row + i, new Item( track ) );
658 i++;
662 //we need to regroup everything below this point as all the index changes
663 regroupAlbums( row, m_items.count() );
665 endInsertRows();
666 //push up the active row if needed
667 if( m_activeRow > row )
669 int oldActiveRow = m_activeRow;
670 m_activeRow += list.size();
671 Q_UNUSED( oldActiveRow );
672 //dataChanged( createIndex( oldActiveRow, 0 ), createIndex( oldActiveRow, columnCount() -1 ) );
673 //dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() -1 ) );
675 dataChanged( createIndex( row, 0 ), createIndex( rowCount() - 1, 0 ) );
676 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
679 emit playlistCountChanged( rowCount() );
683 TrackList
684 Model::removeRowsCommand( int position, int rows )
686 DEBUG_BLOCK
688 beginRemoveRows( QModelIndex(), position, position + rows - 1 );
689 // TrackList::iterator start = m_tracks.begin() + position;
690 // TrackList::iterator end = start + rows;
691 // m_tracks.erase( start, end );
692 TrackList ret;
693 for( int i = position; i < position + rows; i++ )
695 Item* item = m_items.takeAt( position ); //take at position, row times
696 item->track()->unsubscribe( this );
697 if( item->track()->album() )
698 item->track()->album()->unsubscribe( this );
699 ret.push_back( item->track() );
700 delete item;
704 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
706 //update m_activeRow
707 bool activeRowChanged = true;
708 bool oldActiveRow = m_activeRow;
709 if( m_activeRow >= position && m_activeRow < ( position + rows ) )
710 m_activeRow = -1;
711 else if( m_activeRow >= position )
712 m_activeRow = m_activeRow - position;
713 else
714 activeRowChanged = false;
715 if( activeRowChanged )
717 dataChanged( createIndex( oldActiveRow, 0 ), createIndex( oldActiveRow, columnCount() -1 ) );
718 dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() -1 ) );
720 dataChanged( createIndex( position, 0 ), createIndex( rowCount(), 0 ) );
722 //we need to regroup everything below this point as all the index changes
723 //also, use the count before the rows was removed to make sure all groups are deleted
724 regroupAlbums( position, rows, OffsetAfter, 0 - rows );
726 endRemoveRows();
728 emit playlistCountChanged( rowCount() );
729 return ret;
732 void
733 Model::queryDone() //Slot
735 QueryMaker *qm = dynamic_cast<QueryMaker*>( sender() );
736 if( qm )
738 m_queryMap.remove( qm );
739 qm->deleteLater();
743 void
744 Model::newResultReady( const QString &collectionId, const Meta::TrackList &tracks ) //Slot
746 Meta::TrackList ourTracks = tracks;
747 qStableSort( ourTracks.begin(), ourTracks.end(), Amarok::trackNumberLessThan );
748 Q_UNUSED( collectionId )
749 QueryMaker *qm = dynamic_cast<QueryMaker*>( sender() );
750 if( qm )
752 //requires better handling of queries which return multiple results
753 if( m_queryMap.contains( qm ) )
754 insertTracks( m_queryMap.value( qm ), ourTracks );
755 else if( m_optionedQueryMap.contains( qm ) )
756 insertOptioned( ourTracks, m_optionedQueryMap.value( qm ) );
760 QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
763 Q_UNUSED( orientation );
765 if ( role != Qt::DisplayRole )
766 return QVariant();
768 switch ( section )
770 case 0:
771 return "title";
772 case 1:
773 return "album";
774 case 2:
775 return "artist";
776 default:
777 return QVariant();
783 void Model::moveRow(int row, int to)
786 m_items.move( row, to );
788 int offset = -1;
789 if ( to < row )
790 offset = 1;
792 regroupAlbums( QMIN( row, to) , QMAX( row, to ), OffsetBetween, offset );
797 void Model::regroupAlbums( int firstRow, int lastRow, OffsetMode offsetMode, int offset )
799 DEBUG_BLOCK
801 //debug() << "first row: " << firstRow << ", last row: " << lastRow;
804 int area1Start = -1;
805 int area1End = -1;
806 int area2Start = -1;
807 int area2End = -1;
809 int aboveFirst;
810 int belowLast;
812 aboveFirst = firstRow - 1;
813 if ( aboveFirst < 0 ) aboveFirst = 0;
815 belowLast = lastRow + 1;
816 if ( belowLast > ( m_items.count() - 1 ) ) belowLast = m_items.count() - 1;
818 debug() << "aboveFirst: " << aboveFirst << ", belowLast: " << belowLast;
820 //delete affected groups
822 QMapIterator< Meta::AlbumPtr, AlbumGroup *> itt(m_albumGroups);
823 while (itt.hasNext()) {
825 itt.next();
826 AlbumGroup * group = itt.value();
828 bool removeGroupAboveFirstRow = false;
829 bool removeGroupBelowFirstRow = false;
830 bool removeGroupAboveLastRow = false;
831 bool removeGroupBelowLastRow = false;
833 int temp = group->firstInGroup( aboveFirst );
834 if ( temp != -1 ) {
835 debug() << "--3";
836 area1Start = temp;
837 removeGroupAboveFirstRow = true;
840 temp = group->lastInGroup( firstRow + 1 );
841 if ( temp != -1 ) {
842 debug() << "--4";
843 area1End = temp;
844 removeGroupBelowFirstRow = true;
847 temp = group->firstInGroup( lastRow - 1 );
848 if ( temp != -1 ) {
849 debug() << "--5";
850 area2Start = temp;
851 removeGroupAboveLastRow = true;
854 temp = group->lastInGroup( belowLast );
855 if ( temp != -1 ) {
856 debug() << "--6";
857 area2End = temp;
858 removeGroupBelowLastRow = true;
861 if ( removeGroupAboveFirstRow )
862 { group->removeGroup( aboveFirst ); debug() << "removing group at row: " << aboveFirst; }
864 if ( removeGroupBelowFirstRow )
865 { group->removeGroup( firstRow + 1 ); debug() << "removing group at row: " << firstRow + 1; }
867 if ( removeGroupAboveLastRow )
868 { group->removeGroup( lastRow -1 ); debug() << "removing group at row: " << lastRow - 1; }
869 if ( removeGroupBelowLastRow )
870 { group->removeGroup( belowLast ); debug() << "removing group at row: " << belowLast; }
872 group->removeGroup( firstRow );
873 group->removeGroup( lastRow );
875 //if there is nothing left in album group, discard it.
877 if ( group->subgroupCount() == 0 ) {
878 //debug() << "empty...";
879 delete m_albumGroups.take( itt.key() );
890 if ( m_albumGroups.count() == 0 ) { // start from scratch
891 debug() << "--1";
892 area1Start = 0;
893 area1End = m_items.count();
894 area2Start = area1Start; // just to skip second pass
899 if ( area1Start == -1 ) {
900 //debug() << "--2";
901 area1Start = aboveFirst;
902 area1End = belowLast;
903 area2Start = area1Start;
906 if ( area1End == -1 )
907 area1End = belowLast;
909 if ( area1Start < 0 )
910 area1Start = 0;
911 if ( area1End > ( m_items.count() - 1 ) )
912 area1End = m_items.count() - 1;
914 if ( area2Start < 0 )
915 area2Start = 0;
916 if ( area2End > ( m_items.count() - 1 ) )
917 area2End = m_items.count() - 1;
920 // regroup the two affected areas
922 if ( area1Start == area2Start ) //merge areas
923 area1End = QMAX( area1End, area2End );
924 else if ( area1End == area2End ) {//merge areas
925 area1Start = QMIN( area1Start, area2Start );
926 area2Start = area1Start;
930 debug() << "area1Start: " << area1Start << ", area1End: " << area1End;
931 debug() << "area2Start: " << area2Start << ", area2End: " << area2End;
933 //debug stuff
934 debug() << "Groups before:";
935 foreach( AlbumGroup * ag, m_albumGroups)
936 ag->printGroupRows();
940 if ( offsetMode != OffsetNone ) {
942 int offsetFrom;
943 int offsetTo;
945 if ( offsetMode == OffsetBetween ) {
946 offsetFrom = area1End + 1;
947 offsetTo = area2Start - 1;
948 // last but not least, change area1end and area2start to match new offsets
949 if ( area1End != area2End ) {
950 area1End += offset;
951 area2Start += offset;
952 debug() << "area1Start: " << area1Start << ", area1End: " << area1End;
953 debug() << "area2Start: " << area2Start << ", area2End: " << area2End;
955 } else {
956 offsetFrom = lastRow;
957 offsetTo = ( m_items.count() - offset ) + 1;
960 QMapIterator< Meta::AlbumPtr, AlbumGroup *> itt(m_albumGroups);
961 while (itt.hasNext()) {
963 itt.next();
964 AlbumGroup * group = itt.value();
965 group->offsetBetween( offsetFrom, offsetTo, offset);
972 //debug stuff
973 debug() << "Groups after offsetting:";
974 foreach( AlbumGroup * ag, m_albumGroups)
975 ag->printGroupRows();
978 int i;
979 for ( i = area1Start; ( i <= area1End ) && ( i < m_items.count() ); i++ )
982 //debug() << "i: " << i;
984 TrackPtr track = m_items.at( i )->track();
986 if ( !track->album() )
987 continue;
989 if ( m_albumGroups.contains( track->album() ) ) {
990 m_albumGroups[ track->album() ]->addRow( i );
991 } else {
992 //debug() << "Create new group for album " << track->album()->name() ;
993 AlbumGroup * newGroup = new AlbumGroup();
994 newGroup->addRow( i );
995 m_albumGroups.insert( track->album(), newGroup );
999 if ( ( area1Start == area2Start ) || area2Start == -1 ) {
1001 debug() << "Groups after:";
1002 foreach( AlbumGroup * ag, m_albumGroups)
1003 ag->printGroupRows();
1004 return;
1007 for ( i = area2Start; i <= area2End; i++ )
1010 //debug() << "i: " << i;
1012 TrackPtr track = m_items.at( i )->track();
1014 if ( !track->album() )
1015 continue;
1017 if ( m_albumGroups.contains( track->album() ) ) {
1018 m_albumGroups[ track->album() ]->addRow( i );
1019 } else {
1020 AlbumGroup * newGroup = new AlbumGroup();
1021 newGroup->addRow( i );
1022 m_albumGroups.insert( track->album(), newGroup );
1026 debug() << "Groups after:";
1027 foreach( AlbumGroup *ag, m_albumGroups)
1028 ag->printGroupRows();
1032 //make sure that a group containg playing track is expanded
1033 if ( m_activeRow != -1 ){
1034 if ( m_albumGroups.contains( m_items[ m_activeRow ]->track()->album() ) ) {
1035 m_albumGroups[ m_items[ m_activeRow ]->track()->album() ]->setCollapsed( m_activeRow, false );
1036 debug() << "Here";
1037 emit( playlistGroupingChanged() );
1041 //reset();
1045 void Playlist::Model::setCollapsed(int row, bool collapsed)
1047 //DEBUG_BLOCK
1048 m_albumGroups[ m_items[ row ]->track()->album() ]->setCollapsed( row, collapsed );
1049 emit( playlistGroupingChanged() );
1054 namespace The {
1055 Playlist::Model* playlistModel() { return Playlist::Model::s_instance; }
1066 #include "PlaylistModel.moc"