Revert previous commit, was incorrect
[amarok.git] / src / playlist / PlaylistModel.cpp
blob198b599f4962db8f4c31bb1688a9cdf05ee3defc
1 /***************************************************************************
2 * copyright : (C) 2007 Ian Monroe <ian@monroe.nu>
3 * : (C) 2007 Nikolaj Hald Nielsen <nhnFreespirit@gmail.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License or (at your option) version 3 or any later version
8 * accepted by the membership of KDE e.V. (or its successor approved
9 * by the membership of KDE e.V.), which shall act as a proxy
10 * defined in Section 14 of version 3 of the license.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
22 #include "PlaylistModel.h"
24 #include "amarok.h"
25 #include "amarokconfig.h"
26 #include "AmarokMimeData.h"
27 #include "debug.h"
28 #include "enginecontroller.h"
29 #include "PlaylistItem.h"
30 #include "PlaylistGraphicsView.h"
31 #include "RepeatTrackNavigator.h"
32 #include "StandardTrackNavigator.h"
33 #include "ContextStatusBar.h"
34 #include "TheInstances.h"
35 #include "UndoCommands.h"
37 #include "collection/BlockingQuery.h"
38 #include "collection/Collection.h"
39 #include "collection/CollectionManager.h"
40 #include "collection/QueryMaker.h"
41 #include "meta/lastfm/LastFmMeta.h"
43 #include <QAction>
44 #include <QStringList>
45 #include <QUndoStack>
47 #include <KIcon>
48 #include <KUrl>
50 using namespace Playlist;
51 using namespace Meta;
53 namespace Amarok
55 // Sorting of a tracklist.
56 bool trackNumberLessThan( Meta::TrackPtr left, Meta::TrackPtr right )
58 if( left->album() == right->album() )
59 return left->trackNumber() < right->trackNumber();
61 else if( left->artist() == right->artist() )
62 return QString::localeAwareCompare( left->album()->prettyName(), right->album()->prettyName() ) < 0;
63 else // compare artists alphabetically
64 return QString::localeAwareCompare( left->artist()->prettyName(), right->artist()->prettyName() ) < 0;
68 Model *Model::s_instance = 0;
70 Model::Model( QObject* parent )
71 : QAbstractListModel( parent )
72 , m_activeRow( -1 )
73 , m_advancer( new StandardTrackNavigator( this ) )
74 , m_undoStack( new QUndoStack( this ) )
75 , m_playlistHandler ( new PlaylistHandler )
77 connect( EngineController::instance(), SIGNAL( orderNext( bool ) ), this, SLOT( trackFinished() ), Qt::QueuedConnection );
78 connect( EngineController::instance(), SIGNAL( orderCurrent() ), this, SLOT( playCurrentTrack() ), Qt::QueuedConnection );
79 s_instance = this;
82 void
83 Model::init()
86 KActionCollection* ac = Amarok::actionCollection();
87 QAction* undoButton = m_undoStack->createUndoAction( this, i18n("Undo") );
88 undoButton->setIcon( KIcon( Amarok::icon( "undo" ) ) );
89 ac->addAction("playlist_undo", undoButton);
90 QAction* redoButton = m_undoStack->createRedoAction( this, i18n("Redo") );
91 ac->addAction("playlist_redo", redoButton);
92 redoButton->setIcon( KIcon( Amarok::icon( "redo" ) ) );
95 Model::~Model()
97 if( AmarokConfig::savePlaylist() )
99 Meta::TrackList list;
100 foreach( Item* item, itemList() )
102 list << item->track();
104 m_playlistHandler->save( list, defaultPlaylistPath() );
106 delete m_advancer;
107 delete m_playlistHandler;
111 Model::rowCount( const QModelIndex& ) const
113 return m_items.size();
116 QVariant
117 Model::data( const QModelIndex& index, int role ) const
119 int row = index.row();
120 /*if( ( role == Qt::FontRole) && ( row == m_activeRow ) )
122 QFont original;
123 original.setBold( true );
124 return original;
126 else*/
127 if( role == ItemRole && ( row != -1 ) )
128 return QVariant::fromValue( m_items.at( row ) );
130 else if( role == ActiveTrackRole )
131 return ( row == m_activeRow );
133 else if( role == TrackRole && ( row != -1 ) && m_items.at( row )->track() )
134 return QVariant::fromValue( m_items.at( row )->track() );
136 else if ( role == GroupRole )
138 TrackPtr track = m_items.at( row )->track();
140 if ( !track->album() )
141 return None; // no albm set
142 else if ( !m_albumGroups.contains( track->album()->prettyName() ) )
143 return None; // no group for this album, should never happen...
145 AlbumGroup * albumGroup = m_albumGroups.value( track->album()->prettyName() );
146 return albumGroup->groupMode( row );
150 else if ( role == GroupedTracksRole )
152 TrackPtr track = m_items.at( row )->track();
153 AlbumGroup * albumGroup = m_albumGroups.value( track->album()->prettyName() );
154 return albumGroup->elementsInGroup( row );
157 else if ( role == GroupedAlternateRole )
159 TrackPtr track = m_items.at( row )->track();
160 AlbumGroup * albumGroup = m_albumGroups.value( track->album()->prettyName() );
161 if( albumGroup )
162 return albumGroup->alternate( row );
163 return true;
166 else if ( role == GroupedCollapsibleRole )
168 TrackPtr track = m_items.at( row )->track();
169 AlbumGroup * albumGroup = m_albumGroups.value( track->album()->prettyName() );
170 //we cannot collapse the group that contains the currently selected track.
171 return ( albumGroup->firstInGroup( m_activeRow ) == -1 );
175 else if( role == Qt::DisplayRole && row != -1 )
177 switch( index.column() )
179 case 0:
180 return m_items.at( row )->track()->name();
181 case 1:
182 if ( m_items.at( row )->track()->album() )
183 return m_items.at( row )->track()->album()->name();
184 else
185 return "";
186 case 2:
187 if ( m_items.at( row )->track()->artist() )
188 return m_items.at( row )->track()->artist()->name();
189 else
190 return "";
193 // else
194 return QVariant();
195 /* switch( role )
197 case AlbumArtist: return track->album()->albumArtist()->name();
198 case Album: return track->album()->name();
199 case Artist: return track->artist()->name();
200 case Bitrate: return track->bitrate();
201 case Composer: return track->composer()->name();
202 case CoverImage: return track->album()->image( 50 );
203 case Comment: return track->comment();
204 case DiscNumber: return track->discNumber();
205 case Filesize: return track->filesize();
206 case Genre: return track->genre()->name();
207 case Length: return track->length();
208 case Rating: return track->rating();
209 case Score: return track->score();
210 case Title: return track->name();
211 case TrackNumber: return track->trackNumber();
212 case Year: return track->year()->name().toInt();
213 default: return QVariant();
214 } */
217 // void
218 // insertColumns( int column, Column type )
219 // {
220 // beginInsertColumns( QModelIndex(), column, column + 1 );
221 // m_columns.insert( column, type );
222 // endInsertColumns();
223 // return true;
224 // }
226 void
227 Model::insertTrack( int row, TrackPtr track )
229 DEBUG_BLOCK
230 TrackList list;
231 list.append( track );
232 insertTracks( row, list );
236 void
237 Model::insertTracks( int row, TrackList tracks )
239 m_undoStack->push( new AddTracksCmd( 0, row, tracks ) );
242 bool
243 Model::removeRows( int position, int rows, const QModelIndex& /*parent*/ )
245 m_undoStack->push( new RemoveTracksCmd( 0, position, rows ) );
246 return true;
250 void
251 Model::insertTracks( int row, QueryMaker *qm )
253 qm->startTrackQuery();
254 connect( qm, SIGNAL( queryDone() ), SLOT( queryDone() ) );
255 connect( qm, SIGNAL( newResultReady( QString, Meta::TrackList ) ), SLOT( newResultReady( QString, Meta::TrackList ) ) );
256 m_queryMap.insert( qm, row );
257 qm->run();
260 Qt::DropActions
261 Model::supportedDropActions() const
263 return Qt::CopyAction | Qt::MoveAction;
266 void
267 Model::trackFinished()
269 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
270 return;
271 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
272 track->finishedPlaying( 1.0 ); //TODO: get correct value for parameter
273 m_advancer->advanceTrack();
276 void
277 Model::play( const QModelIndex& index )
279 play( index.row() );
282 void
283 Model::play( int row )
286 setActiveRow( row );
287 EngineController::instance()->play( m_items[ m_activeRow ]->track() );
290 void
291 Model::playlistRepeatMode( int item )
293 if( item == 0 )
294 playModeChanged( Playlist::Standard );
295 else //for now just turn on repeat if anything but "off" is clicked
296 playModeChanged( Playlist::Repeat );
299 void
300 Model::next()
302 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
303 return;
304 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
305 track->finishedPlaying( 0.5 ); //TODO: get correct value for parameter
306 m_advancer->userAdvanceTrack();
309 void
310 Model::back()
312 if( m_activeRow < 0 || m_activeRow >= m_items.size() )
313 return;
314 Meta::TrackPtr track = m_items.at( m_activeRow )->track();
315 track->finishedPlaying( 0.5 ); //TODO: get correct value for parameter
316 m_advancer->recedeTrack();
319 void
320 Model::playCurrentTrack()
322 int selected = m_activeRow;
323 if( selected < 0 || selected >= m_items.size() )
325 //play first track if there are tracks in the playlist
326 if( m_items.size() )
327 selected = 0;
328 else
329 return;
331 play( selected );
335 QString
336 Model::prettyColumnName( Column index ) //static
338 switch( index )
340 case Filename: return i18n( "Filename" );
341 case Title: return i18n( "Title" );
342 case Artist: return i18n( "Artist" );
343 case AlbumArtist:return i18n( "Album Artist");
344 case Composer: return i18n( "Composer" );
345 case Year: return i18n( "Year" );
346 case Album: return i18n( "Album" );
347 case DiscNumber: return i18n( "Disc Number" );
348 case TrackNumber:return i18n( "Track" );
349 case Bpm: return i18n( "BPM" );
350 case Genre: return i18n( "Genre" );
351 case Comment: return i18n( "Comment" );
352 case Directory: return i18n( "Directory" );
353 case Type: return i18n( "Type" );
354 case Length: return i18n( "Length" );
355 case Bitrate: return i18n( "Bitrate" );
356 case SampleRate: return i18n( "Sample Rate" );
357 case Score: return i18n( "Score" );
358 case Rating: return i18n( "Rating" );
359 case PlayCount: return i18n( "Play Count" );
360 case LastPlayed: return i18nc( "Column name", "Last Played" );
361 case Mood: return i18n( "Mood" );
362 case Filesize: return i18n( "File Size" );
363 default: return "This is a bug.";
368 void
369 Model::playModeChanged( int row )
371 delete m_advancer;
372 switch (row)
374 case Playlist::Standard:
375 m_advancer = new StandardTrackNavigator(this);
376 break;
377 case Playlist::Repeat:
378 m_advancer = new RepeatTrackNavigator(this);
379 break;
383 void
384 Model::setActiveRow( int row )
386 DEBUG_BLOCK
388 int max = qMax( row, m_activeRow );
389 int min = qMin( row, m_activeRow );
390 if( ( max - min ) == 1 )
391 emit dataChanged( createIndex( min, 0 ), createIndex( max, 0 ) );
392 else
394 emit dataChanged( createIndex( min, 0 ), createIndex( min, 0 ) );
395 emit dataChanged( createIndex( max, 0 ), createIndex( max, 0 ) );
397 debug() << "between " << min << " and " << max;
399 m_activeRow = row;
401 //make sure that the group containg this track is expanded
403 //not all tracks have a valid album ( radio stations for instance... )
404 QString albumName;
405 if ( !m_items[ row ]->track()->album().isNull() ) {
406 albumName = m_items[ row ]->track()->album()->prettyName();
409 if( m_albumGroups.contains( albumName ) && !albumName.isEmpty() )
411 m_albumGroups[ albumName ]->setCollapsed( row, false );
412 debug() << "Here";
413 emit( playlistGroupingChanged() );
418 void
419 Model::metadataChanged( Meta::Track *track )
421 DEBUG_BLOCK
422 const int size = m_items.size();
423 const Meta::TrackPtr needle = Meta::TrackPtr( track );
424 for( int i = 0; i < size; i++ )
426 if( m_items.at( i )->track() == needle )
428 debug() << "Track in playlist";
429 emit dataChanged( createIndex( i, 0 ), createIndex( i, 0 ) );
430 break;
434 #if 0
435 int index = m_tracks.indexOf( Meta::TrackPtr( track ), 0 );
436 if( index != -1 )
437 emit dataChanged( createIndex( index, 0 ), createIndex( index, 0 ) );
438 #endif
441 void
442 Model::metadataChanged(Meta::Album * album)
444 DEBUG_BLOCK
445 //process each track
446 TrackList tracks = album->tracks();
447 foreach( TrackPtr track, tracks ) {
448 metadataChanged( track.data() );
453 void
454 Model::clear()
456 if( m_items.size() < 1 )
457 return;
458 removeRows( 0, m_items.size() );
459 m_albumGroups.clear();
460 m_lastAddedTrackAlbum = AlbumPtr();
461 The::playlistView()->scene()->setSceneRect( The::playlistView()->scene()->itemsBoundingRect() );
462 // m_activeRow = -1;
465 void
466 Model::insertOptioned( Meta::TrackList list, int options )
469 DEBUG_BLOCK
471 //TODO: we call insertOptioned on resume before the statusbar is fully created... We need a better way to handle this
472 if( list.isEmpty() ) {
473 // Amarok::ContextStatusBar::instance()->shortMessage( i18n("Attempted to insert nothing into playlist.") );
474 return; // don't add empty items
478 if( options & Unique )
480 int alreadyOnPlaylist = 0;
481 for( int i = 0; i < list.size(); ++i )
484 Item* item;
485 foreach( item, m_items )
487 if( item->track() == list.at( i ) )
489 list.removeAt( i );
490 alreadyOnPlaylist++;
491 break;
496 if ( alreadyOnPlaylist )
497 Amarok::ContextStatusBar::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 ) );
500 int orgCount = rowCount(); //needed because recursion messes up counting
501 bool playlistAdded = false;
503 //HACK! Check if any of the incomming tracks is really a playlist. Warning, this can get highly recursive
504 for( int i = 0; i < list.size(); ++i )
506 if ( m_playlistHandler->isPlaylist( list.at( i )->url() ) ) {
507 playlistAdded = true;
508 m_playlistHandler->load( list.takeAt( i )->url() );
512 //fix crash when list is empty
513 if ( list.isEmpty() )
514 return;
517 int firstItemAdded = -1;
518 if( options & Replace )
520 clear();
521 firstItemAdded = 0;
522 insertTracks( 0, list );
524 else if( options & Append )
526 if ( playlistAdded )
527 firstItemAdded = orgCount;
528 else
529 firstItemAdded = rowCount();
530 insertTracks( firstItemAdded, list );
531 if( orgCount == 0 && (EngineController::engine()->state() != Engine::Playing ) )
532 play( firstItemAdded );
534 else if( options & Queue )
536 //TODO implement queue
538 if( options & DirectPlay )
540 if ( rowCount() > firstItemAdded )
541 play( firstItemAdded );
543 else if( ( options & StartPlay ) && ( EngineController::engine()->state() != Engine::Playing ) && ( rowCount() != 0 ) )
545 play( firstItemAdded );
547 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
548 The::playlistView()->scene()->setSceneRect( The::playlistView()->scene()->itemsBoundingRect() );
551 void
552 Model::insertOptioned( Meta::TrackPtr track, int options )
554 Meta::TrackList list;
555 list.append( track );
556 insertOptioned( list, options );
559 void
560 Model::insertOptioned( QueryMaker *qm, int options )
562 qm->startTrackQuery();
563 connect( qm, SIGNAL( queryDone() ), SLOT( queryDone() ) );
564 connect( qm, SIGNAL( newResultReady( QString, Meta::TrackList ) ), SLOT( newResultReady( QString, Meta::TrackList ) ) );
565 m_optionedQueryMap.insert( qm, options );
566 qm->run();
569 bool
570 Model::saveM3U( const QString &path ) const
572 Meta::TrackList tl;
573 foreach( Item* item, itemList() )
574 tl << item->track();
575 if( m_playlistHandler->save( tl, path ) )
576 return true;
577 return false;
580 Qt::ItemFlags
581 Model::flags(const QModelIndex &index) const
583 if( index.isValid() )
585 return ( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled |
586 Qt::ItemIsDragEnabled | Qt::ItemIsSelectable );
588 return Qt::ItemIsDropEnabled;
591 QStringList
592 Model::mimeTypes() const //reimplemented
594 QStringList ret = QAbstractListModel::mimeTypes();
595 ret << AmarokMimeData::TRACK_MIME;
596 return ret;
599 QMimeData*
600 Model::mimeData( const QModelIndexList &indexes ) const //reimplemented
602 AmarokMimeData* mime = new AmarokMimeData();
603 Meta::TrackList selectedTracks;
605 foreach( const QModelIndex &it, indexes )
606 selectedTracks << m_items.at( it.row() )->track();
608 mime->setTracks( selectedTracks );
609 return mime;
612 bool
613 Model::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) //reimplemented
615 Q_UNUSED( column ); Q_UNUSED( parent );
616 DEBUG_BLOCK
618 if( action == Qt::IgnoreAction )
619 return true;
621 if( data->hasFormat( AmarokMimeData::TRACK_MIME ) )
623 debug() << "Found track mime type";
625 const AmarokMimeData* trackListDrag = dynamic_cast<const AmarokMimeData*>( data );
626 if( trackListDrag )
628 if( row < 0 )
630 debug() << "Inserting at row: " << row << " so we're appending to the list.";
631 insertOptioned( trackListDrag->tracks(), Playlist::Append );
633 else
635 debug() << "Inserting at row: " << row <<" so its inserted correctly.";
636 insertTracks( row, trackListDrag->tracks() );
638 return true;
641 else if( data->hasUrls() )
643 //probably a drop from an external source
644 debug() << "Drop from external source";
645 QList<QUrl> urls = data->urls();
646 Meta::TrackList tracks;
647 foreach( const QUrl &url, urls )
649 Meta::TrackPtr track = CollectionManager::instance()->trackForUrl( KUrl( url ) );
650 if( track )
651 tracks.append( track );
653 if( !tracks.isEmpty() )
654 insertOptioned( tracks, Playlist::Append );
655 return true;
657 return false;
660 ////////////
661 //Private Methods
662 ///////////
665 void
666 Model::insertTracksCommand( int row, TrackList list )
668 DEBUG_BLOCK
669 debug() << "inserting... " << row << ' ' << list.count();
670 if( !list.size() )
671 return;
673 beginInsertRows( QModelIndex(), row, row + list.size() - 1 );
674 int i = 0;
675 foreach( TrackPtr track , list )
677 if( track )
679 track->subscribe( this );
680 if( track->album() )
681 track->album()->subscribe( this );
683 m_items.insert( row + i, new Item( track ) );
684 i++;
688 //we need to regroup everything below this point as all the index changes
689 regroupAlbums( row, m_items.count() );
691 endInsertRows();
692 //push up the active row if needed
693 if( m_activeRow > row )
695 int oldActiveRow = m_activeRow;
696 m_activeRow += list.size();
697 Q_UNUSED( oldActiveRow );
698 //dataChanged( createIndex( oldActiveRow, 0 ), createIndex( oldActiveRow, columnCount() -1 ) );
699 //dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() -1 ) );
701 dataChanged( createIndex( row, 0 ), createIndex( rowCount() - 1, 0 ) );
702 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
705 emit playlistCountChanged( rowCount() );
709 TrackList
710 Model::removeRowsCommand( int position, int rows )
712 DEBUG_BLOCK
714 beginRemoveRows( QModelIndex(), position, position + rows - 1 );
715 // TrackList::iterator start = m_tracks.begin() + position;
716 // TrackList::iterator end = start + rows;
717 // m_tracks.erase( start, end );
718 TrackList ret;
719 for( int i = position; i < position + rows; i++ )
721 Item* item = m_items.takeAt( position ); //take at position, row times
722 item->track()->unsubscribe( this );
723 if( item->track()->album() )
724 item->track()->album()->unsubscribe( this );
725 ret.push_back( item->track() );
726 delete item;
730 Amarok::actionCollection()->action( "playlist_clear" )->setEnabled( !m_items.isEmpty() );
732 //update m_activeRow
733 bool activeRowChanged = true;
734 bool oldActiveRow = m_activeRow;
735 if( m_activeRow >= position && m_activeRow < ( position + rows ) )
736 m_activeRow = -1;
737 else if( m_activeRow >= position )
738 m_activeRow = m_activeRow - position;
739 else
740 activeRowChanged = false;
741 if( activeRowChanged )
743 dataChanged( createIndex( oldActiveRow, 0 ), createIndex( oldActiveRow, columnCount() -1 ) );
744 dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() -1 ) );
746 dataChanged( createIndex( position, 0 ), createIndex( rowCount(), 0 ) );
748 //we need to regroup everything below this point as all the index changes
749 //also, use the count before the rows was removed to make sure all groups are deleted
750 regroupAlbums( position, rows, OffsetAfter, 0 - rows );
752 endRemoveRows();
754 emit playlistCountChanged( rowCount() );
755 return ret;
758 void
759 Model::queryDone() //Slot
761 QueryMaker *qm = dynamic_cast<QueryMaker*>( sender() );
762 if( qm )
764 m_queryMap.remove( qm );
765 qm->deleteLater();
769 void
770 Model::newResultReady( const QString &collectionId, const Meta::TrackList &tracks ) //Slot
772 Meta::TrackList ourTracks = tracks;
773 qStableSort( ourTracks.begin(), ourTracks.end(), Amarok::trackNumberLessThan );
774 Q_UNUSED( collectionId )
775 QueryMaker *qm = dynamic_cast<QueryMaker*>( sender() );
776 if( qm )
778 //requires better handling of queries which return multiple results
779 if( m_queryMap.contains( qm ) )
780 insertTracks( m_queryMap.value( qm ), ourTracks );
781 else if( m_optionedQueryMap.contains( qm ) )
782 insertOptioned( ourTracks, m_optionedQueryMap.value( qm ) );
786 QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
789 Q_UNUSED( orientation );
791 if ( role != Qt::DisplayRole )
792 return QVariant();
794 switch ( section )
796 case 0:
797 return "title";
798 case 1:
799 return "album";
800 case 2:
801 return "artist";
802 default:
803 return QVariant();
809 void Model::moveRow(int row, int to)
812 m_items.move( row, to );
814 int offset = -1;
815 if ( to < row )
816 offset = 1;
818 regroupAlbums( QMIN( row, to) , QMAX( row, to ), OffsetBetween, offset );
823 void Model::regroupAlbums( int firstRow, int lastRow, OffsetMode offsetMode, int offset )
825 DEBUG_BLOCK
827 //debug() << "first row: " << firstRow << ", last row: " << lastRow;
830 int area1Start = -1;
831 int area1End = -1;
832 int area2Start = -1;
833 int area2End = -1;
835 int aboveFirst;
836 int belowLast;
838 aboveFirst = firstRow - 1;
839 if ( aboveFirst < 0 ) aboveFirst = 0;
841 belowLast = lastRow + 1;
842 if ( belowLast > ( m_items.count() - 1 ) ) belowLast = m_items.count() - 1;
844 debug() << "aboveFirst: " << aboveFirst << ", belowLast: " << belowLast;
846 //delete affected groups
848 QMapIterator< QString, AlbumGroup *> itt(m_albumGroups);
849 while (itt.hasNext()) {
851 itt.next();
852 AlbumGroup * group = itt.value();
854 bool removeGroupAboveFirstRow = false;
855 bool removeGroupBelowFirstRow = false;
856 bool removeGroupAboveLastRow = false;
857 bool removeGroupBelowLastRow = false;
859 int temp = group->firstInGroup( aboveFirst );
860 if ( temp != -1 ) {
861 debug() << "--3";
862 area1Start = temp;
863 removeGroupAboveFirstRow = true;
866 temp = group->lastInGroup( firstRow + 1 );
867 if ( temp != -1 ) {
868 debug() << "--4";
869 area1End = temp;
870 removeGroupBelowFirstRow = true;
873 temp = group->firstInGroup( lastRow - 1 );
874 if ( temp != -1 ) {
875 debug() << "--5";
876 area2Start = temp;
877 removeGroupAboveLastRow = true;
880 temp = group->lastInGroup( belowLast );
881 if ( temp != -1 ) {
882 debug() << "--6";
883 area2End = temp;
884 removeGroupBelowLastRow = true;
887 if ( removeGroupAboveFirstRow )
888 { group->removeGroup( aboveFirst ); debug() << "removing group at row: " << aboveFirst; }
890 if ( removeGroupBelowFirstRow )
891 { group->removeGroup( firstRow + 1 ); debug() << "removing group at row: " << firstRow + 1; }
893 if ( removeGroupAboveLastRow )
894 { group->removeGroup( lastRow -1 ); debug() << "removing group at row: " << lastRow - 1; }
895 if ( removeGroupBelowLastRow )
896 { group->removeGroup( belowLast ); debug() << "removing group at row: " << belowLast; }
898 group->removeGroup( firstRow );
899 group->removeGroup( lastRow );
901 //if there is nothing left in album group, discard it.
903 if ( group->subgroupCount() == 0 ) {
904 //debug() << "empty...";
905 delete m_albumGroups.take( itt.key() );
916 if ( m_albumGroups.count() == 0 ) { // start from scratch
917 debug() << "--1";
918 area1Start = 0;
919 area1End = m_items.count();
920 area2Start = area1Start; // just to skip second pass
925 if ( area1Start == -1 ) {
926 //debug() << "--2";
927 area1Start = aboveFirst;
928 area1End = belowLast;
929 area2Start = area1Start;
932 if ( area1End == -1 )
933 area1End = belowLast;
935 if ( area1Start < 0 )
936 area1Start = 0;
937 if ( area1End > ( m_items.count() - 1 ) )
938 area1End = m_items.count() - 1;
940 if ( area2Start < 0 )
941 area2Start = 0;
942 if ( area2End > ( m_items.count() - 1 ) )
943 area2End = m_items.count() - 1;
946 // regroup the two affected areas
948 if ( area1Start == area2Start ) //merge areas
949 area1End = QMAX( area1End, area2End );
950 else if ( area1End == area2End ) {//merge areas
951 area1Start = QMIN( area1Start, area2Start );
952 area2Start = area1Start;
956 debug() << "area1Start: " << area1Start << ", area1End: " << area1End;
957 debug() << "area2Start: " << area2Start << ", area2End: " << area2End;
959 //debug stuff
960 debug() << "Groups before:";
961 foreach( AlbumGroup * ag, m_albumGroups)
962 ag->printGroupRows();
966 if ( offsetMode != OffsetNone ) {
968 int offsetFrom;
969 int offsetTo;
971 if ( offsetMode == OffsetBetween ) {
972 offsetFrom = area1End + 1;
973 offsetTo = area2Start - 1;
974 // last but not least, change area1end and area2start to match new offsets
975 if ( area1End != area2End ) {
976 area1End += offset;
977 area2Start += offset;
978 debug() << "area1Start: " << area1Start << ", area1End: " << area1End;
979 debug() << "area2Start: " << area2Start << ", area2End: " << area2End;
981 } else {
982 offsetFrom = lastRow;
983 offsetTo = ( m_items.count() - offset ) + 1;
986 QMapIterator< QString, AlbumGroup *> itt(m_albumGroups);
987 while (itt.hasNext()) {
989 itt.next();
990 AlbumGroup * group = itt.value();
991 group->offsetBetween( offsetFrom, offsetTo, offset);
998 //debug stuff
999 debug() << "Groups after offsetting:";
1000 foreach( AlbumGroup * ag, m_albumGroups)
1001 ag->printGroupRows();
1004 int i;
1005 for ( i = area1Start; ( i <= area1End ) && ( i < m_items.count() ); i++ )
1008 //debug() << "i: " << i;
1010 TrackPtr track = m_items.at( i )->track();
1012 if ( !track->album() )
1013 continue;
1015 QString albumName;
1017 //not all tracks have a valid album ( radio stations for instance... )
1018 if ( !track->album().isNull() ) {
1019 albumName = track->album()->prettyName();
1022 if ( m_albumGroups.contains( albumName ) && !albumName.isEmpty() ) {
1023 m_albumGroups[ albumName ]->addRow( i );
1024 } else {
1025 //debug() << "Create new group for album " << track->album()->name() ;
1026 AlbumGroup * newGroup = new AlbumGroup();
1027 newGroup->addRow( i );
1028 m_albumGroups.insert( albumName, newGroup );
1032 if ( ( area1Start == area2Start ) || area2Start == -1 ) {
1034 debug() << "Groups after:";
1035 foreach( AlbumGroup * ag, m_albumGroups)
1036 ag->printGroupRows();
1037 return;
1040 for ( i = area2Start; i <= area2End; i++ )
1043 //debug() << "i: " << i;
1045 TrackPtr track = m_items.at( i )->track();
1047 if ( !track->album() )
1048 continue;
1050 if ( m_albumGroups.contains( track->album()->prettyName() ) ) {
1051 m_albumGroups[ track->album()->prettyName() ]->addRow( i );
1052 } else {
1053 AlbumGroup * newGroup = new AlbumGroup();
1054 newGroup->addRow( i );
1055 m_albumGroups.insert( track->album()->prettyName(), newGroup );
1059 debug() << "Groups after:";
1060 foreach( AlbumGroup *ag, m_albumGroups)
1061 ag->printGroupRows();
1065 //make sure that a group containg playing track is expanded
1066 if ( m_activeRow != -1 ){
1067 if ( m_albumGroups.contains( m_items[ m_activeRow ]->track()->album()->prettyName() ) ) {
1068 m_albumGroups[ m_items[ m_activeRow ]->track()->album()->prettyName() ]->setCollapsed( m_activeRow, false );
1069 debug() << "Here";
1070 emit( playlistGroupingChanged() );
1074 //reset();
1078 void Playlist::Model::setCollapsed(int row, bool collapsed)
1080 //DEBUG_BLOCK
1081 m_albumGroups[ m_items[ row ]->track()->album()->prettyName() ]->setCollapsed( row, collapsed );
1082 emit( playlistGroupingChanged() );
1087 namespace The {
1088 Playlist::Model* playlistModel() { return Playlist::Model::s_instance; }
1099 #include "PlaylistModel.moc"